1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/test/tools/javac/tree/AbstractTreeScannerTest.java Thu Sep 09 13:31:28 2010 -0700 1.3 @@ -0,0 +1,284 @@ 1.4 +/* 1.5 + * Copyright (c) 2010, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.23 + * or visit www.oracle.com if you need additional information or have any 1.24 + * questions. 1.25 + */ 1.26 + 1.27 +import java.io.*; 1.28 +import java.lang.reflect.*; 1.29 +import java.util.*; 1.30 +import javax.tools.*; 1.31 + 1.32 +import com.sun.source.tree.CompilationUnitTree; 1.33 +import com.sun.source.tree.Tree; 1.34 +import com.sun.source.util.JavacTask; 1.35 +import com.sun.tools.javac.api.JavacTool; 1.36 +import com.sun.tools.javac.tree.JCTree; 1.37 +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 1.38 +import com.sun.tools.javac.util.List; 1.39 + 1.40 +public abstract class AbstractTreeScannerTest { 1.41 + 1.42 + /** 1.43 + * Run the program. A base directory can be provided for file arguments. 1.44 + * In jtreg mode, the -r option can be given to change the default base 1.45 + * directory to the test root directory. For other options, see usage(). 1.46 + * @param baseDir base directory for any file arguments. 1.47 + * @param args command line args 1.48 + * @return true if successful or in gui mode 1.49 + */ 1.50 + boolean run(File baseDir, String... args) { 1.51 + if (args.length == 0) { 1.52 + usage(System.out); 1.53 + return true; 1.54 + } 1.55 + 1.56 + ArrayList<File> files = new ArrayList<File>(); 1.57 + for (int i = 0; i < args.length; i++) { 1.58 + String arg = args[i]; 1.59 + if (arg.equals("-q")) 1.60 + quiet = true; 1.61 + else if (arg.equals("-v")) 1.62 + verbose = true; 1.63 + else if (arg.equals("-r")) { 1.64 + File d = baseDir; 1.65 + while (!new File(d, "TEST.ROOT").exists()) { 1.66 + d = d.getParentFile(); 1.67 + if (d == null) 1.68 + throw new Error("cannot find TEST.ROOT"); 1.69 + } 1.70 + baseDir = d; 1.71 + } 1.72 + else if (arg.startsWith("-")) 1.73 + throw new Error("unknown option: " + arg); 1.74 + else { 1.75 + while (i < args.length) 1.76 + files.add(new File(baseDir, args[i++])); 1.77 + } 1.78 + } 1.79 + 1.80 + for (File file: files) { 1.81 + if (file.exists()) 1.82 + test(file); 1.83 + else 1.84 + error("File not found: " + file); 1.85 + } 1.86 + 1.87 + if (fileCount != 1) 1.88 + System.err.println(fileCount + " files read"); 1.89 + System.err.println(treeCount + " tree nodes compared"); 1.90 + if (errors > 0) 1.91 + System.err.println(errors + " errors"); 1.92 + 1.93 + return (errors == 0); 1.94 + } 1.95 + 1.96 + /** 1.97 + * Print command line help. 1.98 + * @param out output stream 1.99 + */ 1.100 + void usage(PrintStream out) { 1.101 + out.println("Usage:"); 1.102 + out.println(" java " + getClass().getName() + " options... files..."); 1.103 + out.println(""); 1.104 + out.println("where options include:"); 1.105 + out.println("-q Quiet: don't report on inapplicable files"); 1.106 + out.println("-v Verbose: report on files as they are being read"); 1.107 + out.println(""); 1.108 + out.println("files may be directories or files"); 1.109 + out.println("directories will be scanned recursively"); 1.110 + out.println("non java files, or java files which cannot be parsed, will be ignored"); 1.111 + out.println(""); 1.112 + } 1.113 + 1.114 + /** 1.115 + * Test a file. If the file is a directory, it will be recursively scanned 1.116 + * for java files. 1.117 + * @param file the file or directory to test 1.118 + */ 1.119 + void test(File file) { 1.120 + if (file.isDirectory()) { 1.121 + for (File f: file.listFiles()) { 1.122 + test(f); 1.123 + } 1.124 + return; 1.125 + } 1.126 + 1.127 + if (file.isFile() && file.getName().endsWith(".java")) { 1.128 + try { 1.129 + if (verbose) 1.130 + System.err.println(file); 1.131 + fileCount++; 1.132 + treeCount += test(read(file)); 1.133 + } catch (ParseException e) { 1.134 + if (!quiet) { 1.135 + error("Error parsing " + file + "\n" + e.getMessage()); 1.136 + } 1.137 + } catch (IOException e) { 1.138 + error("Error reading " + file + ": " + e); 1.139 + } 1.140 + return; 1.141 + } 1.142 + 1.143 + if (!quiet) 1.144 + error("File " + file + " ignored"); 1.145 + } 1.146 + 1.147 + abstract int test(JCCompilationUnit t); 1.148 + 1.149 + /** 1.150 + * Read a file. 1.151 + * @param file the file to be read 1.152 + * @return the tree for the content of the file 1.153 + * @throws IOException if any IO errors occur 1.154 + * @throws TreePosTest.ParseException if any errors occur while parsing the file 1.155 + */ 1.156 + JCCompilationUnit read(File file) throws IOException, ParseException { 1.157 + StringWriter sw = new StringWriter(); 1.158 + PrintWriter pw = new PrintWriter(sw); 1.159 + Reporter r = new Reporter(pw); 1.160 + JavacTool tool = JavacTool.create(); 1.161 + StandardJavaFileManager fm = tool.getStandardFileManager(r, null, null); 1.162 + Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(file); 1.163 + JavacTask task = tool.getTask(pw, fm, r, Collections.<String>emptyList(), null, files); 1.164 + Iterable<? extends CompilationUnitTree> trees = task.parse(); 1.165 + pw.flush(); 1.166 + if (r.errors > 0) 1.167 + throw new ParseException(sw.toString()); 1.168 + Iterator<? extends CompilationUnitTree> iter = trees.iterator(); 1.169 + if (!iter.hasNext()) 1.170 + throw new Error("no trees found"); 1.171 + JCCompilationUnit t = (JCCompilationUnit) iter.next(); 1.172 + if (iter.hasNext()) 1.173 + throw new Error("too many trees found"); 1.174 + return t; 1.175 + } 1.176 + 1.177 + /** 1.178 + * Report an error. When the program is complete, the program will either 1.179 + * exit or throw an Error if any errors have been reported. 1.180 + * @param msg the error message 1.181 + */ 1.182 + void error(String msg) { 1.183 + System.err.println(msg); 1.184 + errors++; 1.185 + } 1.186 + 1.187 + /** 1.188 + * Report an error for a specific tree node. 1.189 + * @param file the source file for the tree 1.190 + * @param t the tree node 1.191 + * @param label an indication of the error 1.192 + */ 1.193 + void error(JavaFileObject file, Tree tree, String msg) { 1.194 + JCTree t = (JCTree) tree; 1.195 + error(file.getName() + ":" + getLine(file, t) + ": " + msg + " " + trim(t, 64)); 1.196 + } 1.197 + 1.198 + /** 1.199 + * Get a trimmed string for a tree node, with normalized white space and limited length. 1.200 + */ 1.201 + String trim(Tree tree, int len) { 1.202 + JCTree t = (JCTree) tree; 1.203 + String s = t.toString().replaceAll("[\r\n]+", " ").replaceAll(" +", " "); 1.204 + return (s.length() < len) ? s : s.substring(0, len); 1.205 + } 1.206 + 1.207 + /** Number of files that have been analyzed. */ 1.208 + int fileCount; 1.209 + /** Number of trees that have been successfully compared. */ 1.210 + int treeCount; 1.211 + /** Number of errors reported. */ 1.212 + int errors; 1.213 + /** Flag: don't report irrelevant files. */ 1.214 + boolean quiet; 1.215 + /** Flag: report files as they are processed. */ 1.216 + boolean verbose; 1.217 + 1.218 + 1.219 + /** 1.220 + * Thrown when errors are found parsing a java file. 1.221 + */ 1.222 + private static class ParseException extends Exception { 1.223 + ParseException(String msg) { 1.224 + super(msg); 1.225 + } 1.226 + } 1.227 + 1.228 + /** 1.229 + * DiagnosticListener to report diagnostics and count any errors that occur. 1.230 + */ 1.231 + private static class Reporter implements DiagnosticListener<JavaFileObject> { 1.232 + Reporter(PrintWriter out) { 1.233 + this.out = out; 1.234 + } 1.235 + 1.236 + public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 1.237 + out.println(diagnostic); 1.238 + switch (diagnostic.getKind()) { 1.239 + case ERROR: 1.240 + errors++; 1.241 + } 1.242 + } 1.243 + int errors; 1.244 + PrintWriter out; 1.245 + } 1.246 + 1.247 + /** 1.248 + * Get the set of fields for a tree node that may contain child tree nodes. 1.249 + * These are the fields that are subtypes of JCTree or List. 1.250 + * The results are cached, based on the tree's tag. 1.251 + */ 1.252 + Set<Field> getFields(JCTree tree) { 1.253 + Set<Field> fields = map.get(tree.getTag()); 1.254 + if (fields == null) { 1.255 + fields = new HashSet<Field>(); 1.256 + for (Field f: tree.getClass().getFields()) { 1.257 + Class<?> fc = f.getType(); 1.258 + if (JCTree.class.isAssignableFrom(fc) || List.class.isAssignableFrom(fc)) 1.259 + fields.add(f); 1.260 + } 1.261 + map.put(tree.getTag(), fields); 1.262 + } 1.263 + return fields; 1.264 + } 1.265 + // where 1.266 + Map<Integer, Set<Field>> map = new HashMap<Integer,Set<Field>>(); 1.267 + 1.268 + /** Get the line number for the primary position for a tree. 1.269 + * The code is intended to be simple, although not necessarily efficient. 1.270 + * However, note that a file manager such as JavacFileManager is likely 1.271 + * to cache the results of file.getCharContent, avoiding the need to read 1.272 + * the bits from disk each time this method is called. 1.273 + */ 1.274 + int getLine(JavaFileObject file, JCTree tree) { 1.275 + try { 1.276 + CharSequence cs = file.getCharContent(true); 1.277 + int line = 1; 1.278 + for (int i = 0; i < tree.pos; i++) { 1.279 + if (cs.charAt(i) == '\n') // jtreg tests always use Unix line endings 1.280 + line++; 1.281 + } 1.282 + return line; 1.283 + } catch (IOException e) { 1.284 + return -1; 1.285 + } 1.286 + } 1.287 +}