private void validateFields(BlockStatement block) {
    Validation.Option mode =
        getEnumMemberValue(
            getAnnotation(annotatedClass, VALIDATION_ANNOTATION),
            "option",
            Validation.Option.class,
            Validation.Option.IGNORE_UNMARKED);
    for (FieldNode fieldNode : annotatedClass.getFields()) {
      if (shouldFieldBeIgnoredForValidation(fieldNode)) continue;

      ClosureExpression validationClosure =
          createGroovyTruthClosureExpression(block.getVariableScope());
      String message = null;

      AnnotationNode validateAnnotation = getAnnotation(fieldNode, VALIDATE_ANNOTATION);
      if (validateAnnotation != null) {
        message =
            getMemberStringValue(
                validateAnnotation, "message", "'" + fieldNode.getName() + "' must be set!");
        Expression member = validateAnnotation.getMember("value");
        if (member instanceof ClassExpression) {
          ClassNode memberType = member.getType();
          if (memberType.equals(ClassHelper.make(Validate.Ignore.class))) continue;
          else if (!memberType.equals(ClassHelper.make(Validate.GroovyTruth.class))) {
            addError(
                "value of Validate must be either Validate.GroovyTruth, Validate.Ignore or a closure.",
                fieldNode);
          }
        } else if (member instanceof ClosureExpression) {
          validationClosure = (ClosureExpression) member;
        }
      }

      if (validateAnnotation != null || mode == Validation.Option.VALIDATE_UNMARKED) {
        block.addStatement(
            new AssertStatement(
                new BooleanExpression(
                    callX(validationClosure, "call", args(varX(fieldNode.getName())))),
                message == null ? ConstantExpression.NULL : new ConstantExpression(message)));
      }
    }
  }
  private void createFactoryMethods() {
    if (isAbstract(annotatedClass)) return;

    MethodBuilder.createPublicMethod("create")
        .returning(newClass(annotatedClass))
        .mod(Opcodes.ACC_STATIC)
        .namedParams("values")
        .optionalStringParam("name", keyField)
        .delegatingClosureParam(annotatedClass)
        .declareVariable(
            "result",
            keyField != null ? ctorX(annotatedClass, args("name")) : ctorX(annotatedClass))
        .callMethod("result", "copyFromTemplate")
        .callMethod("result", "apply", args("values", "closure"))
        .callValidationOn("result")
        .doReturn("result")
        .addTo(annotatedClass);

    MethodBuilder.createPublicMethod("create")
        .returning(newClass(annotatedClass))
        .mod(Opcodes.ACC_STATIC)
        .optionalStringParam("name", keyField)
        .delegatingClosureParam(annotatedClass)
        .doReturn(
            callX(
                annotatedClass,
                "create",
                keyField != null
                    ? args(new MapExpression(), varX("name"), varX("closure"))
                    : args(new MapExpression(), varX("closure"))))
        .addTo(annotatedClass);

    MethodBuilder.createPublicMethod("createFromScript")
        .returning(newClass(annotatedClass))
        .deprecated()
        .mod(Opcodes.ACC_STATIC)
        .classParam("configType", ClassHelper.SCRIPT_TYPE)
        .doReturn(callX(callX(varX("configType"), "newInstance"), "run"))
        .addTo(annotatedClass);

    if (keyField != null) {
      MethodBuilder.createPublicMethod("createFrom")
          .returning(newClass(annotatedClass))
          .mod(Opcodes.ACC_STATIC)
          .stringParam("name")
          .stringParam("text")
          .declareVariable(
              "simpleName",
              callX(
                  callX(
                      callX(callX(varX("name"), "tokenize", args(constX("."))), "first"),
                      "tokenize",
                      args(constX("/"))),
                  "last"))
          .declareVariable("result", callX(annotatedClass, "create", args("simpleName")))
          .declareVariable(
              "loader",
              ctorX(
                  ClassHelper.make(GroovyClassLoader.class),
                  args(
                      callX(
                          callX(ClassHelper.make(Thread.class), "currentThread"),
                          "getContextClassLoader"))))
          .declareVariable("config", ctorX(ClassHelper.make(CompilerConfiguration.class)))
          .assignS(
              propX(varX("config"), "scriptBaseClass"), constX(DelegatingScript.class.getName()))
          .declareVariable("binding", ctorX(ClassHelper.make(Binding.class)))
          .declareVariable(
              "shell",
              ctorX(ClassHelper.make(GroovyShell.class), args("loader", "binding", "config")))
          .declareVariable("script", callX(varX("shell"), "parse", args("text")))
          .callMethod("script", "setDelegate", args("result"))
          .callMethod("script", "run")
          .doReturn("result")
          .addTo(annotatedClass);

      MethodBuilder.createPublicMethod("createFromSnippet")
          .deprecated()
          .returning(newClass(annotatedClass))
          .mod(Opcodes.ACC_STATIC)
          .stringParam("name")
          .stringParam("text")
          .doReturn(callX(annotatedClass, "createFrom", args("name", "text")))
          .addTo(annotatedClass);
    } else {
      MethodBuilder.createPublicMethod("createFrom")
          .returning(newClass(annotatedClass))
          .mod(Opcodes.ACC_STATIC)
          .stringParam("text")
          .declareVariable("result", callX(annotatedClass, "create"))
          .declareVariable(
              "loader",
              ctorX(
                  ClassHelper.make(GroovyClassLoader.class),
                  args(
                      callX(
                          callX(ClassHelper.make(Thread.class), "currentThread"),
                          "getContextClassLoader"))))
          .declareVariable("config", ctorX(ClassHelper.make(CompilerConfiguration.class)))
          .assignS(
              propX(varX("config"), "scriptBaseClass"), constX(DelegatingScript.class.getName()))
          .declareVariable("binding", ctorX(ClassHelper.make(Binding.class)))
          .declareVariable(
              "shell",
              ctorX(ClassHelper.make(GroovyShell.class), args("loader", "binding", "config")))
          .declareVariable("script", callX(varX("shell"), "parse", args("text")))
          .callMethod("script", "setDelegate", args("result"))
          .callMethod("script", "run")
          .doReturn("result")
          .addTo(annotatedClass);

      MethodBuilder.createPublicMethod("createFromSnippet")
          .deprecated()
          .returning(newClass(annotatedClass))
          .mod(Opcodes.ACC_STATIC)
          .stringParam("text")
          .doReturn(callX(annotatedClass, "createFrom", args("text")))
          .addTo(annotatedClass);
    }

    MethodBuilder.createPublicMethod("createFrom")
        .returning(newClass(annotatedClass))
        .mod(Opcodes.ACC_STATIC)
        .param(make(File.class), "src")
        .doReturn(
            callX(
                annotatedClass,
                "createFromSnippet",
                args(callX(callX(varX("src"), "toURI"), "toURL"))))
        .addTo(annotatedClass);

    MethodBuilder.createPublicMethod("createFromSnippet")
        .deprecated()
        .returning(newClass(annotatedClass))
        .mod(Opcodes.ACC_STATIC)
        .param(make(File.class), "src")
        .doReturn(callX(annotatedClass, "createFrom", args("src")))
        .addTo(annotatedClass);

    MethodBuilder.createPublicMethod("createFrom")
        .returning(newClass(annotatedClass))
        .mod(Opcodes.ACC_STATIC)
        .param(make(URL.class), "src")
        .declareVariable("text", propX(varX("src"), "text"))
        .doReturn(
            callX(
                annotatedClass,
                "createFromSnippet",
                keyField != null ? args(propX(varX("src"), "path"), varX("text")) : args("text")))
        .addTo(annotatedClass);

    MethodBuilder.createPublicMethod("createFromSnippet")
        .deprecated()
        .returning(newClass(annotatedClass))
        .mod(Opcodes.ACC_STATIC)
        .param(make(URL.class), "src")
        .doReturn(callX(annotatedClass, "createFrom", args("src")))
        .addTo(annotatedClass);
  }
/**
 * Handles generation of code for the {@code @NotYetImplemented} annotation.
 *
 * @see groovy.transform.NotYetImplemented
 * @author Dierk König
 * @author Andre Steingress
 * @author Ilinca V. Hallberg
 * @author Björn Westlin
 */
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class NotYetImplementedASTTransformation extends AbstractASTTransformation {

  private static final ClassNode CATCHED_THROWABLE_TYPE = ClassHelper.make(Throwable.class);
  private static final ClassNode ASSERTION_FAILED_ERROR_TYPE =
      ClassHelper.make("junit.framework.AssertionFailedError");

  public void visit(ASTNode[] nodes, SourceUnit source) {
    if (nodes.length != 2
        || !(nodes[0] instanceof AnnotationNode)
        || !(nodes[1] instanceof AnnotatedNode)) {
      throw new RuntimeException(
          "Internal error: expecting [AnnotationNode, AnnotatedNode] but got: "
              + Arrays.asList(nodes));
    }

    AnnotationNode annotationNode = (AnnotationNode) nodes[0];
    ASTNode node = nodes[1];

    if (!(node instanceof MethodNode)) {
      addError("@NotYetImplemented must only be applied on test methods!", node);
      return;
    }

    MethodNode methodNode = (MethodNode) node;

    ArrayList<Statement> statements = new ArrayList<Statement>();
    Statement statement = methodNode.getCode();
    if (statement instanceof BlockStatement) {
      statements.addAll(((BlockStatement) statement).getStatements());
    }

    if (statements.size() == 0) return;

    BlockStatement rewrittenMethodCode = new BlockStatement();

    rewrittenMethodCode.addStatement(
        tryCatchAssertionFailedError(annotationNode, methodNode, statements));
    rewrittenMethodCode.addStatement(throwAssertionFailedError(annotationNode));

    methodNode.setCode(rewrittenMethodCode);
  }

  private TryCatchStatement tryCatchAssertionFailedError(
      AnnotationNode annotationNode, MethodNode methodNode, ArrayList<Statement> statements) {
    TryCatchStatement tryCatchStatement =
        new TryCatchStatement(
            new BlockStatement(statements, methodNode.getVariableScope()), EmptyStatement.INSTANCE);
    tryCatchStatement.addCatch(
        new CatchStatement(
            new Parameter(CATCHED_THROWABLE_TYPE, "ex"), ReturnStatement.RETURN_NULL_OR_VOID));
    return tryCatchStatement;
  }

  private Statement throwAssertionFailedError(AnnotationNode annotationNode) {
    ThrowStatement throwStatement =
        new ThrowStatement(
            new ConstructorCallExpression(
                ASSERTION_FAILED_ERROR_TYPE,
                new ArgumentListExpression(
                    new ConstantExpression(
                        "Method is marked with @NotYetImplemented but passes unexpectedly"))));

    throwStatement.setSourcePosition(annotationNode);

    return throwStatement;
  }
}