aoqi@0: /* aoqi@0: * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: import java.io.*; aoqi@0: import java.lang.reflect.*; aoqi@0: import java.util.*; aoqi@0: import javax.tools.*; aoqi@0: aoqi@0: import com.sun.source.tree.CompilationUnitTree; aoqi@0: import com.sun.source.tree.Tree; aoqi@0: import com.sun.source.util.JavacTask; aoqi@0: import com.sun.tools.javac.api.JavacTool; aoqi@0: import com.sun.tools.javac.tree.JCTree; aoqi@0: import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; aoqi@0: import com.sun.tools.javac.util.List; aoqi@0: aoqi@0: public abstract class AbstractTreeScannerTest { aoqi@0: aoqi@0: /** aoqi@0: * Run the program. A base directory can be provided for file arguments. aoqi@0: * In jtreg mode, the -r option can be given to change the default base aoqi@0: * directory to the test root directory. For other options, see usage(). aoqi@0: * @param baseDir base directory for any file arguments. aoqi@0: * @param args command line args aoqi@0: * @return true if successful or in gui mode aoqi@0: */ aoqi@0: boolean run(File baseDir, String... args) { aoqi@0: if (args.length == 0) { aoqi@0: usage(System.out); aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: ArrayList files = new ArrayList(); aoqi@0: for (int i = 0; i < args.length; i++) { aoqi@0: String arg = args[i]; aoqi@0: if (arg.equals("-q")) aoqi@0: quiet = true; aoqi@0: else if (arg.equals("-v")) aoqi@0: verbose = true; aoqi@0: else if (arg.equals("-r")) { aoqi@0: File d = baseDir; aoqi@0: while (!new File(d, "TEST.ROOT").exists()) { aoqi@0: d = d.getParentFile(); aoqi@0: if (d == null) aoqi@0: throw new Error("cannot find TEST.ROOT"); aoqi@0: } aoqi@0: baseDir = d; aoqi@0: } aoqi@0: else if (arg.startsWith("-")) aoqi@0: throw new Error("unknown option: " + arg); aoqi@0: else { aoqi@0: while (i < args.length) aoqi@0: files.add(new File(baseDir, args[i++])); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: for (File file: files) { aoqi@0: if (file.exists()) aoqi@0: test(file); aoqi@0: else aoqi@0: error("File not found: " + file); aoqi@0: } aoqi@0: aoqi@0: if (fileCount != 1) aoqi@0: System.err.println(fileCount + " files read"); aoqi@0: System.err.println(treeCount + " tree nodes compared"); aoqi@0: if (errors > 0) aoqi@0: System.err.println(errors + " errors"); aoqi@0: aoqi@0: return (errors == 0); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Print command line help. aoqi@0: * @param out output stream aoqi@0: */ aoqi@0: void usage(PrintStream out) { aoqi@0: out.println("Usage:"); aoqi@0: out.println(" java " + getClass().getName() + " options... files..."); aoqi@0: out.println(""); aoqi@0: out.println("where options include:"); aoqi@0: out.println("-q Quiet: don't report on inapplicable files"); aoqi@0: out.println("-v Verbose: report on files as they are being read"); aoqi@0: out.println(""); aoqi@0: out.println("files may be directories or files"); aoqi@0: out.println("directories will be scanned recursively"); aoqi@0: out.println("non java files, or java files which cannot be parsed, will be ignored"); aoqi@0: out.println(""); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Test a file. If the file is a directory, it will be recursively scanned aoqi@0: * for java files. aoqi@0: * @param file the file or directory to test aoqi@0: */ aoqi@0: void test(File file) { aoqi@0: if (file.isDirectory()) { aoqi@0: for (File f: file.listFiles()) { aoqi@0: test(f); aoqi@0: } aoqi@0: return; aoqi@0: } aoqi@0: aoqi@0: if (file.isFile() && file.getName().endsWith(".java")) { aoqi@0: try { aoqi@0: if (verbose) aoqi@0: System.err.println(file); aoqi@0: fileCount++; aoqi@0: treeCount += test(read(file)); aoqi@0: } catch (ParseException e) { aoqi@0: if (!quiet) { aoqi@0: error("Error parsing " + file + "\n" + e.getMessage()); aoqi@0: } aoqi@0: } catch (IOException e) { aoqi@0: error("Error reading " + file + ": " + e); aoqi@0: } aoqi@0: return; aoqi@0: } aoqi@0: aoqi@0: if (!quiet) aoqi@0: error("File " + file + " ignored"); aoqi@0: } aoqi@0: aoqi@0: abstract int test(JCCompilationUnit t); aoqi@0: aoqi@0: // See CR: 6982992 Tests CheckAttributedTree.java, JavacTreeScannerTest.java, and SourceTreeeScannerTest.java timeout aoqi@0: StringWriter sw = new StringWriter(); aoqi@0: PrintWriter pw = new PrintWriter(sw); aoqi@0: Reporter r = new Reporter(pw); aoqi@0: JavacTool tool = JavacTool.create(); aoqi@0: StandardJavaFileManager fm = tool.getStandardFileManager(r, null, null); aoqi@0: aoqi@0: /** aoqi@0: * Read a file. aoqi@0: * @param file the file to be read aoqi@0: * @return the tree for the content of the file aoqi@0: * @throws IOException if any IO errors occur aoqi@0: * @throws TreePosTest.ParseException if any errors occur while parsing the file aoqi@0: */ aoqi@0: JCCompilationUnit read(File file) throws IOException, ParseException { aoqi@0: JavacTool tool = JavacTool.create(); aoqi@0: r.errors = 0; aoqi@0: Iterable files = fm.getJavaFileObjects(file); aoqi@0: JavacTask task = tool.getTask(pw, fm, r, Collections.emptyList(), null, files); aoqi@0: Iterable trees = task.parse(); aoqi@0: pw.flush(); aoqi@0: if (r.errors > 0) aoqi@0: throw new ParseException(sw.toString()); aoqi@0: Iterator iter = trees.iterator(); aoqi@0: if (!iter.hasNext()) aoqi@0: throw new Error("no trees found"); aoqi@0: JCCompilationUnit t = (JCCompilationUnit) iter.next(); aoqi@0: if (iter.hasNext()) aoqi@0: throw new Error("too many trees found"); aoqi@0: return t; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Report an error. When the program is complete, the program will either aoqi@0: * exit or throw an Error if any errors have been reported. aoqi@0: * @param msg the error message aoqi@0: */ aoqi@0: void error(String msg) { aoqi@0: System.err.println(msg); aoqi@0: errors++; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Report an error. When the program is complete, the program will either aoqi@0: * exit or throw an Error if any errors have been reported. aoqi@0: * @param msg the error message aoqi@0: */ aoqi@0: void error(JavaFileObject file, String msg) { aoqi@0: System.err.println(file.getName() + ": " + msg); aoqi@0: errors++; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Report an error for a specific tree node. aoqi@0: * @param file the source file for the tree aoqi@0: * @param t the tree node aoqi@0: * @param label an indication of the error aoqi@0: */ aoqi@0: void error(JavaFileObject file, Tree tree, String msg) { aoqi@0: JCTree t = (JCTree) tree; aoqi@0: error(file.getName() + ":" + getLine(file, t) + ": " + msg + " " + trim(t, 64)); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Get a trimmed string for a tree node, with normalized white space and limited length. aoqi@0: */ aoqi@0: String trim(Tree tree, int len) { aoqi@0: JCTree t = (JCTree) tree; aoqi@0: String s = t.toString().replaceAll("\\s+", " "); aoqi@0: return (s.length() < len) ? s : s.substring(0, len); aoqi@0: } aoqi@0: aoqi@0: /** Number of files that have been analyzed. */ aoqi@0: int fileCount; aoqi@0: /** Number of trees that have been successfully compared. */ aoqi@0: int treeCount; aoqi@0: /** Number of errors reported. */ aoqi@0: int errors; aoqi@0: /** Flag: don't report irrelevant files. */ aoqi@0: boolean quiet; aoqi@0: /** Flag: report files as they are processed. */ aoqi@0: boolean verbose; aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Thrown when errors are found parsing a java file. aoqi@0: */ aoqi@0: private static class ParseException extends Exception { aoqi@0: ParseException(String msg) { aoqi@0: super(msg); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * DiagnosticListener to report diagnostics and count any errors that occur. aoqi@0: */ aoqi@0: private static class Reporter implements DiagnosticListener { aoqi@0: Reporter(PrintWriter out) { aoqi@0: this.out = out; aoqi@0: } aoqi@0: aoqi@0: public void report(Diagnostic diagnostic) { aoqi@0: out.println(diagnostic); aoqi@0: switch (diagnostic.getKind()) { aoqi@0: case ERROR: aoqi@0: errors++; aoqi@0: } aoqi@0: } aoqi@0: int errors; aoqi@0: PrintWriter out; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Get the set of fields for a tree node that may contain child tree nodes. aoqi@0: * These are the fields that are subtypes of JCTree or List. aoqi@0: * The results are cached, based on the tree's tag. aoqi@0: */ aoqi@0: Set getFields(JCTree tree) { aoqi@0: Set fields = map.get(tree.getTag()); aoqi@0: if (fields == null) { aoqi@0: fields = new HashSet(); aoqi@0: for (Field f: tree.getClass().getFields()) { aoqi@0: Class fc = f.getType(); aoqi@0: if (JCTree.class.isAssignableFrom(fc) || List.class.isAssignableFrom(fc)) aoqi@0: fields.add(f); aoqi@0: } aoqi@0: map.put(tree.getTag(), fields); aoqi@0: } aoqi@0: return fields; aoqi@0: } aoqi@0: // where aoqi@0: Map> map = new HashMap>(); aoqi@0: aoqi@0: /** Get the line number for the primary position for a tree. aoqi@0: * The code is intended to be simple, although not necessarily efficient. aoqi@0: * However, note that a file manager such as JavacFileManager is likely aoqi@0: * to cache the results of file.getCharContent, avoiding the need to read aoqi@0: * the bits from disk each time this method is called. aoqi@0: */ aoqi@0: int getLine(JavaFileObject file, JCTree tree) { aoqi@0: try { aoqi@0: CharSequence cs = file.getCharContent(true); aoqi@0: int line = 1; aoqi@0: for (int i = 0; i < tree.pos; i++) { aoqi@0: if (cs.charAt(i) == '\n') // jtreg tests always use Unix line endings aoqi@0: line++; aoqi@0: } aoqi@0: return line; aoqi@0: } catch (IOException e) { aoqi@0: return -1; aoqi@0: } aoqi@0: } aoqi@0: }