private Expression convertInOperatorToTernary(
     final BinaryExpression bin,
     final Expression rightExpression,
     final Expression leftExpression) {
   MethodCallExpression call = new MethodCallExpression(rightExpression, "isCase", leftExpression);
   call.setMethodTarget(
       (MethodNode) bin.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
   call.setSourcePosition(bin);
   call.copyNodeMetaData(bin);
   TernaryExpression tExp =
       new TernaryExpression(
           new BooleanExpression(
               new BinaryExpression(
                   rightExpression, Token.newSymbol("==", -1, -1), new ConstantExpression(null))),
           new BinaryExpression(
               leftExpression, Token.newSymbol("==", -1, -1), new ConstantExpression(null)),
           call);
   return staticCompilationTransformer.transform(tExp);
 }
 private TernaryExpression createIfInstanceOfAsImmutableStatement(
     Expression expr, ClassNode type, Expression elseStatement) {
   return new TernaryExpression(
       new BooleanExpression(
           new BinaryExpression(
               expr,
               Token.newSymbol(Types.KEYWORD_INSTANCEOF, -1, -1),
               new ClassExpression(type))),
       createAsImmutableExpression(expr, type),
       elseStatement);
 }
Beispiel #3
0
 public static ExpressionStatement createPrintlnStatement(String message, String variable) {
   return new ExpressionStatement(
       new MethodCallExpression(
           AbstractGrailsArtefactTransformer.THIS_EXPRESSION,
           "println",
           new ArgumentListExpression(
               new BinaryExpression(
                   new ConstantExpression(message),
                   Token.newSymbol(Types.PLUS, 0, 0),
                   new VariableExpression(variable)))));
 }
  private IfStatement getAutowiringIfStatement(
      ClassExpression targetClass,
      VariableExpression fieldExpression,
      BinaryExpression testTargetAssignment) {
    VariableExpression appCtxVar = new VariableExpression("applicationContext");

    BooleanExpression applicationContextCheck =
        new BooleanExpression(
            new BinaryExpression(
                new BinaryExpression(
                    fieldExpression,
                    GrailsASTUtils.EQUALS_OPERATOR,
                    GrailsASTUtils.NULL_EXPRESSION),
                Token.newSymbol("&&", 0, 0),
                new BinaryExpression(
                    appCtxVar,
                    GrailsASTUtils.NOT_EQUALS_OPERATOR,
                    GrailsASTUtils.NULL_EXPRESSION)));
    BlockStatement performAutowireBlock = new BlockStatement();
    ArgumentListExpression arguments = new ArgumentListExpression();
    arguments.addExpression(fieldExpression);
    arguments.addExpression(new ConstantExpression(1));
    arguments.addExpression(new ConstantExpression(false));
    BlockStatement assignFromApplicationContext = new BlockStatement();
    ArgumentListExpression argWithClassName = new ArgumentListExpression();
    MethodCallExpression getClassNameMethodCall =
        new MethodCallExpression(targetClass, "getName", new ArgumentListExpression());
    argWithClassName.addExpression(getClassNameMethodCall);

    assignFromApplicationContext.addStatement(
        new ExpressionStatement(
            new BinaryExpression(
                fieldExpression,
                ASSIGN,
                new MethodCallExpression(appCtxVar, "getBean", argWithClassName))));
    BlockStatement elseBlock = new BlockStatement();
    elseBlock.addStatement(new ExpressionStatement(testTargetAssignment));
    performAutowireBlock.addStatement(
        new IfStatement(
            new BooleanExpression(
                new MethodCallExpression(appCtxVar, "containsBean", argWithClassName)),
            assignFromApplicationContext,
            elseBlock));
    performAutowireBlock.addStatement(
        new ExpressionStatement(
            new MethodCallExpression(
                new PropertyExpression(appCtxVar, "autowireCapableBeanFactory"),
                "autowireBeanProperties",
                arguments)));
    return new IfStatement(applicationContextCheck, performAutowireBlock, new BlockStatement());
  }
Beispiel #5
0
 /**
  * Wraps a method body in try / catch logic that catches any errors and logs an error, but does
  * not rethrow!
  *
  * @param methodNode The method node
  */
 public static void wrapMethodBodyInTryCatchDebugStatements(MethodNode methodNode) {
   BlockStatement code = (BlockStatement) methodNode.getCode();
   BlockStatement newCode = new BlockStatement();
   TryCatchStatement tryCatchStatement = new TryCatchStatement(code, new BlockStatement());
   newCode.addStatement(tryCatchStatement);
   methodNode.setCode(newCode);
   BlockStatement catchBlock = new BlockStatement();
   ArgumentListExpression logArguments = new ArgumentListExpression();
   logArguments.addExpression(
       new BinaryExpression(
           new ConstantExpression("Error initializing class: "),
           Token.newSymbol(Types.PLUS, 0, 0),
           new VariableExpression("e")));
   logArguments.addExpression(new VariableExpression("e"));
   catchBlock.addStatement(
       new ExpressionStatement(
           new MethodCallExpression(new VariableExpression("log"), "error", logArguments)));
   tryCatchStatement.addCatch(
       new CatchStatement(new Parameter(new ClassNode(Throwable.class), "e"), catchBlock));
 }
  protected void addInitErrorsMethod(final ClassNode paramTypeClassNode) {
    final ASTNode initErrorsMethod =
        paramTypeClassNode.getMethod(
            INIT_ERRORS_METHOD_NAME, GrailsArtefactClassInjector.ZERO_PARAMETERS);
    if (initErrorsMethod == null) {
      final BlockStatement initErrorsMethodCode = new BlockStatement();

      final BinaryExpression errorsIsNullExpression =
          new BinaryExpression(
              ERRORS_EXPRESSION, Token.newSymbol(Types.COMPARE_EQUAL, 0, 0), NULL_EXPRESSION);

      Expression beanPropertyBindingResultConstructorArgs =
          new ArgumentListExpression(
              new VariableExpression("this"), new ConstantExpression(paramTypeClassNode.getName()));
      final Statement newEvaluatorExpression =
          new ExpressionStatement(
              new BinaryExpression(
                  ERRORS_EXPRESSION,
                  EQUALS_SYMBOL,
                  new ConstructorCallExpression(
                      new ClassNode(ValidationErrors.class),
                      beanPropertyBindingResultConstructorArgs)));
      final Statement initErrorsIfNullStatement =
          new IfStatement(
              new BooleanExpression(errorsIsNullExpression),
              newEvaluatorExpression,
              new ExpressionStatement(new EmptyExpression()));
      initErrorsMethodCode.addStatement(initErrorsIfNullStatement);
      paramTypeClassNode.addMethod(
          new MethodNode(
              INIT_ERRORS_METHOD_NAME,
              Modifier.PRIVATE,
              ClassHelper.VOID_TYPE,
              GrailsArtefactClassInjector.ZERO_PARAMETERS,
              GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY,
              initErrorsMethodCode));
    }
  }
 Expression transformMethodCallExpression(final MethodCallExpression expr) {
   Expression objectExpression = expr.getObjectExpression();
   if (expr.isSafe()) {
     MethodCallExpression notSafe =
         new MethodCallExpression(objectExpression, expr.getMethod(), expr.getArguments());
     notSafe.copyNodeMetaData(expr);
     notSafe.setSpreadSafe(expr.isSpreadSafe());
     notSafe.setSourcePosition(expr);
     notSafe.setMethodTarget(expr.getMethodTarget());
     TernaryExpression texpr =
         new TernaryExpression(
             new BooleanExpression(
                 new BinaryExpression(
                     objectExpression,
                     Token.newSymbol(
                         "!=",
                         objectExpression.getLineNumber(),
                         objectExpression.getColumnNumber()),
                     ConstantExpression.NULL)),
             notSafe,
             ConstantExpression.NULL);
     return staticCompilationTransformer.transform(texpr);
   }
   ClassNode type =
       staticCompilationTransformer
           .getTypeChooser()
           .resolveType(objectExpression, staticCompilationTransformer.getClassNode());
   if (type != null && type.isArray()) {
     String method = expr.getMethodAsString();
     ClassNode componentType = type.getComponentType();
     if ("getAt".equals(method)) {
       Expression arguments = expr.getArguments();
       if (arguments instanceof TupleExpression) {
         List<Expression> argList = ((TupleExpression) arguments).getExpressions();
         if (argList.size() == 1) {
           Expression indexExpr = argList.get(0);
           ClassNode argType =
               staticCompilationTransformer
                   .getTypeChooser()
                   .resolveType(indexExpr, staticCompilationTransformer.getClassNode());
           ClassNode indexType = ClassHelper.getWrapper(argType);
           if (componentType.isEnum() && ClassHelper.Number_TYPE == indexType) {
             // workaround for generated code in enums which use .next() returning a Number
             indexType = ClassHelper.Integer_TYPE;
           }
           if (argType != null && ClassHelper.Integer_TYPE == indexType) {
             BinaryExpression binaryExpression =
                 new BinaryExpression(
                     objectExpression,
                     Token.newSymbol("[", indexExpr.getLineNumber(), indexExpr.getColumnNumber()),
                     indexExpr);
             binaryExpression.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, componentType);
             return staticCompilationTransformer.transform(binaryExpression);
           }
         }
       }
     }
     if ("putAt".equals(method)) {
       Expression arguments = expr.getArguments();
       if (arguments instanceof TupleExpression) {
         List<Expression> argList = ((TupleExpression) arguments).getExpressions();
         if (argList.size() == 2) {
           Expression indexExpr = argList.get(0);
           Expression objExpr = argList.get(1);
           ClassNode argType =
               staticCompilationTransformer
                   .getTypeChooser()
                   .resolveType(indexExpr, staticCompilationTransformer.getClassNode());
           if (argType != null && ClassHelper.Integer_TYPE == ClassHelper.getWrapper(argType)) {
             BinaryExpression arrayGet =
                 new BinaryExpression(
                     objectExpression,
                     Token.newSymbol("[", indexExpr.getLineNumber(), indexExpr.getColumnNumber()),
                     indexExpr);
             arrayGet.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, componentType);
             BinaryExpression assignment =
                 new BinaryExpression(
                     arrayGet,
                     Token.newSymbol("=", objExpr.getLineNumber(), objExpr.getColumnNumber()),
                     objExpr);
             return staticCompilationTransformer.transform(assignment);
           }
         }
       }
     }
   }
   return staticCompilationTransformer.superTransform(expr);
 }
/**
 * Transformation used by the {@link grails.test.mixin.TestFor} annotation to signify the class
 * under test.
 *
 * @author Graeme Rocher
 * @since 2.0
 */
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
@SuppressWarnings("rawtypes")
public class TestForTransformation extends TestMixinTransformation {

  private static final ClassNode MY_TYPE = new ClassNode(TestFor.class);
  private static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage();
  private static final Token ASSIGN = Token.newSymbol("=", -1, -1);

  protected static final Map<String, Class> artefactTypeToTestMap = new HashMap<String, Class>();

  static {
    artefactTypeToTestMap.put(ControllerArtefactHandler.TYPE, ControllerUnitTestMixin.class);
    artefactTypeToTestMap.put(TagLibArtefactHandler.TYPE, GroovyPageUnitTestMixin.class);
    artefactTypeToTestMap.put(
        FiltersConfigArtefactHandler.getTYPE().toString(), FiltersUnitTestMixin.class);
    artefactTypeToTestMap.put(UrlMappingsArtefactHandler.TYPE, UrlMappingsUnitTestMixin.class);
    artefactTypeToTestMap.put(ServiceArtefactHandler.TYPE, ServiceUnitTestMixin.class);
  }

  public static final String DOMAIN_TYPE = "Domain";
  public static final ClassNode BEFORE_CLASS_NODE = new ClassNode(Before.class);
  public static final AnnotationNode BEFORE_ANNOTATION = new AnnotationNode(BEFORE_CLASS_NODE);

  public static final AnnotationNode TEST_ANNOTATION =
      new AnnotationNode(new ClassNode(Test.class));
  public static final ClassNode GROOVY_TEST_CASE_CLASS = new ClassNode(GroovyTestCase.class);

  private ResourceLocator resourceLocator;

  public ResourceLocator getResourceLocator() {
    if (resourceLocator == null) {
      resourceLocator = new DefaultResourceLocator();
      BuildSettings buildSettings = BuildSettingsHolder.getSettings();
      String basedir;
      if (buildSettings != null) {
        basedir = buildSettings.getBaseDir().getAbsolutePath();
      } else {
        basedir = ".";
      }

      resourceLocator.setSearchLocation(basedir);
    }
    return resourceLocator;
  }

  @Override
  public void visit(ASTNode[] astNodes, SourceUnit source) {
    if (!(astNodes[0] instanceof AnnotationNode) || !(astNodes[1] instanceof AnnotatedNode)) {
      throw new RuntimeException("Internal error: wrong types: $node.class / $parent.class");
    }

    AnnotatedNode parent = (AnnotatedNode) astNodes[1];
    AnnotationNode node = (AnnotationNode) astNodes[0];
    if (!MY_TYPE.equals(node.getClassNode()) || !(parent instanceof ClassNode)) {
      return;
    }

    ClassNode classNode = (ClassNode) parent;
    if (classNode.isInterface() || Modifier.isAbstract(classNode.getModifiers())) {
      return;
    }

    boolean junit3Test = isJunit3Test(classNode);
    boolean spockTest = isSpockTest(classNode);
    boolean isJunit = classNode.getName().endsWith("Tests");

    if (!junit3Test && !spockTest && !isJunit) return;

    Expression value = node.getMember("value");
    ClassExpression ce;
    if (value instanceof ClassExpression) {
      ce = (ClassExpression) value;
      testFor(classNode, ce);
    } else {
      if (!junit3Test) {
        List<AnnotationNode> annotations = classNode.getAnnotations(MY_TYPE);
        if (annotations.size() > 0)
          return; // bail out, in this case it was already applied as a local transform
        // no explicit class specified try by convention
        String fileName = source.getName();
        String className = GrailsResourceUtils.getClassName(new FileSystemResource(fileName));
        if (className != null) {
          boolean isSpock = className.endsWith("Spec");
          String targetClassName = null;

          if (isJunit) {
            targetClassName = className.substring(0, className.indexOf("Tests"));
          } else if (isSpock) {
            targetClassName = className.substring(0, className.indexOf("Spec"));
          }

          if (targetClassName != null) {
            Resource targetResource =
                getResourceLocator().findResourceForClassName(targetClassName);
            if (targetResource != null) {
              try {
                if (GrailsResourceUtils.isDomainClass(targetResource.getURL())) {
                  testFor(
                      classNode,
                      new ClassExpression(
                          new ClassNode(targetClassName, 0, ClassHelper.OBJECT_TYPE)));
                } else {
                  for (String artefactType : artefactTypeToTestMap.keySet()) {
                    if (targetClassName.endsWith(artefactType)) {
                      testFor(
                          classNode,
                          new ClassExpression(
                              new ClassNode(targetClassName, 0, ClassHelper.OBJECT_TYPE)));
                      break;
                    }
                  }
                }
              } catch (IOException e) {
                // ignore
              }
            }
          }
        }
      }
    }
  }

  /**
   * Main entry point for the calling the TestForTransformation programmatically.
   *
   * @param classNode The class node that represents th test
   * @param ce The class expression that represents the class to test
   */
  public void testFor(ClassNode classNode, ClassExpression ce) {
    boolean junit3Test = isJunit3Test(classNode);

    // make sure the 'log' property is not the one from GroovyTestCase
    FieldNode log = classNode.getField("log");
    if (log == null || log.getDeclaringClass().equals(GROOVY_TEST_CASE_CLASS)) {
      LoggingTransformer.addLogField(classNode, classNode.getName());
    }
    boolean isSpockTest = isSpockTest(classNode);

    if (!isSpockTest && !junit3Test) {
      // assume JUnit 4
      Map<String, MethodNode> declaredMethodsMap = classNode.getDeclaredMethodsMap();
      for (String methodName : declaredMethodsMap.keySet()) {
        MethodNode methodNode = declaredMethodsMap.get(methodName);
        if (isCandidateMethod(methodNode) && methodNode.getName().startsWith("test")) {
          if (methodNode.getAnnotations().size() == 0) {
            methodNode.addAnnotation(TEST_ANNOTATION);
          }
        }
      }
    }

    final MethodNode methodToAdd = weaveMock(classNode, ce, true);
    if (methodToAdd != null && junit3Test) {
      addMethodCallsToMethod(classNode, SET_UP_METHOD, Arrays.asList(methodToAdd));
    }
  }

  private Map<ClassNode, List<Class>> wovenMixins = new HashMap<ClassNode, List<Class>>();

  protected MethodNode weaveMock(
      ClassNode classNode, ClassExpression value, boolean isClassUnderTest) {

    ClassNode testTarget = value.getType();
    String className = testTarget.getName();
    MethodNode testForMethod = null;
    for (String artefactType : artefactTypeToTestMap.keySet()) {
      if (className.endsWith(artefactType)) {
        Class mixinClass = artefactTypeToTestMap.get(artefactType);
        if (!isAlreadyWoven(classNode, mixinClass)) {
          weaveMixinClass(classNode, mixinClass);
          if (isClassUnderTest) {
            testForMethod = addClassUnderTestMethod(classNode, value, artefactType);
          } else {
            addMockCollaboratorToSetup(classNode, value, artefactType);
          }
          return testForMethod;
        }

        addMockCollaboratorToSetup(classNode, value, artefactType);
        return null;
      }
    }

    // must be a domain class
    weaveMixinClass(classNode, DomainClassUnitTestMixin.class);
    if (isClassUnderTest) {
      testForMethod = addClassUnderTestMethod(classNode, value, DOMAIN_TYPE);
    } else {
      addMockCollaboratorToSetup(classNode, value, DOMAIN_TYPE);
    }

    return testForMethod;
  }

  private void addMockCollaboratorToSetup(
      ClassNode classNode, ClassExpression targetClassExpression, String artefactType) {

    BlockStatement methodBody;
    if (isJunit3Test(classNode)) {
      methodBody = getJunit3Setup(classNode);
      addMockCollaborator(artefactType, targetClassExpression, methodBody);
    } else {
      addToJunit4BeforeMethods(classNode, artefactType, targetClassExpression);
    }
  }

  private void addToJunit4BeforeMethods(
      ClassNode classNode, String artefactType, ClassExpression targetClassExpression) {
    Map<String, MethodNode> declaredMethodsMap = classNode.getDeclaredMethodsMap();
    boolean weavedIntoBeforeMethods = false;
    for (MethodNode methodNode : declaredMethodsMap.values()) {
      if (isDeclaredBeforeMethod(methodNode)) {
        Statement code = getMethodBody(methodNode);
        addMockCollaborator(artefactType, targetClassExpression, (BlockStatement) code);
        weavedIntoBeforeMethods = true;
      }
    }

    if (!weavedIntoBeforeMethods) {
      BlockStatement junit4Setup = getJunit4Setup(classNode);
      addMockCollaborator(artefactType, targetClassExpression, junit4Setup);
    }
  }

  private Statement getMethodBody(MethodNode methodNode) {
    Statement code = methodNode.getCode();
    if (!(code instanceof BlockStatement)) {
      BlockStatement body = new BlockStatement();
      body.addStatement(code);
      code = body;
    }
    return code;
  }

  private boolean isDeclaredBeforeMethod(MethodNode methodNode) {
    return isPublicInstanceMethod(methodNode)
        && hasAnnotation(methodNode, Before.class)
        && !hasAnnotation(methodNode, MixinMethod.class);
  }

  private boolean isPublicInstanceMethod(MethodNode methodNode) {
    return !methodNode.isSynthetic() && !methodNode.isStatic() && methodNode.isPublic();
  }

  private BlockStatement getJunit4Setup(ClassNode classNode) {
    MethodNode setupMethod =
        classNode.getMethod(SET_UP_METHOD, GrailsArtefactClassInjector.ZERO_PARAMETERS);
    if (setupMethod == null) {
      setupMethod =
          new MethodNode(
              SET_UP_METHOD,
              Modifier.PUBLIC,
              ClassHelper.VOID_TYPE,
              GrailsArtefactClassInjector.ZERO_PARAMETERS,
              null,
              new BlockStatement());
      setupMethod.addAnnotation(MIXIN_METHOD_ANNOTATION);
      classNode.addMethod(setupMethod);
    }
    if (setupMethod.getAnnotations(BEFORE_CLASS_NODE).size() == 0) {
      setupMethod.addAnnotation(BEFORE_ANNOTATION);
    }
    return getOrCreateMethodBody(classNode, setupMethod, SET_UP_METHOD);
  }

  private BlockStatement getJunit3Setup(ClassNode classNode) {
    return getOrCreateNoArgsMethodBody(classNode, SET_UP_METHOD);
  }

  private boolean isAlreadyWoven(ClassNode classNode, Class mixinClass) {
    List<Class> mixinClasses = wovenMixins.get(classNode);
    if (mixinClasses == null) {
      mixinClasses = new ArrayList<Class>();
      mixinClasses.add(mixinClass);
      wovenMixins.put(classNode, mixinClasses);
    } else {
      if (mixinClasses.contains(mixinClass)) {
        return true;
      }

      mixinClasses.add(mixinClass);
    }
    return false;
  }

  protected void weaveMixinClass(ClassNode classNode, Class mixinClass) {
    ListExpression listExpression = new ListExpression();
    listExpression.addExpression(new ClassExpression(new ClassNode(mixinClass)));
    weaveMixinsIntoClass(classNode, listExpression);
  }

  protected MethodNode addClassUnderTestMethod(
      ClassNode classNode, ClassExpression targetClass, String type) {

    String methodName = "setup" + type + "UnderTest";
    String fieldName = GrailsNameUtils.getPropertyName(type);
    String getterName = GrailsNameUtils.getGetterName(fieldName);
    fieldName = '$' + fieldName;

    if (classNode.getField(fieldName) == null) {
      classNode.addField(fieldName, Modifier.PRIVATE, targetClass.getType(), null);
    }

    MethodNode methodNode =
        classNode.getMethod(methodName, GrailsArtefactClassInjector.ZERO_PARAMETERS);

    VariableExpression fieldExpression = new VariableExpression(fieldName);
    if (methodNode == null) {
      BlockStatement setupMethodBody = new BlockStatement();
      addMockCollaborator(type, targetClass, setupMethodBody);

      methodNode =
          new MethodNode(
              methodName,
              Modifier.PUBLIC,
              ClassHelper.VOID_TYPE,
              GrailsArtefactClassInjector.ZERO_PARAMETERS,
              null,
              setupMethodBody);
      methodNode.addAnnotation(BEFORE_ANNOTATION);
      methodNode.addAnnotation(MIXIN_METHOD_ANNOTATION);
      classNode.addMethod(methodNode);
    }

    MethodNode getter =
        classNode.getMethod(getterName, GrailsArtefactClassInjector.ZERO_PARAMETERS);
    if (getter == null) {
      BlockStatement getterBody = new BlockStatement();
      getter =
          new MethodNode(
              getterName,
              Modifier.PUBLIC,
              targetClass.getType().getPlainNodeReference(),
              GrailsArtefactClassInjector.ZERO_PARAMETERS,
              null,
              getterBody);

      BinaryExpression testTargetAssignment =
          new BinaryExpression(
              fieldExpression,
              ASSIGN,
              new ConstructorCallExpression(
                  targetClass.getType(), GrailsArtefactClassInjector.ZERO_ARGS));

      IfStatement autowiringIfStatement =
          getAutowiringIfStatement(targetClass, fieldExpression, testTargetAssignment);
      getterBody.addStatement(autowiringIfStatement);

      getterBody.addStatement(new ReturnStatement(fieldExpression));
      classNode.addMethod(getter);
    }

    return methodNode;
  }

  private IfStatement getAutowiringIfStatement(
      ClassExpression targetClass,
      VariableExpression fieldExpression,
      BinaryExpression testTargetAssignment) {
    VariableExpression appCtxVar = new VariableExpression("applicationContext");

    BooleanExpression applicationContextCheck =
        new BooleanExpression(
            new BinaryExpression(
                new BinaryExpression(
                    fieldExpression,
                    GrailsASTUtils.EQUALS_OPERATOR,
                    GrailsASTUtils.NULL_EXPRESSION),
                Token.newSymbol("&&", 0, 0),
                new BinaryExpression(
                    appCtxVar,
                    GrailsASTUtils.NOT_EQUALS_OPERATOR,
                    GrailsASTUtils.NULL_EXPRESSION)));
    BlockStatement performAutowireBlock = new BlockStatement();
    ArgumentListExpression arguments = new ArgumentListExpression();
    arguments.addExpression(fieldExpression);
    arguments.addExpression(new ConstantExpression(1));
    arguments.addExpression(new ConstantExpression(false));
    BlockStatement assignFromApplicationContext = new BlockStatement();
    ArgumentListExpression argWithClassName = new ArgumentListExpression();
    MethodCallExpression getClassNameMethodCall =
        new MethodCallExpression(targetClass, "getName", new ArgumentListExpression());
    argWithClassName.addExpression(getClassNameMethodCall);

    assignFromApplicationContext.addStatement(
        new ExpressionStatement(
            new BinaryExpression(
                fieldExpression,
                ASSIGN,
                new MethodCallExpression(appCtxVar, "getBean", argWithClassName))));
    BlockStatement elseBlock = new BlockStatement();
    elseBlock.addStatement(new ExpressionStatement(testTargetAssignment));
    performAutowireBlock.addStatement(
        new IfStatement(
            new BooleanExpression(
                new MethodCallExpression(appCtxVar, "containsBean", argWithClassName)),
            assignFromApplicationContext,
            elseBlock));
    performAutowireBlock.addStatement(
        new ExpressionStatement(
            new MethodCallExpression(
                new PropertyExpression(appCtxVar, "autowireCapableBeanFactory"),
                "autowireBeanProperties",
                arguments)));
    return new IfStatement(applicationContextCheck, performAutowireBlock, new BlockStatement());
  }

  protected void addMockCollaborator(
      String mockType, ClassExpression targetClass, BlockStatement methodBody) {
    ArgumentListExpression args = new ArgumentListExpression();
    args.addExpression(targetClass);
    methodBody
        .getStatements()
        .add(
            0,
            new ExpressionStatement(
                new MethodCallExpression(THIS_EXPRESSION, "mock" + mockType, args)));
  }
}
/** @author Jeff Brown */
public class ASTValidationErrorsHelper implements ASTErrorsHelper {
  private static final ConstantExpression NULL_EXPRESSION = new ConstantExpression(null);
  private static final String SET_ERRORS_METHOD_NAME = "setErrors";
  private static final String GET_ERRORS_METHOD_NAME = "getErrors";
  private static final String HAS_ERRORS_METHOD_NAME = "hasErrors";
  private static final String CLEAR_ERRORS_METHOD_NAME = "clearErrors";
  private static final String INIT_ERRORS_METHOD_NAME = "initErrors";
  private static final String ERRORS_PROPERTY_NAME = "errors";
  private static final Token EQUALS_SYMBOL = Token.newSymbol(Types.EQUALS, 0, 0);
  private static final ClassNode ERRORS_CLASS_NODE = new ClassNode(Errors.class);
  private static final VariableExpression ERRORS_EXPRESSION =
      new VariableExpression(ERRORS_PROPERTY_NAME);
  private static final TupleExpression EMPTY_TUPLE = new TupleExpression();

  public void injectErrorsCode(ClassNode classNode) {
    addErrorsField(classNode);
    addInitErrorsMethod(classNode);
    addGetErrorsMethod(classNode);
    addHasErrorsMethod(classNode);
    addSetErrorsMethod(classNode);
    addClearErrorsMethod(classNode);
  }

  protected void addErrorsField(final ClassNode paramTypeClassNode) {
    final ASTNode errorsField = paramTypeClassNode.getField(ERRORS_PROPERTY_NAME);
    if (errorsField == null) {
      paramTypeClassNode.addField(
          new FieldNode(
              ERRORS_PROPERTY_NAME,
              Modifier.PRIVATE,
              ERRORS_CLASS_NODE,
              paramTypeClassNode,
              NULL_EXPRESSION));
    }
  }

  protected void addInitErrorsMethod(final ClassNode paramTypeClassNode) {
    final ASTNode initErrorsMethod =
        paramTypeClassNode.getMethod(
            INIT_ERRORS_METHOD_NAME, GrailsArtefactClassInjector.ZERO_PARAMETERS);
    if (initErrorsMethod == null) {
      final BlockStatement initErrorsMethodCode = new BlockStatement();

      final BinaryExpression errorsIsNullExpression =
          new BinaryExpression(
              ERRORS_EXPRESSION, Token.newSymbol(Types.COMPARE_EQUAL, 0, 0), NULL_EXPRESSION);

      Expression beanPropertyBindingResultConstructorArgs =
          new ArgumentListExpression(
              new VariableExpression("this"), new ConstantExpression(paramTypeClassNode.getName()));
      final Statement newEvaluatorExpression =
          new ExpressionStatement(
              new BinaryExpression(
                  ERRORS_EXPRESSION,
                  EQUALS_SYMBOL,
                  new ConstructorCallExpression(
                      new ClassNode(ValidationErrors.class),
                      beanPropertyBindingResultConstructorArgs)));
      final Statement initErrorsIfNullStatement =
          new IfStatement(
              new BooleanExpression(errorsIsNullExpression),
              newEvaluatorExpression,
              new ExpressionStatement(new EmptyExpression()));
      initErrorsMethodCode.addStatement(initErrorsIfNullStatement);
      paramTypeClassNode.addMethod(
          new MethodNode(
              INIT_ERRORS_METHOD_NAME,
              Modifier.PRIVATE,
              ClassHelper.VOID_TYPE,
              GrailsArtefactClassInjector.ZERO_PARAMETERS,
              GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY,
              initErrorsMethodCode));
    }
  }

  protected void addClearErrorsMethod(final ClassNode paramTypeClassNode) {
    final ASTNode clearErrorsMethod =
        paramTypeClassNode.getMethod(
            CLEAR_ERRORS_METHOD_NAME, GrailsArtefactClassInjector.ZERO_PARAMETERS);
    if (clearErrorsMethod == null) {
      final BlockStatement clearErrorsMethodCode = new BlockStatement();
      Expression nullOutErrorsFieldExpression =
          new BinaryExpression(ERRORS_EXPRESSION, EQUALS_SYMBOL, NULL_EXPRESSION);
      clearErrorsMethodCode.addStatement(new ExpressionStatement(nullOutErrorsFieldExpression));
      paramTypeClassNode.addMethod(
          new MethodNode(
              CLEAR_ERRORS_METHOD_NAME,
              Modifier.PUBLIC,
              ClassHelper.VOID_TYPE,
              GrailsArtefactClassInjector.ZERO_PARAMETERS,
              GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY,
              clearErrorsMethodCode));
    }
  }

  protected void addHasErrorsMethod(final ClassNode paramTypeClassNode) {
    final ASTNode getErrorsMethod =
        paramTypeClassNode.getMethod(
            HAS_ERRORS_METHOD_NAME, GrailsArtefactClassInjector.ZERO_PARAMETERS);
    if (getErrorsMethod == null) {
      final BlockStatement hasErrorsMethodCode = new BlockStatement();
      final Expression initErrorsMethodCallExpression =
          new MethodCallExpression(
              new VariableExpression("this"), INIT_ERRORS_METHOD_NAME, EMPTY_TUPLE);
      hasErrorsMethodCode.addStatement(new ExpressionStatement(initErrorsMethodCallExpression));
      final Statement returnStatement =
          new ReturnStatement(
              new BooleanExpression(
                  new MethodCallExpression(
                      ERRORS_EXPRESSION, HAS_ERRORS_METHOD_NAME, EMPTY_TUPLE)));
      hasErrorsMethodCode.addStatement(returnStatement);
      paramTypeClassNode.addMethod(
          new MethodNode(
              HAS_ERRORS_METHOD_NAME,
              Modifier.PUBLIC,
              new ClassNode(Boolean.class),
              GrailsArtefactClassInjector.ZERO_PARAMETERS,
              GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY,
              hasErrorsMethodCode));
    }
  }

  protected void addGetErrorsMethod(final ClassNode paramTypeClassNode) {
    final ASTNode getErrorsMethod =
        paramTypeClassNode.getMethod(
            GET_ERRORS_METHOD_NAME, GrailsArtefactClassInjector.ZERO_PARAMETERS);
    if (getErrorsMethod == null) {
      final BlockStatement getErrorsMethodCode = new BlockStatement();
      final Expression initErrorsMethodCallExpression =
          new MethodCallExpression(
              new VariableExpression("this"), INIT_ERRORS_METHOD_NAME, EMPTY_TUPLE);
      getErrorsMethodCode.addStatement(new ExpressionStatement(initErrorsMethodCallExpression));
      final Statement returnStatement = new ReturnStatement(ERRORS_EXPRESSION);
      getErrorsMethodCode.addStatement(returnStatement);
      paramTypeClassNode.addMethod(
          new MethodNode(
              GET_ERRORS_METHOD_NAME,
              Modifier.PUBLIC,
              ERRORS_CLASS_NODE,
              GrailsArtefactClassInjector.ZERO_PARAMETERS,
              GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY,
              getErrorsMethodCode));
    }
  }

  protected void addSetErrorsMethod(final ClassNode paramTypeClassNode) {
    final String errorsArgumentName = "$errorsArg";
    MethodNode setErrorsMethod =
        paramTypeClassNode.getMethod(
            SET_ERRORS_METHOD_NAME,
            new Parameter[] {new Parameter(ERRORS_CLASS_NODE, errorsArgumentName)});
    if (setErrorsMethod == null) {
      final Expression assignErrorsExpression =
          new BinaryExpression(
              ERRORS_EXPRESSION, EQUALS_SYMBOL, new VariableExpression(errorsArgumentName));
      setErrorsMethod =
          new MethodNode(
              SET_ERRORS_METHOD_NAME,
              Modifier.PUBLIC,
              ClassHelper.VOID_TYPE,
              new Parameter[] {new Parameter(ERRORS_CLASS_NODE, errorsArgumentName)},
              GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY,
              new ExpressionStatement(assignErrorsExpression));
      paramTypeClassNode.addMethod(setErrorsMethod);
    }
  }
}
  public void eval(BinaryExpression expression) {
    switch (expression.getOperation().getType()) {
      case EQUAL: // = assignment
        evaluateEqual(expression, false);
        break;

      case COMPARE_EQUAL: // ==
        evaluateCompareExpression(compareEqualMethod, expression);
        break;

      case COMPARE_NOT_EQUAL:
        evaluateCompareExpression(compareNotEqualMethod, expression);
        break;

      case COMPARE_TO:
        evaluateCompareTo(expression);
        break;

      case COMPARE_GREATER_THAN:
        evaluateCompareExpression(compareGreaterThanMethod, expression);
        break;

      case COMPARE_GREATER_THAN_EQUAL:
        evaluateCompareExpression(compareGreaterThanEqualMethod, expression);
        break;

      case COMPARE_LESS_THAN:
        evaluateCompareExpression(compareLessThanMethod, expression);
        break;

      case COMPARE_LESS_THAN_EQUAL:
        evaluateCompareExpression(compareLessThanEqualMethod, expression);
        break;

      case LOGICAL_AND:
        evaluateLogicalAndExpression(expression);
        break;

      case LOGICAL_OR:
        evaluateLogicalOrExpression(expression);
        break;

      case BITWISE_AND:
        evaluateBinaryExpression("and", expression);
        break;

      case BITWISE_AND_EQUAL:
        evaluateBinaryExpressionWithAssignment("and", expression);
        break;

      case BITWISE_OR:
        evaluateBinaryExpression("or", expression);
        break;

      case BITWISE_OR_EQUAL:
        evaluateBinaryExpressionWithAssignment("or", expression);
        break;

      case BITWISE_XOR:
        evaluateBinaryExpression("xor", expression);
        break;

      case BITWISE_XOR_EQUAL:
        evaluateBinaryExpressionWithAssignment("xor", expression);
        break;

      case PLUS:
        evaluateBinaryExpression("plus", expression);
        break;

      case PLUS_EQUAL:
        evaluateBinaryExpressionWithAssignment("plus", expression);
        break;

      case MINUS:
        evaluateBinaryExpression("minus", expression);
        break;

      case MINUS_EQUAL:
        evaluateBinaryExpressionWithAssignment("minus", expression);
        break;

      case MULTIPLY:
        evaluateBinaryExpression("multiply", expression);
        break;

      case MULTIPLY_EQUAL:
        evaluateBinaryExpressionWithAssignment("multiply", expression);
        break;

      case DIVIDE:
        evaluateBinaryExpression("div", expression);
        break;

      case DIVIDE_EQUAL:
        // SPG don't use divide since BigInteger implements directly
        // and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
        evaluateBinaryExpressionWithAssignment("div", expression);
        break;

      case INTDIV:
        evaluateBinaryExpression("intdiv", expression);
        break;

      case INTDIV_EQUAL:
        evaluateBinaryExpressionWithAssignment("intdiv", expression);
        break;

      case MOD:
        evaluateBinaryExpression("mod", expression);
        break;

      case MOD_EQUAL:
        evaluateBinaryExpressionWithAssignment("mod", expression);
        break;

      case POWER:
        evaluateBinaryExpression("power", expression);
        break;

      case POWER_EQUAL:
        evaluateBinaryExpressionWithAssignment("power", expression);
        break;

      case LEFT_SHIFT:
        evaluateBinaryExpression("leftShift", expression);
        break;

      case LEFT_SHIFT_EQUAL:
        evaluateBinaryExpressionWithAssignment("leftShift", expression);
        break;

      case RIGHT_SHIFT:
        evaluateBinaryExpression("rightShift", expression);
        break;

      case RIGHT_SHIFT_EQUAL:
        evaluateBinaryExpressionWithAssignment("rightShift", expression);
        break;

      case RIGHT_SHIFT_UNSIGNED:
        evaluateBinaryExpression("rightShiftUnsigned", expression);
        break;

      case RIGHT_SHIFT_UNSIGNED_EQUAL:
        evaluateBinaryExpressionWithAssignment("rightShiftUnsigned", expression);
        break;

      case KEYWORD_INSTANCEOF:
        evaluateInstanceof(expression);
        break;

      case FIND_REGEX:
        evaluateCompareExpression(findRegexMethod, expression);
        break;

      case MATCH_REGEX:
        evaluateCompareExpression(matchRegexMethod, expression);
        break;

      case LEFT_SQUARE_BRACKET:
        if (controller.getCompileStack().isLHS()) {
          evaluateEqual(expression, false);
        } else {
          evaluateBinaryExpression("getAt", expression);
        }
        break;

      case KEYWORD_IN:
        evaluateCompareExpression(isCaseMethod, expression);
        break;

      case COMPARE_IDENTICAL:
      case COMPARE_NOT_IDENTICAL:
        Token op = expression.getOperation();
        Throwable cause =
            new SyntaxException(
                "Operator " + op + " not supported",
                op.getStartLine(),
                op.getStartColumn(),
                op.getStartLine(),
                op.getStartColumn() + 3);
        throw new GroovyRuntimeException(cause);

      default:
        throw new GroovyBugError("Operation: " + expression.getOperation() + " not supported");
    }
  }
Beispiel #11
0
/**
 * Helper methods for working with Groovy AST trees.
 *
 * @author Graeme Rocher
 * @since 0.3
 */
public class GrailsASTUtils {

  public static final String METHOD_MISSING_METHOD_NAME = "methodMissing";
  public static final String STATIC_METHOD_MISSING_METHOD_NAME = "$static_methodMissing";
  public static final Token EQUALS_OPERATOR = Token.newSymbol("==", 0, 0);
  public static final Token NOT_EQUALS_OPERATOR = Token.newSymbol("!=", 0, 0);

  public static final ClassNode MISSING_METHOD_EXCEPTION =
      new ClassNode(MissingMethodException.class);
  public static final ConstantExpression NULL_EXPRESSION = new ConstantExpression(null);
  public static final Token ASSIGNMENT_OPERATOR = Token.newSymbol(Types.ASSIGNMENT_OPERATOR, 0, 0);

  public static void warning(
      final SourceUnit sourceUnit, final ASTNode node, final String warningMessage) {
    final String sample =
        sourceUnit.getSample(node.getLineNumber(), node.getColumnNumber(), new Janitor());
    GrailsConsole.getInstance().warning(warningMessage + "\n\n" + sample);
  }

  /**
   * Generates a fatal compilation error
   *
   * @param sourceUnit the SourceUnit
   * @param astNode the ASTNode which caused the error
   * @param message The error message
   */
  public static void error(
      final SourceUnit sourceUnit, final ASTNode astNode, final String message) {
    error(sourceUnit, astNode, message, true);
  }

  /**
   * Generates a fatal compilation error
   *
   * @param sourceUnit the SourceUnit
   * @param astNode the ASTNode which caused the error
   * @param message The error message
   * @param fatal indicates if this is a fatal error
   */
  public static void error(
      final SourceUnit sourceUnit,
      final ASTNode astNode,
      final String message,
      final boolean fatal) {
    final SyntaxException syntaxException =
        new SyntaxException(message, astNode.getLineNumber(), astNode.getColumnNumber());
    final SyntaxErrorMessage syntaxErrorMessage =
        new SyntaxErrorMessage(syntaxException, sourceUnit);
    sourceUnit.getErrorCollector().addError(syntaxErrorMessage, fatal);
  }

  /**
   * Returns whether a classNode has the specified property or not
   *
   * @param classNode The ClassNode
   * @param propertyName The name of the property
   * @return True if the property exists in the ClassNode
   */
  public static boolean hasProperty(ClassNode classNode, String propertyName) {
    if (classNode == null || StringUtils.isBlank(propertyName)) {
      return false;
    }

    final MethodNode method =
        classNode.getMethod(GrailsNameUtils.getGetterName(propertyName), Parameter.EMPTY_ARRAY);
    if (method != null) return true;

    for (PropertyNode pn : classNode.getProperties()) {
      if (pn.getName().equals(propertyName) && !pn.isPrivate()) {
        return true;
      }
    }

    return false;
  }

  public static boolean hasOrInheritsProperty(ClassNode classNode, String propertyName) {
    if (hasProperty(classNode, propertyName)) {
      return true;
    }

    ClassNode parent = classNode.getSuperClass();
    while (parent != null && !getFullName(parent).equals("java.lang.Object")) {
      if (hasProperty(parent, propertyName)) {
        return true;
      }
      parent = parent.getSuperClass();
    }

    return false;
  }

  /**
   * Tests whether the ClasNode implements the specified method name.
   *
   * @param classNode The ClassNode
   * @param methodName The method name
   * @return True if it does implement the method
   */
  public static boolean implementsZeroArgMethod(ClassNode classNode, String methodName) {
    MethodNode method = classNode.getDeclaredMethod(methodName, Parameter.EMPTY_ARRAY);
    return method != null && (method.isPublic() || method.isProtected()) && !method.isAbstract();
  }

  @SuppressWarnings("rawtypes")
  public static boolean implementsOrInheritsZeroArgMethod(
      ClassNode classNode, String methodName, List ignoreClasses) {
    if (implementsZeroArgMethod(classNode, methodName)) {
      return true;
    }

    ClassNode parent = classNode.getSuperClass();
    while (parent != null && !getFullName(parent).equals("java.lang.Object")) {
      if (!ignoreClasses.contains(parent) && implementsZeroArgMethod(parent, methodName)) {
        return true;
      }
      parent = parent.getSuperClass();
    }
    return false;
  }

  /**
   * Gets the full name of a ClassNode.
   *
   * @param classNode The class node
   * @return The full name
   */
  public static String getFullName(ClassNode classNode) {
    return classNode.getName();
  }

  public static ClassNode getFurthestParent(ClassNode classNode) {
    ClassNode parent = classNode.getSuperClass();
    while (parent != null && !getFullName(parent).equals("java.lang.Object")) {
      classNode = parent;
      parent = parent.getSuperClass();
    }
    return classNode;
  }

  public static ClassNode getFurthestUnresolvedParent(ClassNode classNode) {
    ClassNode parent = classNode.getSuperClass();

    while (parent != null
        && !getFullName(parent).equals("java.lang.Object")
        && !parent.isResolved()
        && !Modifier.isAbstract(parent.getModifiers())) {
      classNode = parent;
      parent = parent.getSuperClass();
    }
    return classNode;
  }

  /**
   * Adds a delegate method to the target class node where the first argument is to the delegate
   * method is 'this'. In other words a method such as foo(Object instance, String bar) would be
   * added with a signature of foo(String) and 'this' is passed to the delegate instance
   *
   * @param classNode The class node
   * @param delegate The expression that looks up the delegate
   * @param declaredMethod The declared method
   * @return The added method node or null if it couldn't be added
   */
  public static MethodNode addDelegateInstanceMethod(
      ClassNode classNode, Expression delegate, MethodNode declaredMethod) {
    return addDelegateInstanceMethod(classNode, delegate, declaredMethod, true);
  }

  /**
   * Adds a delegate method to the target class node where the first argument is to the delegate
   * method is 'this'. In other words a method such as foo(Object instance, String bar) would be
   * added with a signature of foo(String) and 'this' is passed to the delegate instance
   *
   * @param classNode The class node
   * @param delegate The expression that looks up the delegate
   * @param declaredMethod The declared method
   * @param thisAsFirstArgument Whether 'this' should be passed as the first argument to the method
   * @return The added method node or null if it couldn't be added
   */
  public static MethodNode addDelegateInstanceMethod(
      ClassNode classNode,
      Expression delegate,
      MethodNode declaredMethod,
      boolean thisAsFirstArgument) {
    Parameter[] parameterTypes =
        thisAsFirstArgument
            ? getRemainingParameterTypes(declaredMethod.getParameters())
            : declaredMethod.getParameters();
    String methodName = declaredMethod.getName();
    if (classNode.hasDeclaredMethod(methodName, parameterTypes)) {
      return null;
    }
    String propertyName = GrailsClassUtils.getPropertyForGetter(methodName);
    if (propertyName != null && parameterTypes.length == 0 && classNode.hasProperty(propertyName)) {
      return null;
    }
    propertyName = GrailsClassUtils.getPropertyForSetter(methodName);
    if (propertyName != null && parameterTypes.length == 1 && classNode.hasProperty(propertyName)) {
      return null;
    }

    BlockStatement methodBody = new BlockStatement();
    ArgumentListExpression arguments =
        createArgumentListFromParameters(parameterTypes, thisAsFirstArgument);

    ClassNode returnType = nonGeneric(declaredMethod.getReturnType());

    MethodCallExpression methodCallExpression =
        new MethodCallExpression(delegate, methodName, arguments);
    methodCallExpression.setMethodTarget(declaredMethod);
    ThrowStatement missingMethodException = createMissingMethodThrowable(classNode, declaredMethod);
    VariableExpression apiVar = addApiVariableDeclaration(delegate, declaredMethod, methodBody);
    IfStatement ifStatement =
        createIfElseStatementForApiMethodCall(methodCallExpression, apiVar, missingMethodException);

    methodBody.addStatement(ifStatement);
    MethodNode methodNode =
        new MethodNode(
            methodName,
            Modifier.PUBLIC,
            returnType,
            copyParameters(parameterTypes),
            GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY,
            methodBody);
    methodNode.addAnnotations(declaredMethod.getAnnotations());

    classNode.addMethod(methodNode);
    return methodNode;
  }

  private static IfStatement createIfElseStatementForApiMethodCall(
      MethodCallExpression methodCallExpression,
      VariableExpression apiVar,
      ThrowStatement missingMethodException) {
    BlockStatement ifBlock = new BlockStatement();
    ifBlock.addStatement(missingMethodException);
    BlockStatement elseBlock = new BlockStatement();
    elseBlock.addStatement(new ExpressionStatement(methodCallExpression));

    return new IfStatement(
        new BooleanExpression(new BinaryExpression(apiVar, EQUALS_OPERATOR, NULL_EXPRESSION)),
        ifBlock,
        elseBlock);
  }

  private static VariableExpression addApiVariableDeclaration(
      Expression delegate, MethodNode declaredMethod, BlockStatement methodBody) {
    VariableExpression apiVar = new VariableExpression("$api_" + declaredMethod.getName());
    DeclarationExpression de = new DeclarationExpression(apiVar, ASSIGNMENT_OPERATOR, delegate);
    methodBody.addStatement(new ExpressionStatement(de));
    return apiVar;
  }

  private static ThrowStatement createMissingMethodThrowable(
      ClassNode classNode, MethodNode declaredMethodNode) {
    ArgumentListExpression exceptionArgs = new ArgumentListExpression();
    exceptionArgs.addExpression(new ConstantExpression(declaredMethodNode.getName()));
    exceptionArgs.addExpression(new ClassExpression(classNode));
    return new ThrowStatement(
        new ConstructorCallExpression(MISSING_METHOD_EXCEPTION, exceptionArgs));
  }

  /**
   * Creates an argument list from the given parameter types.
   *
   * @param parameterTypes The parameter types
   * @param thisAsFirstArgument Whether to include a reference to 'this' as the first argument
   * @return the arguments
   */
  public static ArgumentListExpression createArgumentListFromParameters(
      Parameter[] parameterTypes, boolean thisAsFirstArgument) {
    ArgumentListExpression arguments = new ArgumentListExpression();

    if (thisAsFirstArgument) {
      arguments.addExpression(AbstractGrailsArtefactTransformer.THIS_EXPRESSION);
    }

    for (Parameter parameterType : parameterTypes) {
      arguments.addExpression(new VariableExpression(parameterType.getName()));
    }
    return arguments;
  }

  /**
   * Gets the remaining parameters excluding the first parameter in the given list
   *
   * @param parameters The parameters
   * @return A new array with the first parameter removed
   */
  public static Parameter[] getRemainingParameterTypes(Parameter[] parameters) {
    if (parameters.length == 0) {
      return GrailsArtefactClassInjector.ZERO_PARAMETERS;
    }

    Parameter[] newParameters = new Parameter[parameters.length - 1];
    System.arraycopy(parameters, 1, newParameters, 0, parameters.length - 1);
    return newParameters;
  }

  /**
   * Adds a static method call to given class node that delegates to the given method
   *
   * @param classNode The class node
   * @param delegateMethod The delegate method
   * @return The added method node or null if it couldn't be added
   */
  public static MethodNode addDelegateStaticMethod(ClassNode classNode, MethodNode delegateMethod) {
    ClassExpression classExpression = new ClassExpression(delegateMethod.getDeclaringClass());
    return addDelegateStaticMethod(classExpression, classNode, delegateMethod);
  }

  /**
   * Adds a static method to the given class node that delegates to the given method and resolves
   * the object to invoke the method on from the given expression.
   *
   * @param expression The expression
   * @param classNode The class node
   * @param delegateMethod The delegate method
   * @return The added method node or null if it couldn't be added
   */
  public static MethodNode addDelegateStaticMethod(
      Expression expression, ClassNode classNode, MethodNode delegateMethod) {
    Parameter[] parameterTypes = delegateMethod.getParameters();
    String declaredMethodName = delegateMethod.getName();
    if (classNode.hasDeclaredMethod(declaredMethodName, parameterTypes)) {
      return null;
    }

    BlockStatement methodBody = new BlockStatement();
    ArgumentListExpression arguments = new ArgumentListExpression();

    for (Parameter parameterType : parameterTypes) {
      arguments.addExpression(new VariableExpression(parameterType.getName()));
    }
    MethodCallExpression methodCallExpression =
        new MethodCallExpression(expression, declaredMethodName, arguments);
    methodCallExpression.setMethodTarget(delegateMethod);

    ThrowStatement missingMethodException = createMissingMethodThrowable(classNode, delegateMethod);
    VariableExpression apiVar = addApiVariableDeclaration(expression, delegateMethod, methodBody);
    IfStatement ifStatement =
        createIfElseStatementForApiMethodCall(methodCallExpression, apiVar, missingMethodException);

    methodBody.addStatement(ifStatement);
    ClassNode returnType = nonGeneric(delegateMethod.getReturnType());
    if (METHOD_MISSING_METHOD_NAME.equals(declaredMethodName)) {
      declaredMethodName = STATIC_METHOD_MISSING_METHOD_NAME;
    }
    MethodNode methodNode = classNode.getDeclaredMethod(declaredMethodName, parameterTypes);
    if (methodNode == null) {
      methodNode =
          new MethodNode(
              declaredMethodName,
              Modifier.PUBLIC | Modifier.STATIC,
              returnType,
              copyParameters(parameterTypes),
              GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY,
              methodBody);
      methodNode.addAnnotations(delegateMethod.getAnnotations());

      classNode.addMethod(methodNode);
    }
    return methodNode;
  }

  /**
   * Adds or modifies an existing constructor to delegate to the given static constructor method for
   * initialization logic.
   *
   * @param classNode The class node
   * @param constructorMethod The constructor static method
   */
  public static void addDelegateConstructor(ClassNode classNode, MethodNode constructorMethod) {
    BlockStatement constructorBody = new BlockStatement();
    Parameter[] constructorParams = getRemainingParameterTypes(constructorMethod.getParameters());
    ArgumentListExpression arguments = createArgumentListFromParameters(constructorParams, true);
    MethodCallExpression constructCallExpression =
        new MethodCallExpression(
            new ClassExpression(constructorMethod.getDeclaringClass()), "initialize", arguments);
    constructCallExpression.setMethodTarget(constructorMethod);
    ExpressionStatement constructorInitExpression =
        new ExpressionStatement(constructCallExpression);
    if (constructorParams.length > 0) {
      constructorBody.addStatement(
          new ExpressionStatement(
              new ConstructorCallExpression(
                  ClassNode.THIS, GrailsArtefactClassInjector.ZERO_ARGS)));
    }
    constructorBody.addStatement(constructorInitExpression);

    if (constructorParams.length == 0) {
      // handle default constructor

      ConstructorNode constructorNode = getDefaultConstructor(classNode);
      if (constructorNode != null) {
        List<AnnotationNode> annotations =
            constructorNode.getAnnotations(new ClassNode(GrailsDelegatingConstructor.class));
        if (annotations.size() == 0) {
          Statement existingBodyCode = constructorNode.getCode();
          if (existingBodyCode instanceof BlockStatement) {
            ((BlockStatement) existingBodyCode).addStatement(constructorInitExpression);
          } else {
            constructorNode.setCode(constructorBody);
          }
        }
      } else {
        constructorNode = new ConstructorNode(Modifier.PUBLIC, constructorBody);
        classNode.addConstructor(constructorNode);
      }
      constructorNode.addAnnotation(
          new AnnotationNode(new ClassNode(GrailsDelegatingConstructor.class)));
    } else {
      // create new constructor, restoring default constructor if there is none
      ConstructorNode cn = findConstructor(classNode, constructorParams);
      if (cn == null) {
        cn =
            new ConstructorNode(
                Modifier.PUBLIC, copyParameters(constructorParams), null, constructorBody);
        classNode.addConstructor(cn);
      } else {
        List<AnnotationNode> annotations =
            cn.getAnnotations(new ClassNode(GrailsDelegatingConstructor.class));
        if (annotations.size() == 0) {
          Statement code = cn.getCode();
          constructorBody.addStatement(code);
          cn.setCode(constructorBody);
        }
      }

      ConstructorNode defaultConstructor = getDefaultConstructor(classNode);
      if (defaultConstructor == null) {
        // add empty
        classNode.addConstructor(new ConstructorNode(Modifier.PUBLIC, new BlockStatement()));
      }
      cn.addAnnotation(new AnnotationNode(new ClassNode(GrailsDelegatingConstructor.class)));
    }
  }

  /**
   * Finds a constructor for the given class node and parameter types
   *
   * @param classNode The class node
   * @param constructorParams The parameter types
   * @return The located constructor or null
   */
  public static ConstructorNode findConstructor(
      ClassNode classNode, Parameter[] constructorParams) {
    List<ConstructorNode> declaredConstructors = classNode.getDeclaredConstructors();
    for (ConstructorNode declaredConstructor : declaredConstructors) {
      if (parametersEqual(constructorParams, declaredConstructor.getParameters())) {
        return declaredConstructor;
      }
    }
    return null;
  }

  /** @return true if the two arrays are of the same size and have the same contents */
  public static boolean parametersEqual(Parameter[] a, Parameter[] b) {
    if (a.length != b.length) {
      return false;
    }

    for (int i = 0; i < a.length; i++) {
      if (!a[i].getType().equals(b[i].getType())) {
        return false;
      }
    }

    return true;
  }

  /**
   * Obtains the default constructor for the given class node.
   *
   * @param classNode The class node
   * @return The default constructor or null if there isn't one
   */
  public static ConstructorNode getDefaultConstructor(ClassNode classNode) {
    for (ConstructorNode cons : classNode.getDeclaredConstructors()) {
      if (cons.getParameters().length == 0) {
        return cons;
      }
    }
    return null;
  }

  private static Parameter[] copyParameters(Parameter[] parameterTypes) {
    Parameter[] newParameterTypes = new Parameter[parameterTypes.length];
    for (int i = 0; i < parameterTypes.length; i++) {
      Parameter parameterType = parameterTypes[i];
      Parameter newParameter =
          new Parameter(
              nonGeneric(parameterType.getType()),
              parameterType.getName(),
              parameterType.getInitialExpression());
      newParameter.addAnnotations(parameterType.getAnnotations());
      newParameterTypes[i] = newParameter;
    }
    return newParameterTypes;
  }

  public static ClassNode nonGeneric(ClassNode type) {
    if (type.isUsingGenerics()) {
      final ClassNode nonGen = ClassHelper.makeWithoutCaching(type.getName());
      nonGen.setRedirect(type);
      nonGen.setGenericsTypes(null);
      nonGen.setUsingGenerics(false);
      return nonGen;
    }

    if (type.isArray()) {
      final ClassNode nonGen = ClassHelper.makeWithoutCaching(Object.class);
      nonGen.setUsingGenerics(false);
      return nonGen.makeArray();
    }

    return type.getPlainNodeReference();
  }

  public static boolean isCandidateInstanceMethod(ClassNode classNode, MethodNode declaredMethod) {
    Parameter[] parameterTypes = declaredMethod.getParameters();
    return isCandidateMethod(declaredMethod)
        && parameterTypes != null
        && parameterTypes.length > 0
        && isAssignableFrom(parameterTypes[0].getType(), classNode);
  }

  /**
   * Determines if the class or interface represented by the superClass argument is either the same
   * as, or is a superclass or superinterface of, the class or interface represented by the
   * specified subClass parameter.
   *
   * @param superClass The super class to check
   * @param childClass The sub class the check
   * @return true if the childClass is either equal to or a sub class of the specified superClass
   */
  private static boolean isAssignableFrom(ClassNode superClass, ClassNode childClass) {
    ClassNode currentSuper = childClass;
    while (currentSuper != null) {
      if (currentSuper.equals(superClass)) {
        return true;
      }

      currentSuper = currentSuper.getSuperClass();
    }
    return false;
  }

  public static boolean isCandidateMethod(MethodNode declaredMethod) {
    return !declaredMethod.isSynthetic()
        && !declaredMethod.getName().contains("$")
        && Modifier.isPublic(declaredMethod.getModifiers())
        && !Modifier.isAbstract(declaredMethod.getModifiers());
  }

  public static boolean isConstructorMethod(MethodNode declaredMethod) {
    return declaredMethod.isStatic()
        && declaredMethod.isPublic()
        && declaredMethod.getName().equals("initialize")
        && declaredMethod.getParameters().length >= 1
        && declaredMethod
            .getParameters()[0]
            .getType()
            .equals(AbstractGrailsArtefactTransformer.OBJECT_CLASS);
  }

  public static void addDelegateInstanceMethods(
      ClassNode classNode, ClassNode delegateNode, Expression delegateInstance) {
    addDelegateInstanceMethods(classNode, classNode, delegateNode, delegateInstance);
  }

  public static void addDelegateInstanceMethods(
      ClassNode supportedSuperType,
      ClassNode classNode,
      ClassNode delegateNode,
      Expression delegateInstance) {
    while (!delegateNode.equals(AbstractGrailsArtefactTransformer.OBJECT_CLASS)) {
      List<MethodNode> declaredMethods = delegateNode.getMethods();
      for (MethodNode declaredMethod : declaredMethods) {

        if (isConstructorMethod(declaredMethod)) {
          addDelegateConstructor(classNode, declaredMethod);

        } else if (isCandidateInstanceMethod(supportedSuperType, declaredMethod)) {
          addDelegateInstanceMethod(classNode, delegateInstance, declaredMethod);
        }
      }
      delegateNode = delegateNode.getSuperClass();
    }
  }

  public static void addFieldIfNonExistent(
      ClassNode classNode, ClassNode fieldType, String fieldName) {
    if (classNode != null && classNode.getField(fieldName) == null) {
      classNode.addField(
          fieldName,
          Modifier.PRIVATE,
          fieldType,
          new ConstructorCallExpression(fieldType, new ArgumentListExpression()));
    }
  }

  public static void addAnnotationIfNecessary(
      ClassNode classNode, @SuppressWarnings("unused") Class<Entity> entityClass) {
    List<AnnotationNode> annotations = classNode.getAnnotations();
    ClassNode annotationClassNode = new ClassNode(Entity.class);
    AnnotationNode annotationToAdd = new AnnotationNode(annotationClassNode);
    if (annotations.isEmpty()) {
      classNode.addAnnotation(annotationToAdd);
    } else {
      boolean foundAnn = findAnnotation(annotationClassNode, annotations) != null;
      if (!foundAnn) classNode.addAnnotation(annotationToAdd);
    }
  }

  public static AnnotationNode findAnnotation(
      ClassNode annotationClassNode, List<AnnotationNode> annotations) {
    for (AnnotationNode annotation : annotations) {
      if (annotation.getClassNode().equals(annotationClassNode)) {
        return annotation;
      }
    }
    return null;
  }

  public static AnnotationNode findAnnotation(ClassNode classNode, Class type) {
    List<AnnotationNode> annotations = classNode.getAnnotations();
    if (annotations != null) {
      return findAnnotation(new ClassNode(type), annotations);
    }
    return null;
  }

  /**
   * Returns true if classNode is marked with annotationClass
   *
   * @param classNode A ClassNode to inspect
   * @param annotationClass an annotation to look for
   * @return true if classNode is marked with annotationClass, otherwise false
   */
  public static boolean hasAnnotation(
      final ClassNode classNode, final Class<? extends Annotation> annotationClass) {
    List<AnnotationNode> annotations = classNode.getAnnotations(new ClassNode(annotationClass));
    return annotations.size() > 0;
  }

  /**
   * @param classNode a ClassNode to search
   * @param annotationsToLookFor Annotations to look for
   * @return true if classNode is marked with any of the annotations in annotationsToLookFor
   */
  public static boolean hasAnyAnnotations(
      final ClassNode classNode, final Class<? extends Annotation>... annotationsToLookFor) {
    return CollectionUtils.exists(
        Arrays.asList(annotationsToLookFor),
        new Predicate() {
          @SuppressWarnings({"unchecked", "rawtypes"})
          public boolean evaluate(Object object) {
            return hasAnnotation(classNode, (Class) object);
          }
        });
  }

  public static void addMethodIfNotPresent(ClassNode controllerClassNode, MethodNode methodNode) {
    MethodNode existing =
        controllerClassNode.getMethod(methodNode.getName(), methodNode.getParameters());
    if (existing == null) {
      controllerClassNode.addMethod(methodNode);
    }
  }

  public static ExpressionStatement createPrintlnStatement(String message) {
    return new ExpressionStatement(
        new MethodCallExpression(
            AbstractGrailsArtefactTransformer.THIS_EXPRESSION,
            "println",
            new ArgumentListExpression(new ConstantExpression(message))));
  }

  public static ExpressionStatement createPrintlnStatement(String message, String variable) {
    return new ExpressionStatement(
        new MethodCallExpression(
            AbstractGrailsArtefactTransformer.THIS_EXPRESSION,
            "println",
            new ArgumentListExpression(
                new BinaryExpression(
                    new ConstantExpression(message),
                    Token.newSymbol(Types.PLUS, 0, 0),
                    new VariableExpression(variable)))));
  }

  /**
   * Wraps a method body in try / catch logic that catches any errors and logs an error, but does
   * not rethrow!
   *
   * @param methodNode The method node
   */
  public static void wrapMethodBodyInTryCatchDebugStatements(MethodNode methodNode) {
    BlockStatement code = (BlockStatement) methodNode.getCode();
    BlockStatement newCode = new BlockStatement();
    TryCatchStatement tryCatchStatement = new TryCatchStatement(code, new BlockStatement());
    newCode.addStatement(tryCatchStatement);
    methodNode.setCode(newCode);
    BlockStatement catchBlock = new BlockStatement();
    ArgumentListExpression logArguments = new ArgumentListExpression();
    logArguments.addExpression(
        new BinaryExpression(
            new ConstantExpression("Error initializing class: "),
            Token.newSymbol(Types.PLUS, 0, 0),
            new VariableExpression("e")));
    logArguments.addExpression(new VariableExpression("e"));
    catchBlock.addStatement(
        new ExpressionStatement(
            new MethodCallExpression(new VariableExpression("log"), "error", logArguments)));
    tryCatchStatement.addCatch(
        new CatchStatement(new Parameter(new ClassNode(Throwable.class), "e"), catchBlock));
  }

  /**
   * Evaluates a constraints closure and returns metadata about the constraints configured in the
   * closure. The Map returned has property names as keys and the value associated with each of
   * those property names is a Map<String, Expression> which has constraint names as keys and the
   * Expression associated with that constraint as values
   *
   * @param closureExpression the closure expression to evaluate
   * @return the Map as described above
   */
  public static Map<String, Map<String, Expression>> getConstraintMetadata(
      final ClosureExpression closureExpression) {

    final List<MethodCallExpression> methodExpressions = new ArrayList<MethodCallExpression>();

    final Map<String, Map<String, Expression>> results =
        new LinkedHashMap<String, Map<String, Expression>>();
    final Statement closureCode = closureExpression.getCode();
    if (closureCode instanceof BlockStatement) {
      final List<Statement> closureStatements = ((BlockStatement) closureCode).getStatements();
      for (final Statement closureStatement : closureStatements) {
        if (closureStatement instanceof ExpressionStatement) {
          final Expression expression = ((ExpressionStatement) closureStatement).getExpression();
          if (expression instanceof MethodCallExpression) {
            methodExpressions.add((MethodCallExpression) expression);
          }
        } else if (closureStatement instanceof ReturnStatement) {
          final ReturnStatement returnStatement = (ReturnStatement) closureStatement;
          Expression expression = returnStatement.getExpression();
          if (expression instanceof MethodCallExpression) {
            methodExpressions.add((MethodCallExpression) expression);
          }
        }

        for (final MethodCallExpression methodCallExpression : methodExpressions) {
          final Expression objectExpression = methodCallExpression.getObjectExpression();
          if (objectExpression instanceof VariableExpression
              && "this".equals(((VariableExpression) objectExpression).getName())) {
            final Expression methodCallArguments = methodCallExpression.getArguments();
            if (methodCallArguments instanceof TupleExpression) {
              final List<Expression> methodCallArgumentExpressions =
                  ((TupleExpression) methodCallArguments).getExpressions();
              if (methodCallArgumentExpressions != null
                  && methodCallArgumentExpressions.size() == 1
                  && methodCallArgumentExpressions.get(0) instanceof NamedArgumentListExpression) {
                final Map<String, Expression> constraintNameToExpression =
                    new LinkedHashMap<String, Expression>();
                final List<MapEntryExpression> mapEntryExpressions =
                    ((NamedArgumentListExpression) methodCallArgumentExpressions.get(0))
                        .getMapEntryExpressions();
                for (final MapEntryExpression mapEntryExpression : mapEntryExpressions) {
                  final Expression keyExpression = mapEntryExpression.getKeyExpression();
                  if (keyExpression instanceof ConstantExpression) {
                    final Object value = ((ConstantExpression) keyExpression).getValue();
                    if (value instanceof String) {
                      constraintNameToExpression.put(
                          (String) value, mapEntryExpression.getValueExpression());
                    }
                  }
                }
                results.put(methodCallExpression.getMethodAsString(), constraintNameToExpression);
              }
            }
          }
        }
      }
    }
    return results;
  }

  @Target(ElementType.CONSTRUCTOR)
  @Retention(RetentionPolicy.SOURCE)
  private static @interface GrailsDelegatingConstructor {}
}
/**
 * Handy methods when working with the Groovy AST
 *
 * @author Guillaume Laforge
 * @author Paul King
 * @author Andre Steingress
 * @author Graeme Rocher
 */
public class GeneralUtils {
  public static final Token ASSIGN = Token.newSymbol(Types.ASSIGN, -1, -1);
  public static final Token EQ = Token.newSymbol(Types.COMPARE_EQUAL, -1, -1);
  public static final Token NE = Token.newSymbol(Types.COMPARE_NOT_EQUAL, -1, -1);
  public static final Token LT = Token.newSymbol(Types.COMPARE_LESS_THAN, -1, -1);
  public static final Token AND = Token.newSymbol(Types.LOGICAL_AND, -1, -1);
  public static final Token OR = Token.newSymbol(Types.LOGICAL_OR, -1, -1);
  public static final Token CMP = Token.newSymbol(Types.COMPARE_TO, -1, -1);
  private static final Token INSTANCEOF = Token.newSymbol(Types.KEYWORD_INSTANCEOF, -1, -1);
  private static final Token PLUS = Token.newSymbol(Types.PLUS, -1, -1);
  private static final Token INDEX = Token.newSymbol("[", -1, -1);

  public static BinaryExpression andX(Expression lhv, Expression rhv) {
    return new BinaryExpression(lhv, AND, rhv);
  }

  public static ArgumentListExpression args(Expression... expressions) {
    List<Expression> args = new ArrayList<Expression>();
    Collections.addAll(args, expressions);
    return new ArgumentListExpression(args);
  }

  public static ArgumentListExpression args(List<Expression> expressions) {
    return new ArgumentListExpression(expressions);
  }

  public static ArgumentListExpression args(Parameter[] parameters) {
    return new ArgumentListExpression(parameters);
  }

  public static ArgumentListExpression args(String... names) {
    List<Expression> vars = new ArrayList<Expression>();
    for (String name : names) {
      vars.add(varX(name));
    }
    return new ArgumentListExpression(vars);
  }

  public static Statement assignS(Expression target, Expression value) {
    return new ExpressionStatement(assignX(target, value));
  }

  public static Expression assignX(Expression target, Expression value) {
    return new BinaryExpression(target, ASSIGN, value);
  }

  public static Expression attrX(Expression oe, Expression prop) {
    return new AttributeExpression(oe, prop);
  }

  public static BlockStatement block(VariableScope varScope, Statement... stmts) {
    BlockStatement block = new BlockStatement();
    block.setVariableScope(varScope);
    for (Statement stmt : stmts) block.addStatement(stmt);
    return block;
  }

  public static BlockStatement block(Statement... stmts) {
    BlockStatement block = new BlockStatement();
    for (Statement stmt : stmts) block.addStatement(stmt);
    return block;
  }

  public static MethodCallExpression callSuperX(String methodName, Expression args) {
    return callX(varX("super"), methodName, args);
  }

  public static MethodCallExpression callSuperX(String methodName) {
    return callSuperX(methodName, MethodCallExpression.NO_ARGUMENTS);
  }

  public static MethodCallExpression callThisX(String methodName, Expression args) {
    return callX(varX("this"), methodName, args);
  }

  public static MethodCallExpression callThisX(String methodName) {
    return callThisX(methodName, MethodCallExpression.NO_ARGUMENTS);
  }

  public static MethodCallExpression callX(
      Expression receiver, String methodName, Expression args) {
    return new MethodCallExpression(receiver, methodName, args);
  }

  public static MethodCallExpression callX(
      Expression receiver, Expression method, Expression args) {
    return new MethodCallExpression(receiver, method, args);
  }

  public static MethodCallExpression callX(Expression receiver, String methodName) {
    return callX(receiver, methodName, MethodCallExpression.NO_ARGUMENTS);
  }

  public static StaticMethodCallExpression callX(
      ClassNode receiver, String methodName, Expression args) {
    return new StaticMethodCallExpression(receiver, methodName, args);
  }

  public static StaticMethodCallExpression callX(ClassNode receiver, String methodName) {
    return callX(receiver, methodName, MethodCallExpression.NO_ARGUMENTS);
  }

  public static CastExpression castX(ClassNode type, Expression expression) {
    return new CastExpression(type, expression);
  }

  public static CastExpression castX(
      ClassNode type, Expression expression, boolean ignoreAutoboxing) {
    return new CastExpression(type, expression, ignoreAutoboxing);
  }

  public static ClassExpression classX(ClassNode clazz) {
    return new ClassExpression(clazz);
  }

  public static ClassExpression classX(Class clazz) {
    return classX(ClassHelper.make(clazz).getPlainNodeReference());
  }

  public static ClosureExpression closureX(Parameter[] params, Statement code) {
    return new ClosureExpression(params, code);
  }

  public static ClosureExpression closureX(Statement code) {
    return closureX(Parameter.EMPTY_ARRAY, code);
  }

  public static Parameter[] cloneParams(Parameter[] source) {
    Parameter[] result = new Parameter[source.length];
    for (int i = 0; i < source.length; i++) {
      Parameter srcParam = source[i];
      Parameter dstParam = new Parameter(srcParam.getOriginType(), srcParam.getName());
      result[i] = dstParam;
    }
    return result;
  }

  public static BinaryExpression cmpX(Expression lhv, Expression rhv) {
    return new BinaryExpression(lhv, CMP, rhv);
  }

  public static ConstantExpression constX(Object val) {
    return new ConstantExpression(val);
  }

  public static ConstantExpression constX(Object val, boolean keepPrimitive) {
    return new ConstantExpression(val, keepPrimitive);
  }

  /**
   * Copies all <tt>candidateAnnotations</tt> with retention policy {@link
   * java.lang.annotation.RetentionPolicy#RUNTIME} and {@link
   * java.lang.annotation.RetentionPolicy#CLASS}.
   *
   * <p>Annotations with {@link org.codehaus.groovy.runtime.GeneratedClosure} members are not
   * supported at present.
   */
  public static void copyAnnotatedNodeAnnotations(
      final AnnotatedNode annotatedNode,
      final List<AnnotationNode> copied,
      List<AnnotationNode> notCopied) {
    List<AnnotationNode> annotationList = annotatedNode.getAnnotations();
    for (AnnotationNode annotation : annotationList) {

      List<AnnotationNode> annotations =
          annotation.getClassNode().getAnnotations(AbstractASTTransformation.RETENTION_CLASSNODE);
      if (annotations.isEmpty()) continue;

      if (hasClosureMember(annotation)) {
        notCopied.add(annotation);
        continue;
      }

      AnnotationNode retentionPolicyAnnotation = annotations.get(0);
      Expression valueExpression = retentionPolicyAnnotation.getMember("value");
      if (!(valueExpression instanceof PropertyExpression)) continue;

      PropertyExpression propertyExpression = (PropertyExpression) valueExpression;
      boolean processAnnotation =
          propertyExpression.getProperty() instanceof ConstantExpression
              && ("RUNTIME"
                      .equals(((ConstantExpression) (propertyExpression.getProperty())).getValue())
                  || "CLASS"
                      .equals(
                          ((ConstantExpression) (propertyExpression.getProperty())).getValue()));

      if (processAnnotation) {
        AnnotationNode newAnnotation = new AnnotationNode(annotation.getClassNode());
        for (Map.Entry<String, Expression> member : annotation.getMembers().entrySet()) {
          newAnnotation.addMember(member.getKey(), member.getValue());
        }
        newAnnotation.setSourcePosition(annotatedNode);

        copied.add(newAnnotation);
      }
    }
  }

  public static Statement createConstructorStatementDefault(FieldNode fNode) {
    final String name = fNode.getName();
    final ClassNode fType = fNode.getType();
    final Expression fieldExpr = propX(varX("this"), name);
    Expression initExpr = fNode.getInitialValueExpression();
    Statement assignInit;
    if (initExpr == null
        || (initExpr instanceof ConstantExpression
            && ((ConstantExpression) initExpr).isNullExpression())) {
      if (ClassHelper.isPrimitiveType(fType)) {
        assignInit = EmptyStatement.INSTANCE;
      } else {
        assignInit = assignS(fieldExpr, ConstantExpression.EMPTY_EXPRESSION);
      }
    } else {
      assignInit = assignS(fieldExpr, initExpr);
    }
    fNode.setInitialValueExpression(null);
    Expression value = findArg(name);
    return ifElseS(equalsNullX(value), assignInit, assignS(fieldExpr, castX(fType, value)));
  }

  public static ConstructorCallExpression ctorX(ClassNode type, Expression args) {
    return new ConstructorCallExpression(type, args);
  }

  public static ConstructorCallExpression ctorX(ClassNode type) {
    return new ConstructorCallExpression(type, ArgumentListExpression.EMPTY_ARGUMENTS);
  }

  public static Statement ctorSuperS(Expression args) {
    return stmt(ctorX(ClassNode.SUPER, args));
  }

  public static Statement ctorThisS(Expression args) {
    return stmt(ctorX(ClassNode.THIS, args));
  }

  public static Statement ctorSuperS() {
    return stmt(ctorX(ClassNode.SUPER));
  }

  public static Statement ctorThisS() {
    return stmt(ctorX(ClassNode.THIS));
  }

  public static Statement declS(Expression target, Expression init) {
    return new ExpressionStatement(new DeclarationExpression(target, ASSIGN, init));
  }

  public static BinaryExpression eqX(Expression lhv, Expression rhv) {
    return new BinaryExpression(lhv, EQ, rhv);
  }

  public static BooleanExpression equalsNullX(Expression argExpr) {
    return new BooleanExpression(eqX(argExpr, new ConstantExpression(null)));
  }

  public static FieldExpression fieldX(FieldNode fieldNode) {
    return new FieldExpression(fieldNode);
  }

  public static FieldExpression fieldX(ClassNode owner, String fieldName) {
    return new FieldExpression(owner.getField(fieldName));
  }

  public static Expression findArg(String argName) {
    return new PropertyExpression(new VariableExpression("args"), argName);
  }

  public static List<MethodNode> getAllMethods(ClassNode type) {
    ClassNode node = type;
    List<MethodNode> result = new ArrayList<MethodNode>();
    while (node != null) {
      result.addAll(node.getMethods());
      node = node.getSuperClass();
    }
    return result;
  }

  public static List<PropertyNode> getAllProperties(ClassNode type) {
    ClassNode node = type;
    List<PropertyNode> result = new ArrayList<PropertyNode>();
    while (node != null) {
      result.addAll(node.getProperties());
      node = node.getSuperClass();
    }
    return result;
  }

  public static String getGetterName(PropertyNode pNode) {
    return "get" + Verifier.capitalize(pNode.getName());
  }

  public static List<FieldNode> getInstanceNonPropertyFields(ClassNode cNode) {
    final List<FieldNode> result = new ArrayList<FieldNode>();
    for (FieldNode fNode : cNode.getFields()) {
      if (!fNode.isStatic() && cNode.getProperty(fNode.getName()) == null) {
        result.add(fNode);
      }
    }
    return result;
  }

  public static List<String> getInstanceNonPropertyFieldNames(ClassNode cNode) {
    List<FieldNode> fList = getInstanceNonPropertyFields(cNode);
    List<String> result = new ArrayList<String>(fList.size());
    for (FieldNode fNode : fList) {
      result.add(fNode.getName());
    }
    return result;
  }

  public static List<PropertyNode> getInstanceProperties(ClassNode cNode) {
    final List<PropertyNode> result = new ArrayList<PropertyNode>();
    for (PropertyNode pNode : cNode.getProperties()) {
      if (!pNode.isStatic()) {
        result.add(pNode);
      }
    }
    return result;
  }

  public static List<String> getInstancePropertyNames(ClassNode cNode) {
    List<PropertyNode> pList = BeanUtils.getAllProperties(cNode, false, false, true);
    List<String> result = new ArrayList<String>(pList.size());
    for (PropertyNode pNode : pList) {
      result.add(pNode.getName());
    }
    return result;
  }

  public static List<FieldNode> getInstancePropertyFields(ClassNode cNode) {
    final List<FieldNode> result = new ArrayList<FieldNode>();
    for (PropertyNode pNode : cNode.getProperties()) {
      if (!pNode.isStatic()) {
        result.add(pNode.getField());
      }
    }
    return result;
  }

  public static Set<ClassNode> getInterfacesAndSuperInterfaces(ClassNode type) {
    Set<ClassNode> res = new HashSet<ClassNode>();
    if (type.isInterface()) {
      res.add(type);
      return res;
    }
    ClassNode next = type;
    while (next != null) {
      Collections.addAll(res, next.getInterfaces());
      next = next.getSuperClass();
    }
    return res;
  }

  public static List<FieldNode> getSuperNonPropertyFields(ClassNode cNode) {
    final List<FieldNode> result;
    if (cNode == ClassHelper.OBJECT_TYPE) {
      result = new ArrayList<FieldNode>();
    } else {
      result = getSuperNonPropertyFields(cNode.getSuperClass());
    }
    for (FieldNode fNode : cNode.getFields()) {
      if (!fNode.isStatic() && cNode.getProperty(fNode.getName()) == null) {
        result.add(fNode);
      }
    }
    return result;
  }

  public static List<FieldNode> getSuperPropertyFields(ClassNode cNode) {
    final List<FieldNode> result;
    if (cNode == ClassHelper.OBJECT_TYPE) {
      result = new ArrayList<FieldNode>();
    } else {
      result = getSuperPropertyFields(cNode.getSuperClass());
    }
    for (PropertyNode pNode : cNode.getProperties()) {
      if (!pNode.isStatic()) {
        result.add(pNode.getField());
      }
    }
    return result;
  }

  public static BinaryExpression hasClassX(Expression instance, ClassNode cNode) {
    return eqX(classX(cNode), callX(instance, "getClass"));
  }

  private static boolean hasClosureMember(AnnotationNode annotation) {

    Map<String, Expression> members = annotation.getMembers();
    for (Map.Entry<String, Expression> member : members.entrySet()) {
      if (member.getValue() instanceof ClosureExpression) return true;

      if (member.getValue() instanceof ClassExpression) {
        ClassExpression classExpression = (ClassExpression) member.getValue();
        Class<?> typeClass =
            classExpression.getType().isResolved()
                ? classExpression.getType().redirect().getTypeClass()
                : null;
        if (typeClass != null && GeneratedClosure.class.isAssignableFrom(typeClass)) return true;
      }
    }

    return false;
  }

  public static boolean hasDeclaredMethod(ClassNode cNode, String name, int argsCount) {
    List<MethodNode> ms = cNode.getDeclaredMethods(name);
    for (MethodNode m : ms) {
      Parameter[] paras = m.getParameters();
      if (paras != null && paras.length == argsCount) {
        return true;
      }
    }
    return false;
  }

  public static BinaryExpression hasEqualFieldX(FieldNode fNode, Expression other) {
    return eqX(varX(fNode), propX(other, fNode.getName()));
  }

  public static BinaryExpression hasEqualPropertyX(
      ClassNode annotatedNode, PropertyNode pNode, VariableExpression other) {
    return eqX(getterThisX(annotatedNode, pNode), getterX(other.getOriginType(), other, pNode));
  }

  @Deprecated
  public static BinaryExpression hasEqualPropertyX(PropertyNode pNode, Expression other) {
    String getterName = getGetterName(pNode);
    return eqX(callThisX(getterName), callX(other, getterName));
  }

  public static BooleanExpression hasSameFieldX(FieldNode fNode, Expression other) {
    return sameX(varX(fNode), propX(other, fNode.getName()));
  }

  public static BooleanExpression hasSamePropertyX(PropertyNode pNode, Expression other) {
    String getterName = getGetterName(pNode);
    return sameX(callThisX(getterName), callX(other, getterName));
  }

  public static Statement ifElseS(Expression cond, Statement thenStmt, Statement elseStmt) {
    return new IfStatement(
        cond instanceof BooleanExpression ? (BooleanExpression) cond : new BooleanExpression(cond),
        thenStmt,
        elseStmt);
  }

  public static Statement ifS(Expression cond, Expression trueExpr) {
    return ifS(cond, new ExpressionStatement(trueExpr));
  }

  public static Statement ifS(Expression cond, Statement trueStmt) {
    return new IfStatement(
        cond instanceof BooleanExpression ? (BooleanExpression) cond : new BooleanExpression(cond),
        trueStmt,
        EmptyStatement.INSTANCE);
  }

  public static Expression indexX(Expression target, Expression value) {
    return new BinaryExpression(target, INDEX, value);
  }

  public static BooleanExpression isInstanceOfX(Expression objectExpression, ClassNode cNode) {
    return new BooleanExpression(new BinaryExpression(objectExpression, INSTANCEOF, classX(cNode)));
  }

  public static BooleanExpression isOneX(Expression expr) {
    return new BooleanExpression(new BinaryExpression(expr, EQ, new ConstantExpression(1)));
  }

  public static boolean isOrImplements(ClassNode type, ClassNode interfaceType) {
    return type.equals(interfaceType) || type.implementsInterface(interfaceType);
  }

  public static BooleanExpression isTrueX(Expression argExpr) {
    return new BooleanExpression(
        new BinaryExpression(argExpr, EQ, new ConstantExpression(Boolean.TRUE)));
  }

  public static BooleanExpression isZeroX(Expression expr) {
    return new BooleanExpression(new BinaryExpression(expr, EQ, new ConstantExpression(0)));
  }

  public static BinaryExpression ltX(Expression lhv, Expression rhv) {
    return new BinaryExpression(lhv, LT, rhv);
  }

  public static String makeDescriptorWithoutReturnType(MethodNode mn) {
    StringBuilder sb = new StringBuilder();
    sb.append(mn.getName()).append(':');
    for (Parameter p : mn.getParameters()) {
      sb.append(p.getType()).append(',');
    }
    return sb.toString();
  }

  public static BinaryExpression neX(Expression lhv, Expression rhv) {
    return new BinaryExpression(lhv, NE, rhv);
  }

  public static BooleanExpression notNullX(Expression argExpr) {
    return new BooleanExpression(new BinaryExpression(argExpr, NE, new ConstantExpression(null)));
  }

  public static NotExpression notX(Expression expr) {
    return new NotExpression(
        expr instanceof BooleanExpression ? expr : new BooleanExpression(expr));
  }

  public static BinaryExpression orX(Expression lhv, Expression rhv) {
    return new BinaryExpression(lhv, OR, rhv);
  }

  public static Parameter param(ClassNode type, String name) {
    return param(type, name, null);
  }

  public static Parameter param(ClassNode type, String name, Expression initialExpression) {
    Parameter param = new Parameter(type, name);
    if (initialExpression != null) {
      param.setInitialExpression(initialExpression);
    }
    return param;
  }

  public static Parameter[] params(Parameter... params) {
    return params != null ? params : Parameter.EMPTY_ARRAY;
  }

  public static BinaryExpression plusX(Expression lhv, Expression rhv) {
    return new BinaryExpression(lhv, PLUS, rhv);
  }

  public static Expression propX(Expression owner, String property) {
    return new PropertyExpression(owner, property);
  }

  public static Expression propX(Expression owner, Expression property) {
    return new PropertyExpression(owner, property);
  }

  public static Statement returnS(Expression expr) {
    return new ReturnStatement(new ExpressionStatement(expr));
  }

  public static Statement safeExpression(Expression fieldExpr, Expression expression) {
    return new IfStatement(
        equalsNullX(fieldExpr),
        new ExpressionStatement(fieldExpr),
        new ExpressionStatement(expression));
  }

  public static BooleanExpression sameX(Expression self, Expression other) {
    return new BooleanExpression(callX(self, "is", args(other)));
  }

  public static Statement stmt(Expression expr) {
    return new ExpressionStatement(expr);
  }

  public static TernaryExpression ternaryX(
      Expression cond, Expression trueExpr, Expression elseExpr) {
    return new TernaryExpression(
        cond instanceof BooleanExpression ? (BooleanExpression) cond : new BooleanExpression(cond),
        trueExpr,
        elseExpr);
  }

  public static VariableExpression varX(String name) {
    return new VariableExpression(name);
  }

  public static VariableExpression varX(Variable variable) {
    return new VariableExpression(variable);
  }

  public static VariableExpression varX(String name, ClassNode type) {
    return new VariableExpression(name, type);
  }

  /**
   * This method is similar to {@link #propX(Expression, Expression)} but will make sure that if the
   * property being accessed is defined inside the classnode provided as a parameter, then a getter
   * call is generated instead of a field access.
   *
   * @param annotatedNode the class node where the property node is accessed from
   * @param pNode the property being accessed
   * @return a method call expression or a property expression
   */
  public static Expression getterThisX(ClassNode annotatedNode, PropertyNode pNode) {
    ClassNode owner = pNode.getDeclaringClass();
    if (annotatedNode.equals(owner)) {
      String getterName = "get" + MetaClassHelper.capitalize(pNode.getName());
      boolean existingExplicitGetter =
          annotatedNode.getMethod(getterName, Parameter.EMPTY_ARRAY) != null;
      if (ClassHelper.boolean_TYPE.equals(pNode.getOriginType()) && !existingExplicitGetter) {
        getterName = "is" + MetaClassHelper.capitalize(pNode.getName());
      }
      return callThisX(getterName);
    }
    return propX(new VariableExpression("this"), pNode.getName());
  }

  /**
   * This method is similar to {@link #propX(Expression, Expression)} but will make sure that if the
   * property being accessed is defined inside the classnode provided as a parameter, then a getter
   * call is generated instead of a field access.
   *
   * @param annotatedNode the class node where the property node is accessed from
   * @param receiver the object having the property
   * @param pNode the property being accessed
   * @return a method call expression or a property expression
   */
  public static Expression getterX(
      ClassNode annotatedNode, Expression receiver, PropertyNode pNode) {
    ClassNode owner = pNode.getDeclaringClass();
    if (annotatedNode.equals(owner)) {
      String getterName = "get" + MetaClassHelper.capitalize(pNode.getName());
      boolean existingExplicitGetter =
          annotatedNode.getMethod(getterName, Parameter.EMPTY_ARRAY) != null;
      if (ClassHelper.boolean_TYPE.equals(pNode.getOriginType()) && !existingExplicitGetter) {
        getterName = "is" + MetaClassHelper.capitalize(pNode.getName());
      }
      return callX(receiver, getterName);
    }
    return propX(receiver, pNode.getName());
  }
}
  Expression transformBinaryExpression(final BinaryExpression bin) {
    if (bin instanceof DeclarationExpression) {
      Expression optimized = transformDeclarationExpression(bin);
      if (optimized != null) {
        return optimized;
      }
    }
    Object[] list = bin.getNodeMetaData(BINARY_EXP_TARGET);
    Token operation = bin.getOperation();
    int operationType = operation.getType();
    Expression rightExpression = bin.getRightExpression();
    Expression leftExpression = bin.getLeftExpression();
    if (bin instanceof DeclarationExpression && leftExpression instanceof VariableExpression) {
      ClassNode declarationType = ((VariableExpression) leftExpression).getOriginType();
      if (rightExpression instanceof ConstantExpression) {
        ClassNode unwrapper = ClassHelper.getUnwrapper(declarationType);
        ClassNode wrapper = ClassHelper.getWrapper(declarationType);
        if (!rightExpression.getType().equals(declarationType)
            && wrapper.isDerivedFrom(ClassHelper.Number_TYPE)
            && WideningCategories.isDoubleCategory(unwrapper)) {
          ConstantExpression constant = (ConstantExpression) rightExpression;
          if (constant.getValue() != null) {
            return optimizeConstantInitialization(
                bin, operation, constant, leftExpression, declarationType);
          }
        }
      }
    }
    if (operationType == Types.EQUAL && leftExpression instanceof PropertyExpression) {
      MethodNode directMCT =
          leftExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
      if (directMCT != null) {
        return transformPropertyAssignmentToSetterCall(
            (PropertyExpression) leftExpression, rightExpression, directMCT);
      }
    }
    if (operationType == Types.COMPARE_EQUAL || operationType == Types.COMPARE_NOT_EQUAL) {
      // let's check if one of the operands is the null constant
      CompareToNullExpression compareToNullExpression = null;
      if (isNullConstant(leftExpression)) {
        compareToNullExpression =
            new CompareToNullExpression(
                staticCompilationTransformer.transform(rightExpression),
                operationType == Types.COMPARE_EQUAL);
      } else if (isNullConstant(rightExpression)) {
        compareToNullExpression =
            new CompareToNullExpression(
                staticCompilationTransformer.transform(leftExpression),
                operationType == Types.COMPARE_EQUAL);
      }
      if (compareToNullExpression != null) {
        compareToNullExpression.setSourcePosition(bin);
        return compareToNullExpression;
      }
    } else if (operationType == Types.KEYWORD_IN) {
      return convertInOperatorToTernary(bin, rightExpression, leftExpression);
    }
    if (list != null) {
      if (operationType == Types.COMPARE_TO) {
        StaticTypesTypeChooser typeChooser = staticCompilationTransformer.getTypeChooser();
        ClassNode classNode = staticCompilationTransformer.getClassNode();
        ClassNode leftType = typeChooser.resolveType(leftExpression, classNode);
        if (leftType.implementsInterface(ClassHelper.COMPARABLE_TYPE)) {
          ClassNode rightType = typeChooser.resolveType(rightExpression, classNode);
          if (rightType.implementsInterface(ClassHelper.COMPARABLE_TYPE)) {
            Expression left = staticCompilationTransformer.transform(leftExpression);
            Expression right = staticCompilationTransformer.transform(rightExpression);
            MethodCallExpression call =
                new MethodCallExpression(left, "compareTo", new ArgumentListExpression(right));
            call.setImplicitThis(false);
            call.setMethodTarget(COMPARE_TO_METHOD);

            CompareIdentityExpression compareIdentity = new CompareIdentityExpression(left, right);
            compareIdentity.putNodeMetaData(
                StaticTypesMarker.INFERRED_RETURN_TYPE, ClassHelper.boolean_TYPE);
            TernaryExpression result =
                new TernaryExpression(
                    new BooleanExpression(compareIdentity), // a==b
                    CONSTANT_ZERO,
                    new TernaryExpression(
                        new BooleanExpression(new CompareToNullExpression(left, true)), // a==null
                        CONSTANT_MINUS_ONE,
                        new TernaryExpression(
                            new BooleanExpression(
                                new CompareToNullExpression(right, true)), // b==null
                            CONSTANT_ONE,
                            call)));
            compareIdentity.putNodeMetaData(
                StaticTypesMarker.INFERRED_RETURN_TYPE, ClassHelper.int_TYPE);
            result.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            TernaryExpression expr = (TernaryExpression) result.getFalseExpression();
            expr.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            expr.getFalseExpression()
                .putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            return result;
          }
        }
      }
      boolean isAssignment = StaticTypeCheckingSupport.isAssignment(operationType);
      MethodCallExpression call;
      MethodNode node = (MethodNode) list[0];
      String name = (String) list[1];
      Expression left = staticCompilationTransformer.transform(leftExpression);
      Expression right = staticCompilationTransformer.transform(rightExpression);
      BinaryExpression optimized = tryOptimizeCharComparison(left, right, bin);
      if (optimized != null) {
        optimized.removeNodeMetaData(BINARY_EXP_TARGET);
        return transformBinaryExpression(optimized);
      }
      call = new MethodCallExpression(left, name, new ArgumentListExpression(right));
      call.setImplicitThis(false);
      call.setMethodTarget(node);
      MethodNode adapter = StaticCompilationTransformer.BYTECODE_BINARY_ADAPTERS.get(operationType);
      if (adapter != null) {
        ClassExpression sba =
            new ClassExpression(StaticCompilationTransformer.BYTECODE_ADAPTER_CLASS);
        // replace with compareEquals
        call =
            new MethodCallExpression(sba, "compareEquals", new ArgumentListExpression(left, right));
        call.setMethodTarget(adapter);
        call.setImplicitThis(false);
      }
      if (!isAssignment) return call;
      // case of +=, -=, /=, ...
      // the method represents the operation type only, and we must add an assignment
      return new BinaryExpression(
          left, Token.newSymbol("=", operation.getStartLine(), operation.getStartColumn()), call);
    }
    if (bin.getOperation().getType() == Types.EQUAL
        && leftExpression instanceof TupleExpression
        && rightExpression instanceof ListExpression) {
      // multiple assignment
      ListOfExpressionsExpression cle = new ListOfExpressionsExpression();
      boolean isDeclaration = bin instanceof DeclarationExpression;
      List<Expression> leftExpressions = ((TupleExpression) leftExpression).getExpressions();
      List<Expression> rightExpressions = ((ListExpression) rightExpression).getExpressions();
      Iterator<Expression> leftIt = leftExpressions.iterator();
      Iterator<Expression> rightIt = rightExpressions.iterator();
      if (isDeclaration) {
        while (leftIt.hasNext()) {
          Expression left = leftIt.next();
          if (rightIt.hasNext()) {
            Expression right = rightIt.next();
            BinaryExpression bexp = new DeclarationExpression(left, bin.getOperation(), right);
            bexp.setSourcePosition(right);
            cle.addExpression(bexp);
          }
        }
      } else {
        // (next, result) = [ result, next+result ]
        // -->
        // def tmp1 = result
        // def tmp2 = next+result
        // next = tmp1
        // result = tmp2
        int size = rightExpressions.size();
        List<Expression> tmpAssignments = new ArrayList<Expression>(size);
        List<Expression> finalAssignments = new ArrayList<Expression>(size);
        for (int i = 0; i < Math.min(size, leftExpressions.size()); i++) {
          Expression left = leftIt.next();
          Expression right = rightIt.next();
          VariableExpression tmpVar = new VariableExpression("$tmpVar$" + tmpVarCounter++);
          BinaryExpression bexp = new DeclarationExpression(tmpVar, bin.getOperation(), right);
          bexp.setSourcePosition(right);
          tmpAssignments.add(bexp);
          bexp = new BinaryExpression(left, bin.getOperation(), new VariableExpression(tmpVar));
          bexp.setSourcePosition(left);
          finalAssignments.add(bexp);
        }
        for (Expression tmpAssignment : tmpAssignments) {
          cle.addExpression(tmpAssignment);
        }
        for (Expression finalAssignment : finalAssignments) {
          cle.addExpression(finalAssignment);
        }
      }
      return staticCompilationTransformer.transform(cle);
    }
    return staticCompilationTransformer.superTransform(bin);
  }