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