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