Log.java revision 3591:8382e92dd1f9
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
26package com.sun.tools.javac.util;
27
28import java.io.*;
29import java.util.Arrays;
30import java.util.EnumMap;
31import java.util.EnumSet;
32import java.util.HashSet;
33import java.util.Map;
34import java.util.Queue;
35import java.util.Set;
36
37import javax.tools.DiagnosticListener;
38import javax.tools.JavaFileObject;
39
40import com.sun.tools.javac.api.DiagnosticFormatter;
41import com.sun.tools.javac.main.Main;
42import com.sun.tools.javac.main.Option;
43import com.sun.tools.javac.tree.EndPosTable;
44import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
45import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
46import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
47
48import 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 */
58public 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    /** A set of "not-supported-in-source-X" errors produced so far. This is used to only generate
432     *  one such error per file.
433     */
434    protected Set<Pair<JavaFileObject, String>>  recordedSourceLevelErrors = new HashSet<>();
435
436    public boolean hasDiagnosticListener() {
437        return diagListener != null;
438    }
439
440    public void setEndPosTable(JavaFileObject name, EndPosTable endPosTable) {
441        Assert.checkNonNull(name);
442        getSource(name).setEndPosTable(endPosTable);
443    }
444
445    /** Return current sourcefile.
446     */
447    public JavaFileObject currentSourceFile() {
448        return source == null ? null : source.getFile();
449    }
450
451    /** Get the current diagnostic formatter.
452     */
453    public DiagnosticFormatter<JCDiagnostic> getDiagnosticFormatter() {
454        return diagFormatter;
455    }
456
457    /** Set the current diagnostic formatter.
458     */
459    public void setDiagnosticFormatter(DiagnosticFormatter<JCDiagnostic> diagFormatter) {
460        this.diagFormatter = diagFormatter;
461    }
462
463    public PrintWriter getWriter(WriterKind kind) {
464        return writers.get(kind);
465    }
466
467    public void setWriter(WriterKind kind, PrintWriter pw) {
468        Assert.checkNonNull(pw);
469        writers.put(kind, pw);
470    }
471
472    public void setWriters(PrintWriter pw) {
473        Assert.checkNonNull(pw);
474        for (WriterKind k: WriterKind.values())
475            writers.put(k, pw);
476    }
477
478    /**
479     * Replace the specified diagnostic handler with the
480     * handler that was current at the time this handler was created.
481     * The given handler must be the currently installed handler;
482     * it must be specified explicitly for clarity and consistency checking.
483     */
484    public void popDiagnosticHandler(DiagnosticHandler h) {
485        Assert.check(diagnosticHandler == h);
486        diagnosticHandler = h.prev;
487    }
488
489    /** Flush the logs
490     */
491    public void flush() {
492        for (PrintWriter pw: writers.values()) {
493            pw.flush();
494        }
495    }
496
497    public void flush(WriterKind kind) {
498        getWriter(kind).flush();
499    }
500
501    /** Returns true if an error needs to be reported for a given
502     * source name and pos.
503     */
504    protected boolean shouldReport(JavaFileObject file, int pos) {
505        if (file == null)
506            return true;
507
508        Pair<JavaFileObject,Integer> coords = new Pair<>(file, pos);
509        boolean shouldReport = !recorded.contains(coords);
510        if (shouldReport)
511            recorded.add(coords);
512        return shouldReport;
513    }
514
515    /** Returns true if a diagnostics needs to be reported.
516     */
517    private boolean shouldReport(JCDiagnostic d) {
518        JavaFileObject file = d.getSource();
519
520        if (file == null)
521            return true;
522
523        if (!shouldReport(file, d.getIntPosition()))
524            return false;
525
526        if (!d.isFlagSet(DiagnosticFlag.SOURCE_LEVEL))
527            return true;
528
529        Pair<JavaFileObject, String> coords = new Pair<>(file, d.getCode());
530        boolean shouldReport = !recordedSourceLevelErrors.contains(coords);
531        if (shouldReport)
532            recordedSourceLevelErrors.add(coords);
533        return shouldReport;
534    }
535
536    /** Prompt user after an error.
537     */
538    public void prompt() {
539        if (promptOnError) {
540            System.err.println(localize("resume.abort"));
541            try {
542                while (true) {
543                    switch (System.in.read()) {
544                    case 'a': case 'A':
545                        System.exit(-1);
546                        return;
547                    case 'r': case 'R':
548                        return;
549                    case 'x': case 'X':
550                        throw new AssertionError("user abort");
551                    default:
552                    }
553                }
554            } catch (IOException e) {}
555        }
556    }
557
558    /** Print the faulty source code line and point to the error.
559     *  @param pos   Buffer index of the error position, must be on current line
560     */
561    private void printErrLine(int pos, PrintWriter writer) {
562        String line = (source == null ? null : source.getLine(pos));
563        if (line == null)
564            return;
565        int col = source.getColumnNumber(pos, false);
566
567        printRawLines(writer, line);
568        for (int i = 0; i < col - 1; i++) {
569            writer.print((line.charAt(i) == '\t') ? "\t" : " ");
570        }
571        writer.println("^");
572        writer.flush();
573    }
574
575    public void printNewline() {
576        PrintWriter noticeWriter = writers.get(WriterKind.NOTICE);
577        noticeWriter.println();
578    }
579
580    public void printNewline(WriterKind wk) {
581        getWriter(wk).println();
582    }
583
584    public void printLines(String key, Object... args) {
585        PrintWriter noticeWriter = writers.get(WriterKind.NOTICE);
586        printRawLines(noticeWriter, localize(key, args));
587    }
588
589    public void printLines(PrefixKind pk, String key, Object... args) {
590        PrintWriter noticeWriter = writers.get(WriterKind.NOTICE);
591        printRawLines(noticeWriter, localize(pk, key, args));
592    }
593
594    public void printLines(WriterKind wk, String key, Object... args) {
595        printRawLines(getWriter(wk), localize(key, args));
596    }
597
598    public void printLines(WriterKind wk, PrefixKind pk, String key, Object... args) {
599        printRawLines(getWriter(wk), localize(pk, key, args));
600    }
601
602    /** Print the text of a message, translating newlines appropriately
603     *  for the platform.
604     */
605    public void printRawLines(String msg) {
606        PrintWriter noticeWriter = writers.get(WriterKind.NOTICE);
607        printRawLines(noticeWriter, msg);
608    }
609
610    /** Print the text of a message, translating newlines appropriately
611     *  for the platform.
612     */
613    public void printRawLines(WriterKind kind, String msg) {
614        printRawLines(getWriter(kind), msg);
615    }
616
617    /** Print the text of a message, translating newlines appropriately
618     *  for the platform.
619     */
620    public static void printRawLines(PrintWriter writer, String msg) {
621        int nl;
622        while ((nl = msg.indexOf('\n')) != -1) {
623            writer.println(msg.substring(0, nl));
624            msg = msg.substring(nl+1);
625        }
626        if (msg.length() != 0) writer.println(msg);
627    }
628
629    /**
630     * Print the localized text of a "verbose" message to the
631     * noticeWriter stream.
632     */
633    public void printVerbose(String key, Object... args) {
634        PrintWriter noticeWriter = writers.get(WriterKind.NOTICE);
635        printRawLines(noticeWriter, localize("verbose." + key, args));
636    }
637
638    @Override
639    protected void directError(String key, Object... args) {
640        PrintWriter errWriter = writers.get(WriterKind.ERROR);
641        printRawLines(errWriter, localize(key, args));
642        errWriter.flush();
643    }
644
645    /** Report a warning that cannot be suppressed.
646     *  @param pos    The source position at which to report the warning.
647     *  @param key    The key for the localized warning message.
648     *  @param args   Fields of the warning message.
649     */
650    public void strictWarning(DiagnosticPosition pos, String key, Object ... args) {
651        writeDiagnostic(diags.warning(null, source, pos, key, args));
652        nwarnings++;
653    }
654
655    /**
656     * Primary method to report a diagnostic.
657     * @param diagnostic
658     */
659    @Override
660    public void report(JCDiagnostic diagnostic) {
661        diagnosticHandler.report(diagnostic);
662     }
663
664    /**
665     * Common diagnostic handling.
666     * The diagnostic is counted, and depending on the options and how many diagnostics have been
667     * reported so far, the diagnostic may be handed off to writeDiagnostic.
668     */
669    private class DefaultDiagnosticHandler extends DiagnosticHandler {
670        @Override
671        public void report(JCDiagnostic diagnostic) {
672            if (expectDiagKeys != null)
673                expectDiagKeys.remove(diagnostic.getCode());
674
675            switch (diagnostic.getType()) {
676            case FRAGMENT:
677                throw new IllegalArgumentException();
678
679            case NOTE:
680                // Print out notes only when we are permitted to report warnings
681                // Notes are only generated at the end of a compilation, so should be small
682                // in number.
683                if ((emitWarnings || diagnostic.isMandatory()) && !suppressNotes) {
684                    writeDiagnostic(diagnostic);
685                }
686                break;
687
688            case WARNING:
689                if (emitWarnings || diagnostic.isMandatory()) {
690                    if (nwarnings < MaxWarnings) {
691                        writeDiagnostic(diagnostic);
692                        nwarnings++;
693                    }
694                }
695                break;
696
697            case ERROR:
698                if (nerrors < MaxErrors &&
699                    (diagnostic.isFlagSet(DiagnosticFlag.MULTIPLE) ||
700                     shouldReport(diagnostic))) {
701                    writeDiagnostic(diagnostic);
702                    nerrors++;
703                }
704                break;
705            }
706            if (diagnostic.isFlagSet(JCDiagnostic.DiagnosticFlag.COMPRESSED)) {
707                compressedOutput = true;
708            }
709        }
710    }
711
712    /**
713     * Write out a diagnostic.
714     */
715    protected void writeDiagnostic(JCDiagnostic diag) {
716        if (diagListener != null) {
717            diagListener.report(diag);
718            return;
719        }
720
721        PrintWriter writer = getWriterForDiagnosticType(diag.getType());
722
723        printRawLines(writer, diagFormatter.format(diag, messages.getCurrentLocale()));
724
725        if (promptOnError) {
726            switch (diag.getType()) {
727            case ERROR:
728            case WARNING:
729                prompt();
730            }
731        }
732
733        if (dumpOnError)
734            new RuntimeException().printStackTrace(writer);
735
736        writer.flush();
737    }
738
739    @Deprecated
740    protected PrintWriter getWriterForDiagnosticType(DiagnosticType dt) {
741        switch (dt) {
742        case FRAGMENT:
743            throw new IllegalArgumentException();
744
745        case NOTE:
746            return writers.get(WriterKind.NOTICE);
747
748        case WARNING:
749            return writers.get(WriterKind.WARNING);
750
751        case ERROR:
752            return writers.get(WriterKind.ERROR);
753
754        default:
755            throw new Error();
756        }
757    }
758
759    /** Find a localized string in the resource bundle.
760     *  Because this method is static, it ignores the locale.
761     *  Use localize(key, args) when possible.
762     *  @param key    The key for the localized string.
763     *  @param args   Fields to substitute into the string.
764     */
765    public static String getLocalizedString(String key, Object ... args) {
766        return JavacMessages.getDefaultLocalizedString(PrefixKind.COMPILER_MISC.key(key), args);
767    }
768
769    /** Find a localized string in the resource bundle.
770     *  @param key    The key for the localized string.
771     *  @param args   Fields to substitute into the string.
772     */
773    public String localize(String key, Object... args) {
774        return localize(PrefixKind.COMPILER_MISC, key, args);
775    }
776
777    public String localize(JCDiagnostic.DiagnosticInfo diagInfo) {
778        if (useRawMessages) {
779            return diagInfo.key();
780        } else {
781            return messages.getLocalizedString(diagInfo.key(), diagInfo.args);
782        }
783    }
784
785    /** Find a localized string in the resource bundle.
786     *  @param key    The key for the localized string.
787     *  @param args   Fields to substitute into the string.
788     */
789    public String localize(PrefixKind pk, String key, Object... args) {
790        if (useRawMessages)
791            return pk.key(key);
792        else
793            return messages.getLocalizedString(pk.key(key), args);
794    }
795    // where
796        // backdoor hook for testing, should transition to use -XDrawDiagnostics
797        private static boolean useRawMessages = false;
798
799/***************************************************************************
800 * raw error messages without internationalization; used for experimentation
801 * and quick prototyping
802 ***************************************************************************/
803
804    /** print an error or warning message:
805     */
806    private void printRawDiag(PrintWriter pw, String prefix, int pos, String msg) {
807        if (source == null || pos == Position.NOPOS) {
808            printRawLines(pw, prefix + msg);
809        } else {
810            int line = source.getLineNumber(pos);
811            JavaFileObject file = source.getFile();
812            if (file != null)
813                printRawLines(pw,
814                           file.getName() + ":" +
815                           line + ": " + msg);
816            printErrLine(pos, pw);
817        }
818        pw.flush();
819    }
820
821    /** report an error:
822     */
823    public void rawError(int pos, String msg) {
824        PrintWriter errWriter = writers.get(WriterKind.ERROR);
825        if (nerrors < MaxErrors && shouldReport(currentSourceFile(), pos)) {
826            printRawDiag(errWriter, "error: ", pos, msg);
827            prompt();
828            nerrors++;
829        }
830        errWriter.flush();
831    }
832
833    /** report a warning:
834     */
835    public void rawWarning(int pos, String msg) {
836        PrintWriter warnWriter = writers.get(WriterKind.ERROR);
837        if (nwarnings < MaxWarnings && emitWarnings) {
838            printRawDiag(warnWriter, "warning: ", pos, msg);
839        }
840        prompt();
841        nwarnings++;
842        warnWriter.flush();
843    }
844
845    public static String format(String fmt, Object... args) {
846        return String.format((java.util.Locale)null, fmt, args);
847    }
848
849}
850