1 /* 2 * Copyright (c) 2010, 2014, 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.util.*; 26 import java.util.List; 27 import javax.tools.*; 28 29 import com.sun.tools.javac.api.*; 30 import com.sun.tools.javac.api.DiagnosticFormatter.Configuration.DiagnosticPart; 31 import com.sun.tools.javac.api.Formattable.LocalizedString; 32 import com.sun.tools.javac.code.Flags.Flag; 33 import com.sun.tools.javac.code.Kinds.KindName; 34 import com.sun.tools.javac.code.*; 35 import com.sun.tools.javac.file.*; 36 import com.sun.tools.javac.main.Main; 37 import com.sun.tools.javac.main.JavaCompiler; 38 import com.sun.tools.javac.parser.Tokens.TokenKind; 39 import com.sun.tools.javac.util.*; 40 import com.sun.tools.javac.util.AbstractDiagnosticFormatter.SimpleConfiguration; 41 import javax.lang.model.SourceVersion; 42 43 /** 44 * Compiler factory for instances of Example.Compiler that use custom 45 * DiagnosticFormatter and Messages objects to track the types of args 46 * when when localizing diagnostics. 47 * The compiler objects only support "output" mode, not "check" mode. 48 */ 49 class ArgTypeCompilerFactory implements Example.Compiler.Factory { 50 // Same code as Example.Compiler.DefaultFactory, but the names resolve differently getCompiler(List<String> opts, boolean verbose)51 public Example.Compiler getCompiler(List<String> opts, boolean verbose) { 52 String first; 53 String[] rest; 54 if (opts == null || opts.isEmpty()) { 55 first = null; 56 rest = new String[0]; 57 } else { 58 first = opts.get(0); 59 rest = opts.subList(1, opts.size()).toArray(new String[opts.size() - 1]); 60 } 61 if (first == null || first.equals("jsr199")) 62 return new Jsr199Compiler(verbose, rest); 63 else if (first.equals("simple")) 64 return new SimpleCompiler(verbose); 65 else if (first.equals("backdoor")) 66 return new BackdoorCompiler(verbose); 67 else 68 throw new IllegalArgumentException(first); 69 } 70 71 /** 72 * Compile using the JSR 199 API. The diagnostics generated are 73 * scanned for resource keys. Not all diagnostic keys are generated 74 * via the JSR 199 API -- for example, rich diagnostics are not directly 75 * accessible, and some diagnostics generated by the file manager may 76 * not be generated (for example, the JSR 199 file manager does not see 77 * -Xlint:path). 78 */ 79 static class Jsr199Compiler extends Example.Compiler { 80 List<String> fmOpts; 81 Jsr199Compiler(boolean verbose, String... args)82 Jsr199Compiler(boolean verbose, String... args) { 83 super(verbose); 84 for (int i = 0; i < args.length; i++) { 85 String arg = args[i]; 86 if (arg.equals("-filemanager") && (i + 1 < args.length)) { 87 fmOpts = Arrays.asList(args[++i].split(",")); 88 } else 89 throw new IllegalArgumentException(arg); 90 } 91 } 92 93 @Override run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files)94 boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) { 95 assert out != null && keys == null; 96 97 if (verbose) 98 System.err.println("run_jsr199: " + opts + " " + files); 99 100 JavacTool tool = JavacTool.create(); 101 102 StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null); 103 try { 104 if (fmOpts != null) 105 fm = new FileManager(fm, fmOpts); 106 107 Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files); 108 109 Context c = initContext(); 110 JavacTaskImpl t = (JavacTaskImpl) tool.getTask(out, fm, null, opts, null, fos, c); 111 return t.call(); 112 } finally { 113 close(fm); 114 } 115 } 116 } 117 118 119 /** 120 * Run the test using the standard simple entry point. 121 */ 122 static class SimpleCompiler extends Example.Compiler { SimpleCompiler(boolean verbose)123 SimpleCompiler(boolean verbose) { 124 super(verbose); 125 } 126 127 @Override run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files)128 boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) { 129 assert out != null && keys == null; 130 131 if (verbose) 132 System.err.println("run_simple: " + opts + " " + files); 133 134 List<String> args = new ArrayList<String>(); 135 136 args.addAll(opts); 137 for (File f: files) 138 args.add(f.getPath()); 139 140 Main main = new Main("javac", out); 141 Context c = initContext(); 142 JavacFileManager.preRegister(c); // can't create it until Log has been set up 143 144 try { 145 Main.Result result = main.compile(args.toArray(new String[args.size()]), c); 146 147 return result.isOK(); 148 } finally { 149 close(c.get(JavaFileManager.class)); 150 } 151 } 152 } 153 154 static class BackdoorCompiler extends Example.Compiler { BackdoorCompiler(boolean verbose)155 BackdoorCompiler(boolean verbose) { 156 super(verbose); 157 } 158 159 @Override run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files)160 boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) { 161 assert out != null && keys == null; 162 163 if (verbose) 164 System.err.println("run_simple: " + opts + " " + files); 165 166 List<String> args = new ArrayList<String>(opts); 167 for (File f: files) 168 args.add(f.getPath()); 169 170 Context c = initContext(); 171 JavacFileManager.preRegister(c); // can't create it until Log has been set up 172 173 try { 174 Main m = new Main("javac", out); 175 Main.Result result = m.compile(args.toArray(new String[args.size()]), c); 176 177 return result.isOK(); 178 } finally { 179 close(c.get(JavaFileManager.class)); 180 } 181 } 182 } 183 initContext()184 private static Context initContext() { 185 Context context = new Context(); 186 ArgTypeMessages.preRegister(context); 187 Options options = Options.instance(context); 188 options.addListener(() -> { 189 Log log = Log.instance(context); 190 log.setDiagnosticFormatter(new ArgTypeDiagnosticFormatter(options)); 191 }); 192 return context; 193 } 194 195 // <editor-fold defaultstate="collapsed" desc="Custom Javac components"> 196 197 /** 198 * Diagnostic formatter which reports formats a diag as a series of lines 199 * containing a key, and a possibly empty set of descriptive strings for the 200 * arg types. 201 */ 202 static class ArgTypeDiagnosticFormatter extends AbstractDiagnosticFormatter { 203 ArgTypeDiagnosticFormatter(Options options)204 ArgTypeDiagnosticFormatter(Options options) { 205 super(null, new SimpleConfiguration(options, 206 EnumSet.of(DiagnosticPart.SUMMARY, 207 DiagnosticPart.DETAILS, 208 DiagnosticPart.SUBDIAGNOSTICS))); 209 } 210 211 @Override formatDiagnostic(JCDiagnostic d, Locale locale)212 protected String formatDiagnostic(JCDiagnostic d, Locale locale) { 213 return formatMessage(d, locale); 214 } 215 216 @Override formatMessage(JCDiagnostic d, Locale l)217 public String formatMessage(JCDiagnostic d, Locale l) { 218 StringBuilder buf = new StringBuilder(); 219 formatMessage(d, buf); 220 return buf.toString(); 221 } 222 formatMessage(JCDiagnostic d, StringBuilder buf)223 private void formatMessage(JCDiagnostic d, StringBuilder buf) { 224 String key = d.getCode(); 225 Object[] args = d.getArgs(); 226 // report the primary arg types, without recursing into diag fragments 227 buf.append(getKeyArgsString(key, args)); 228 // report details for any diagnostic fragments 229 for (Object arg: args) { 230 if (arg instanceof JCDiagnostic) { 231 buf.append("\n"); 232 formatMessage((JCDiagnostic) arg, buf); 233 } 234 } 235 // report details for any subdiagnostics 236 for (String s: formatSubdiagnostics(d, null)) { 237 buf.append("\n"); 238 buf.append(s); 239 } 240 } 241 242 @Override isRaw()243 public boolean isRaw() { 244 return true; 245 } 246 } 247 248 /** 249 * Diagnostic formatter which "localizes" a message as a line 250 * containing a key, and a possibly empty set of descriptive strings for the 251 * arg types. 252 */ 253 static class ArgTypeMessages extends JavacMessages { preRegister(Context context)254 static void preRegister(Context context) { 255 context.put(JavacMessages.messagesKey, new Context.Factory<JavacMessages>() { 256 public JavacMessages make(Context c) { 257 return new ArgTypeMessages(c) { 258 @Override 259 public String getLocalizedString(Locale l, String key, Object... args) { 260 return getKeyArgsString(key, args); 261 } 262 }; 263 } 264 }); 265 } 266 ArgTypeMessages(Context context)267 ArgTypeMessages(Context context) { 268 super(context); 269 } 270 } 271 272 /** 273 * Utility method to generate a string for key and args 274 */ getKeyArgsString(String key, Object... args)275 static String getKeyArgsString(String key, Object... args) { 276 StringBuilder buf = new StringBuilder(); 277 buf.append(key); 278 String sep = ": "; 279 for (Object o : args) { 280 buf.append(sep); 281 buf.append(getArgTypeOrStringValue(o)); 282 sep = ", "; 283 } 284 return buf.toString(); 285 } 286 287 static boolean showStringValues = false; 288 getArgTypeOrStringValue(Object o)289 static String getArgTypeOrStringValue(Object o) { 290 if (showStringValues && o instanceof String) 291 return "\"" + o + "\""; 292 return getArgType(o); 293 } 294 getArgType(Object o)295 static String getArgType(Object o) { 296 if (o == null) 297 return "null"; 298 if (o instanceof Name) 299 return "name"; 300 if (o instanceof Boolean) 301 return "boolean"; 302 if (o instanceof Integer) 303 return "number"; 304 if (o instanceof String) 305 return "string"; 306 if (o instanceof Flag) 307 return "modifier"; 308 if (o instanceof KindName) 309 return "symbol kind"; 310 if (o instanceof TokenKind) 311 return "token"; 312 if (o instanceof Symbol) 313 return "symbol"; 314 if (o instanceof Type) 315 return "type"; 316 if (o instanceof List) { 317 List<?> l = (List<?>) o; 318 if (l.isEmpty()) 319 return "list"; 320 else 321 return "list of " + getArgType(l.get(0)); 322 } 323 if (o instanceof ListBuffer) 324 return getArgType(((ListBuffer) o).toList()); 325 if (o instanceof Set) { 326 Set<?> s = (Set<?>) o; 327 if (s.isEmpty()) 328 return "set"; 329 else 330 return "set of " + getArgType(s.iterator().next()); 331 } 332 if (o instanceof SourceVersion) 333 return "source version"; 334 if (o instanceof FileObject || o instanceof File) 335 return "file name"; 336 if (o instanceof JCDiagnostic) 337 return "message segment"; 338 if (o instanceof LocalizedString) 339 return "message segment"; // only instance is "no arguments" 340 String s = o.getClass().getSimpleName(); 341 return (s.isEmpty() ? o.getClass().getName() : s); 342 } 343 344 // </editor-fold> 345 346 } 347