You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2019/02/25 02:23:08 UTC

[groovy] branch GROOVY_2_5_X updated: minor refactor: properly declare local variable

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new 2a41cab  minor refactor: properly declare local variable
2a41cab is described below

commit 2a41cabce6e02038560f91adc8980a57256df653
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon Feb 25 09:20:01 2019 +1000

    minor refactor: properly declare local variable
---
 .../groovy/beans/BindableASTTransformation.java    |   5 +-
 .../beans/ListenerListASTTransformation.groovy     |   8 +-
 .../groovy/beans/VetoableASTTransformation.java    |   5 +-
 .../groovy/transform/builder/DefaultStrategy.java  |   3 +-
 .../groovy/transform/builder/ExternalStrategy.java |   3 +-
 .../groovy/transform/tailrec/AstHelper.groovy      |   3 +-
 .../codehaus/groovy/ast/tools/GeneralUtils.java    |   6 +
 .../org/codehaus/groovy/classgen/EnumVisitor.java  | 890 +++++++++++----------
 .../EqualsAndHashCodeASTTransformation.java        | 688 ++++++++--------
 .../transform/ImmutableASTTransformation.java      |   9 +-
 .../groovy/transform/LazyASTTransformation.java    | 493 ++++++------
 .../transform/SortableASTTransformation.java       |   7 +-
 .../transform/ToStringASTTransformation.java       |   2 +-
 13 files changed, 1070 insertions(+), 1052 deletions(-)

diff --git a/src/main/groovy/groovy/beans/BindableASTTransformation.java b/src/main/groovy/groovy/beans/BindableASTTransformation.java
index c17be72..19d66ba 100644
--- a/src/main/groovy/groovy/beans/BindableASTTransformation.java
+++ b/src/main/groovy/groovy/beans/BindableASTTransformation.java
@@ -52,6 +52,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
 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;
@@ -188,8 +189,8 @@ public class BindableASTTransformation implements ASTTransformation, Opcodes {
             // Get the existing code block
             Statement code = setter.getCode();
 
-            Expression oldValue = varX("$oldValue");
-            Expression newValue = varX("$newValue");
+            Expression oldValue = localVarX("$oldValue");
+            Expression newValue = localVarX("$newValue");
             BlockStatement block = new BlockStatement();
 
             // create a local variable to hold the old value from the getter
diff --git a/src/main/groovy/groovy/beans/ListenerListASTTransformation.groovy b/src/main/groovy/groovy/beans/ListenerListASTTransformation.groovy
index ef61a78..3dc0b57 100644
--- a/src/main/groovy/groovy/beans/ListenerListASTTransformation.groovy
+++ b/src/main/groovy/groovy/beans/ListenerListASTTransformation.groovy
@@ -54,6 +54,8 @@ import org.codehaus.groovy.transform.ASTTransformation
 import org.codehaus.groovy.transform.GroovyASTTransformation
 import org.objectweb.asm.Opcodes
 
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX
+
 /**
  * Handles generation of code for the {@code @ListenerList} annotation.
  * <p>
@@ -68,7 +70,7 @@ class ListenerListASTTransformation implements ASTTransformation, Opcodes {
     private static final Class MY_CLASS = groovy.beans.ListenerList.class
     private static final ClassNode COLLECTION_TYPE = ClassHelper.make(Collection)
 
-    public void visit(ASTNode[] nodes, SourceUnit source) {
+    void visit(ASTNode[] nodes, SourceUnit source) {
         if (!(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) {
             throw new RuntimeException("Internal error: wrong types: ${node.class} / ${parent.class}")
         }
@@ -277,7 +279,7 @@ class ListenerListASTTransformation implements ASTTransformation, Opcodes {
         block.addStatements([
                 new ExpressionStatement(
                         new DeclarationExpression(
-                                new VariableExpression("__result", ClassHelper.DYNAMIC_TYPE),
+                                localVarX("__result", ClassHelper.DYNAMIC_TYPE),
                                 Token.newSymbol(Types.EQUALS, 0, 0),
                                 new ListExpression()
                         )),
@@ -345,7 +347,7 @@ class ListenerListASTTransformation implements ASTTransformation, Opcodes {
                         new BlockStatement([
                                 new ExpressionStatement(
                                         new DeclarationExpression(
-                                                new VariableExpression('__list', listenerListType),
+                                                localVarX('__list', listenerListType),
                                                 Token.newSymbol(Types.EQUALS, 0, 0),
                                                 new ConstructorCallExpression(listenerListType, new ArgumentListExpression(
                                                         new VariableExpression(field.name)
diff --git a/src/main/groovy/groovy/beans/VetoableASTTransformation.java b/src/main/groovy/groovy/beans/VetoableASTTransformation.java
index aa2f0b3..44e1da7 100644
--- a/src/main/groovy/groovy/beans/VetoableASTTransformation.java
+++ b/src/main/groovy/groovy/beans/VetoableASTTransformation.java
@@ -52,6 +52,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
 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;
@@ -172,8 +173,8 @@ public class VetoableASTTransformation extends BindableASTTransformation {
             // Get the existing code block
             Statement code = setter.getCode();
 
-            Expression oldValue = varX("$oldValue");
-            Expression newValue = varX("$newValue");
+            Expression oldValue = localVarX("$oldValue");
+            Expression newValue = localVarX("$newValue");
             Expression proposedValue = varX(setter.getParameters()[0].getName());
             BlockStatement block = new BlockStatement();
 
diff --git a/src/main/groovy/groovy/transform/builder/DefaultStrategy.java b/src/main/groovy/groovy/transform/builder/DefaultStrategy.java
index b0dfae4..e3cd82a 100644
--- a/src/main/groovy/groovy/transform/builder/DefaultStrategy.java
+++ b/src/main/groovy/groovy/transform/builder/DefaultStrategy.java
@@ -43,6 +43,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
 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;
@@ -284,7 +285,7 @@ public class DefaultStrategy extends BuilderASTTransformation.AbstractBuilderStr
     }
 
     private static Expression initializeInstance(ClassNode buildee, List<PropertyInfo> props, BlockStatement body) {
-        Expression instance = varX("_the" + buildee.getNameWithoutPackage(), buildee);
+        Expression instance = localVarX("_the" + buildee.getNameWithoutPackage(), buildee);
         body.addStatement(declS(instance, ctorX(buildee)));
         for (PropertyInfo pi : props) {
             body.addStatement(stmt(assignX(propX(instance, pi.getName()), varX(pi.getName(), pi.getType()))));
diff --git a/src/main/groovy/groovy/transform/builder/ExternalStrategy.java b/src/main/groovy/groovy/transform/builder/ExternalStrategy.java
index cba3b8b..fafc4fe 100644
--- a/src/main/groovy/groovy/transform/builder/ExternalStrategy.java
+++ b/src/main/groovy/groovy/transform/builder/ExternalStrategy.java
@@ -36,6 +36,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
 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;
@@ -148,7 +149,7 @@ public class ExternalStrategy extends BuilderASTTransformation.AbstractBuilderSt
     }
 
     private static Expression initializeInstance(ClassNode sourceClass, List<PropertyInfo> props, BlockStatement body) {
-        Expression instance = varX("_the" + sourceClass.getNameWithoutPackage(), sourceClass);
+        Expression instance = localVarX("_the" + sourceClass.getNameWithoutPackage(), sourceClass);
         body.addStatement(declS(instance, ctorX(sourceClass)));
         for (PropertyInfo prop : props) {
             body.addStatement(stmt(assignX(propX(instance, prop.getName()), varX(prop.getName().equals("class") ? "clazz" : prop.getName(), newClass(prop.getType())))));
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/AstHelper.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/AstHelper.groovy
index 3f7740d..fbfad98 100644
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/AstHelper.groovy
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/AstHelper.groovy
@@ -31,6 +31,7 @@ import java.lang.reflect.Modifier
 
 import static org.codehaus.groovy.ast.tools.GeneralUtils.classX
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX
 import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX
 
@@ -40,7 +41,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.varX
 @CompileStatic
 class AstHelper {
 	static ExpressionStatement createVariableDefinition(String variableName, ClassNode variableType, Expression value, boolean variableShouldBeFinal = false ) {
-        def newVariable = varX(variableName, variableType)
+        def newVariable = localVarX(variableName, variableType)
         if (variableShouldBeFinal)
             newVariable.setModifiers(Modifier.FINAL)
         (ExpressionStatement) declS(newVariable, value)
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
index d9abc76..2acdef6 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
@@ -647,6 +647,12 @@ public class GeneralUtils {
         return result;
     }
 
+    public static VariableExpression localVarX(String name, ClassNode type) {
+        VariableExpression result = new VariableExpression(name, type);
+        result.setAccessedVariable(result);
+        return result;
+    }
+
     public static BinaryExpression ltX(Expression lhv, Expression rhv) {
         return new BinaryExpression(lhv, LT, rhv);
     }
diff --git a/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java b/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java
index 743801f..208a34e 100644
--- a/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java
@@ -1,444 +1,446 @@
-/*
- *  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.classgen;
-
-import org.codehaus.groovy.ast.AnnotatedNode;
-import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
-import org.codehaus.groovy.ast.ClassHelper;
-import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.EnumConstantClassNode;
-import org.codehaus.groovy.ast.FieldNode;
-import org.codehaus.groovy.ast.InnerClassNode;
-import org.codehaus.groovy.ast.MethodNode;
-import org.codehaus.groovy.ast.Parameter;
-import org.codehaus.groovy.ast.expr.ArgumentListExpression;
-import org.codehaus.groovy.ast.expr.ArrayExpression;
-import org.codehaus.groovy.ast.expr.BinaryExpression;
-import org.codehaus.groovy.ast.expr.BooleanExpression;
-import org.codehaus.groovy.ast.expr.ClassExpression;
-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.ListExpression;
-import org.codehaus.groovy.ast.expr.MapEntryExpression;
-import org.codehaus.groovy.ast.expr.MapExpression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.expr.SpreadExpression;
-import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
-import org.codehaus.groovy.ast.expr.VariableExpression;
-import org.codehaus.groovy.ast.stmt.BlockStatement;
-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.control.CompilationUnit;
-import org.codehaus.groovy.control.SourceUnit;
-import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
-import org.codehaus.groovy.syntax.SyntaxException;
-import org.codehaus.groovy.syntax.Token;
-import org.codehaus.groovy.syntax.Types;
-import org.objectweb.asm.Opcodes;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class EnumVisitor extends ClassCodeVisitorSupport {
-    // some constants for modifiers
-    private static final int FS = Opcodes.ACC_FINAL | Opcodes.ACC_STATIC;
-    private static final int PS = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
-    private static final int PUBLIC_FS = Opcodes.ACC_PUBLIC | FS;
-    private static final int PRIVATE_FS = Opcodes.ACC_PRIVATE | FS;
-
-    private final SourceUnit sourceUnit;
-
-
-    public EnumVisitor(CompilationUnit cu, SourceUnit su) {
-        sourceUnit = su;
-    }
-
-    public void visitClass(ClassNode node) {
-        if (!node.isEnum()) return;
-        completeEnum(node);
-    }
-
-    protected SourceUnit getSourceUnit() {
-        return sourceUnit;
-    }
-
-    private void completeEnum(ClassNode enumClass) {
-        boolean isAic = isAnonymousInnerClass(enumClass);
-        // create MIN_VALUE and MAX_VALUE fields
-        FieldNode minValue = null, maxValue = null, values = null;
-
-        if (!isAic) {
-            ClassNode enumRef = enumClass.getPlainNodeReference();
-
-            // create values field
-            values = new FieldNode("$VALUES", PRIVATE_FS | Opcodes.ACC_SYNTHETIC, enumRef.makeArray(), enumClass, null);
-            values.setSynthetic(true);
-
-            addMethods(enumClass, values);
-            checkForAbstractMethods(enumClass);
-
-            // create MIN_VALUE and MAX_VALUE fields
-            minValue = new FieldNode("MIN_VALUE", PUBLIC_FS, enumRef, enumClass, null);
-            maxValue = new FieldNode("MAX_VALUE", PUBLIC_FS, enumRef, enumClass, null);
-        }
-        addInit(enumClass, minValue, maxValue, values, isAic);
-    }
-
-    private static void checkForAbstractMethods(ClassNode enumClass) {
-        List<MethodNode> methods = enumClass.getMethods();
-        for (MethodNode m : methods) {
-            if (m.isAbstract()) {
-                // make the class abstract also see Effective Java p.152
-                enumClass.setModifiers(enumClass.getModifiers() | Opcodes.ACC_ABSTRACT);
-                break;
-            }
-        }
-    }
-
-    private static void addMethods(ClassNode enumClass, FieldNode values) {
-        List<MethodNode> methods = enumClass.getMethods();
-
-        boolean hasNext = false;
-        boolean hasPrevious = false;
-        for (MethodNode m : methods) {
-            if (m.getName().equals("next") && m.getParameters().length == 0) hasNext = true;
-            if (m.getName().equals("previous") && m.getParameters().length == 0) hasPrevious = true;
-            if (hasNext && hasPrevious) break;
-        }
-
-        ClassNode enumRef = enumClass.getPlainNodeReference();
-
-        {
-            // create values() method
-            MethodNode valuesMethod = new MethodNode("values", PUBLIC_FS, enumRef.makeArray(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null);
-            valuesMethod.setSynthetic(true);
-            BlockStatement code = new BlockStatement();
-            MethodCallExpression cloneCall = new MethodCallExpression(new FieldExpression(values), "clone", MethodCallExpression.NO_ARGUMENTS);
-            cloneCall.setMethodTarget(values.getType().getMethod("clone", Parameter.EMPTY_ARRAY));
-            code.addStatement(new ReturnStatement(cloneCall));
-            valuesMethod.setCode(code);
-            enumClass.addMethod(valuesMethod);
-        }
-
-        if (!hasNext) {
-            // create next() method, code:
-            //     Day next() {
-            //        int ordinal = ordinal().next()
-            //        if (ordinal >= values().size()) ordinal = 0
-            //        return values()[ordinal]
-            //     }
-            Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);
-            Token ge = Token.newSymbol(Types.COMPARE_GREATER_THAN_EQUAL, -1, -1);
-            MethodNode nextMethod = new MethodNode("next", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null);
-            nextMethod.setSynthetic(true);
-            BlockStatement code = new BlockStatement();
-            BlockStatement ifStatement = new BlockStatement();
-            ifStatement.addStatement(
-                    new ExpressionStatement(
-                            new BinaryExpression(new VariableExpression("ordinal"), assign, new ConstantExpression(0))
-                    )
-            );
-
-            code.addStatement(
-                    new ExpressionStatement(
-                            new DeclarationExpression(
-                                    new VariableExpression("ordinal"),
-                                    assign,
-                                    new MethodCallExpression(
-                                            new MethodCallExpression(
-                                                    VariableExpression.THIS_EXPRESSION,
-                                                    "ordinal",
-                                                    MethodCallExpression.NO_ARGUMENTS),
-                                            "next",
-                                            MethodCallExpression.NO_ARGUMENTS
-                                    )
-                            )
-                    )
-            );
-            code.addStatement(
-                    new IfStatement(
-                            new BooleanExpression(new BinaryExpression(
-                                    new VariableExpression("ordinal"),
-                                    ge,
-                                    new MethodCallExpression(
-                                            new FieldExpression(values),
-                                            "size",
-                                            MethodCallExpression.NO_ARGUMENTS
-                                    )
-                            )),
-                            ifStatement,
-                            EmptyStatement.INSTANCE
-                    )
-            );
-            code.addStatement(
-                    new ReturnStatement(
-                            new MethodCallExpression(new FieldExpression(values), "getAt", new VariableExpression("ordinal"))
-                    )
-            );
-            nextMethod.setCode(code);
-            enumClass.addMethod(nextMethod);
-        }
-
-        if (!hasPrevious) {
-            // create previous() method, code:
-            //    Day previous() {
-            //        int ordinal = ordinal().previous()
-            //        if (ordinal < 0) ordinal = values().size() - 1
-            //        return values()[ordinal]
-            //    }
-            Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);
-            Token lt = Token.newSymbol(Types.COMPARE_LESS_THAN, -1, -1);
-            MethodNode nextMethod = new MethodNode("previous", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null);
-            nextMethod.setSynthetic(true);
-            BlockStatement code = new BlockStatement();
-            BlockStatement ifStatement = new BlockStatement();
-            ifStatement.addStatement(
-                    new ExpressionStatement(
-                            new BinaryExpression(new VariableExpression("ordinal"), assign,
-                                    new MethodCallExpression(
-                                            new MethodCallExpression(
-                                                    new FieldExpression(values),
-                                                    "size",
-                                                    MethodCallExpression.NO_ARGUMENTS
-                                            ),
-                                            "minus",
-                                            new ConstantExpression(1)
-                                    )
-                            )
-                    )
-            );
-
-            code.addStatement(
-                    new ExpressionStatement(
-                            new DeclarationExpression(
-                                    new VariableExpression("ordinal"),
-                                    assign,
-                                    new MethodCallExpression(
-                                            new MethodCallExpression(
-                                                    VariableExpression.THIS_EXPRESSION,
-                                                    "ordinal",
-                                                    MethodCallExpression.NO_ARGUMENTS),
-                                            "previous",
-                                            MethodCallExpression.NO_ARGUMENTS
-                                    )
-                            )
-                    )
-            );
-            code.addStatement(
-                    new IfStatement(
-                            new BooleanExpression(new BinaryExpression(
-                                    new VariableExpression("ordinal"),
-                                    lt,
-                                    new ConstantExpression(0)
-                            )),
-                            ifStatement,
-                            EmptyStatement.INSTANCE
-                    )
-            );
-            code.addStatement(
-                    new ReturnStatement(
-                            new MethodCallExpression(new FieldExpression(values), "getAt", new VariableExpression("ordinal"))
-                    )
-            );
-            nextMethod.setCode(code);
-            enumClass.addMethod(nextMethod);
-        }
-
-        {
-            // create valueOf
-            Parameter stringParameter = new Parameter(ClassHelper.STRING_TYPE, "name");
-            MethodNode valueOfMethod = new MethodNode("valueOf", PS, enumRef, new Parameter[]{stringParameter}, ClassNode.EMPTY_ARRAY, null);
-            ArgumentListExpression callArguments = new ArgumentListExpression();
-            callArguments.addExpression(new ClassExpression(enumClass));
-            callArguments.addExpression(new VariableExpression("name"));
-
-            BlockStatement code = new BlockStatement();
-            code.addStatement(
-                    new ReturnStatement(
-                            new MethodCallExpression(new ClassExpression(ClassHelper.Enum_Type), "valueOf", callArguments)
-                    )
-            );
-            valueOfMethod.setCode(code);
-            valueOfMethod.setSynthetic(true);
-            enumClass.addMethod(valueOfMethod);
-        }
-    }
-
-    private void addInit(ClassNode enumClass, FieldNode minValue,
-                         FieldNode maxValue, FieldNode values,
-                         boolean isAic) {
-        // constructor helper
-        // This method is used instead of calling the constructor as
-        // calling the constructor may require a table with MetaClass
-        // selecting the constructor for each enum value. So instead we
-        // use this method to have a central point for constructor selection
-        // and only one table. The whole construction is needed because 
-        // Reflection forbids access to the enum constructor.
-        // code:
-        // def $INIT(Object[] para) {
-        //  return this(*para)
-        // }
-        ClassNode enumRef = enumClass.getPlainNodeReference();
-        Parameter[] parameter = new Parameter[]{new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "para")};
-        MethodNode initMethod = new MethodNode("$INIT", PUBLIC_FS | Opcodes.ACC_SYNTHETIC, enumRef, parameter, ClassNode.EMPTY_ARRAY, null);
-        initMethod.setSynthetic(true);
-        ConstructorCallExpression cce = new ConstructorCallExpression(
-                ClassNode.THIS,
-                new ArgumentListExpression(
-                        new SpreadExpression(new VariableExpression("para"))
-                )
-        );
-        BlockStatement code = new BlockStatement();
-        code.addStatement(new ReturnStatement(cce));
-        initMethod.setCode(code);
-        enumClass.addMethod(initMethod);
-
-        // static init
-        List<FieldNode> fields = enumClass.getFields();
-        List<Expression> arrayInit = new ArrayList<Expression>();
-        int value = -1;
-        Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);
-        List<Statement> block = new ArrayList<Statement>();
-        FieldNode tempMin = null;
-        FieldNode tempMax = null;
-        for (FieldNode field : fields) {
-            if ((field.getModifiers() & Opcodes.ACC_ENUM) == 0) continue;
-            value++;
-            if (tempMin == null) tempMin = field;
-            tempMax = field;
-
-            ClassNode enumBase = enumClass;
-            ArgumentListExpression args = new ArgumentListExpression();
-            args.addExpression(new ConstantExpression(field.getName()));
-            args.addExpression(new ConstantExpression(value));
-            if (field.getInitialExpression() == null) {
-                if ((enumClass.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) {
-                    addError(field, "The enum constant " + field.getName() + " must override abstract methods from " + enumBase.getName() + ".");
-                    continue;
-                }
-            } else {
-                ListExpression oldArgs = (ListExpression) field.getInitialExpression();
-                List<MapEntryExpression> savedMapEntries = new ArrayList<MapEntryExpression>();
-                for (Expression exp : oldArgs.getExpressions()) {
-                    if (exp instanceof MapEntryExpression) {
-                        savedMapEntries.add((MapEntryExpression) exp);
-                        continue;
-                    }
-
-                    InnerClassNode inner = null;
-                    if (exp instanceof ClassExpression) {
-                        ClassExpression clazzExp = (ClassExpression) exp;
-                        ClassNode ref = clazzExp.getType();
-                        if (ref instanceof EnumConstantClassNode) {
-                            inner = (InnerClassNode) ref;
-                        }
-                    }
-                    if (inner != null) {
-                        List<MethodNode> baseMethods = enumBase.getMethods();
-                        for (MethodNode methodNode : baseMethods) {
-                            if (!methodNode.isAbstract()) continue;
-                            MethodNode enumConstMethod = inner.getMethod(methodNode.getName(), methodNode.getParameters());
-                            if (enumConstMethod == null || (enumConstMethod.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) {
-                                addError(field, "Can't have an abstract method in enum constant " + field.getName() + ". Implement method '" + methodNode.getTypeDescriptor() + "'.");
-                            }
-                        }
-                        if (inner.getVariableScope() == null) {
-                            enumBase = inner;
-                            /*
-                             * GROOVY-3985: Remove the final modifier from $INIT method in this case
-                             * so that subclasses of enum generated for enum constants (aic) can provide
-                             * their own $INIT method
-                             */
-                            initMethod.setModifiers(initMethod.getModifiers() & ~Opcodes.ACC_FINAL);
-                            continue;
-                        }
-                    }
-                    args.addExpression(exp);
-                }
-                if (!savedMapEntries.isEmpty()) {
-                    args.getExpressions().add(2, new MapExpression(savedMapEntries));
-                }
-            }
-            field.setInitialValueExpression(null);
-            block.add(
-                    new ExpressionStatement(
-                            new BinaryExpression(
-                                    new FieldExpression(field),
-                                    assign,
-                                    new StaticMethodCallExpression(enumBase, "$INIT", args)
-                            )
-                    )
-            );
-            arrayInit.add(new FieldExpression(field));
-        }
-
-        if (!isAic) {
-            if (tempMin != null) {
-                block.add(
-                        new ExpressionStatement(
-                                new BinaryExpression(
-                                        new FieldExpression(minValue),
-                                        assign,
-                                        new FieldExpression(tempMin)
-                                )
-                        )
-                );
-                block.add(
-                        new ExpressionStatement(
-                                new BinaryExpression(
-                                        new FieldExpression(maxValue),
-                                        assign,
-                                        new FieldExpression(tempMax)
-                                )
-                        )
-                );
-                enumClass.addField(minValue);
-                enumClass.addField(maxValue);
-            }
-
-            block.add(
-                    new ExpressionStatement(
-                            new BinaryExpression(new FieldExpression(values), assign, new ArrayExpression(enumClass, arrayInit))
-                    )
-            );
-            enumClass.addField(values);
-        }
-        enumClass.addStaticInitializerStatements(block, true);
-    }
-
-    private void addError(AnnotatedNode exp, String msg) {
-        sourceUnit.getErrorCollector().addErrorAndContinue(
-                new SyntaxErrorMessage(
-                        new SyntaxException(msg + '\n', exp.getLineNumber(), exp.getColumnNumber(), exp.getLastLineNumber(), exp.getLastColumnNumber()), sourceUnit)
-        );
-    }
-
-    private static boolean isAnonymousInnerClass(ClassNode enumClass) {
-        if (!(enumClass instanceof EnumConstantClassNode)) return false;
-        InnerClassNode ic = (InnerClassNode) enumClass;
-        return ic.getVariableScope() == null;
-    }
-
-}
+/*
+ *  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.classgen;
+
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.EnumConstantClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+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.ListExpression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MapExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.SpreadExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+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.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+
+public class EnumVisitor extends ClassCodeVisitorSupport {
+    // some constants for modifiers
+    private static final int FS = Opcodes.ACC_FINAL | Opcodes.ACC_STATIC;
+    private static final int PS = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
+    private static final int PUBLIC_FS = Opcodes.ACC_PUBLIC | FS;
+    private static final int PRIVATE_FS = Opcodes.ACC_PRIVATE | FS;
+
+    private final SourceUnit sourceUnit;
+
+
+    public EnumVisitor(CompilationUnit cu, SourceUnit su) {
+        sourceUnit = su;
+    }
+
+    public void visitClass(ClassNode node) {
+        if (!node.isEnum()) return;
+        completeEnum(node);
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return sourceUnit;
+    }
+
+    private void completeEnum(ClassNode enumClass) {
+        boolean isAic = isAnonymousInnerClass(enumClass);
+        // create MIN_VALUE and MAX_VALUE fields
+        FieldNode minValue = null, maxValue = null, values = null;
+
+        if (!isAic) {
+            ClassNode enumRef = enumClass.getPlainNodeReference();
+
+            // create values field
+            values = new FieldNode("$VALUES", PRIVATE_FS | Opcodes.ACC_SYNTHETIC, enumRef.makeArray(), enumClass, null);
+            values.setSynthetic(true);
+
+            addMethods(enumClass, values);
+            checkForAbstractMethods(enumClass);
+
+            // create MIN_VALUE and MAX_VALUE fields
+            minValue = new FieldNode("MIN_VALUE", PUBLIC_FS, enumRef, enumClass, null);
+            maxValue = new FieldNode("MAX_VALUE", PUBLIC_FS, enumRef, enumClass, null);
+        }
+        addInit(enumClass, minValue, maxValue, values, isAic);
+    }
+
+    private static void checkForAbstractMethods(ClassNode enumClass) {
+        List<MethodNode> methods = enumClass.getMethods();
+        for (MethodNode m : methods) {
+            if (m.isAbstract()) {
+                // make the class abstract also see Effective Java p.152
+                enumClass.setModifiers(enumClass.getModifiers() | Opcodes.ACC_ABSTRACT);
+                break;
+            }
+        }
+    }
+
+    private static void addMethods(ClassNode enumClass, FieldNode values) {
+        List<MethodNode> methods = enumClass.getMethods();
+
+        boolean hasNext = false;
+        boolean hasPrevious = false;
+        for (MethodNode m : methods) {
+            if (m.getName().equals("next") && m.getParameters().length == 0) hasNext = true;
+            if (m.getName().equals("previous") && m.getParameters().length == 0) hasPrevious = true;
+            if (hasNext && hasPrevious) break;
+        }
+
+        ClassNode enumRef = enumClass.getPlainNodeReference();
+
+        {
+            // create values() method
+            MethodNode valuesMethod = new MethodNode("values", PUBLIC_FS, enumRef.makeArray(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null);
+            valuesMethod.setSynthetic(true);
+            BlockStatement code = new BlockStatement();
+            MethodCallExpression cloneCall = new MethodCallExpression(new FieldExpression(values), "clone", MethodCallExpression.NO_ARGUMENTS);
+            cloneCall.setMethodTarget(values.getType().getMethod("clone", Parameter.EMPTY_ARRAY));
+            code.addStatement(new ReturnStatement(cloneCall));
+            valuesMethod.setCode(code);
+            enumClass.addMethod(valuesMethod);
+        }
+
+        if (!hasNext) {
+            // create next() method, code:
+            //     Day next() {
+            //        int ordinal = ordinal().next()
+            //        if (ordinal >= values().size()) ordinal = 0
+            //        return values()[ordinal]
+            //     }
+            Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);
+            Token ge = Token.newSymbol(Types.COMPARE_GREATER_THAN_EQUAL, -1, -1);
+            MethodNode nextMethod = new MethodNode("next", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null);
+            nextMethod.setSynthetic(true);
+            BlockStatement code = new BlockStatement();
+            BlockStatement ifStatement = new BlockStatement();
+            ifStatement.addStatement(
+                    new ExpressionStatement(
+                            new BinaryExpression(new VariableExpression("ordinal"), assign, new ConstantExpression(0))
+                    )
+            );
+
+            code.addStatement(
+                    new ExpressionStatement(
+                            new DeclarationExpression(
+                                    localVarX("ordinal"),
+                                    assign,
+                                    new MethodCallExpression(
+                                            new MethodCallExpression(
+                                                    VariableExpression.THIS_EXPRESSION,
+                                                    "ordinal",
+                                                    MethodCallExpression.NO_ARGUMENTS),
+                                            "next",
+                                            MethodCallExpression.NO_ARGUMENTS
+                                    )
+                            )
+                    )
+            );
+            code.addStatement(
+                    new IfStatement(
+                            new BooleanExpression(new BinaryExpression(
+                                    new VariableExpression("ordinal"),
+                                    ge,
+                                    new MethodCallExpression(
+                                            new FieldExpression(values),
+                                            "size",
+                                            MethodCallExpression.NO_ARGUMENTS
+                                    )
+                            )),
+                            ifStatement,
+                            EmptyStatement.INSTANCE
+                    )
+            );
+            code.addStatement(
+                    new ReturnStatement(
+                            new MethodCallExpression(new FieldExpression(values), "getAt", new VariableExpression("ordinal"))
+                    )
+            );
+            nextMethod.setCode(code);
+            enumClass.addMethod(nextMethod);
+        }
+
+        if (!hasPrevious) {
+            // create previous() method, code:
+            //    Day previous() {
+            //        int ordinal = ordinal().previous()
+            //        if (ordinal < 0) ordinal = values().size() - 1
+            //        return values()[ordinal]
+            //    }
+            Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);
+            Token lt = Token.newSymbol(Types.COMPARE_LESS_THAN, -1, -1);
+            MethodNode nextMethod = new MethodNode("previous", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null);
+            nextMethod.setSynthetic(true);
+            BlockStatement code = new BlockStatement();
+            BlockStatement ifStatement = new BlockStatement();
+            ifStatement.addStatement(
+                    new ExpressionStatement(
+                            new BinaryExpression(new VariableExpression("ordinal"), assign,
+                                    new MethodCallExpression(
+                                            new MethodCallExpression(
+                                                    new FieldExpression(values),
+                                                    "size",
+                                                    MethodCallExpression.NO_ARGUMENTS
+                                            ),
+                                            "minus",
+                                            new ConstantExpression(1)
+                                    )
+                            )
+                    )
+            );
+
+            code.addStatement(
+                    new ExpressionStatement(
+                            new DeclarationExpression(
+                                    localVarX("ordinal"),
+                                    assign,
+                                    new MethodCallExpression(
+                                            new MethodCallExpression(
+                                                    VariableExpression.THIS_EXPRESSION,
+                                                    "ordinal",
+                                                    MethodCallExpression.NO_ARGUMENTS),
+                                            "previous",
+                                            MethodCallExpression.NO_ARGUMENTS
+                                    )
+                            )
+                    )
+            );
+            code.addStatement(
+                    new IfStatement(
+                            new BooleanExpression(new BinaryExpression(
+                                    new VariableExpression("ordinal"),
+                                    lt,
+                                    new ConstantExpression(0)
+                            )),
+                            ifStatement,
+                            EmptyStatement.INSTANCE
+                    )
+            );
+            code.addStatement(
+                    new ReturnStatement(
+                            new MethodCallExpression(new FieldExpression(values), "getAt", new VariableExpression("ordinal"))
+                    )
+            );
+            nextMethod.setCode(code);
+            enumClass.addMethod(nextMethod);
+        }
+
+        {
+            // create valueOf
+            Parameter stringParameter = new Parameter(ClassHelper.STRING_TYPE, "name");
+            MethodNode valueOfMethod = new MethodNode("valueOf", PS, enumRef, new Parameter[]{stringParameter}, ClassNode.EMPTY_ARRAY, null);
+            ArgumentListExpression callArguments = new ArgumentListExpression();
+            callArguments.addExpression(new ClassExpression(enumClass));
+            callArguments.addExpression(new VariableExpression("name"));
+
+            BlockStatement code = new BlockStatement();
+            code.addStatement(
+                    new ReturnStatement(
+                            new MethodCallExpression(new ClassExpression(ClassHelper.Enum_Type), "valueOf", callArguments)
+                    )
+            );
+            valueOfMethod.setCode(code);
+            valueOfMethod.setSynthetic(true);
+            enumClass.addMethod(valueOfMethod);
+        }
+    }
+
+    private void addInit(ClassNode enumClass, FieldNode minValue,
+                         FieldNode maxValue, FieldNode values,
+                         boolean isAic) {
+        // constructor helper
+        // This method is used instead of calling the constructor as
+        // calling the constructor may require a table with MetaClass
+        // selecting the constructor for each enum value. So instead we
+        // use this method to have a central point for constructor selection
+        // and only one table. The whole construction is needed because 
+        // Reflection forbids access to the enum constructor.
+        // code:
+        // def $INIT(Object[] para) {
+        //  return this(*para)
+        // }
+        ClassNode enumRef = enumClass.getPlainNodeReference();
+        Parameter[] parameter = new Parameter[]{new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "para")};
+        MethodNode initMethod = new MethodNode("$INIT", PUBLIC_FS | Opcodes.ACC_SYNTHETIC, enumRef, parameter, ClassNode.EMPTY_ARRAY, null);
+        initMethod.setSynthetic(true);
+        ConstructorCallExpression cce = new ConstructorCallExpression(
+                ClassNode.THIS,
+                new ArgumentListExpression(
+                        new SpreadExpression(new VariableExpression("para"))
+                )
+        );
+        BlockStatement code = new BlockStatement();
+        code.addStatement(new ReturnStatement(cce));
+        initMethod.setCode(code);
+        enumClass.addMethod(initMethod);
+
+        // static init
+        List<FieldNode> fields = enumClass.getFields();
+        List<Expression> arrayInit = new ArrayList<Expression>();
+        int value = -1;
+        Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);
+        List<Statement> block = new ArrayList<Statement>();
+        FieldNode tempMin = null;
+        FieldNode tempMax = null;
+        for (FieldNode field : fields) {
+            if ((field.getModifiers() & Opcodes.ACC_ENUM) == 0) continue;
+            value++;
+            if (tempMin == null) tempMin = field;
+            tempMax = field;
+
+            ClassNode enumBase = enumClass;
+            ArgumentListExpression args = new ArgumentListExpression();
+            args.addExpression(new ConstantExpression(field.getName()));
+            args.addExpression(new ConstantExpression(value));
+            if (field.getInitialExpression() == null) {
+                if ((enumClass.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) {
+                    addError(field, "The enum constant " + field.getName() + " must override abstract methods from " + enumBase.getName() + ".");
+                    continue;
+                }
+            } else {
+                ListExpression oldArgs = (ListExpression) field.getInitialExpression();
+                List<MapEntryExpression> savedMapEntries = new ArrayList<MapEntryExpression>();
+                for (Expression exp : oldArgs.getExpressions()) {
+                    if (exp instanceof MapEntryExpression) {
+                        savedMapEntries.add((MapEntryExpression) exp);
+                        continue;
+                    }
+
+                    InnerClassNode inner = null;
+                    if (exp instanceof ClassExpression) {
+                        ClassExpression clazzExp = (ClassExpression) exp;
+                        ClassNode ref = clazzExp.getType();
+                        if (ref instanceof EnumConstantClassNode) {
+                            inner = (InnerClassNode) ref;
+                        }
+                    }
+                    if (inner != null) {
+                        List<MethodNode> baseMethods = enumBase.getMethods();
+                        for (MethodNode methodNode : baseMethods) {
+                            if (!methodNode.isAbstract()) continue;
+                            MethodNode enumConstMethod = inner.getMethod(methodNode.getName(), methodNode.getParameters());
+                            if (enumConstMethod == null || (enumConstMethod.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) {
+                                addError(field, "Can't have an abstract method in enum constant " + field.getName() + ". Implement method '" + methodNode.getTypeDescriptor() + "'.");
+                            }
+                        }
+                        if (inner.getVariableScope() == null) {
+                            enumBase = inner;
+                            /*
+                             * GROOVY-3985: Remove the final modifier from $INIT method in this case
+                             * so that subclasses of enum generated for enum constants (aic) can provide
+                             * their own $INIT method
+                             */
+                            initMethod.setModifiers(initMethod.getModifiers() & ~Opcodes.ACC_FINAL);
+                            continue;
+                        }
+                    }
+                    args.addExpression(exp);
+                }
+                if (!savedMapEntries.isEmpty()) {
+                    args.getExpressions().add(2, new MapExpression(savedMapEntries));
+                }
+            }
+            field.setInitialValueExpression(null);
+            block.add(
+                    new ExpressionStatement(
+                            new BinaryExpression(
+                                    new FieldExpression(field),
+                                    assign,
+                                    new StaticMethodCallExpression(enumBase, "$INIT", args)
+                            )
+                    )
+            );
+            arrayInit.add(new FieldExpression(field));
+        }
+
+        if (!isAic) {
+            if (tempMin != null) {
+                block.add(
+                        new ExpressionStatement(
+                                new BinaryExpression(
+                                        new FieldExpression(minValue),
+                                        assign,
+                                        new FieldExpression(tempMin)
+                                )
+                        )
+                );
+                block.add(
+                        new ExpressionStatement(
+                                new BinaryExpression(
+                                        new FieldExpression(maxValue),
+                                        assign,
+                                        new FieldExpression(tempMax)
+                                )
+                        )
+                );
+                enumClass.addField(minValue);
+                enumClass.addField(maxValue);
+            }
+
+            block.add(
+                    new ExpressionStatement(
+                            new BinaryExpression(new FieldExpression(values), assign, new ArrayExpression(enumClass, arrayInit))
+                    )
+            );
+            enumClass.addField(values);
+        }
+        enumClass.addStaticInitializerStatements(block, true);
+    }
+
+    private void addError(AnnotatedNode exp, String msg) {
+        sourceUnit.getErrorCollector().addErrorAndContinue(
+                new SyntaxErrorMessage(
+                        new SyntaxException(msg + '\n', exp.getLineNumber(), exp.getColumnNumber(), exp.getLastLineNumber(), exp.getLastColumnNumber()), sourceUnit)
+        );
+    }
+
+    private static boolean isAnonymousInnerClass(ClassNode enumClass) {
+        if (!(enumClass instanceof EnumConstantClassNode)) return false;
+        InnerClassNode ic = (InnerClassNode) enumClass;
+        return ic.getVariableScope() == null;
+    }
+
+}
diff --git a/src/main/java/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java
index ca4b660..7e7d0c7 100644
--- a/src/main/java/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java
@@ -1,344 +1,344 @@
-/*
- *  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.transform;
-
-import groovy.transform.EqualsAndHashCode;
-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.Parameter;
-import org.codehaus.groovy.ast.PropertyNode;
-import org.codehaus.groovy.ast.expr.BinaryExpression;
-import org.codehaus.groovy.ast.expr.CastExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.VariableExpression;
-import org.codehaus.groovy.ast.stmt.BlockStatement;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.ast.tools.GenericsUtils;
-import org.codehaus.groovy.control.CompilePhase;
-import org.codehaus.groovy.control.SourceUnit;
-import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
-import org.codehaus.groovy.util.HashCodeHelper;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
-import static org.codehaus.groovy.ast.ClassHelper.make;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.andX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.callSuperX;
-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.declS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.equalsNullX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.getAllProperties;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.getGetterName;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstanceNonPropertyFields;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.getterThisX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.hasClassX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.hasDeclaredMethod;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.hasEqualFieldX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.hasEqualPropertyX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.hasSameFieldX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.hasSamePropertyX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ifElseS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.isInstanceOfX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.isTrueX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.isZeroX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.notX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.orX;
-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.sameX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
-import static org.codehaus.groovy.ast.tools.GenericsUtils.makeClassSafe;
-
-@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
-public class EqualsAndHashCodeASTTransformation extends AbstractASTTransformation {
-    static final Class MY_CLASS = EqualsAndHashCode.class;
-    static final ClassNode MY_TYPE = make(MY_CLASS);
-    static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage();
-    private static final ClassNode HASHUTIL_TYPE = make(HashCodeHelper.class);
-    private static final ClassNode OBJECT_TYPE = makeClassSafe(Object.class);
-
-    public void visit(ASTNode[] nodes, SourceUnit source) {
-        init(nodes, source);
-        AnnotatedNode parent = (AnnotatedNode) nodes[1];
-        AnnotationNode anno = (AnnotationNode) nodes[0];
-        if (!MY_TYPE.equals(anno.getClassNode())) return;
-
-        if (parent instanceof ClassNode) {
-            ClassNode cNode = (ClassNode) parent;
-            if (!checkNotInterface(cNode, MY_TYPE_NAME)) return;
-            boolean callSuper = memberHasValue(anno, "callSuper", true);
-            boolean cacheHashCode = memberHasValue(anno, "cache", true);
-            boolean useCanEqual = !memberHasValue(anno, "useCanEqual", false);
-            if (callSuper && cNode.getSuperClass().getName().equals("java.lang.Object")) {
-                addError("Error during " + MY_TYPE_NAME + " processing: callSuper=true but '" + cNode.getName() + "' has no super class.", anno);
-            }
-            boolean includeFields = memberHasValue(anno, "includeFields", true);
-            List<String> excludes = getMemberStringList(anno, "excludes");
-            List<String> includes = getMemberStringList(anno, "includes");
-            final boolean allNames = memberHasValue(anno, "allNames", true);
-            final boolean allProperties = memberHasValue(anno, "allProperties", true);
-            if (!checkIncludeExcludeUndefinedAware(anno, excludes, includes, MY_TYPE_NAME)) return;
-            if (!checkPropertyList(cNode, includes, "includes", anno, MY_TYPE_NAME, includeFields)) return;
-            if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields)) return;
-            createHashCode(cNode, cacheHashCode, includeFields, callSuper, excludes, includes, allNames, allProperties);
-            createEquals(cNode, includeFields, callSuper, useCanEqual, excludes, includes, allNames, allProperties);
-        }
-    }
-
-    public static void createHashCode(ClassNode cNode, boolean cacheResult, boolean includeFields, boolean callSuper, List<String> excludes, List<String> includes) {
-        createHashCode(cNode, cacheResult, includeFields, callSuper, excludes, includes, false);
-    }
-
-    public static void createHashCode(ClassNode cNode, boolean cacheResult, boolean includeFields, boolean callSuper, List<String> excludes, List<String> includes, boolean allNames) {
-        createHashCode(cNode, cacheResult, includeFields, callSuper, excludes, includes, allNames,false);
-    }
-
-    public static void createHashCode(ClassNode cNode, boolean cacheResult, boolean includeFields, boolean callSuper, List<String> excludes, List<String> includes, boolean allNames, boolean allProperties) {
-        // make a public method if none exists otherwise try a private method with leading underscore
-        boolean hasExistingHashCode = hasDeclaredMethod(cNode, "hashCode", 0);
-        if (hasExistingHashCode && hasDeclaredMethod(cNode, "_hashCode", 0)) return;
-
-        final BlockStatement body = new BlockStatement();
-        // TODO use pList and fList
-        if (cacheResult) {
-            final FieldNode hashField = cNode.addField("$hash$code", ACC_PRIVATE | ACC_SYNTHETIC, ClassHelper.int_TYPE, null);
-            final Expression hash = varX(hashField);
-            body.addStatement(ifS(
-                    isZeroX(hash),
-                    calculateHashStatements(cNode, hash, includeFields, callSuper, excludes, includes, allNames, allProperties)
-            ));
-            body.addStatement(returnS(hash));
-        } else {
-            body.addStatement(calculateHashStatements(cNode, null, includeFields, callSuper, excludes, includes, allNames, allProperties));
-        }
-
-        addGeneratedMethod(cNode,
-                hasExistingHashCode ? "_hashCode" : "hashCode",
-                hasExistingHashCode ? ACC_PRIVATE : ACC_PUBLIC,
-                ClassHelper.int_TYPE,
-                Parameter.EMPTY_ARRAY,
-                ClassNode.EMPTY_ARRAY,
-                body);
-    }
-
-    private static Statement calculateHashStatements(ClassNode cNode, Expression hash, boolean includeFields, boolean callSuper, List<String> excludes, List<String> includes, boolean allNames, boolean allProperties) {
-        final Set<String> names = new HashSet<String>();
-        final List<PropertyNode> pList = getAllProperties(names, cNode, true, false, allProperties, false, false, false);
-        final List<FieldNode> fList = new ArrayList<FieldNode>();
-        if (includeFields) {
-            fList.addAll(getInstanceNonPropertyFields(cNode));
-        }
-        final BlockStatement body = new BlockStatement();
-        // def _result = HashCodeHelper.initHash()
-        final Expression result = localVarX("_result");
-        body.addStatement(declS(result, callX(HASHUTIL_TYPE, "initHash")));
-
-        for (PropertyNode pNode : pList) {
-            if (shouldSkipUndefinedAware(pNode.getName(), excludes, includes, allNames)) continue;
-            // _result = HashCodeHelper.updateHash(_result, getProperty()) // plus self-reference checking
-            Expression getter = getterThisX(cNode, pNode);
-            final Expression current = callX(HASHUTIL_TYPE, "updateHash", args(result, getter));
-            body.addStatement(ifS(
-                    notX(sameX(getter, varX("this"))),
-                    assignS(result, current)));
-
-        }
-        for (FieldNode fNode : fList) {
-            if (shouldSkipUndefinedAware(fNode.getName(), excludes, includes, allNames)) continue;
-            // _result = HashCodeHelper.updateHash(_result, field) // plus self-reference checking
-            final Expression fieldExpr = varX(fNode);
-            final Expression current = callX(HASHUTIL_TYPE, "updateHash", args(result, fieldExpr));
-            body.addStatement(ifS(
-                    notX(sameX(fieldExpr, varX("this"))),
-                    assignS(result, current)));
-        }
-        if (callSuper) {
-            // _result = HashCodeHelper.updateHash(_result, super.hashCode())
-            final Expression current = callX(HASHUTIL_TYPE, "updateHash", args(result, callSuperX("hashCode")));
-            body.addStatement(assignS(result, current));
-        }
-        // $hash$code = _result
-        if (hash != null) {
-            body.addStatement(assignS(hash, result));
-        } else {
-            body.addStatement(returnS(result));
-        }
-        return body;
-    }
-
-    private static void createCanEqual(ClassNode cNode) {
-        boolean hasExistingCanEqual = hasDeclaredMethod(cNode, "canEqual", 1);
-        if (hasExistingCanEqual && hasDeclaredMethod(cNode, "_canEqual", 1)) return;
-
-        final BlockStatement body = new BlockStatement();
-        VariableExpression other = varX("other");
-        body.addStatement(returnS(isInstanceOfX(other, GenericsUtils.nonGeneric(cNode))));
-        addGeneratedMethod(cNode,
-                hasExistingCanEqual ? "_canEqual" : "canEqual",
-                hasExistingCanEqual ? ACC_PRIVATE : ACC_PUBLIC,
-                ClassHelper.boolean_TYPE,
-                params(param(OBJECT_TYPE, other.getName())),
-                ClassNode.EMPTY_ARRAY,
-                body);
-    }
-
-    public static void createEquals(ClassNode cNode, boolean includeFields, boolean callSuper, boolean useCanEqual, List<String> excludes, List<String> includes) {
-        createEquals(cNode, includeFields, callSuper, useCanEqual, excludes, includes, false);
-    }
-
-    public static void createEquals(ClassNode cNode, boolean includeFields, boolean callSuper, boolean useCanEqual, List<String> excludes, List<String> includes, boolean allNames) {
-        createEquals(cNode, includeFields, callSuper, useCanEqual, excludes, includes, allNames,false);
-    }
-
-    public static void createEquals(ClassNode cNode, boolean includeFields, boolean callSuper, boolean useCanEqual, List<String> excludes, List<String> includes, boolean allNames, boolean allProperties) {
-        if (useCanEqual) createCanEqual(cNode);
-        // make a public method if none exists otherwise try a private method with leading underscore
-        boolean hasExistingEquals = hasDeclaredMethod(cNode, "equals", 1);
-        if (hasExistingEquals && hasDeclaredMethod(cNode, "_equals", 1)) return;
-
-        final BlockStatement body = new BlockStatement();
-        VariableExpression other = varX("other");
-
-        // some short circuit cases for efficiency
-        body.addStatement(ifS(equalsNullX(other), returnS(constX(Boolean.FALSE, true))));
-        body.addStatement(ifS(sameX(varX("this"), other), returnS(constX(Boolean.TRUE, true))));
-
-        if (useCanEqual) {
-            body.addStatement(ifS(notX(isInstanceOfX(other, GenericsUtils.nonGeneric(cNode))), returnS(constX(Boolean.FALSE,true))));
-        } else {
-            body.addStatement(ifS(notX(hasClassX(other, GenericsUtils.nonGeneric(cNode))), returnS(constX(Boolean.FALSE,true))));
-        }
-
-        VariableExpression otherTyped = varX("otherTyped", GenericsUtils.nonGeneric(cNode));
-        CastExpression castExpression = new CastExpression(GenericsUtils.nonGeneric(cNode), other);
-        castExpression.setStrict(true);
-        body.addStatement(declS(otherTyped, castExpression));
-
-        if (useCanEqual) {
-            body.addStatement(ifS(notX(callX(otherTyped, "canEqual", varX("this"))), returnS(constX(Boolean.FALSE,true))));
-        }
-
-        final Set<String> names = new HashSet<String>();
-        final List<PropertyNode> pList = getAllProperties(names, cNode, true, includeFields, allProperties, false, false, false);
-        for (PropertyNode pNode : pList) {
-            if (shouldSkipUndefinedAware(pNode.getName(), excludes, includes, allNames)) continue;
-            boolean canBeSelf = StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(
-                    pNode.getOriginType(), cNode
-            );
-            if (!canBeSelf) {
-                body.addStatement(ifS(notX(hasEqualPropertyX(otherTyped.getOriginType(), pNode, otherTyped)), returnS(constX(Boolean.FALSE, true))));
-            } else {
-                body.addStatement(
-                        ifS(notX(hasSamePropertyX(pNode, otherTyped)),
-                                ifElseS(differentSelfRecursivePropertyX(pNode, otherTyped),
-                                        returnS(constX(Boolean.FALSE, true)),
-                                        ifS(notX(bothSelfRecursivePropertyX(pNode, otherTyped)),
-                                                ifS(notX(hasEqualPropertyX(otherTyped.getOriginType(), pNode, otherTyped)), returnS(constX(Boolean.FALSE, true))))
-                                )
-                        )
-                );
-            }
-        }
-        List<FieldNode> fList = new ArrayList<FieldNode>();
-        if (includeFields) {
-            fList.addAll(getInstanceNonPropertyFields(cNode));
-        }
-        for (FieldNode fNode : fList) {
-            if (shouldSkipUndefinedAware(fNode.getName(), excludes, includes, allNames)) continue;
-            body.addStatement(
-                    ifS(notX(hasSameFieldX(fNode, otherTyped)),
-                            ifElseS(differentSelfRecursiveFieldX(fNode, otherTyped),
-                                    returnS(constX(Boolean.FALSE,true)),
-                                    ifS(notX(bothSelfRecursiveFieldX(fNode, otherTyped)),
-                                            ifS(notX(hasEqualFieldX(fNode, otherTyped)), returnS(constX(Boolean.FALSE,true)))))
-                    ));
-        }
-        if (callSuper) {
-            body.addStatement(ifS(
-                    notX(isTrueX(callSuperX("equals", other))),
-                    returnS(constX(Boolean.FALSE,true))
-            ));
-        }
-
-        // default
-        body.addStatement(returnS(constX(Boolean.TRUE,true)));
-
-        addGeneratedMethod(cNode,
-                hasExistingEquals ? "_equals" : "equals",
-                hasExistingEquals ? ACC_PRIVATE : ACC_PUBLIC,
-                ClassHelper.boolean_TYPE,
-                params(param(OBJECT_TYPE, other.getName())),
-                ClassNode.EMPTY_ARRAY,
-                body);
-    }
-
-    private static BinaryExpression differentSelfRecursivePropertyX(PropertyNode pNode, Expression other) {
-        String getterName = getGetterName(pNode);
-        Expression selfGetter = callThisX(getterName);
-        Expression otherGetter = callX(other, getterName);
-        return orX(
-                andX(sameX(selfGetter, varX("this")), notX(sameX(otherGetter, other))),
-                andX(notX(sameX(selfGetter, varX("this"))), sameX(otherGetter, other))
-        );
-    }
-
-    private static BinaryExpression bothSelfRecursivePropertyX(PropertyNode pNode, Expression other) {
-        String getterName = getGetterName(pNode);
-        Expression selfGetter = callThisX(getterName);
-        Expression otherGetter = callX(other, getterName);
-        return andX(
-                sameX(selfGetter, varX("this")),
-                sameX(otherGetter, other)
-        );
-    }
-
-    private static BinaryExpression differentSelfRecursiveFieldX(FieldNode fNode, Expression other) {
-        final Expression fieldExpr = varX(fNode);
-        final Expression otherExpr = propX(other, fNode.getName());
-        return orX(
-                andX(sameX(fieldExpr, varX("this")), notX(sameX(otherExpr, other))),
-                andX(notX(sameX(fieldExpr, varX("this"))), sameX(otherExpr, other))
-        );
-    }
-
-    private static BinaryExpression bothSelfRecursiveFieldX(FieldNode fNode, Expression other) {
-        final Expression fieldExpr = varX(fNode);
-        final Expression otherExpr = propX(other, fNode.getName());
-        return andX(
-                sameX(fieldExpr, varX("this")),
-                sameX(otherExpr, other)
-        );
-    }
-}
+/*
+ *  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.transform;
+
+import groovy.transform.EqualsAndHashCode;
+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.Parameter;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.tools.GenericsUtils;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
+import org.codehaus.groovy.util.HashCodeHelper;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
+import static org.codehaus.groovy.ast.ClassHelper.make;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.andX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callSuperX;
+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.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.equalsNullX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.getAllProperties;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.getGetterName;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstanceNonPropertyFields;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.getterThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.hasClassX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.hasDeclaredMethod;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.hasEqualFieldX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.hasEqualPropertyX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.hasSameFieldX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.hasSamePropertyX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ifElseS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.isInstanceOfX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.isTrueX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.isZeroX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.notX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.orX;
+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.sameX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.makeClassSafe;
+
+@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
+public class EqualsAndHashCodeASTTransformation extends AbstractASTTransformation {
+    static final Class MY_CLASS = EqualsAndHashCode.class;
+    static final ClassNode MY_TYPE = make(MY_CLASS);
+    static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage();
+    private static final ClassNode HASHUTIL_TYPE = make(HashCodeHelper.class);
+    private static final ClassNode OBJECT_TYPE = makeClassSafe(Object.class);
+
+    public void visit(ASTNode[] nodes, SourceUnit source) {
+        init(nodes, source);
+        AnnotatedNode parent = (AnnotatedNode) nodes[1];
+        AnnotationNode anno = (AnnotationNode) nodes[0];
+        if (!MY_TYPE.equals(anno.getClassNode())) return;
+
+        if (parent instanceof ClassNode) {
+            ClassNode cNode = (ClassNode) parent;
+            if (!checkNotInterface(cNode, MY_TYPE_NAME)) return;
+            boolean callSuper = memberHasValue(anno, "callSuper", true);
+            boolean cacheHashCode = memberHasValue(anno, "cache", true);
+            boolean useCanEqual = !memberHasValue(anno, "useCanEqual", false);
+            if (callSuper && cNode.getSuperClass().getName().equals("java.lang.Object")) {
+                addError("Error during " + MY_TYPE_NAME + " processing: callSuper=true but '" + cNode.getName() + "' has no super class.", anno);
+            }
+            boolean includeFields = memberHasValue(anno, "includeFields", true);
+            List<String> excludes = getMemberStringList(anno, "excludes");
+            List<String> includes = getMemberStringList(anno, "includes");
+            final boolean allNames = memberHasValue(anno, "allNames", true);
+            final boolean allProperties = memberHasValue(anno, "allProperties", true);
+            if (!checkIncludeExcludeUndefinedAware(anno, excludes, includes, MY_TYPE_NAME)) return;
+            if (!checkPropertyList(cNode, includes, "includes", anno, MY_TYPE_NAME, includeFields)) return;
+            if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields)) return;
+            createHashCode(cNode, cacheHashCode, includeFields, callSuper, excludes, includes, allNames, allProperties);
+            createEquals(cNode, includeFields, callSuper, useCanEqual, excludes, includes, allNames, allProperties);
+        }
+    }
+
+    public static void createHashCode(ClassNode cNode, boolean cacheResult, boolean includeFields, boolean callSuper, List<String> excludes, List<String> includes) {
+        createHashCode(cNode, cacheResult, includeFields, callSuper, excludes, includes, false);
+    }
+
+    public static void createHashCode(ClassNode cNode, boolean cacheResult, boolean includeFields, boolean callSuper, List<String> excludes, List<String> includes, boolean allNames) {
+        createHashCode(cNode, cacheResult, includeFields, callSuper, excludes, includes, allNames,false);
+    }
+
+    public static void createHashCode(ClassNode cNode, boolean cacheResult, boolean includeFields, boolean callSuper, List<String> excludes, List<String> includes, boolean allNames, boolean allProperties) {
+        // make a public method if none exists otherwise try a private method with leading underscore
+        boolean hasExistingHashCode = hasDeclaredMethod(cNode, "hashCode", 0);
+        if (hasExistingHashCode && hasDeclaredMethod(cNode, "_hashCode", 0)) return;
+
+        final BlockStatement body = new BlockStatement();
+        // TODO use pList and fList
+        if (cacheResult) {
+            final FieldNode hashField = cNode.addField("$hash$code", ACC_PRIVATE | ACC_SYNTHETIC, ClassHelper.int_TYPE, null);
+            final Expression hash = varX(hashField);
+            body.addStatement(ifS(
+                    isZeroX(hash),
+                    calculateHashStatements(cNode, hash, includeFields, callSuper, excludes, includes, allNames, allProperties)
+            ));
+            body.addStatement(returnS(hash));
+        } else {
+            body.addStatement(calculateHashStatements(cNode, null, includeFields, callSuper, excludes, includes, allNames, allProperties));
+        }
+
+        addGeneratedMethod(cNode,
+                hasExistingHashCode ? "_hashCode" : "hashCode",
+                hasExistingHashCode ? ACC_PRIVATE : ACC_PUBLIC,
+                ClassHelper.int_TYPE,
+                Parameter.EMPTY_ARRAY,
+                ClassNode.EMPTY_ARRAY,
+                body);
+    }
+
+    private static Statement calculateHashStatements(ClassNode cNode, Expression hash, boolean includeFields, boolean callSuper, List<String> excludes, List<String> includes, boolean allNames, boolean allProperties) {
+        final Set<String> names = new HashSet<String>();
+        final List<PropertyNode> pList = getAllProperties(names, cNode, true, false, allProperties, false, false, false);
+        final List<FieldNode> fList = new ArrayList<FieldNode>();
+        if (includeFields) {
+            fList.addAll(getInstanceNonPropertyFields(cNode));
+        }
+        final BlockStatement body = new BlockStatement();
+        // def _result = HashCodeHelper.initHash()
+        final Expression result = localVarX("_result");
+        body.addStatement(declS(result, callX(HASHUTIL_TYPE, "initHash")));
+
+        for (PropertyNode pNode : pList) {
+            if (shouldSkipUndefinedAware(pNode.getName(), excludes, includes, allNames)) continue;
+            // _result = HashCodeHelper.updateHash(_result, getProperty()) // plus self-reference checking
+            Expression getter = getterThisX(cNode, pNode);
+            final Expression current = callX(HASHUTIL_TYPE, "updateHash", args(result, getter));
+            body.addStatement(ifS(
+                    notX(sameX(getter, varX("this"))),
+                    assignS(result, current)));
+
+        }
+        for (FieldNode fNode : fList) {
+            if (shouldSkipUndefinedAware(fNode.getName(), excludes, includes, allNames)) continue;
+            // _result = HashCodeHelper.updateHash(_result, field) // plus self-reference checking
+            final Expression fieldExpr = varX(fNode);
+            final Expression current = callX(HASHUTIL_TYPE, "updateHash", args(result, fieldExpr));
+            body.addStatement(ifS(
+                    notX(sameX(fieldExpr, varX("this"))),
+                    assignS(result, current)));
+        }
+        if (callSuper) {
+            // _result = HashCodeHelper.updateHash(_result, super.hashCode())
+            final Expression current = callX(HASHUTIL_TYPE, "updateHash", args(result, callSuperX("hashCode")));
+            body.addStatement(assignS(result, current));
+        }
+        // $hash$code = _result
+        if (hash != null) {
+            body.addStatement(assignS(hash, result));
+        } else {
+            body.addStatement(returnS(result));
+        }
+        return body;
+    }
+
+    private static void createCanEqual(ClassNode cNode) {
+        boolean hasExistingCanEqual = hasDeclaredMethod(cNode, "canEqual", 1);
+        if (hasExistingCanEqual && hasDeclaredMethod(cNode, "_canEqual", 1)) return;
+
+        final BlockStatement body = new BlockStatement();
+        VariableExpression other = varX("other");
+        body.addStatement(returnS(isInstanceOfX(other, GenericsUtils.nonGeneric(cNode))));
+        addGeneratedMethod(cNode,
+                hasExistingCanEqual ? "_canEqual" : "canEqual",
+                hasExistingCanEqual ? ACC_PRIVATE : ACC_PUBLIC,
+                ClassHelper.boolean_TYPE,
+                params(param(OBJECT_TYPE, other.getName())),
+                ClassNode.EMPTY_ARRAY,
+                body);
+    }
+
+    public static void createEquals(ClassNode cNode, boolean includeFields, boolean callSuper, boolean useCanEqual, List<String> excludes, List<String> includes) {
+        createEquals(cNode, includeFields, callSuper, useCanEqual, excludes, includes, false);
+    }
+
+    public static void createEquals(ClassNode cNode, boolean includeFields, boolean callSuper, boolean useCanEqual, List<String> excludes, List<String> includes, boolean allNames) {
+        createEquals(cNode, includeFields, callSuper, useCanEqual, excludes, includes, allNames,false);
+    }
+
+    public static void createEquals(ClassNode cNode, boolean includeFields, boolean callSuper, boolean useCanEqual, List<String> excludes, List<String> includes, boolean allNames, boolean allProperties) {
+        if (useCanEqual) createCanEqual(cNode);
+        // make a public method if none exists otherwise try a private method with leading underscore
+        boolean hasExistingEquals = hasDeclaredMethod(cNode, "equals", 1);
+        if (hasExistingEquals && hasDeclaredMethod(cNode, "_equals", 1)) return;
+
+        final BlockStatement body = new BlockStatement();
+        VariableExpression other = varX("other");
+
+        // some short circuit cases for efficiency
+        body.addStatement(ifS(equalsNullX(other), returnS(constX(Boolean.FALSE, true))));
+        body.addStatement(ifS(sameX(varX("this"), other), returnS(constX(Boolean.TRUE, true))));
+
+        if (useCanEqual) {
+            body.addStatement(ifS(notX(isInstanceOfX(other, GenericsUtils.nonGeneric(cNode))), returnS(constX(Boolean.FALSE,true))));
+        } else {
+            body.addStatement(ifS(notX(hasClassX(other, GenericsUtils.nonGeneric(cNode))), returnS(constX(Boolean.FALSE,true))));
+        }
+
+        VariableExpression otherTyped = localVarX("otherTyped", GenericsUtils.nonGeneric(cNode));
+        CastExpression castExpression = new CastExpression(GenericsUtils.nonGeneric(cNode), other);
+        castExpression.setStrict(true);
+        body.addStatement(declS(otherTyped, castExpression));
+
+        if (useCanEqual) {
+            body.addStatement(ifS(notX(callX(otherTyped, "canEqual", varX("this"))), returnS(constX(Boolean.FALSE,true))));
+        }
+
+        final Set<String> names = new HashSet<String>();
+        final List<PropertyNode> pList = getAllProperties(names, cNode, true, includeFields, allProperties, false, false, false);
+        for (PropertyNode pNode : pList) {
+            if (shouldSkipUndefinedAware(pNode.getName(), excludes, includes, allNames)) continue;
+            boolean canBeSelf = StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(
+                    pNode.getOriginType(), cNode
+            );
+            if (!canBeSelf) {
+                body.addStatement(ifS(notX(hasEqualPropertyX(otherTyped.getOriginType(), pNode, otherTyped)), returnS(constX(Boolean.FALSE, true))));
+            } else {
+                body.addStatement(
+                        ifS(notX(hasSamePropertyX(pNode, otherTyped)),
+                                ifElseS(differentSelfRecursivePropertyX(pNode, otherTyped),
+                                        returnS(constX(Boolean.FALSE, true)),
+                                        ifS(notX(bothSelfRecursivePropertyX(pNode, otherTyped)),
+                                                ifS(notX(hasEqualPropertyX(otherTyped.getOriginType(), pNode, otherTyped)), returnS(constX(Boolean.FALSE, true))))
+                                )
+                        )
+                );
+            }
+        }
+        List<FieldNode> fList = new ArrayList<FieldNode>();
+        if (includeFields) {
+            fList.addAll(getInstanceNonPropertyFields(cNode));
+        }
+        for (FieldNode fNode : fList) {
+            if (shouldSkipUndefinedAware(fNode.getName(), excludes, includes, allNames)) continue;
+            body.addStatement(
+                    ifS(notX(hasSameFieldX(fNode, otherTyped)),
+                            ifElseS(differentSelfRecursiveFieldX(fNode, otherTyped),
+                                    returnS(constX(Boolean.FALSE,true)),
+                                    ifS(notX(bothSelfRecursiveFieldX(fNode, otherTyped)),
+                                            ifS(notX(hasEqualFieldX(fNode, otherTyped)), returnS(constX(Boolean.FALSE,true)))))
+                    ));
+        }
+        if (callSuper) {
+            body.addStatement(ifS(
+                    notX(isTrueX(callSuperX("equals", other))),
+                    returnS(constX(Boolean.FALSE,true))
+            ));
+        }
+
+        // default
+        body.addStatement(returnS(constX(Boolean.TRUE,true)));
+
+        addGeneratedMethod(cNode,
+                hasExistingEquals ? "_equals" : "equals",
+                hasExistingEquals ? ACC_PRIVATE : ACC_PUBLIC,
+                ClassHelper.boolean_TYPE,
+                params(param(OBJECT_TYPE, other.getName())),
+                ClassNode.EMPTY_ARRAY,
+                body);
+    }
+
+    private static BinaryExpression differentSelfRecursivePropertyX(PropertyNode pNode, Expression other) {
+        String getterName = getGetterName(pNode);
+        Expression selfGetter = callThisX(getterName);
+        Expression otherGetter = callX(other, getterName);
+        return orX(
+                andX(sameX(selfGetter, varX("this")), notX(sameX(otherGetter, other))),
+                andX(notX(sameX(selfGetter, varX("this"))), sameX(otherGetter, other))
+        );
+    }
+
+    private static BinaryExpression bothSelfRecursivePropertyX(PropertyNode pNode, Expression other) {
+        String getterName = getGetterName(pNode);
+        Expression selfGetter = callThisX(getterName);
+        Expression otherGetter = callX(other, getterName);
+        return andX(
+                sameX(selfGetter, varX("this")),
+                sameX(otherGetter, other)
+        );
+    }
+
+    private static BinaryExpression differentSelfRecursiveFieldX(FieldNode fNode, Expression other) {
+        final Expression fieldExpr = varX(fNode);
+        final Expression otherExpr = propX(other, fNode.getName());
+        return orX(
+                andX(sameX(fieldExpr, varX("this")), notX(sameX(otherExpr, other))),
+                andX(notX(sameX(fieldExpr, varX("this"))), sameX(otherExpr, other))
+        );
+    }
+
+    private static BinaryExpression bothSelfRecursiveFieldX(FieldNode fNode, Expression other) {
+        final Expression fieldExpr = varX(fNode);
+        final Expression otherExpr = propX(other, fNode.getName());
+        return andX(
+                sameX(fieldExpr, varX("this")),
+                sameX(otherExpr, other)
+        );
+    }
+}
diff --git a/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
index 500e3b1..b81046f 100644
--- a/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
@@ -72,6 +72,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.hasDeclaredMethod;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ifElseS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.isTrueX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.neX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.orX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
@@ -239,7 +240,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation implem
                         block(
                                 new VariableScope(),
                                 declS(
-                                        varX("newValue", ClassHelper.OBJECT_TYPE),
+                                        localVarX("newValue", ClassHelper.OBJECT_TYPE),
                                         callX(
                                                 varX("map", HMAP_TYPE),
                                                 "get",
@@ -247,7 +248,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation implem
                                         )
                                 ),
                                 declS(
-                                        varX("oldValue", ClassHelper.OBJECT_TYPE),
+                                        localVarX("oldValue", ClassHelper.OBJECT_TYPE),
                                         callThisX(getGetterName(pNode))
                                 ),
                                 ifS(
@@ -298,8 +299,8 @@ public class ImmutableASTTransformation extends AbstractASTTransformation implem
                 ),
                 returnS(varX("this", cNode))
         ));
-        body.addStatement(declS(varX("dirty", ClassHelper.boolean_TYPE), ConstantExpression.PRIM_FALSE));
-        body.addStatement(declS(varX("construct", HMAP_TYPE), ctorX(HMAP_TYPE)));
+        body.addStatement(declS(localVarX("dirty", ClassHelper.boolean_TYPE), ConstantExpression.PRIM_FALSE));
+        body.addStatement(declS(localVarX("construct", HMAP_TYPE), ctorX(HMAP_TYPE)));
 
         // Check for each property
         for (final PropertyNode pNode : pList) {
diff --git a/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java
index ce21481..06ec761 100644
--- a/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java
@@ -1,246 +1,247 @@
-/*
- *  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.transform;
-
-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.InnerClassNode;
-import org.codehaus.groovy.ast.Parameter;
-import org.codehaus.groovy.ast.PropertyNode;
-import org.codehaus.groovy.ast.expr.ConstantExpression;
-import org.codehaus.groovy.ast.expr.EmptyExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.expr.VariableExpression;
-import org.codehaus.groovy.ast.stmt.BlockStatement;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
-import org.codehaus.groovy.control.CompilePhase;
-import org.codehaus.groovy.control.SourceUnit;
-import org.codehaus.groovy.runtime.MetaClassHelper;
-
-import java.lang.ref.SoftReference;
-
-import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
-import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
-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.classX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ifElseS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.notNullX;
-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;
-
-/**
- * Handles generation of code for the @Lazy annotation
- */
-@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
-public class LazyASTTransformation extends AbstractASTTransformation {
-
-    private static final ClassNode SOFT_REF = makeWithoutCaching(SoftReference.class, false);
-    private static final Expression NULL_EXPR = ConstantExpression.NULL;
-
-    public void visit(ASTNode[] nodes, SourceUnit source) {
-        init(nodes, source);
-        AnnotatedNode parent = (AnnotatedNode) nodes[1];
-        AnnotationNode node = (AnnotationNode) nodes[0];
-
-        if (parent instanceof FieldNode) {
-            final FieldNode fieldNode = (FieldNode) parent;
-            visitField(this, node, fieldNode);
-        }
-    }
-
-    static void visitField(ErrorCollecting xform, AnnotationNode node, FieldNode fieldNode) {
-        final Expression soft = node.getMember("soft");
-        final Expression init = getInitExpr(xform, fieldNode);
-
-        String backingFieldName = "$" + fieldNode.getName();
-        fieldNode.rename(backingFieldName);
-        fieldNode.setModifiers(ACC_PRIVATE | (fieldNode.getModifiers() & (~(ACC_PUBLIC | ACC_PROTECTED))));
-        PropertyNode pNode = fieldNode.getDeclaringClass().getProperty(backingFieldName);
-        if (pNode != null) {
-            fieldNode.getDeclaringClass().getProperties().remove(pNode);
-        }
-
-        if (soft instanceof ConstantExpression && ((ConstantExpression) soft).getValue().equals(true)) {
-            createSoft(fieldNode, init);
-        } else {
-            create(fieldNode, init);
-            // @Lazy not meaningful with primitive so convert to wrapper if needed
-            if (ClassHelper.isPrimitiveType(fieldNode.getType())) {
-                fieldNode.setType(ClassHelper.getWrapper(fieldNode.getType()));
-            }
-        }
-    }
-
-    private static void create(FieldNode fieldNode, final Expression initExpr) {
-        final BlockStatement body = new BlockStatement();
-        if (fieldNode.isStatic()) {
-            addHolderClassIdiomBody(body, fieldNode, initExpr);
-        } else if (fieldNode.isVolatile()) {
-            addDoubleCheckedLockingBody(body, fieldNode, initExpr);
-        } else {
-            addNonThreadSafeBody(body, fieldNode, initExpr);
-        }
-        addMethod(fieldNode, body, fieldNode.getType());
-    }
-
-    private static void addHolderClassIdiomBody(BlockStatement body, FieldNode fieldNode, Expression initExpr) {
-        final ClassNode declaringClass = fieldNode.getDeclaringClass();
-        final ClassNode fieldType = fieldNode.getType();
-        final int visibility = ACC_PRIVATE | ACC_STATIC;
-        final String fullName = declaringClass.getName() + "$" + fieldType.getNameWithoutPackage() + "Holder_" + fieldNode.getName().substring(1);
-        final InnerClassNode holderClass = new InnerClassNode(declaringClass, fullName, visibility, ClassHelper.OBJECT_TYPE);
-        final String innerFieldName = "INSTANCE";
-
-        // we have two options:
-        // (1) embed initExpr within holder class but redirect field access/method calls to declaring class members
-        // (2) keep initExpr within a declaring class method that is only called by the holder class
-        // currently we have gone with (2) for simplicity with only a slight memory footprint increase in the declaring class
-        final String initializeMethodName = (fullName + "_initExpr").replace('.', '_');
-        addGeneratedMethod(declaringClass, initializeMethodName, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, fieldType,
-                Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, returnS(initExpr));
-        holderClass.addField(innerFieldName, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, fieldType,
-                callX(declaringClass, initializeMethodName));
-
-        final Expression innerField = propX(classX(holderClass), innerFieldName);
-        declaringClass.getModule().addClass(holderClass);
-        body.addStatement(returnS(innerField));
-    }
-
-    private static void addDoubleCheckedLockingBody(BlockStatement body, FieldNode fieldNode, Expression initExpr) {
-        final Expression fieldExpr = varX(fieldNode);
-        final VariableExpression localVar = varX(fieldNode.getName() + "_local");
-        body.addStatement(declS(localVar, fieldExpr));
-        body.addStatement(ifElseS(
-                notNullX(localVar),
-                returnS(localVar),
-                new SynchronizedStatement(
-                        syncTarget(fieldNode),
-                        ifElseS(
-                                notNullX(fieldExpr),
-                                returnS(fieldExpr),
-                                returnS(assignX(fieldExpr, initExpr))
-                        )
-                )
-        ));
-    }
-
-    private static void addNonThreadSafeBody(BlockStatement body, FieldNode fieldNode, Expression initExpr) {
-        final Expression fieldExpr = varX(fieldNode);
-        body.addStatement(ifElseS(notNullX(fieldExpr), stmt(fieldExpr), assignS(fieldExpr, initExpr)));
-    }
-
-    private static void addMethod(FieldNode fieldNode, BlockStatement body, ClassNode type) {
-        int visibility = ACC_PUBLIC;
-        if (fieldNode.isStatic()) visibility |= ACC_STATIC;
-        String propName = MetaClassHelper.capitalize(fieldNode.getName().substring(1));
-        ClassNode declaringClass = fieldNode.getDeclaringClass();
-        addGeneratedMethod(declaringClass, "get" + propName, visibility, type, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
-        if (ClassHelper.boolean_TYPE.equals(type)) {
-            addGeneratedMethod(declaringClass, "is" + propName, visibility, type,
-                    Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, stmt(callThisX("get" + propName)));
-        }
-    }
-
-    private static void createSoft(FieldNode fieldNode, Expression initExpr) {
-        final ClassNode type = fieldNode.getType();
-        fieldNode.setType(SOFT_REF);
-        createSoftGetter(fieldNode, initExpr, type);
-        createSoftSetter(fieldNode, type);
-    }
-
-    private static void createSoftGetter(FieldNode fieldNode, Expression initExpr, ClassNode type) {
-        final BlockStatement body = new BlockStatement();
-        final Expression fieldExpr = varX(fieldNode);
-        final Expression resExpr = varX("res", type);
-        final MethodCallExpression callExpression = callX(fieldExpr, "get");
-        callExpression.setSafe(true);
-        body.addStatement(declS(resExpr, callExpression));
-
-        final Statement mainIf = ifElseS(notNullX(resExpr), stmt(resExpr), block(
-                assignS(resExpr, initExpr),
-                assignS(fieldExpr, ctorX(SOFT_REF, resExpr)),
-                stmt(resExpr)));
-
-        if (fieldNode.isVolatile()) {
-            body.addStatement(ifElseS(
-                    notNullX(resExpr),
-                    stmt(resExpr),
-                    new SynchronizedStatement(syncTarget(fieldNode), block(
-                            assignS(resExpr, callExpression),
-                            mainIf)
-                    )
-            ));
-        } else {
-            body.addStatement(mainIf);
-        }
-        addMethod(fieldNode, body, type);
-    }
-
-    private static void createSoftSetter(FieldNode fieldNode, ClassNode type) {
-        final BlockStatement body = new BlockStatement();
-        final Expression fieldExpr = varX(fieldNode);
-        final String name = "set" + MetaClassHelper.capitalize(fieldNode.getName().substring(1));
-        final Parameter parameter = param(type, "value");
-        final Expression paramExpr = varX(parameter);
-        body.addStatement(ifElseS(
-                notNullX(paramExpr),
-                assignS(fieldExpr, ctorX(SOFT_REF, paramExpr)),
-                assignS(fieldExpr, NULL_EXPR)
-        ));
-        int visibility = ACC_PUBLIC;
-        if (fieldNode.isStatic()) visibility |= ACC_STATIC;
-        ClassNode declaringClass = fieldNode.getDeclaringClass();
-        addGeneratedMethod(declaringClass, name, visibility, ClassHelper.VOID_TYPE, params(parameter), ClassNode.EMPTY_ARRAY, body);
-    }
-
-    private static Expression syncTarget(FieldNode fieldNode) {
-        return fieldNode.isStatic() ? classX(fieldNode.getDeclaringClass()) : varX("this");
-    }
-
-    private static Expression getInitExpr(ErrorCollecting xform, FieldNode fieldNode) {
-        Expression initExpr = fieldNode.getInitialValueExpression();
-        fieldNode.setInitialValueExpression(null);
-
-        if (initExpr == null || initExpr instanceof EmptyExpression) {
-            if (fieldNode.getType().isAbstract()) {
-                xform.addError("You cannot lazily initialize '" + fieldNode.getName() + "' from the abstract class '" +
-                        fieldNode.getType().getName() + "'", fieldNode);
-            }
-            initExpr = ctorX(fieldNode.getType());
-        }
-
-        return initExpr;
-    }
-}
+/*
+ *  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.transform;
+
+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.InnerClassNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+
+import java.lang.ref.SoftReference;
+
+import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
+import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
+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.classX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ifElseS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.notNullX;
+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;
+
+/**
+ * Handles generation of code for the @Lazy annotation
+ */
+@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+public class LazyASTTransformation extends AbstractASTTransformation {
+
+    private static final ClassNode SOFT_REF = makeWithoutCaching(SoftReference.class, false);
+    private static final Expression NULL_EXPR = ConstantExpression.NULL;
+
+    public void visit(ASTNode[] nodes, SourceUnit source) {
+        init(nodes, source);
+        AnnotatedNode parent = (AnnotatedNode) nodes[1];
+        AnnotationNode node = (AnnotationNode) nodes[0];
+
+        if (parent instanceof FieldNode) {
+            final FieldNode fieldNode = (FieldNode) parent;
+            visitField(this, node, fieldNode);
+        }
+    }
+
+    static void visitField(ErrorCollecting xform, AnnotationNode node, FieldNode fieldNode) {
+        final Expression soft = node.getMember("soft");
+        final Expression init = getInitExpr(xform, fieldNode);
+
+        String backingFieldName = "$" + fieldNode.getName();
+        fieldNode.rename(backingFieldName);
+        fieldNode.setModifiers(ACC_PRIVATE | (fieldNode.getModifiers() & (~(ACC_PUBLIC | ACC_PROTECTED))));
+        PropertyNode pNode = fieldNode.getDeclaringClass().getProperty(backingFieldName);
+        if (pNode != null) {
+            fieldNode.getDeclaringClass().getProperties().remove(pNode);
+        }
+
+        if (soft instanceof ConstantExpression && ((ConstantExpression) soft).getValue().equals(true)) {
+            createSoft(fieldNode, init);
+        } else {
+            create(fieldNode, init);
+            // @Lazy not meaningful with primitive so convert to wrapper if needed
+            if (ClassHelper.isPrimitiveType(fieldNode.getType())) {
+                fieldNode.setType(ClassHelper.getWrapper(fieldNode.getType()));
+            }
+        }
+    }
+
+    private static void create(FieldNode fieldNode, final Expression initExpr) {
+        final BlockStatement body = new BlockStatement();
+        if (fieldNode.isStatic()) {
+            addHolderClassIdiomBody(body, fieldNode, initExpr);
+        } else if (fieldNode.isVolatile()) {
+            addDoubleCheckedLockingBody(body, fieldNode, initExpr);
+        } else {
+            addNonThreadSafeBody(body, fieldNode, initExpr);
+        }
+        addMethod(fieldNode, body, fieldNode.getType());
+    }
+
+    private static void addHolderClassIdiomBody(BlockStatement body, FieldNode fieldNode, Expression initExpr) {
+        final ClassNode declaringClass = fieldNode.getDeclaringClass();
+        final ClassNode fieldType = fieldNode.getType();
+        final int visibility = ACC_PRIVATE | ACC_STATIC;
+        final String fullName = declaringClass.getName() + "$" + fieldType.getNameWithoutPackage() + "Holder_" + fieldNode.getName().substring(1);
+        final InnerClassNode holderClass = new InnerClassNode(declaringClass, fullName, visibility, ClassHelper.OBJECT_TYPE);
+        final String innerFieldName = "INSTANCE";
+
+        // we have two options:
+        // (1) embed initExpr within holder class but redirect field access/method calls to declaring class members
+        // (2) keep initExpr within a declaring class method that is only called by the holder class
+        // currently we have gone with (2) for simplicity with only a slight memory footprint increase in the declaring class
+        final String initializeMethodName = (fullName + "_initExpr").replace('.', '_');
+        addGeneratedMethod(declaringClass, initializeMethodName, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, fieldType,
+                Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, returnS(initExpr));
+        holderClass.addField(innerFieldName, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, fieldType,
+                callX(declaringClass, initializeMethodName));
+
+        final Expression innerField = propX(classX(holderClass), innerFieldName);
+        declaringClass.getModule().addClass(holderClass);
+        body.addStatement(returnS(innerField));
+    }
+
+    private static void addDoubleCheckedLockingBody(BlockStatement body, FieldNode fieldNode, Expression initExpr) {
+        final Expression fieldExpr = varX(fieldNode);
+        final VariableExpression localVar = localVarX(fieldNode.getName() + "_local");
+        body.addStatement(declS(localVar, fieldExpr));
+        body.addStatement(ifElseS(
+                notNullX(localVar),
+                returnS(localVar),
+                new SynchronizedStatement(
+                        syncTarget(fieldNode),
+                        ifElseS(
+                                notNullX(fieldExpr),
+                                returnS(fieldExpr),
+                                returnS(assignX(fieldExpr, initExpr))
+                        )
+                )
+        ));
+    }
+
+    private static void addNonThreadSafeBody(BlockStatement body, FieldNode fieldNode, Expression initExpr) {
+        final Expression fieldExpr = varX(fieldNode);
+        body.addStatement(ifElseS(notNullX(fieldExpr), stmt(fieldExpr), assignS(fieldExpr, initExpr)));
+    }
+
+    private static void addMethod(FieldNode fieldNode, BlockStatement body, ClassNode type) {
+        int visibility = ACC_PUBLIC;
+        if (fieldNode.isStatic()) visibility |= ACC_STATIC;
+        String propName = MetaClassHelper.capitalize(fieldNode.getName().substring(1));
+        ClassNode declaringClass = fieldNode.getDeclaringClass();
+        addGeneratedMethod(declaringClass, "get" + propName, visibility, type, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
+        if (ClassHelper.boolean_TYPE.equals(type)) {
+            addGeneratedMethod(declaringClass, "is" + propName, visibility, type,
+                    Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, stmt(callThisX("get" + propName)));
+        }
+    }
+
+    private static void createSoft(FieldNode fieldNode, Expression initExpr) {
+        final ClassNode type = fieldNode.getType();
+        fieldNode.setType(SOFT_REF);
+        createSoftGetter(fieldNode, initExpr, type);
+        createSoftSetter(fieldNode, type);
+    }
+
+    private static void createSoftGetter(FieldNode fieldNode, Expression initExpr, ClassNode type) {
+        final BlockStatement body = new BlockStatement();
+        final Expression fieldExpr = varX(fieldNode);
+        final Expression resExpr = localVarX("_result", type);
+        final MethodCallExpression callExpression = callX(fieldExpr, "get");
+        callExpression.setSafe(true);
+        body.addStatement(declS(resExpr, callExpression));
+
+        final Statement mainIf = ifElseS(notNullX(resExpr), stmt(resExpr), block(
+                assignS(resExpr, initExpr),
+                assignS(fieldExpr, ctorX(SOFT_REF, resExpr)),
+                stmt(resExpr)));
+
+        if (fieldNode.isVolatile()) {
+            body.addStatement(ifElseS(
+                    notNullX(resExpr),
+                    stmt(resExpr),
+                    new SynchronizedStatement(syncTarget(fieldNode), block(
+                            assignS(resExpr, callExpression),
+                            mainIf)
+                    )
+            ));
+        } else {
+            body.addStatement(mainIf);
+        }
+        addMethod(fieldNode, body, type);
+    }
+
+    private static void createSoftSetter(FieldNode fieldNode, ClassNode type) {
+        final BlockStatement body = new BlockStatement();
+        final Expression fieldExpr = varX(fieldNode);
+        final String name = "set" + MetaClassHelper.capitalize(fieldNode.getName().substring(1));
+        final Parameter parameter = param(type, "value");
+        final Expression paramExpr = varX(parameter);
+        body.addStatement(ifElseS(
+                notNullX(paramExpr),
+                assignS(fieldExpr, ctorX(SOFT_REF, paramExpr)),
+                assignS(fieldExpr, NULL_EXPR)
+        ));
+        int visibility = ACC_PUBLIC;
+        if (fieldNode.isStatic()) visibility |= ACC_STATIC;
+        ClassNode declaringClass = fieldNode.getDeclaringClass();
+        addGeneratedMethod(declaringClass, name, visibility, ClassHelper.VOID_TYPE, params(parameter), ClassNode.EMPTY_ARRAY, body);
+    }
+
+    private static Expression syncTarget(FieldNode fieldNode) {
+        return fieldNode.isStatic() ? classX(fieldNode.getDeclaringClass()) : varX("this");
+    }
+
+    private static Expression getInitExpr(ErrorCollecting xform, FieldNode fieldNode) {
+        Expression initExpr = fieldNode.getInitialValueExpression();
+        fieldNode.setInitialValueExpression(null);
+
+        if (initExpr == null || initExpr instanceof EmptyExpression) {
+            if (fieldNode.getType().isAbstract()) {
+                xform.addError("You cannot lazily initialize '" + fieldNode.getName() + "' from the abstract class '" +
+                        fieldNode.getType().getName() + "'", fieldNode);
+            }
+            initExpr = ctorX(fieldNode.getType());
+        }
+
+        return initExpr;
+    }
+}
diff --git a/src/main/java/org/codehaus/groovy/transform/SortableASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/SortableASTTransformation.java
index 4a1304a..9886d20 100644
--- a/src/main/java/org/codehaus/groovy/transform/SortableASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/SortableASTTransformation.java
@@ -63,6 +63,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.equalsNullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.getAllProperties;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.neX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.notNullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
@@ -147,12 +148,12 @@ public class SortableASTTransformation extends AbstractASTTransformation {
         if (properties.isEmpty()) {
             // perhaps overkill but let compareTo be based on hashes for commutativity
             // return this.hashCode() <=> other.hashCode()
-            statements.add(declS(varX(THIS_HASH, ClassHelper.Integer_TYPE), callX(varX("this"), "hashCode")));
-            statements.add(declS(varX(OTHER_HASH, ClassHelper.Integer_TYPE), callX(varX(OTHER), "hashCode")));
+            statements.add(declS(localVarX(THIS_HASH, ClassHelper.Integer_TYPE), callX(varX("this"), "hashCode")));
+            statements.add(declS(localVarX(OTHER_HASH, ClassHelper.Integer_TYPE), callX(varX(OTHER), "hashCode")));
             statements.add(returnS(compareExpr(varX(THIS_HASH), varX(OTHER_HASH), reversed)));
         } else {
             // int value = 0;
-            statements.add(declS(varX(VALUE, ClassHelper.int_TYPE), constX(0)));
+            statements.add(declS(localVarX(VALUE, ClassHelper.int_TYPE), constX(0)));
             for (PropertyNode property : properties) {
                 String propName = property.getName();
                 // value = this.prop <=> other.prop;
diff --git a/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java
index b0a0570..eff7aa6 100644
--- a/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java
@@ -181,7 +181,7 @@ public class ToStringASTTransformation extends AbstractASTTransformation {
         List<ToStringElement> elements = new ArrayList<ToStringElement>();
 
         // def $toStringFirst = true
-        final VariableExpression first = varX("$toStringFirst");
+        final VariableExpression first = localVarX("$toStringFirst");
         body.addStatement(declS(first, constX(Boolean.TRUE)));
 
         // <class_name>(