1 /*
   2  * Copyright (c) 1999, 2016, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.javac.util;
  27 
  28 import java.io.*;
  29 import java.util.Arrays;
  30 import java.util.EnumMap;
  31 import java.util.EnumSet;
  32 import java.util.HashSet;
  33 import java.util.Map;
  34 import java.util.Queue;
  35 import java.util.Set;
  36 
  37 import javax.tools.DiagnosticListener;
  38 import javax.tools.JavaFileObject;
  39 
  40 import com.sun.tools.javac.api.DiagnosticFormatter;
  41 import com.sun.tools.javac.main.Main;
  42 import com.sun.tools.javac.main.Option;
  43 import com.sun.tools.javac.tree.EndPosTable;
  44 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
  45 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  46 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
  47 
  48 import static com.sun.tools.javac.main.Option.*;
  49 
  50 /** A class for error logs. Reports errors and warnings, and
  51  *  keeps track of error numbers and positions.
  52  *
  53  *  <p><b>This is NOT part of any supported API.
  54  *  If you write code that depends on this, you do so at your own risk.
  55  *  This code and its internal interfaces are subject to change or
  56  *  deletion without notice.</b>
  57  */
  58 public class Log extends AbstractLog {
  59     /** The context key for the log. */
  60     public static final Context.Key<Log> logKey = new Context.Key<>();
  61 
  62     /** The context key for the standard output PrintWriter. */
  63     public static final Context.Key<PrintWriter> outKey = new Context.Key<>();
  64 
  65     /** The context key for the diagnostic PrintWriter. */
  66     public static final Context.Key<PrintWriter> errKey = new Context.Key<>();
  67 
  68     /* TODO: Should unify this with prefix handling in JCDiagnostic.Factory. */
  69     public enum PrefixKind {
  70         JAVAC("javac."),
  71         COMPILER_MISC("compiler.misc.");
  72         PrefixKind(String v) {
  73             value = v;
  74         }
  75         public String key(String k) {
  76             return value + k;
  77         }
  78         final String value;
  79     }
  80 
  81     /**
  82      * DiagnosticHandler's provide the initial handling for diagnostics.
  83      * When a diagnostic handler is created and has been initialized, it
  84      * should install itself as the current diagnostic handler. When a
  85      * client has finished using a handler, the client should call
  86      * {@code log.removeDiagnosticHandler();}
  87      *
  88      * Note that javax.tools.DiagnosticListener (if set) is called later in the
  89      * diagnostic pipeline.
  90      */
  91     public static abstract class DiagnosticHandler {
  92         /**
  93          * The previously installed diagnostic handler.
  94          */
  95         protected DiagnosticHandler prev;
  96 
  97         /**
  98          * Install this diagnostic handler as the current one,
  99          * recording the previous one.
 100          */
 101         protected void install(Log log) {
 102             prev = log.diagnosticHandler;
 103             log.diagnosticHandler = this;
 104         }
 105 
 106         /**
 107          * Handle a diagnostic.
 108          */
 109         public abstract void report(JCDiagnostic diag);
 110     }
 111 
 112     /**
 113      * A DiagnosticHandler that discards all diagnostics.
 114      */
 115     public static class DiscardDiagnosticHandler extends DiagnosticHandler {
 116         public DiscardDiagnosticHandler(Log log) {
 117             install(log);
 118         }
 119 
 120         @Override
 121         public void report(JCDiagnostic diag) { }
 122     }
 123 
 124     /**
 125      * A DiagnosticHandler that can defer some or all diagnostics,
 126      * by buffering them for later examination and/or reporting.
 127      * If a diagnostic is not deferred, or is subsequently reported
 128      * with reportAllDiagnostics(), it will be reported to the previously
 129      * active diagnostic handler.
 130      */
 131     public static class DeferredDiagnosticHandler extends DiagnosticHandler {
 132         private Queue<JCDiagnostic> deferred = new ListBuffer<>();
 133         private final Filter<JCDiagnostic> filter;
 134 
 135         public DeferredDiagnosticHandler(Log log) {
 136             this(log, null);
 137         }
 138 
 139         public DeferredDiagnosticHandler(Log log, Filter<JCDiagnostic> filter) {
 140             this.filter = filter;
 141             install(log);
 142         }
 143 
 144         @Override
 145         public void report(JCDiagnostic diag) {
 146             if (!diag.isFlagSet(JCDiagnostic.DiagnosticFlag.NON_DEFERRABLE) &&
 147                 (filter == null || filter.accepts(diag))) {
 148                 deferred.add(diag);
 149             } else {
 150                 prev.report(diag);
 151             }
 152         }
 153 
 154         public Queue<JCDiagnostic> getDiagnostics() {
 155             return deferred;
 156         }
 157 
 158         /** Report all deferred diagnostics. */
 159         public void reportDeferredDiagnostics() {
 160             reportDeferredDiagnostics(EnumSet.allOf(JCDiagnostic.Kind.class));
 161         }
 162 
 163         /** Report selected deferred diagnostics. */
 164         public void reportDeferredDiagnostics(Set<JCDiagnostic.Kind> kinds) {
 165             JCDiagnostic d;
 166             while ((d = deferred.poll()) != null) {
 167                 if (kinds.contains(d.getKind()))
 168                     prev.report(d);
 169             }
 170             deferred = null; // prevent accidental ongoing use
 171         }
 172     }
 173 
 174     public enum WriterKind { NOTICE, WARNING, ERROR, STDOUT, STDERR }
 175 
 176     private final Map<WriterKind, PrintWriter> writers;
 177 
 178     /** The maximum number of errors/warnings that are reported.
 179      */
 180     protected int MaxErrors;
 181     protected int MaxWarnings;
 182 
 183     /** Switch: prompt user on each error.
 184      */
 185     public boolean promptOnError;
 186 
 187     /** Switch: emit warning messages.
 188      */
 189     public boolean emitWarnings;
 190 
 191     /** Switch: suppress note messages.
 192      */
 193     public boolean suppressNotes;
 194 
 195     /** Print stack trace on errors?
 196      */
 197     public boolean dumpOnError;
 198 
 199     /**
 200      * Diagnostic listener, if provided through programmatic
 201      * interface to javac (JSR 199).
 202      */
 203     protected DiagnosticListener<? super JavaFileObject> diagListener;
 204 
 205     /**
 206      * Formatter for diagnostics.
 207      */
 208     private DiagnosticFormatter<JCDiagnostic> diagFormatter;
 209 
 210     /**
 211      * Keys for expected diagnostics.
 212      */
 213     public Set<String> expectDiagKeys;
 214 
 215     /**
 216      * Set to true if a compressed diagnostic is reported
 217      */
 218     public boolean compressedOutput;
 219 
 220     /**
 221      * JavacMessages object used for localization.
 222      */
 223     private JavacMessages messages;
 224 
 225     /**
 226      * Handler for initial dispatch of diagnostics.
 227      */
 228     private DiagnosticHandler diagnosticHandler;
 229 
 230     /** Get the Log instance for this context. */
 231     public static Log instance(Context context) {
 232         Log instance = context.get(logKey);
 233         if (instance == null)
 234             instance = new Log(context);
 235         return instance;
 236     }
 237 
 238     /**
 239      * Register a Context.Factory to create a Log.
 240      */
 241     public static void preRegister(Context context, PrintWriter w) {
 242         context.put(Log.class, (Context.Factory<Log>) (c -> new Log(c, w)));
 243     }
 244 
 245     /**
 246      * Construct a log with default settings.
 247      * If no streams are set in the context, the log will be initialized to use
 248      * System.out for normal output, and System.err for all diagnostic output.
 249      * If one stream is set in the context, with either Log.outKey or Log.errKey,
 250      * it will be used for all output.
 251      * Otherwise, the log will be initialized to use both streams found in the context.
 252      */
 253     protected Log(Context context) {
 254         this(context, initWriters(context));
 255     }
 256 
 257     /**
 258      * Initialize a map of writers based on values found in the context
 259      * @param context the context in which to find writers to use
 260      * @return a map of writers
 261      */
 262     private static Map<WriterKind, PrintWriter> initWriters(Context context) {
 263         PrintWriter out = context.get(outKey);
 264         PrintWriter err = context.get(errKey);
 265         if (out == null && err == null) {
 266             out = new PrintWriter(System.out, true);
 267             err = new PrintWriter(System.err, true);
 268             return initWriters(out, err);
 269         } else if (out == null || err == null) {
 270             PrintWriter pw = (out != null) ? out : err;
 271             return initWriters(pw, pw);
 272         } else {
 273             return initWriters(out, err);
 274         }
 275     }
 276 
 277     /**
 278      * Construct a log with all output sent to a single output stream.
 279      */
 280     protected Log(Context context, PrintWriter writer) {
 281         this(context, initWriters(writer, writer));
 282     }
 283 
 284     /**
 285      * Construct a log.
 286      * The log will be initialized to use stdOut for normal output, and stdErr
 287      * for all diagnostic output.
 288      */
 289     protected Log(Context context, PrintWriter out, PrintWriter err) {
 290         this(context, initWriters(out, err));
 291     }
 292 
 293     /**
 294      * Initialize a writer map for a stream for normal output, and a stream for diagnostics.
 295      * @param out a stream to be used for normal output
 296      * @param err a stream to be used for diagnostic messages, such as errors, warnings, etc
 297      * @return a map of writers
 298      */
 299     private static Map<WriterKind, PrintWriter> initWriters(PrintWriter out, PrintWriter err) {
 300         Map<WriterKind, PrintWriter> writers = new EnumMap<>(WriterKind.class);
 301         writers.put(WriterKind.ERROR, err);
 302         writers.put(WriterKind.WARNING, err);
 303         writers.put(WriterKind.NOTICE, err);
 304 
 305         writers.put(WriterKind.STDOUT, out);
 306         writers.put(WriterKind.STDERR, err);
 307 
 308         return writers;
 309     }
 310 
 311     /**
 312      * Construct a log with given I/O redirections.
 313      * @deprecated
 314      * This constructor is provided to support the supported but now-deprecated javadoc entry point
 315      *      com.sun.tools.javadoc.Main.execute(String programName,
 316      *          PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter,
 317      *          String defaultDocletClassName, String... args)
 318      */
 319     @Deprecated
 320     protected Log(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) {
 321         this(context, initWriters(errWriter, warnWriter, noticeWriter));
 322     }
 323 
 324     /**
 325      * Initialize a writer map with different streams for different types of diagnostics.
 326      * @param errWriter a stream for writing error messages
 327      * @param warnWriter a stream for writing warning messages
 328      * @param noticeWriter a stream for writing notice messages
 329      * @return a map of writers
 330      * @deprecated This method exists to support a supported but now deprecated javadoc entry point.
 331      */
 332     @Deprecated
 333     private static Map<WriterKind, PrintWriter>  initWriters(PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) {
 334         Map<WriterKind, PrintWriter> writers = new EnumMap<>(WriterKind.class);
 335         writers.put(WriterKind.ERROR, errWriter);
 336         writers.put(WriterKind.WARNING, warnWriter);
 337         writers.put(WriterKind.NOTICE, noticeWriter);
 338 
 339         writers.put(WriterKind.STDOUT, noticeWriter);
 340         writers.put(WriterKind.STDERR, errWriter);
 341 
 342         return writers;
 343     }
 344 
 345     /**
 346      * Creates a log.
 347      * @param context the context in which the log should be registered
 348      * @param writers a map of writers that can be accessed by the kind of writer required
 349      */
 350     private Log(Context context, Map<WriterKind, PrintWriter> writers) {
 351         super(JCDiagnostic.Factory.instance(context));
 352         context.put(logKey, this);
 353         this.writers = writers;
 354 
 355         @SuppressWarnings("unchecked") // FIXME
 356         DiagnosticListener<? super JavaFileObject> dl =
 357             context.get(DiagnosticListener.class);
 358         this.diagListener = dl;
 359 
 360         diagnosticHandler = new DefaultDiagnosticHandler();
 361 
 362         messages = JavacMessages.instance(context);
 363         messages.add(Main.javacBundleName);
 364 
 365         final Options options = Options.instance(context);
 366         initOptions(options);
 367         options.addListener(new Runnable() {
 368             @Override
 369             public void run() {
 370                 initOptions(options);
 371             }
 372         });
 373     }
 374     // where
 375         private void initOptions(Options options) {
 376             this.dumpOnError = options.isSet(DOE);
 377             this.promptOnError = options.isSet(PROMPT);
 378             this.emitWarnings = options.isUnset(XLINT_CUSTOM, "none");
 379             this.suppressNotes = options.isSet("suppressNotes");
 380             this.MaxErrors = getIntOption(options, XMAXERRS, getDefaultMaxErrors());
 381             this.MaxWarnings = getIntOption(options, XMAXWARNS, getDefaultMaxWarnings());
 382 
 383             boolean rawDiagnostics = options.isSet("rawDiagnostics");
 384             this.diagFormatter = rawDiagnostics ? new RawDiagnosticFormatter(options) :
 385                                                   new BasicDiagnosticFormatter(options, messages);
 386 
 387             String ek = options.get("expectKeys");
 388             if (ek != null)
 389                 expectDiagKeys = new HashSet<>(Arrays.asList(ek.split(", *")));
 390         }
 391 
 392         private int getIntOption(Options options, Option option, int defaultValue) {
 393             String s = options.get(option);
 394             try {
 395                 if (s != null) {
 396                     int n = Integer.parseInt(s);
 397                     return (n <= 0 ? Integer.MAX_VALUE : n);
 398                 }
 399             } catch (NumberFormatException e) {
 400                 // silently ignore ill-formed numbers
 401             }
 402             return defaultValue;
 403         }
 404 
 405         /** Default value for -Xmaxerrs.
 406          */
 407         protected int getDefaultMaxErrors() {
 408             return 100;
 409         }
 410 
 411         /** Default value for -Xmaxwarns.
 412          */
 413         protected int getDefaultMaxWarnings() {
 414             return 100;
 415         }
 416 
 417     /** The number of errors encountered so far.
 418      */
 419     public int nerrors = 0;
 420 
 421     /** The number of warnings encountered so far.
 422      */
 423     public int nwarnings = 0;
 424 
 425     /** A set of all errors generated so far. This is used to avoid printing an
 426      *  error message more than once. For each error, a pair consisting of the
 427      *  source file name and source code position of the error is added to the set.
 428      */
 429     protected Set<Pair<JavaFileObject, Integer>> recorded = new HashSet<>();
 430 
 431     public boolean hasDiagnosticListener() {
 432         return diagListener != null;
 433     }
 434 
 435     public void setEndPosTable(JavaFileObject name, EndPosTable endPosTable) {
 436         Assert.checkNonNull(name);
 437         getSource(name).setEndPosTable(endPosTable);
 438     }
 439 
 440     /** Return current sourcefile.
 441      */
 442     public JavaFileObject currentSourceFile() {
 443         return source == null ? null : source.getFile();
 444     }
 445 
 446     /** Get the current diagnostic formatter.
 447      */
 448     public DiagnosticFormatter<JCDiagnostic> getDiagnosticFormatter() {
 449         return diagFormatter;
 450     }
 451 
 452     /** Set the current diagnostic formatter.
 453      */
 454     public void setDiagnosticFormatter(DiagnosticFormatter<JCDiagnostic> diagFormatter) {
 455         this.diagFormatter = diagFormatter;
 456     }
 457 
 458     public PrintWriter getWriter(WriterKind kind) {
 459         return writers.get(kind);
 460     }
 461 
 462     public void setWriter(WriterKind kind, PrintWriter pw) {
 463         Assert.checkNonNull(pw);
 464         writers.put(kind, pw);
 465     }
 466 
 467     public void setWriters(PrintWriter pw) {
 468         Assert.checkNonNull(pw);
 469         for (WriterKind k: WriterKind.values())
 470             writers.put(k, pw);
 471     }
 472 
 473     /**
 474      * Replace the specified diagnostic handler with the
 475      * handler that was current at the time this handler was created.
 476      * The given handler must be the currently installed handler;
 477      * it must be specified explicitly for clarity and consistency checking.
 478      */
 479     public void popDiagnosticHandler(DiagnosticHandler h) {
 480         Assert.check(diagnosticHandler == h);
 481         diagnosticHandler = h.prev;
 482     }
 483 
 484     /** Flush the logs
 485      */
 486     public void flush() {
 487         for (PrintWriter pw: writers.values()) {
 488             pw.flush();
 489         }
 490     }
 491 
 492     public void flush(WriterKind kind) {
 493         getWriter(kind).flush();
 494     }
 495 
 496     /** Returns true if an error needs to be reported for a given
 497      * source name and pos.
 498      */
 499     protected boolean shouldReport(JavaFileObject file, int pos) {
 500         if (file == null)
 501             return true;
 502 
 503         Pair<JavaFileObject,Integer> coords = new Pair<>(file, pos);
 504         boolean shouldReport = !recorded.contains(coords);
 505         if (shouldReport)
 506             recorded.add(coords);
 507         return shouldReport;
 508     }
 509 
 510     /** Prompt user after an error.
 511      */
 512     public void prompt() {
 513         if (promptOnError) {
 514             System.err.println(localize("resume.abort"));
 515             try {
 516                 while (true) {
 517                     switch (System.in.read()) {
 518                     case 'a': case 'A':
 519                         System.exit(-1);
 520                         return;
 521                     case 'r': case 'R':
 522                         return;
 523                     case 'x': case 'X':
 524                         throw new AssertionError("user abort");
 525                     default:
 526                     }
 527                 }
 528             } catch (IOException e) {}
 529         }
 530     }
 531 
 532     /** Print the faulty source code line and point to the error.
 533      *  @param pos   Buffer index of the error position, must be on current line
 534      */
 535     private void printErrLine(int pos, PrintWriter writer) {
 536         String line = (source == null ? null : source.getLine(pos));
 537         if (line == null)
 538             return;
 539         int col = source.getColumnNumber(pos, false);
 540 
 541         printRawLines(writer, line);
 542         for (int i = 0; i < col - 1; i++) {
 543             writer.print((line.charAt(i) == '\t') ? "\t" : " ");
 544         }
 545         writer.println("^");
 546         writer.flush();
 547     }
 548 
 549     public void printNewline() {
 550         PrintWriter noticeWriter = writers.get(WriterKind.NOTICE);
 551         noticeWriter.println();
 552     }
 553 
 554     public void printNewline(WriterKind wk) {
 555         getWriter(wk).println();
 556     }
 557 
 558     public void printLines(String key, Object... args) {
 559         PrintWriter noticeWriter = writers.get(WriterKind.NOTICE);
 560         printRawLines(noticeWriter, localize(key, args));
 561     }
 562 
 563     public void printLines(PrefixKind pk, String key, Object... args) {
 564         PrintWriter noticeWriter = writers.get(WriterKind.NOTICE);
 565         printRawLines(noticeWriter, localize(pk, key, args));
 566     }
 567 
 568     public void printLines(WriterKind wk, String key, Object... args) {
 569         printRawLines(getWriter(wk), localize(key, args));
 570     }
 571 
 572     public void printLines(WriterKind wk, PrefixKind pk, String key, Object... args) {
 573         printRawLines(getWriter(wk), localize(pk, key, args));
 574     }
 575 
 576     /** Print the text of a message, translating newlines appropriately
 577      *  for the platform.
 578      */
 579     public void printRawLines(String msg) {
 580         PrintWriter noticeWriter = writers.get(WriterKind.NOTICE);
 581         printRawLines(noticeWriter, msg);
 582     }
 583 
 584     /** Print the text of a message, translating newlines appropriately
 585      *  for the platform.
 586      */
 587     public void printRawLines(WriterKind kind, String msg) {
 588         printRawLines(getWriter(kind), msg);
 589     }
 590 
 591     /** Print the text of a message, translating newlines appropriately
 592      *  for the platform.
 593      */
 594     public static void printRawLines(PrintWriter writer, String msg) {
 595         int nl;
 596         while ((nl = msg.indexOf('\n')) != -1) {
 597             writer.println(msg.substring(0, nl));
 598             msg = msg.substring(nl+1);
 599         }
 600         if (msg.length() != 0) writer.println(msg);
 601     }
 602 
 603     /**
 604      * Print the localized text of a "verbose" message to the
 605      * noticeWriter stream.
 606      */
 607     public void printVerbose(String key, Object... args) {
 608         PrintWriter noticeWriter = writers.get(WriterKind.NOTICE);
 609         printRawLines(noticeWriter, localize("verbose." + key, args));
 610     }
 611 
 612     @Override
 613     protected void directError(String key, Object... args) {
 614         PrintWriter errWriter = writers.get(WriterKind.ERROR);
 615         printRawLines(errWriter, localize(key, args));
 616         errWriter.flush();
 617     }
 618 
 619     /** Report a warning that cannot be suppressed.
 620      *  @param pos    The source position at which to report the warning.
 621      *  @param key    The key for the localized warning message.
 622      *  @param args   Fields of the warning message.
 623      */
 624     public void strictWarning(DiagnosticPosition pos, String key, Object ... args) {
 625         writeDiagnostic(diags.warning(null, source, pos, key, args));
 626         nwarnings++;
 627     }
 628 
 629     /**
 630      * Primary method to report a diagnostic.
 631      * @param diagnostic
 632      */
 633     @Override
 634     public void report(JCDiagnostic diagnostic) {
 635         diagnosticHandler.report(diagnostic);
 636      }
 637 
 638     /**
 639      * Common diagnostic handling.
 640      * The diagnostic is counted, and depending on the options and how many diagnostics have been
 641      * reported so far, the diagnostic may be handed off to writeDiagnostic.
 642      */
 643     private class DefaultDiagnosticHandler extends DiagnosticHandler {
 644         @Override
 645         public void report(JCDiagnostic diagnostic) {
 646             if (expectDiagKeys != null)
 647                 expectDiagKeys.remove(diagnostic.getCode());
 648 
 649             switch (diagnostic.getType()) {
 650             case FRAGMENT:
 651                 throw new IllegalArgumentException();
 652 
 653             case NOTE:
 654                 // Print out notes only when we are permitted to report warnings
 655                 // Notes are only generated at the end of a compilation, so should be small
 656                 // in number.
 657                 if ((emitWarnings || diagnostic.isMandatory()) && !suppressNotes) {
 658                     writeDiagnostic(diagnostic);
 659                 }
 660                 break;
 661 
 662             case WARNING:
 663                 if (emitWarnings || diagnostic.isMandatory()) {
 664                     if (nwarnings < MaxWarnings) {
 665                         writeDiagnostic(diagnostic);
 666                         nwarnings++;
 667                     }
 668                 }
 669                 break;
 670 
 671             case ERROR:
 672                 if (nerrors < MaxErrors &&
 673                     (diagnostic.isFlagSet(DiagnosticFlag.MULTIPLE) ||
 674                      shouldReport(diagnostic.getSource(), diagnostic.getIntPosition()))) {
 675                     writeDiagnostic(diagnostic);
 676                     nerrors++;
 677                 }
 678                 break;
 679             }
 680             if (diagnostic.isFlagSet(JCDiagnostic.DiagnosticFlag.COMPRESSED)) {
 681                 compressedOutput = true;
 682             }
 683         }
 684     }
 685 
 686     /**
 687      * Write out a diagnostic.
 688      */
 689     protected void writeDiagnostic(JCDiagnostic diag) {
 690         if (diagListener != null) {
 691             diagListener.report(diag);
 692             return;
 693         }
 694 
 695         PrintWriter writer = getWriterForDiagnosticType(diag.getType());
 696 
 697         printRawLines(writer, diagFormatter.format(diag, messages.getCurrentLocale()));
 698 
 699         if (promptOnError) {
 700             switch (diag.getType()) {
 701             case ERROR:
 702             case WARNING:
 703                 prompt();
 704             }
 705         }
 706 
 707         if (dumpOnError)
 708             new RuntimeException().printStackTrace(writer);
 709 
 710         writer.flush();
 711     }
 712 
 713     @Deprecated
 714     protected PrintWriter getWriterForDiagnosticType(DiagnosticType dt) {
 715         switch (dt) {
 716         case FRAGMENT:
 717             throw new IllegalArgumentException();
 718 
 719         case NOTE:
 720             return writers.get(WriterKind.NOTICE);
 721 
 722         case WARNING:
 723             return writers.get(WriterKind.WARNING);
 724 
 725         case ERROR:
 726             return writers.get(WriterKind.ERROR);
 727 
 728         default:
 729             throw new Error();
 730         }
 731     }
 732 
 733     /** Find a localized string in the resource bundle.
 734      *  Because this method is static, it ignores the locale.
 735      *  Use localize(key, args) when possible.
 736      *  @param key    The key for the localized string.
 737      *  @param args   Fields to substitute into the string.
 738      */
 739     public static String getLocalizedString(String key, Object ... args) {
 740         return JavacMessages.getDefaultLocalizedString(PrefixKind.COMPILER_MISC.key(key), args);
 741     }
 742 
 743     /** Find a localized string in the resource bundle.
 744      *  @param key    The key for the localized string.
 745      *  @param args   Fields to substitute into the string.
 746      */
 747     public String localize(String key, Object... args) {
 748         return localize(PrefixKind.COMPILER_MISC, key, args);
 749     }
 750 
 751     /** Find a localized string in the resource bundle.
 752      *  @param key    The key for the localized string.
 753      *  @param args   Fields to substitute into the string.
 754      */
 755     public String localize(PrefixKind pk, String key, Object... args) {
 756         if (useRawMessages)
 757             return pk.key(key);
 758         else
 759             return messages.getLocalizedString(pk.key(key), args);
 760     }
 761     // where
 762         // backdoor hook for testing, should transition to use -XDrawDiagnostics
 763         private static boolean useRawMessages = false;
 764 
 765 /***************************************************************************
 766  * raw error messages without internationalization; used for experimentation
 767  * and quick prototyping
 768  ***************************************************************************/
 769 
 770     /** print an error or warning message:
 771      */
 772     private void printRawDiag(PrintWriter pw, String prefix, int pos, String msg) {
 773         if (source == null || pos == Position.NOPOS) {
 774             printRawLines(pw, prefix + msg);
 775         } else {
 776             int line = source.getLineNumber(pos);
 777             JavaFileObject file = source.getFile();
 778             if (file != null)
 779                 printRawLines(pw,
 780                            file.getName() + ":" +
 781                            line + ": " + msg);
 782             printErrLine(pos, pw);
 783         }
 784         pw.flush();
 785     }
 786 
 787     /** report an error:
 788      */
 789     public void rawError(int pos, String msg) {
 790         PrintWriter errWriter = writers.get(WriterKind.ERROR);
 791         if (nerrors < MaxErrors && shouldReport(currentSourceFile(), pos)) {
 792             printRawDiag(errWriter, "error: ", pos, msg);
 793             prompt();
 794             nerrors++;
 795         }
 796         errWriter.flush();
 797     }
 798 
 799     /** report a warning:
 800      */
 801     public void rawWarning(int pos, String msg) {
 802         PrintWriter warnWriter = writers.get(WriterKind.ERROR);
 803         if (nwarnings < MaxWarnings && emitWarnings) {
 804             printRawDiag(warnWriter, "warning: ", pos, msg);
 805         }
 806         prompt();
 807         nwarnings++;
 808         warnWriter.flush();
 809     }
 810 
 811     public static String format(String fmt, Object... args) {
 812         return String.format((java.util.Locale)null, fmt, args);
 813     }
 814 
 815 }