test/tools/javac/diags/Example.java

Wed, 14 Nov 2012 17:23:10 -0800

author
jjg
date
Wed, 14 Nov 2012 17:23:10 -0800
changeset 1409
33abf479f202
parent 1097
497571d34112
child 1773
3d9750039fff
permissions
-rw-r--r--

7021614: extend com.sun.source API to support parsing javadoc comments
Reviewed-by: ksrini, strarup

     1 /*
     2  * Copyright (c) 2010, 2012, 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  */
    24 import java.io.*;
    25 import java.net.URL;
    26 import java.net.URLClassLoader;
    27 import java.util.*;
    28 import java.util.regex.*;
    29 import javax.annotation.processing.Processor;
    30 import javax.tools.Diagnostic;
    31 import javax.tools.DiagnosticCollector;
    32 import javax.tools.JavaCompiler;
    33 import javax.tools.JavaCompiler.CompilationTask;
    34 import javax.tools.JavaFileObject;
    35 import javax.tools.StandardJavaFileManager;
    36 import javax.tools.ToolProvider;
    38 // The following two classes are both used, but cannot be imported directly
    39 // import com.sun.tools.javac.Main
    40 // import com.sun.tools.javac.main.Main
    42 import com.sun.tools.javac.api.ClientCodeWrapper;
    43 import com.sun.tools.javac.file.JavacFileManager;
    44 import com.sun.tools.javac.main.Main;
    45 import com.sun.tools.javac.util.Context;
    46 import com.sun.tools.javac.util.JavacMessages;
    47 import com.sun.tools.javac.util.JCDiagnostic;
    49 /**
    50  * Class to handle example code designed to illustrate javac diagnostic messages.
    51  */
    52 class Example implements Comparable<Example> {
    53     /* Create an Example from the files found at path.
    54      * The head of the file, up to the first Java code, is scanned
    55      * for information about the test, such as what resource keys it
    56      * generates when run, what options are required to run it, and so on.
    57      */
    58     Example(File file) {
    59         this.file = file;
    60         declaredKeys = new TreeSet<String>();
    61         srcFiles = new ArrayList<File>();
    62         procFiles = new ArrayList<File>();
    63         supportFiles = new ArrayList<File>();
    64         srcPathFiles = new ArrayList<File>();
    66         findFiles(file, srcFiles);
    67         for (File f: srcFiles) {
    68             parse(f);
    69         }
    71         if (infoFile == null)
    72             throw new Error("Example " + file + " has no info file");
    73     }
    75     private void findFiles(File f, List<File> files) {
    76         if (f.isDirectory()) {
    77             for (File c: f.listFiles()) {
    78                 if (files == srcFiles && c.getName().equals("processors"))
    79                     findFiles(c, procFiles);
    80                 else if (files == srcFiles && c.getName().equals("sourcepath")) {
    81                     srcPathDir = c;
    82                     findFiles(c, srcPathFiles);
    83                 } else if (files == srcFiles && c.getName().equals("support"))
    84                     findFiles(c, supportFiles);
    85                 else
    86                     findFiles(c, files);
    87             }
    88         } else if (f.isFile() && f.getName().endsWith(".java")) {
    89             files.add(f);
    90         }
    91     }
    93     private void parse(File f) {
    94         Pattern keyPat = Pattern.compile(" *// *key: *([^ ]+) *");
    95         Pattern optPat = Pattern.compile(" *// *options: *(.*)");
    96         Pattern runPat = Pattern.compile(" *// *run: *(.*)");
    97         Pattern javaPat = Pattern.compile(" *@?[A-Za-z].*");
    98         try {
    99             String[] lines = read(f).split("[\r\n]+");
   100             for (String line: lines) {
   101                 Matcher keyMatch = keyPat.matcher(line);
   102                 if (keyMatch.matches()) {
   103                     foundInfo(f);
   104                     declaredKeys.add(keyMatch.group(1));
   105                     continue;
   106                 }
   107                 Matcher optMatch = optPat.matcher(line);
   108                 if (optMatch.matches()) {
   109                     foundInfo(f);
   110                     options = Arrays.asList(optMatch.group(1).trim().split(" +"));
   111                     continue;
   112                 }
   113                 Matcher runMatch = runPat.matcher(line);
   114                 if (runMatch.matches()) {
   115                     foundInfo(f);
   116                     runOpts = Arrays.asList(runMatch.group(1).trim().split(" +"));
   117                 }
   118                 if (javaPat.matcher(line).matches())
   119                     break;
   120             }
   121         } catch (IOException e) {
   122             throw new Error(e);
   123         }
   124     }
   126     private void foundInfo(File file) {
   127         if (infoFile != null && !infoFile.equals(file))
   128             throw new Error("multiple info files found: " + infoFile + ", " + file);
   129         infoFile = file;
   130     }
   132     String getName() {
   133         return file.getName();
   134     }
   136     /**
   137      * Get the set of resource keys that this test declares it will generate
   138      * when it is run.
   139      */
   140     Set<String> getDeclaredKeys() {
   141         return declaredKeys;
   142     }
   144     /**
   145      * Get the set of resource keys that this test generates when it is run.
   146      * The test will be run if it has not already been run.
   147      */
   148     Set<String> getActualKeys() {
   149         if (actualKeys == null)
   150             actualKeys = run(false);
   151         return actualKeys;
   152     }
   154     /**
   155      * Run the test.  Information in the test header is used to determine
   156      * how to run the test.
   157      */
   158     void run(PrintWriter out, boolean raw, boolean verbose) {
   159         if (out == null)
   160             throw new NullPointerException();
   161         try {
   162             run(out, null, raw, verbose);
   163         } catch (IOException e) {
   164             e.printStackTrace(out);
   165         }
   166     }
   168     Set<String> run(boolean verbose) {
   169         Set<String> keys = new TreeSet<String>();
   170         try {
   171             run(null, keys, true, verbose);
   172         } catch (IOException e) {
   173             e.printStackTrace(System.err);
   174         }
   175         return keys;
   176     }
   178     /**
   179      * Run the test.  Information in the test header is used to determine
   180      * how to run the test.
   181      */
   182     private void run(PrintWriter out, Set<String> keys, boolean raw, boolean verbose)
   183             throws IOException {
   184         ClassLoader loader = getClass().getClassLoader();
   185         if (supportFiles.size() > 0) {
   186             File supportDir = new File(tempDir, "support");
   187             supportDir.mkdirs();
   188             clean(supportDir);
   189             List<String> sOpts = Arrays.asList("-d", supportDir.getPath());
   190             new Jsr199Compiler(verbose).run(null, null, false, sOpts, procFiles);
   191             URLClassLoader ucl =
   192                     new URLClassLoader(new URL[] { supportDir.toURI().toURL() }, loader);
   193             loader = ucl;
   194         }
   196         File classesDir = new File(tempDir, "classes");
   197         classesDir.mkdirs();
   198         clean(classesDir);
   200         List<String> opts = new ArrayList<String>();
   201         opts.add("-d");
   202         opts.add(classesDir.getPath());
   203         if (options != null)
   204             opts.addAll(options);
   206         if (procFiles.size() > 0) {
   207             List<String> pOpts = Arrays.asList("-d", classesDir.getPath());
   208             new Jsr199Compiler(verbose).run(null, null, false, pOpts, procFiles);
   209             opts.add("-classpath"); // avoid using -processorpath for now
   210             opts.add(classesDir.getPath());
   211             createAnnotationServicesFile(classesDir, procFiles);
   212         } else if (options != null) {
   213             int i = options.indexOf("-processor");
   214             // check for built-in anno-processor(s)
   215             if (i != -1 && options.get(i + 1).equals("DocCommentProcessor")) {
   216                 opts.add("-classpath");
   217                 opts.add(System.getProperty("test.classes"));
   218             }
   219         }
   221         if (srcPathDir != null) {
   222             opts.add("-sourcepath");
   223             opts.add(srcPathDir.getPath());
   224         }
   226         try {
   227             Compiler c = Compiler.getCompiler(runOpts, verbose);
   228             c.run(out, keys, raw, opts, srcFiles);
   229         } catch (IllegalArgumentException e) {
   230             if (out != null) {
   231                 out.println("Invalid value for run tag: " + runOpts);
   232             }
   233         }
   234     }
   236     void createAnnotationServicesFile(File dir, List<File> procFiles) throws IOException {
   237         File servicesDir = new File(new File(dir, "META-INF"), "services");
   238         servicesDir.mkdirs();
   239         File annoServices = new File(servicesDir, Processor.class.getName());
   240         Writer out = new FileWriter(annoServices);
   241         try {
   242             for (File f: procFiles) {
   243                 out.write(f.getName().toString().replace(".java", ""));
   244             }
   245         } finally {
   246             out.close();
   247         }
   248     }
   250     @Override
   251     public int compareTo(Example e) {
   252         return file.compareTo(e.file);
   253     }
   255     @Override
   256     public String toString() {
   257         return file.getPath();
   258     }
   260     /**
   261      * Read the contents of a file.
   262      */
   263     private String read(File f) throws IOException {
   264         byte[] bytes = new byte[(int) f.length()];
   265         DataInputStream in = new DataInputStream(new FileInputStream(f));
   266         try {
   267             in.readFully(bytes);
   268         } finally {
   269             in.close();
   270         }
   271         return new String(bytes);
   272     }
   274     /**
   275      * Clean the contents of a directory.
   276      */
   277     boolean clean(File dir) {
   278         boolean ok = true;
   279         for (File f: dir.listFiles()) {
   280             if (f.isDirectory())
   281                 ok &= clean(f);
   282             ok &= f.delete();
   283         }
   284         return ok;
   285     }
   287     File file;
   288     List<File> srcFiles;
   289     List<File> procFiles;
   290     File srcPathDir;
   291     List<File> srcPathFiles;
   292     List<File> supportFiles;
   293     File infoFile;
   294     private List<String> runOpts;
   295     private List<String> options;
   296     private Set<String> actualKeys;
   297     private Set<String> declaredKeys;
   299     static File tempDir = new File(System.getProperty("java.io.tmpdir"));
   300     static void setTempDir(File tempDir) {
   301         Example.tempDir = tempDir;
   302     }
   304     abstract static class Compiler {
   305         interface Factory {
   306             Compiler getCompiler(List<String> opts, boolean verbose);
   307         }
   309         static class DefaultFactory implements Factory {
   310             public Compiler getCompiler(List<String> opts, boolean verbose) {
   311             String first;
   312             String[] rest;
   313                 if (opts == null || opts.isEmpty()) {
   314                 first = null;
   315                 rest = new String[0];
   316             } else {
   317                 first = opts.get(0);
   318                 rest = opts.subList(1, opts.size()).toArray(new String[opts.size() - 1]);
   319             }
   320             if (first == null || first.equals("jsr199"))
   321                 return new Jsr199Compiler(verbose, rest);
   322             else if (first.equals("simple"))
   323                 return new SimpleCompiler(verbose);
   324             else if (first.equals("backdoor"))
   325                 return new BackdoorCompiler(verbose);
   326             else
   327                 throw new IllegalArgumentException(first);
   328                 }
   329         }
   331         static Factory factory;
   333         static Compiler getCompiler(List<String> opts, boolean verbose) {
   334             if (factory == null)
   335                 factory = new DefaultFactory();
   337             return factory.getCompiler(opts, verbose);
   338         }
   340         protected Compiler(boolean verbose) {
   341             this.verbose = verbose;
   342         }
   344         abstract boolean run(PrintWriter out, Set<String> keys, boolean raw,
   345                 List<String> opts,  List<File> files);
   347         void setSupportClassLoader(ClassLoader cl) {
   348             loader = cl;
   349         }
   351         protected ClassLoader loader;
   352         protected boolean verbose;
   353     }
   355     /**
   356      * Compile using the JSR 199 API.  The diagnostics generated are
   357      * scanned for resource keys.   Not all diagnostic keys are generated
   358      * via the JSR 199 API -- for example, rich diagnostics are not directly
   359      * accessible, and some diagnostics generated by the file manager may
   360      * not be generated (for example, the JSR 199 file manager does not see
   361      * -Xlint:path).
   362      */
   363     static class Jsr199Compiler extends Compiler {
   364         List<String> fmOpts;
   366         Jsr199Compiler(boolean verbose, String... args) {
   367             super(verbose);
   368             for (int i = 0; i < args.length; i++) {
   369                 String arg = args[i];
   370                 if (arg.equals("-filemanager") && (i + 1 < args.length)) {
   371                     fmOpts = Arrays.asList(args[++i].split(","));
   372                 } else
   373                     throw new IllegalArgumentException(arg);
   374             }
   375         }
   377         @Override
   378         boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
   379             if (out != null && keys != null)
   380                 throw new IllegalArgumentException();
   382             if (verbose)
   383                 System.err.println("run_jsr199: " + opts + " " + files);
   385             DiagnosticCollector<JavaFileObject> dc = null;
   386             if (keys != null)
   387                 dc = new DiagnosticCollector<JavaFileObject>();
   389             if (raw) {
   390                 List<String> newOpts = new ArrayList<String>();
   391                 newOpts.add("-XDrawDiagnostics");
   392                 newOpts.addAll(opts);
   393                 opts = newOpts;
   394             }
   396             JavaCompiler c = ToolProvider.getSystemJavaCompiler();
   398             StandardJavaFileManager fm = c.getStandardFileManager(dc, null, null);
   399             if (fmOpts != null)
   400                 fm = new FileManager(fm, fmOpts);
   402             Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files);
   404             CompilationTask t = c.getTask(out, fm, dc, opts, null, fos);
   405             Boolean ok = t.call();
   407             if (keys != null) {
   408                 for (Diagnostic<? extends JavaFileObject> d: dc.getDiagnostics()) {
   409                     scanForKeys(unwrap(d), keys);
   410                 }
   411             }
   413             return ok;
   414         }
   416         /**
   417          * Scan a diagnostic for resource keys.  This will not detect additional
   418          * sub diagnostics that might be generated by a rich diagnostic formatter.
   419          */
   420         private static void scanForKeys(JCDiagnostic d, Set<String> keys) {
   421             keys.add(d.getCode());
   422             for (Object o: d.getArgs()) {
   423                 if (o instanceof JCDiagnostic) {
   424                     scanForKeys((JCDiagnostic) o, keys);
   425                 }
   426             }
   427             for (JCDiagnostic sd: d.getSubdiagnostics())
   428                 scanForKeys(sd, keys);
   429         }
   431         private JCDiagnostic unwrap(Diagnostic<? extends JavaFileObject> diagnostic) {
   432             if (diagnostic instanceof JCDiagnostic)
   433                 return (JCDiagnostic) diagnostic;
   434             if (diagnostic instanceof ClientCodeWrapper.DiagnosticSourceUnwrapper)
   435                 return ((ClientCodeWrapper.DiagnosticSourceUnwrapper)diagnostic).d;
   436             throw new IllegalArgumentException();
   437         }
   438     }
   440     /**
   441      * Run the test using the standard simple entry point.
   442      */
   443     static class SimpleCompiler extends Compiler {
   444         SimpleCompiler(boolean verbose) {
   445             super(verbose);
   446         }
   448         @Override
   449         boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
   450             if (out != null && keys != null)
   451                 throw new IllegalArgumentException();
   453             if (verbose)
   454                 System.err.println("run_simple: " + opts + " " + files);
   456             List<String> args = new ArrayList<String>();
   458             if (keys != null || raw)
   459                 args.add("-XDrawDiagnostics");
   461             args.addAll(opts);
   462             for (File f: files)
   463                 args.add(f.getPath());
   465             StringWriter sw = null;
   466             PrintWriter pw;
   467             if (keys != null) {
   468                 sw = new StringWriter();
   469                 pw = new PrintWriter(sw);
   470             } else
   471                 pw = out;
   473             int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw);
   475             if (keys != null) {
   476                 pw.close();
   477                 scanForKeys(sw.toString(), keys);
   478             }
   480             return (rc == 0);
   481         }
   483         private static void scanForKeys(String text, Set<String> keys) {
   484             StringTokenizer st = new StringTokenizer(text, " ,\r\n():");
   485             while (st.hasMoreElements()) {
   486                 String t = st.nextToken();
   487                 if (t.startsWith("compiler."))
   488                     keys.add(t);
   489             }
   490         }
   491     }
   493     static class BackdoorCompiler extends Compiler {
   494         BackdoorCompiler(boolean verbose) {
   495             super(verbose);
   496         }
   498         @Override
   499         boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
   500             if (out != null && keys != null)
   501                 throw new IllegalArgumentException();
   503             if (verbose)
   504                 System.err.println("run_simple: " + opts + " " + files);
   506             List<String> args = new ArrayList<String>();
   508             if (out != null && raw)
   509                 args.add("-XDrawDiagnostics");
   511             args.addAll(opts);
   512             for (File f: files)
   513                 args.add(f.getPath());
   515             StringWriter sw = null;
   516             PrintWriter pw;
   517             if (keys != null) {
   518                 sw = new StringWriter();
   519                 pw = new PrintWriter(sw);
   520             } else
   521                 pw = out;
   523             Context c = new Context();
   524             JavacFileManager.preRegister(c); // can't create it until Log has been set up
   525             MessageTracker.preRegister(c, keys);
   526             Main m = new Main("javac", pw);
   527             Main.Result rc = m.compile(args.toArray(new String[args.size()]), c);
   529             if (keys != null) {
   530                 pw.close();
   531             }
   533             return rc.isOK();
   534         }
   536         static class MessageTracker extends JavacMessages {
   538             MessageTracker(Context context) {
   539                 super(context);
   540             }
   542             static void preRegister(Context c, final Set<String> keys) {
   543                 if (keys != null) {
   544                     c.put(JavacMessages.messagesKey, new Context.Factory<JavacMessages>() {
   545                         public JavacMessages make(Context c) {
   546                             return new MessageTracker(c) {
   547                                 @Override
   548                                 public String getLocalizedString(Locale l, String key, Object... args) {
   549                                     keys.add(key);
   550                                     return super.getLocalizedString(l, key, args);
   551                                 }
   552                             };
   553                         }
   554                     });
   555                 }
   556             }
   557         }
   559     }
   560 }

mercurial