langtools/test/tools/javac/tree/AbstractTreeScannerTest.java
changeset 6597 9367c22c445f
parent 5520 86e4b9a9da40
child 6601 90c4a1a64217
equal deleted inserted replaced
6596:3274cf9d4873 6597:9367c22c445f
       
     1 /*
       
     2  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 import java.io.*;
       
    25 import java.lang.reflect.*;
       
    26 import java.util.*;
       
    27 import javax.tools.*;
       
    28 
       
    29 import com.sun.source.tree.CompilationUnitTree;
       
    30 import com.sun.source.tree.Tree;
       
    31 import com.sun.source.util.JavacTask;
       
    32 import com.sun.tools.javac.api.JavacTool;
       
    33 import com.sun.tools.javac.tree.JCTree;
       
    34 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
       
    35 import com.sun.tools.javac.util.List;
       
    36 
       
    37 public abstract class AbstractTreeScannerTest {
       
    38 
       
    39     /**
       
    40      * Run the program. A base directory can be provided for file arguments.
       
    41      * In jtreg mode, the -r option can be given to change the default base
       
    42      * directory to the test root directory. For other options, see usage().
       
    43      * @param baseDir base directory for any file arguments.
       
    44      * @param args command line args
       
    45      * @return true if successful or in gui mode
       
    46      */
       
    47     boolean run(File baseDir, String... args) {
       
    48         if (args.length == 0) {
       
    49             usage(System.out);
       
    50             return true;
       
    51         }
       
    52 
       
    53         ArrayList<File> files = new ArrayList<File>();
       
    54         for (int i = 0; i < args.length; i++) {
       
    55             String arg = args[i];
       
    56             if (arg.equals("-q"))
       
    57                 quiet = true;
       
    58             else if (arg.equals("-v"))
       
    59                 verbose = true;
       
    60             else if (arg.equals("-r")) {
       
    61                 File d = baseDir;
       
    62                 while (!new File(d, "TEST.ROOT").exists()) {
       
    63                     d = d.getParentFile();
       
    64                     if (d == null)
       
    65                         throw new Error("cannot find TEST.ROOT");
       
    66                 }
       
    67                 baseDir = d;
       
    68             }
       
    69             else if (arg.startsWith("-"))
       
    70                 throw new Error("unknown option: " + arg);
       
    71             else {
       
    72                 while (i < args.length)
       
    73                     files.add(new File(baseDir, args[i++]));
       
    74             }
       
    75         }
       
    76 
       
    77         for (File file: files) {
       
    78             if (file.exists())
       
    79                 test(file);
       
    80             else
       
    81                 error("File not found: " + file);
       
    82         }
       
    83 
       
    84         if (fileCount != 1)
       
    85             System.err.println(fileCount + " files read");
       
    86         System.err.println(treeCount + " tree nodes compared");
       
    87         if (errors > 0)
       
    88             System.err.println(errors + " errors");
       
    89 
       
    90         return (errors == 0);
       
    91     }
       
    92 
       
    93     /**
       
    94      * Print command line help.
       
    95      * @param out output stream
       
    96      */
       
    97     void usage(PrintStream out) {
       
    98         out.println("Usage:");
       
    99         out.println("  java " + getClass().getName() + " options... files...");
       
   100         out.println("");
       
   101         out.println("where options include:");
       
   102         out.println("-q        Quiet: don't report on inapplicable files");
       
   103         out.println("-v        Verbose: report on files as they are being read");
       
   104         out.println("");
       
   105         out.println("files may be directories or files");
       
   106         out.println("directories will be scanned recursively");
       
   107         out.println("non java files, or java files which cannot be parsed, will be ignored");
       
   108         out.println("");
       
   109     }
       
   110 
       
   111     /**
       
   112      * Test a file. If the file is a directory, it will be recursively scanned
       
   113      * for java files.
       
   114      * @param file the file or directory to test
       
   115      */
       
   116     void test(File file) {
       
   117         if (file.isDirectory()) {
       
   118             for (File f: file.listFiles()) {
       
   119                 test(f);
       
   120             }
       
   121             return;
       
   122         }
       
   123 
       
   124         if (file.isFile() && file.getName().endsWith(".java")) {
       
   125             try {
       
   126                 if (verbose)
       
   127                     System.err.println(file);
       
   128                 fileCount++;
       
   129                 treeCount += test(read(file));
       
   130             } catch (ParseException e) {
       
   131                 if (!quiet) {
       
   132                     error("Error parsing " + file + "\n" + e.getMessage());
       
   133                 }
       
   134             } catch (IOException e) {
       
   135                 error("Error reading " + file + ": " + e);
       
   136             }
       
   137             return;
       
   138         }
       
   139 
       
   140         if (!quiet)
       
   141             error("File " + file + " ignored");
       
   142     }
       
   143 
       
   144     abstract int test(JCCompilationUnit t);
       
   145 
       
   146     /**
       
   147      * Read a file.
       
   148      * @param file the file to be read
       
   149      * @return the tree for the content of the file
       
   150      * @throws IOException if any IO errors occur
       
   151      * @throws TreePosTest.ParseException if any errors occur while parsing the file
       
   152      */
       
   153     JCCompilationUnit read(File file) throws IOException, ParseException {
       
   154         StringWriter sw = new StringWriter();
       
   155         PrintWriter pw = new PrintWriter(sw);
       
   156         Reporter r = new Reporter(pw);
       
   157         JavacTool tool = JavacTool.create();
       
   158         StandardJavaFileManager fm = tool.getStandardFileManager(r, null, null);
       
   159         Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(file);
       
   160         JavacTask task = tool.getTask(pw, fm, r, Collections.<String>emptyList(), null, files);
       
   161         Iterable<? extends CompilationUnitTree> trees = task.parse();
       
   162         pw.flush();
       
   163         if (r.errors > 0)
       
   164             throw new ParseException(sw.toString());
       
   165         Iterator<? extends CompilationUnitTree> iter = trees.iterator();
       
   166         if (!iter.hasNext())
       
   167             throw new Error("no trees found");
       
   168         JCCompilationUnit t = (JCCompilationUnit) iter.next();
       
   169         if (iter.hasNext())
       
   170             throw new Error("too many trees found");
       
   171         return t;
       
   172     }
       
   173 
       
   174     /**
       
   175      * Report an error. When the program is complete, the program will either
       
   176      * exit or throw an Error if any errors have been reported.
       
   177      * @param msg the error message
       
   178      */
       
   179     void error(String msg) {
       
   180         System.err.println(msg);
       
   181         errors++;
       
   182     }
       
   183 
       
   184     /**
       
   185      *  Report an error for a specific tree node.
       
   186      *  @param file the source file for the tree
       
   187      *  @param t    the tree node
       
   188      *  @param label an indication of the error
       
   189      */
       
   190     void error(JavaFileObject file, Tree tree, String msg) {
       
   191         JCTree t = (JCTree) tree;
       
   192         error(file.getName() + ":" + getLine(file, t) + ": " + msg + " " + trim(t, 64));
       
   193     }
       
   194 
       
   195     /**
       
   196      * Get a trimmed string for a tree node, with normalized white space and limited length.
       
   197      */
       
   198     String trim(Tree tree, int len) {
       
   199         JCTree t = (JCTree) tree;
       
   200         String s = t.toString().replaceAll("[\r\n]+", " ").replaceAll(" +", " ");
       
   201         return (s.length() < len) ? s : s.substring(0, len);
       
   202     }
       
   203 
       
   204     /** Number of files that have been analyzed. */
       
   205     int fileCount;
       
   206     /** Number of trees that have been successfully compared. */
       
   207     int treeCount;
       
   208     /** Number of errors reported. */
       
   209     int errors;
       
   210     /** Flag: don't report irrelevant files. */
       
   211     boolean quiet;
       
   212     /** Flag: report files as they are processed. */
       
   213     boolean verbose;
       
   214 
       
   215 
       
   216     /**
       
   217      * Thrown when errors are found parsing a java file.
       
   218      */
       
   219     private static class ParseException extends Exception {
       
   220         ParseException(String msg) {
       
   221             super(msg);
       
   222         }
       
   223     }
       
   224 
       
   225     /**
       
   226      * DiagnosticListener to report diagnostics and count any errors that occur.
       
   227      */
       
   228     private static class Reporter implements DiagnosticListener<JavaFileObject> {
       
   229         Reporter(PrintWriter out) {
       
   230             this.out = out;
       
   231         }
       
   232 
       
   233         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
       
   234             out.println(diagnostic);
       
   235             switch (diagnostic.getKind()) {
       
   236                 case ERROR:
       
   237                     errors++;
       
   238             }
       
   239         }
       
   240         int errors;
       
   241         PrintWriter out;
       
   242     }
       
   243 
       
   244     /**
       
   245      * Get the set of fields for a tree node that may contain child tree nodes.
       
   246      * These are the fields that are subtypes of JCTree or List.
       
   247      * The results are cached, based on the tree's tag.
       
   248      */
       
   249     Set<Field> getFields(JCTree tree) {
       
   250         Set<Field> fields = map.get(tree.getTag());
       
   251         if (fields == null) {
       
   252             fields = new HashSet<Field>();
       
   253             for (Field f: tree.getClass().getFields()) {
       
   254                 Class<?> fc = f.getType();
       
   255                 if (JCTree.class.isAssignableFrom(fc) || List.class.isAssignableFrom(fc))
       
   256                     fields.add(f);
       
   257             }
       
   258             map.put(tree.getTag(), fields);
       
   259         }
       
   260         return fields;
       
   261     }
       
   262     // where
       
   263     Map<Integer, Set<Field>> map = new HashMap<Integer,Set<Field>>();
       
   264 
       
   265     /** Get the line number for the primary position for a tree.
       
   266      * The code is intended to be simple, although not necessarily efficient.
       
   267      * However, note that a file manager such as JavacFileManager is likely
       
   268      * to cache the results of file.getCharContent, avoiding the need to read
       
   269      * the bits from disk each time this method is called.
       
   270      */
       
   271     int getLine(JavaFileObject file, JCTree tree) {
       
   272         try {
       
   273             CharSequence cs = file.getCharContent(true);
       
   274             int line = 1;
       
   275             for (int i = 0; i < tree.pos; i++) {
       
   276                 if (cs.charAt(i) == '\n') // jtreg tests always use Unix line endings
       
   277                     line++;
       
   278             }
       
   279             return line;
       
   280         } catch (IOException e) {
       
   281             return -1;
       
   282         }
       
   283     }
       
   284 }