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);
  }