1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/test/tools/javac/treeannotests/TestProcessor.java Wed Feb 03 16:58:57 2010 -0800 1.3 @@ -0,0 +1,302 @@ 1.4 +/* 1.5 + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. 1.11 + * 1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 + * version 2 for more details (a copy is included in the LICENSE file that 1.16 + * accompanied this code). 1.17 + * 1.18 + * You should have received a copy of the GNU General Public License version 1.19 + * 2 along with this work; if not, write to the Free Software Foundation, 1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 + * 1.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 1.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 1.24 + * have any questions. 1.25 + */ 1.26 + 1.27 +import java.io.*; 1.28 +import java.util.*; 1.29 +import javax.annotation.processing.*; 1.30 +import javax.lang.model.*; 1.31 +import javax.lang.model.element.*; 1.32 +import javax.lang.model.util.*; 1.33 +import javax.tools.*; 1.34 + 1.35 +import com.sun.source.util.*; 1.36 +import com.sun.tools.javac.code.BoundKind; 1.37 +import com.sun.tools.javac.tree.JCTree.*; 1.38 +import com.sun.tools.javac.tree.TreeScanner; 1.39 +import com.sun.tools.javac.tree.*; 1.40 +import com.sun.tools.javac.util.List; 1.41 + 1.42 +/** 1.43 + * Test processor used to check test programs using the @Test, @DA, and @TA 1.44 + * annotations. 1.45 + * 1.46 + * The processor looks for elements annotated with @Test, and analyzes the 1.47 + * syntax trees for those elements. Within such trees, the processor looks 1.48 + * for the DA annotations on decls and TA annotations on types. 1.49 + * The value of these annotations should be a simple string rendition of 1.50 + * the tree node to which it is attached. 1.51 + * The expected number of annotations is given by the parameter to the 1.52 + * @Test annotation itself. 1.53 + */ 1.54 +@SupportedAnnotationTypes({"Test"}) 1.55 +public class TestProcessor extends AbstractProcessor { 1.56 + public SourceVersion getSupportedSourceVersion() { 1.57 + return SourceVersion.latest(); 1.58 + } 1.59 + 1.60 + /** Process trees for elements annotated with the @Test(n) annotation. */ 1.61 + public boolean process(Set<? extends TypeElement> annos, RoundEnvironment renv) { 1.62 + if (renv.processingOver()) 1.63 + return true; 1.64 + 1.65 + Elements elements = processingEnv.getElementUtils(); 1.66 + Trees trees = Trees.instance(processingEnv); 1.67 + 1.68 + TypeElement testAnno = elements.getTypeElement("Test"); 1.69 + for (Element elem: renv.getElementsAnnotatedWith(testAnno)) { 1.70 + System.err.println("ELEM: " + elem); 1.71 + int count = getValue(getAnnoMirror(elem, testAnno), Integer.class); 1.72 + System.err.println("count: " + count); 1.73 + TreePath p = trees.getPath(elem); 1.74 + JavaFileObject file = p.getCompilationUnit().getSourceFile(); 1.75 + JCTree tree = (JCTree) p.getLeaf(); 1.76 + System.err.println("tree: " + tree); 1.77 + new TestScanner(file).check(tree, count); 1.78 + } 1.79 + return true; 1.80 + } 1.81 + 1.82 + /** Get the AnnotationMirror on an element for a given annotation. */ 1.83 + AnnotationMirror getAnnoMirror(Element e, TypeElement anno) { 1.84 + Types types = processingEnv.getTypeUtils(); 1.85 + for (AnnotationMirror m: e.getAnnotationMirrors()) { 1.86 + if (types.isSameType(m.getAnnotationType(), anno.asType())) 1.87 + return m; 1.88 + } 1.89 + return null; 1.90 + } 1.91 + 1.92 + /** Get the value of the value element of an annotation mirror. */ 1.93 + <T> T getValue(AnnotationMirror m, Class<T> type) { 1.94 + for (Map.Entry<? extends ExecutableElement,? extends AnnotationValue> e: m.getElementValues().entrySet()) { 1.95 + ExecutableElement ee = e.getKey(); 1.96 + if (ee.getSimpleName().contentEquals("value")) { 1.97 + AnnotationValue av = e.getValue(); 1.98 + return type.cast(av.getValue()); 1.99 + } 1.100 + } 1.101 + return null; 1.102 + } 1.103 + 1.104 + /** Report an error to the annotation processing system. */ 1.105 + void error(String msg) { 1.106 + Messager messager = processingEnv.getMessager(); 1.107 + messager.printMessage(Diagnostic.Kind.ERROR, msg); 1.108 + } 1.109 + 1.110 + /** Report an error to the annotation processing system. */ 1.111 + void error(JavaFileObject file, JCTree tree, String msg) { 1.112 + // need better API for reporting tree position errors to the messager 1.113 + Messager messager = processingEnv.getMessager(); 1.114 + String text = file.getName() + ":" + getLine(file, tree) + ": " + msg; 1.115 + messager.printMessage(Diagnostic.Kind.ERROR, text); 1.116 + } 1.117 + 1.118 + /** Get the line number for the primary position for a tree. 1.119 + * The code is intended to be simple, although not necessarily efficient. 1.120 + * However, note that a file manager such as JavacFileManager is likely 1.121 + * to cache the results of file.getCharContent, avoiding the need to read 1.122 + * the bits from disk each time this method is called. 1.123 + */ 1.124 + int getLine(JavaFileObject file, JCTree tree) { 1.125 + try { 1.126 + CharSequence cs = file.getCharContent(true); 1.127 + int line = 1; 1.128 + for (int i = 0; i < tree.pos; i++) { 1.129 + if (cs.charAt(i) == '\n') // jtreg tests always use Unix line endings 1.130 + line++; 1.131 + } 1.132 + return line; 1.133 + } catch (IOException e) { 1.134 + return -1; 1.135 + } 1.136 + } 1.137 + 1.138 + /** Scan a tree, looking for @DA and @TA annotations, and verifying that such 1.139 + * annotations are attached to the expected tree node matching the string 1.140 + * parameter of the annotation. 1.141 + */ 1.142 + class TestScanner extends TreeScanner { 1.143 + /** Create a scanner for a given file. */ 1.144 + TestScanner(JavaFileObject file) { 1.145 + this.file = file; 1.146 + } 1.147 + 1.148 + /** Check the annotations in a given tree. */ 1.149 + void check(JCTree tree, int expectCount) { 1.150 + foundCount = 0; 1.151 + scan(tree); 1.152 + if (foundCount != expectCount) 1.153 + error(file, tree, "Wrong number of annotations found: " + foundCount + ", expected: " + expectCount); 1.154 + } 1.155 + 1.156 + /** Check @DA annotations on a class declaration. */ 1.157 + @Override 1.158 + public void visitClassDef(JCClassDecl tree) { 1.159 + super.visitClassDef(tree); 1.160 + check(tree.mods.annotations, "DA", tree); 1.161 + } 1.162 + 1.163 + /** Check @DA annotations on a method declaration. */ 1.164 + @Override 1.165 + public void visitMethodDef(JCMethodDecl tree) { 1.166 + super.visitMethodDef(tree); 1.167 + check(tree.mods.annotations, "DA", tree); 1.168 + } 1.169 + 1.170 + /** Check @DA annotations on a field, parameter or local variable declaration. */ 1.171 + @Override 1.172 + public void visitVarDef(JCVariableDecl tree) { 1.173 + super.visitVarDef(tree); 1.174 + check(tree.mods.annotations, "DA", tree); 1.175 + } 1.176 + 1.177 + /** Check @TA annotations on a type. */ 1.178 + public void visitAnnotatedType(JCAnnotatedType tree) { 1.179 + super.visitAnnotatedType(tree); 1.180 + check(tree.annotations, "TA", tree); 1.181 + } 1.182 + 1.183 + /** Check to see if a list of annotations contains a named annotation, and 1.184 + * if so, verify the annotation is expected by comparing the value of the 1.185 + * annotation's argument against the string rendition of the reference tree 1.186 + * node. 1.187 + * @param annos the list of annotations to be checked 1.188 + * @param name the name of the annotation to be checked 1.189 + * @param tree the tree against which to compare the annotations's argument 1.190 + */ 1.191 + void check(List<? extends JCAnnotation> annos, String name, JCTree tree) { 1.192 + for (List<? extends JCAnnotation> l = annos; l.nonEmpty(); l = l.tail) { 1.193 + JCAnnotation anno = l.head; 1.194 + if (anno.annotationType.toString().equals(name) && (anno.args.size() == 1)) { 1.195 + String expect = getStringValue(anno.args.head); 1.196 + foundCount++; 1.197 + System.err.println("found: " + name + " " + expect); 1.198 + String found = new TypePrinter().print(tree); 1.199 + if (!found.equals(expect)) 1.200 + error(file, anno, "Unexpected result: expected: \"" + expect + "\", found: \"" + found + "\""); 1.201 + } 1.202 + } 1.203 + } 1.204 + 1.205 + /** Get the string value of an annotation argument, which is given by the 1.206 + * expression <i>name</i>=<i>value</i>. 1.207 + */ 1.208 + String getStringValue(JCExpression e) { 1.209 + if (e.getTag() == JCTree.ASSIGN) { 1.210 + JCAssign a = (JCAssign) e; 1.211 + JCExpression rhs = a.rhs; 1.212 + if (rhs.getTag() == JCTree.LITERAL) { 1.213 + JCLiteral l = (JCLiteral) rhs; 1.214 + return (String) l.value; 1.215 + } 1.216 + } 1.217 + throw new IllegalArgumentException(e.toString()); 1.218 + } 1.219 + 1.220 + /** The file for the tree. Used to locate errors. */ 1.221 + JavaFileObject file; 1.222 + /** The number of annotations that have been found. @see #check */ 1.223 + int foundCount; 1.224 + } 1.225 + 1.226 + /** Convert a type or decl tree to a reference string used by the @DA and @TA annotations. */ 1.227 + class TypePrinter extends Visitor { 1.228 + /** Convert a type or decl tree to a string. */ 1.229 + String print(JCTree tree) { 1.230 + if (tree == null) 1.231 + return null; 1.232 + tree.accept(this); 1.233 + return result; 1.234 + } 1.235 + 1.236 + String print(List<? extends JCTree> list) { 1.237 + return print(list, ", "); 1.238 + } 1.239 + 1.240 + String print(List<? extends JCTree> list, String sep) { 1.241 + StringBuilder sb = new StringBuilder(); 1.242 + if (list.nonEmpty()) { 1.243 + sb.append(print(list.head)); 1.244 + for (List<? extends JCTree> l = list.tail; l.nonEmpty(); l = l.tail) { 1.245 + sb.append(sep); 1.246 + sb.append(print(l.head)); 1.247 + } 1.248 + } 1.249 + return sb.toString(); 1.250 + } 1.251 + 1.252 + @Override 1.253 + public void visitClassDef(JCClassDecl tree) { 1.254 + result = tree.name.toString(); 1.255 + } 1.256 + 1.257 + @Override 1.258 + public void visitMethodDef(JCMethodDecl tree) { 1.259 + result = tree.name.toString(); 1.260 + } 1.261 + 1.262 + @Override 1.263 + public void visitVarDef(JCVariableDecl tree) { 1.264 + tree.vartype.accept(this); 1.265 + } 1.266 + 1.267 + @Override 1.268 + public void visitAnnotatedType(JCAnnotatedType tree) { 1.269 + tree.underlyingType.accept(this); 1.270 + } 1.271 + 1.272 + @Override 1.273 + public void visitTypeIdent(JCPrimitiveTypeTree tree) { 1.274 + result = tree.toString(); 1.275 + } 1.276 + 1.277 + @Override 1.278 + public void visitTypeArray(JCArrayTypeTree tree) { 1.279 + result = print(tree.elemtype) + "[]"; 1.280 + } 1.281 + 1.282 + @Override 1.283 + public void visitTypeApply(JCTypeApply tree) { 1.284 + result = print(tree.clazz) + "<" + print(tree.arguments) + ">"; 1.285 + } 1.286 + 1.287 + @Override 1.288 + public void visitTypeParameter(JCTypeParameter tree) { 1.289 + if (tree.bounds.isEmpty()) 1.290 + result = tree.name.toString(); 1.291 + else 1.292 + result = tree.name + " extends " + print(tree.bounds, "&"); 1.293 + } 1.294 + 1.295 + @Override 1.296 + public void visitWildcard(JCWildcard tree) { 1.297 + if (tree.kind.kind == BoundKind.UNBOUND) 1.298 + result = tree.kind.toString(); 1.299 + else 1.300 + result = tree.kind + " " + print(tree.inner); 1.301 + } 1.302 + 1.303 + private String result; 1.304 + } 1.305 +}