jjg@1755: /* jjg@1755: * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. jjg@1755: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@1755: * jjg@1755: * This code is free software; you can redistribute it and/or modify it jjg@1755: * under the terms of the GNU General Public License version 2 only, as jjg@1755: * published by the Free Software Foundation. jjg@1755: * jjg@1755: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@1755: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@1755: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@1755: * version 2 for more details (a copy is included in the LICENSE file that jjg@1755: * accompanied this code). jjg@1755: * jjg@1755: * You should have received a copy of the GNU General Public License version jjg@1755: * 2 along with this work; if not, write to the Free Software Foundation, jjg@1755: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@1755: * jjg@1755: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jjg@1755: * or visit www.oracle.com if you need additional information or have any jjg@1755: * questions. jjg@1755: */ jjg@1755: jjg@1755: /* jjg@1755: * @test jjg@1755: * @bug 1234567 jjg@1755: * @summary Annotations on types jjg@1755: * @library /tools/javac/lib jjg@2134: * @ignore jjg@1755: * @build JavacTestingAbstractProcessor DPrinter BasicAnnoTests jjg@1755: * @compile/process -processor BasicAnnoTests -proc:only BasicAnnoTests.java jjg@1755: */ jjg@1755: jjg@1755: import java.io.PrintWriter; jjg@1755: import java.lang.annotation.Annotation; jjg@1755: import java.lang.annotation.ElementType; jjg@1755: import java.lang.annotation.Target; jjg@1755: import java.util.Map; jjg@1755: import java.util.Set; jjg@1755: jjg@1755: import javax.annotation.processing.ProcessingEnvironment; jjg@1755: import javax.annotation.processing.RoundEnvironment; jjg@1755: import javax.lang.model.AnnotatedConstruct; jjg@1755: import javax.lang.model.element.AnnotationMirror; jjg@1755: import javax.lang.model.element.AnnotationValue; jjg@1755: import javax.lang.model.element.Element; jjg@1755: import javax.lang.model.element.ExecutableElement; jjg@1755: import javax.lang.model.element.TypeElement; jjg@1755: import javax.lang.model.type.ArrayType; jjg@1755: import javax.lang.model.type.ExecutableType; jjg@1755: import javax.lang.model.type.TypeMirror; jjg@1755: import javax.lang.model.type.TypeVariable; jjg@1755: import javax.lang.model.type.WildcardType; jjg@1755: import javax.tools.Diagnostic.Kind; jjg@1755: jjg@1755: import com.sun.tools.javac.code.Symbol; jjg@1755: import com.sun.tools.javac.code.Type; jjg@1755: import com.sun.tools.javac.processing.JavacProcessingEnvironment; jjg@1755: jjg@1755: /** jjg@1755: * The test scans this file looking for test cases annotated with @Test. jjg@1755: */ jjg@1755: public class BasicAnnoTests extends JavacTestingAbstractProcessor { jjg@1755: DPrinter dprinter; jjg@1755: PrintWriter out; jjg@1755: boolean verbose = true; jjg@1755: jjg@1755: @Override jjg@1755: public void init(ProcessingEnvironment pEnv) { jjg@1755: super.init(pEnv); jjg@1755: dprinter = new DPrinter(((JavacProcessingEnvironment) pEnv).getContext()); jjg@1755: out = dprinter.out; jjg@1755: } jjg@1755: jjg@1755: @Override jjg@1755: public boolean process(Set annotations, RoundEnvironment roundEnv) { jjg@1755: TestElementScanner s = new TestElementScanner(); jjg@1755: for (Element e: roundEnv.getRootElements()) { jjg@1755: s.scan(e); jjg@1755: } jjg@1755: return true; jjg@1755: } jjg@1755: jjg@1755: void error(Element e, String msg) { jjg@1755: messager.printMessage(Kind.ERROR, msg, e); jjg@1755: errors++; jjg@1755: } jjg@1755: jjg@1755: int errors; jjg@1755: jjg@1755: /** jjg@1755: * Scan an element looking for declarations annotated with @Test. jjg@1755: * Run a TestTypeScanner on the annotations that are found. jjg@1755: */ jjg@1755: class TestElementScanner extends ElementScanner { jjg@1755: public Void scan(Element elem, Void ignore) { jjg@1755: AnnotationMirror test = getAnnotation(elem, Test.class.getName().replace('$', '.')); jjg@1755: if (test != null) { jjg@1755: out.println("Test: " + elem + " " + test); jjg@1755: TestTypeScanner s = new TestTypeScanner(elem, test); jjg@1755: s.scan(elem.asType(), null); jjg@1755: if (getPosn(test) >= s.count) jjg@1755: error(elem, "position " + getPosn(test) + " not found"); jjg@1755: if (!s.found) { jjg@1755: dprinter.printSymbol("element", (Symbol) elem); jjg@1755: dprinter.printType("type", (Type) elem.asType()); jjg@1755: } jjg@1755: out.println(); jjg@1755: } jjg@1755: return super.scan(elem, ignore); jjg@1755: } jjg@1755: } jjg@1755: jjg@1755: /** jjg@1755: * Scan the type of an element, looking for an annotation jjg@1755: * to match the expected annotation specified in the @Test annotation. jjg@1755: */ jjg@1755: class TestTypeScanner extends TypeScanner { jjg@1755: Element elem; jjg@1755: AnnotationMirror test; jjg@1755: int count = 0; jjg@1755: boolean found = false; jjg@1755: jjg@1755: TestTypeScanner(Element elem, AnnotationMirror test) { jjg@1755: this.elem = elem; jjg@1755: this.test = test; jjg@1755: } jjg@1755: jjg@1755: @Override jjg@1755: Void scan(TypeMirror t, Void ignore) { jjg@1755: if (t == null) jjg@1755: return DEFAULT_VALUE; jjg@1755: if (verbose) jjg@1755: out.println("scan " + count + ": " + t); jjg@1755: if (count == getPosn(test)) { jjg@1755: String annoType = getAnnoType(test); jjg@1755: AnnotationMirror anno = getAnnotation(t, annoType); jjg@1755: if (anno == null) { jjg@1755: error(elem, "annotation not found on " + count + ": " + t); jjg@1755: } else { jjg@1755: String v = getValue(anno, "value").toString(); jjg@1755: if (v.equals(getExpect(test))) { jjg@1755: out.println("found " + anno + " as expected"); jjg@1755: found = true; jjg@1755: } else { jjg@1755: error(elem, "Unexpected value: " + v + ", expected: " + getExpect(test)); jjg@1755: } jjg@1755: } jjg@1755: } jjg@1755: count++; jjg@1755: return super.scan(t, ignore); jjg@1755: } jjg@1755: } jjg@1755: jjg@1755: /** Get the position value from an @Test annotation mirror. */ jjg@1755: static int getPosn(AnnotationMirror test) { jjg@1755: AnnotationValue v = getValue(test, "posn"); jjg@1755: return (Integer) v.getValue(); jjg@1755: } jjg@1755: jjg@1755: /** Get the expect value from an @Test annotation mirror. */ jjg@1755: static String getExpect(AnnotationMirror test) { jjg@1755: AnnotationValue v = getValue(test, "expect"); jjg@1755: return (String) v.getValue(); jjg@1755: } jjg@1755: jjg@1755: /** Get the annoType value from an @Test annotation mirror. */ jjg@1755: static String getAnnoType(AnnotationMirror test) { jjg@1755: AnnotationValue v = getValue(test, "annoType"); jjg@1755: TypeMirror m = (TypeMirror) v.getValue(); jjg@1755: return m.toString(); jjg@1755: } jjg@1755: jjg@1755: /** jjg@1755: * Get a specific annotation mirror from an annotated construct. jjg@1755: */ jjg@1755: static AnnotationMirror getAnnotation(AnnotatedConstruct e, String name) { jjg@1755: for (AnnotationMirror m: e.getAnnotationMirrors()) { jjg@1755: TypeElement te = (TypeElement) m.getAnnotationType().asElement(); jjg@1755: if (te.getQualifiedName().contentEquals(name)) { jjg@1755: return m; jjg@1755: } jjg@1755: } jjg@1755: return null; jjg@1755: } jjg@1755: jjg@1755: /** jjg@1755: * Get a specific value from an annotation mirror. jjg@1755: */ jjg@1755: static AnnotationValue getValue(AnnotationMirror anno, String name) { jjg@1755: Map map = anno.getElementValues(); jjg@1755: for (Map.Entry e: map.entrySet()) { jjg@1755: if (e.getKey().getSimpleName().contentEquals(name)) { jjg@1755: return e.getValue(); jjg@1755: } jjg@1755: } jjg@1755: return null; jjg@1755: } jjg@1755: jjg@1755: /** jjg@1755: * The Language Model API does not provide a type scanner, so provide jjg@1755: * one sufficient for our needs. jjg@1755: */ jjg@1755: static class TypeScanner extends SimpleTypeVisitor { jjg@1755: @Override jjg@1755: public R visitArray(ArrayType t, P p) { jjg@1755: scan(t.getComponentType(), p); jjg@1755: return super.visitArray(t, p); jjg@1755: } jjg@1755: jjg@1755: @Override jjg@1755: public R visitExecutable(ExecutableType t, P p) { jjg@1755: scan(t.getReceiverType()); jjg@1755: //out.println(" params: " + t.getParameterTypes()); jjg@1755: scan(t.getParameterTypes(), p); jjg@1755: //out.println(" return: " + t.getReturnType()); jjg@1755: scan(t.getReturnType(), p); jjg@1755: //out.println(" throws: " + t.getThrownTypes()); jjg@1755: scan(t.getThrownTypes(), p); jjg@1755: return super.visitExecutable(t, p); jjg@1755: } jjg@1755: jjg@1755: @Override jjg@1755: public R visitTypeVariable(TypeVariable t, P p) { jjg@1755: scan(t.getLowerBound(), p); jjg@1755: scan(t.getUpperBound(), p); jjg@1755: return super.visitTypeVariable(t, p); jjg@1755: } jjg@1755: jjg@1755: @Override jjg@1755: public R visitWildcard(WildcardType t, P p) { jjg@1755: scan(t.getExtendsBound(), p); jjg@1755: scan(t.getSuperBound(), p); jjg@1755: return super.visitWildcard(t, p); jjg@1755: } jjg@1755: jjg@1755: R scan(TypeMirror t) { jjg@1755: return scan(t, null); jjg@1755: } jjg@1755: jjg@1755: R scan(TypeMirror t, P p) { jjg@1755: return (t == null) ? DEFAULT_VALUE : t.accept(this, p); jjg@1755: } jjg@1755: jjg@1755: R scan(Iterable iter, P p) { jjg@1755: if (iter == null) jjg@1755: return DEFAULT_VALUE; jjg@1755: R result = DEFAULT_VALUE; jjg@1755: for (TypeMirror t: iter) jjg@1755: result = scan(t, p); jjg@1755: return result; jjg@1755: } jjg@1755: } jjg@1755: jjg@1755: /** Annotation to identify test cases. */ jjg@1755: @interface Test { jjg@1755: /** Where to look for the annotation, expressed as a scan index. */ jjg@1755: int posn(); jjg@1755: /** The annotation to look for. */ jjg@1755: Class annoType(); jjg@1755: /** The string representation of the annotation's value. */ jjg@1755: String expect(); jjg@1755: } jjg@1755: jjg@1755: /** Type annotation to use in test cases. */ jjg@1755: @Target(ElementType.TYPE_USE) jjg@1755: public @interface TA { jjg@1755: int value(); jjg@1755: } jjg@1755: jjg@1755: @Test(posn=0, annoType=TA.class, expect="1") jjg@1755: public @TA(1) int f1; jjg@1755: jjg@1755: @Test(posn=0, annoType=TA.class, expect="2") jjg@1755: public int @TA(2) [] f2; jjg@1755: jjg@1755: @Test(posn=1, annoType=TA.class, expect="3") jjg@1755: public @TA(3) int [] f3; jjg@1755: jjg@1755: @Test(posn=1, annoType=TA.class, expect="4") jjg@1755: public int m1(@TA(4) float a) throws Exception { return 0; } jjg@1755: jjg@1755: @Test(posn=2, annoType=TA.class, expect="5") jjg@1755: public @TA(5) int m2(float a) throws Exception { return 0; } jjg@1755: jjg@1755: @Test(posn=3, annoType=TA.class, expect="6") jjg@1755: public int m3(float a) throws @TA(6) Exception { return 0; } jjg@1755: }