jjg@842: /* jjg@842: * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. jjg@842: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@842: * jjg@842: * This code is free software; you can redistribute it and/or modify it jjg@842: * under the terms of the GNU General Public License version 2 only, as jjg@842: * published by the Free Software Foundation. jjg@842: * jjg@842: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@842: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@842: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@842: * version 2 for more details (a copy is included in the LICENSE file that jjg@842: * accompanied this code). jjg@842: * jjg@842: * You should have received a copy of the GNU General Public License version jjg@842: * 2 along with this work; if not, write to the Free Software Foundation, jjg@842: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@842: * jjg@842: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jjg@842: * or visit www.oracle.com if you need additional information or have any jjg@842: * questions. jjg@842: */ jjg@842: jjg@842: import java.io.*; jjg@842: import java.util.*; jjg@842: import java.util.List; jjg@842: import javax.tools.*; jjg@842: jjg@842: import com.sun.tools.javac.api.*; jjg@842: import com.sun.tools.javac.api.DiagnosticFormatter.Configuration.DiagnosticPart; jjg@842: import com.sun.tools.javac.api.Formattable.LocalizedString; jjg@842: import com.sun.tools.javac.code.Flags.Flag; jjg@842: import com.sun.tools.javac.code.Kinds.KindName; jjg@842: import com.sun.tools.javac.code.*; jjg@842: import com.sun.tools.javac.file.*; jjg@842: import com.sun.tools.javac.main.Main; jjg@893: import com.sun.tools.javac.main.JavaCompiler; jjg@842: import com.sun.tools.javac.parser.Token; jjg@842: import com.sun.tools.javac.util.*; jjg@842: import com.sun.tools.javac.util.AbstractDiagnosticFormatter.SimpleConfiguration; jjg@842: import javax.lang.model.SourceVersion; jjg@842: jjg@842: /** jjg@842: * Compiler factory for instances of Example.Compiler that use custom jjg@842: * DiagnosticFormatter and Messages objects to track the types of args jjg@842: * when when localizing diagnostics. jjg@842: * The compiler objects only support "output" mode, not "check" mode. jjg@842: */ jjg@842: class ArgTypeCompilerFactory implements Example.Compiler.Factory { jjg@842: // Same code as Example.Compiler.DefaultFactory, but the names resolve differently jjg@842: public Example.Compiler getCompiler(List opts, boolean verbose) { jjg@842: String first; jjg@842: String[] rest; jjg@842: if (opts == null || opts.isEmpty()) { jjg@842: first = null; jjg@842: rest = new String[0]; jjg@842: } else { jjg@842: first = opts.get(0); jjg@842: rest = opts.subList(1, opts.size()).toArray(new String[opts.size() - 1]); jjg@842: } jjg@842: if (first == null || first.equals("jsr199")) jjg@842: return new Jsr199Compiler(verbose, rest); jjg@842: else if (first.equals("simple")) jjg@842: return new SimpleCompiler(verbose); jjg@842: else if (first.equals("backdoor")) jjg@842: return new BackdoorCompiler(verbose); jjg@842: else jjg@842: throw new IllegalArgumentException(first); jjg@842: } jjg@842: jjg@842: /** jjg@842: * Compile using the JSR 199 API. The diagnostics generated are jjg@842: * scanned for resource keys. Not all diagnostic keys are generated jjg@842: * via the JSR 199 API -- for example, rich diagnostics are not directly jjg@842: * accessible, and some diagnostics generated by the file manager may jjg@842: * not be generated (for example, the JSR 199 file manager does not see jjg@842: * -Xlint:path). jjg@842: */ jjg@842: static class Jsr199Compiler extends Example.Compiler { jjg@842: List fmOpts; jjg@842: jjg@842: Jsr199Compiler(boolean verbose, String... args) { jjg@842: super(verbose); jjg@842: for (int i = 0; i < args.length; i++) { jjg@842: String arg = args[i]; jjg@842: if (arg.equals("-filemanager") && (i + 1 < args.length)) { jjg@842: fmOpts = Arrays.asList(args[++i].split(",")); jjg@842: } else jjg@842: throw new IllegalArgumentException(arg); jjg@842: } jjg@842: } jjg@842: jjg@842: @Override jjg@842: boolean run(PrintWriter out, Set keys, boolean raw, List opts, List files) { jjg@842: assert out != null && keys == null; jjg@842: jjg@842: if (verbose) jjg@842: System.err.println("run_jsr199: " + opts + " " + files); jjg@842: jjg@842: JavacTool tool = JavacTool.create(); jjg@842: jjg@842: StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null); jjg@842: if (fmOpts != null) jjg@842: fm = new FileManager(fm, fmOpts); jjg@842: jjg@842: Iterable fos = fm.getJavaFileObjectsFromFiles(files); jjg@842: jjg@842: JavacTaskImpl t = (JavacTaskImpl) tool.getTask(out, fm, null, opts, null, fos); jjg@842: Context c = t.getContext(); jjg@842: ArgTypeMessages.preRegister(c); jjg@893: ArgTypeJavaCompiler.preRegister(c); jjg@842: Boolean ok = t.call(); jjg@842: jjg@842: return ok; jjg@842: } jjg@842: } jjg@842: jjg@842: /** jjg@842: * Run the test using the standard simple entry point. jjg@842: */ jjg@842: static class SimpleCompiler extends Example.Compiler { jjg@842: SimpleCompiler(boolean verbose) { jjg@842: super(verbose); jjg@842: } jjg@842: jjg@842: @Override jjg@842: boolean run(PrintWriter out, Set keys, boolean raw, List opts, List files) { jjg@842: assert out != null && keys == null; jjg@842: jjg@842: if (verbose) jjg@842: System.err.println("run_simple: " + opts + " " + files); jjg@842: jjg@842: List args = new ArrayList(); jjg@842: jjg@842: args.addAll(opts); jjg@842: for (File f: files) jjg@842: args.add(f.getPath()); jjg@842: jjg@842: Main main = new Main("javac", out); jjg@842: Context c = new Context() { jjg@842: @Override public void clear() { jjg@842: ((JavacFileManager) get(JavaFileManager.class)).close(); jjg@842: super.clear(); jjg@842: } jjg@842: }; jjg@842: JavacFileManager.preRegister(c); // can't create it until Log has been set up jjg@893: ArgTypeJavaCompiler.preRegister(c); jjg@842: ArgTypeMessages.preRegister(c); jjg@842: int result = main.compile(args.toArray(new String[args.size()]), c); jjg@842: jjg@842: return (result == 0); jjg@842: } jjg@842: } jjg@842: jjg@842: static class BackdoorCompiler extends Example.Compiler { jjg@842: BackdoorCompiler(boolean verbose) { jjg@842: super(verbose); jjg@842: } jjg@842: jjg@842: @Override jjg@842: boolean run(PrintWriter out, Set keys, boolean raw, List opts, List files) { jjg@842: assert out != null && keys == null; jjg@842: jjg@842: if (verbose) jjg@842: System.err.println("run_simple: " + opts + " " + files); jjg@842: jjg@842: List args = new ArrayList(opts); jjg@842: for (File f: files) jjg@842: args.add(f.getPath()); jjg@842: jjg@842: Context c = new Context(); jjg@842: JavacFileManager.preRegister(c); // can't create it until Log has been set up jjg@893: ArgTypeJavaCompiler.preRegister(c); jjg@842: ArgTypeMessages.preRegister(c); jjg@842: com.sun.tools.javac.main.Main m = new com.sun.tools.javac.main.Main("javac", out); jjg@842: int rc = m.compile(args.toArray(new String[args.size()]), c); jjg@842: jjg@842: return (rc == 0); jjg@842: } jjg@842: jjg@842: } jjg@842: jjg@842: jjg@842: // jjg@842: jjg@842: /** jjg@842: * Diagnostic formatter which reports formats a diag as a series of lines jjg@842: * containing a key, and a possibly empty set of descriptive strings for the jjg@842: * arg types. jjg@842: */ jjg@842: static class ArgTypeDiagnosticFormatter extends AbstractDiagnosticFormatter { jjg@842: jjg@842: ArgTypeDiagnosticFormatter(Options options) { jjg@842: super(null, new SimpleConfiguration(options, jjg@842: EnumSet.of(DiagnosticPart.SUMMARY, jjg@842: DiagnosticPart.DETAILS, jjg@842: DiagnosticPart.SUBDIAGNOSTICS))); jjg@842: } jjg@842: jjg@842: @Override jjg@842: protected String formatDiagnostic(JCDiagnostic d, Locale locale) { jjg@842: return formatMessage(d, locale); jjg@842: } jjg@842: jjg@842: @Override jjg@842: public String formatMessage(JCDiagnostic d, Locale l) { jjg@842: StringBuilder buf = new StringBuilder(); jjg@842: formatMessage(d, buf); jjg@842: return buf.toString(); jjg@842: } jjg@842: jjg@842: private void formatMessage(JCDiagnostic d, StringBuilder buf) { jjg@842: String key = d.getCode(); jjg@842: Object[] args = d.getArgs(); jjg@842: // report the primary arg types, without recursing into diag fragments jjg@842: buf.append(getKeyArgsString(key, args)); jjg@842: // report details for any diagnostic fragments jjg@842: for (Object arg: args) { jjg@842: if (arg instanceof JCDiagnostic) { jjg@842: buf.append("\n"); jjg@842: formatMessage((JCDiagnostic) arg, buf); jjg@842: } jjg@842: } jjg@842: // report details for any subdiagnostics jjg@842: for (String s: formatSubdiagnostics(d, null)) { jjg@842: buf.append("\n"); jjg@842: buf.append(s); jjg@842: } jjg@842: } jjg@842: jjg@842: @Override jjg@842: public boolean isRaw() { jjg@842: return true; jjg@842: } jjg@842: } jjg@842: jjg@842: /** jjg@893: * Trivial subtype of JavaCompiler to get access to the protected compilerKey field. jjg@893: * The factory is used to ensure that the log is initialized with an instance of jjg@893: * ArgTypeDiagnosticFormatter before we create the required JavaCompiler. jjg@893: */ jjg@893: static class ArgTypeJavaCompiler extends JavaCompiler { jjg@893: static void preRegister(Context context) { jjg@893: context.put(compilerKey, new Context.Factory() { jjg@893: public JavaCompiler make(Context c) { jjg@893: Log log = Log.instance(c); jjg@893: Options options = Options.instance(c); jjg@893: log.setDiagnosticFormatter(new ArgTypeDiagnosticFormatter(options)); jjg@893: return new JavaCompiler(c); jjg@893: } jjg@893: }); jjg@893: } jjg@893: jjg@893: // not used jjg@893: private ArgTypeJavaCompiler() { jjg@893: super(null); jjg@893: } jjg@893: } jjg@893: jjg@893: /** jjg@842: * Diagnostic formatter which "localizes" a message as a line jjg@842: * containing a key, and a possibly empty set of descriptive strings for the jjg@842: * arg types. jjg@842: */ jjg@842: static class ArgTypeMessages extends JavacMessages { jjg@893: static void preRegister(Context context) { jjg@893: context.put(JavacMessages.messagesKey, new Context.Factory() { jjg@893: public JavacMessages make(Context c) { jjg@842: return new ArgTypeMessages(c) { jjg@842: @Override jjg@842: public String getLocalizedString(Locale l, String key, Object... args) { jjg@842: return getKeyArgsString(key, args); jjg@842: } jjg@842: }; jjg@842: } jjg@842: }); jjg@842: } jjg@842: jjg@842: ArgTypeMessages(Context context) { jjg@842: super(context); jjg@842: } jjg@842: } jjg@842: jjg@842: /** jjg@842: * Utility method to generate a string for key and args jjg@842: */ jjg@842: static String getKeyArgsString(String key, Object... args) { jjg@842: StringBuilder buf = new StringBuilder(); jjg@842: buf.append(key); jjg@842: String sep = ": "; jjg@842: for (Object o : args) { jjg@842: buf.append(sep); jjg@842: buf.append(getArgTypeOrStringValue(o)); jjg@842: sep = ", "; jjg@842: } jjg@842: return buf.toString(); jjg@842: } jjg@842: jjg@842: static boolean showStringValues = false; jjg@842: jjg@842: static String getArgTypeOrStringValue(Object o) { jjg@842: if (showStringValues && o instanceof String) jjg@842: return "\"" + o + "\""; jjg@842: return getArgType(o); jjg@842: } jjg@842: jjg@842: static String getArgType(Object o) { jjg@842: if (o == null) jjg@842: return "null"; jjg@842: if (o instanceof Name) jjg@842: return "name"; jjg@842: if (o instanceof Boolean) jjg@842: return "boolean"; jjg@842: if (o instanceof Integer) jjg@842: return "number"; jjg@842: if (o instanceof String) jjg@842: return "string"; jjg@842: if (o instanceof Flag) jjg@842: return "modifier"; jjg@842: if (o instanceof KindName) jjg@842: return "symbol kind"; jjg@842: if (o instanceof Token) jjg@842: return "token"; jjg@842: if (o instanceof Symbol) jjg@842: return "symbol"; jjg@842: if (o instanceof Type) jjg@842: return "type"; jjg@842: if (o instanceof List) { jjg@842: List l = (List) o; jjg@842: if (l.isEmpty()) jjg@842: return "list"; jjg@842: else jjg@842: return "list of " + getArgType(l.get(0)); jjg@842: } jjg@842: if (o instanceof ListBuffer) jjg@842: return getArgType(((ListBuffer) o).toList()); jjg@842: if (o instanceof Set) { jjg@842: Set s = (Set) o; jjg@842: if (s.isEmpty()) jjg@842: return "set"; jjg@842: else jjg@842: return "set of " + getArgType(s.iterator().next()); jjg@842: } jjg@842: if (o instanceof SourceVersion) jjg@842: return "source version"; jjg@842: if (o instanceof FileObject || o instanceof File) jjg@842: return "file name"; jjg@842: if (o instanceof JCDiagnostic) jjg@842: return "message segment"; jjg@842: if (o instanceof LocalizedString) jjg@842: return "message segment"; // only instance is "no arguments" jjg@842: String s = o.getClass().getSimpleName(); jjg@842: return (s.isEmpty() ? o.getClass().getName() : s); jjg@842: } jjg@842: jjg@842: // jjg@842: jjg@842: }