src/share/classes/com/sun/tools/javac/util/Log.java

Tue, 08 Nov 2011 17:06:58 -0800

author
jjg
date
Tue, 08 Nov 2011 17:06:58 -0800
changeset 1136
ae361e7f435a
parent 1135
36553cb94345
child 1138
7375d4979bd3
permissions
-rw-r--r--

7108669: cleanup Log methods for direct printing to streams
Reviewed-by: mcimadamore

     1 /*
     2  * Copyright (c) 1999, 2011, 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  */
    26 package com.sun.tools.javac.util;
    28 import com.sun.tools.javac.main.Main;
    29 import java.io.*;
    30 import java.util.Arrays;
    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 import javax.tools.DiagnosticListener;
    37 import javax.tools.JavaFileObject;
    39 import com.sun.tools.javac.api.DiagnosticFormatter;
    40 import com.sun.tools.javac.main.OptionName;
    41 import com.sun.tools.javac.tree.JCTree;
    42 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
    43 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
    45 import static com.sun.tools.javac.main.OptionName.*;
    47 /** A class for error logs. Reports errors and warnings, and
    48  *  keeps track of error numbers and positions.
    49  *
    50  *  <p><b>This is NOT part of any supported API.
    51  *  If you write code that depends on this, you do so at your own risk.
    52  *  This code and its internal interfaces are subject to change or
    53  *  deletion without notice.</b>
    54  */
    55 public class Log extends AbstractLog {
    56     /** The context key for the log. */
    57     public static final Context.Key<Log> logKey
    58         = new Context.Key<Log>();
    60     /** The context key for the output PrintWriter. */
    61     public static final Context.Key<PrintWriter> outKey =
    62         new Context.Key<PrintWriter>();
    64     /* TODO: Should unify this with prefix handling in JCDiagnostic.Factory. */
    65     public enum PrefixKind {
    66         JAVAC("javac."),
    67         COMPILER_MISC("compiler.misc.");
    68         PrefixKind(String v) {
    69             value = v;
    70         }
    71         public String key(String k) {
    72             return value + k;
    73         }
    74         final String value;
    75     }
    77     public enum WriterKind { NOTICE, WARNING, ERROR };
    79     protected PrintWriter errWriter;
    81     protected PrintWriter warnWriter;
    83     protected PrintWriter noticeWriter;
    85     /** The maximum number of errors/warnings that are reported.
    86      */
    87     protected int MaxErrors;
    88     protected int MaxWarnings;
    90     /** Switch: prompt user on each error.
    91      */
    92     public boolean promptOnError;
    94     /** Switch: emit warning messages.
    95      */
    96     public boolean emitWarnings;
    98     /** Switch: suppress note messages.
    99      */
   100     public boolean suppressNotes;
   102     /** Print stack trace on errors?
   103      */
   104     public boolean dumpOnError;
   106     /** Print multiple errors for same source locations.
   107      */
   108     public boolean multipleErrors;
   110     /**
   111      * Diagnostic listener, if provided through programmatic
   112      * interface to javac (JSR 199).
   113      */
   114     protected DiagnosticListener<? super JavaFileObject> diagListener;
   116     /**
   117      * Formatter for diagnostics.
   118      */
   119     private DiagnosticFormatter<JCDiagnostic> diagFormatter;
   121     /**
   122      * Keys for expected diagnostics.
   123      */
   124     public Set<String> expectDiagKeys;
   126     /**
   127      * JavacMessages object used for localization.
   128      */
   129     private JavacMessages messages;
   131     /**
   132      * Deferred diagnostics
   133      */
   134     public boolean deferDiagnostics;
   135     public Queue<JCDiagnostic> deferredDiagnostics = new ListBuffer<JCDiagnostic>();
   137     /** Construct a log with given I/O redirections.
   138      */
   139     @Deprecated
   140     protected Log(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) {
   141         super(JCDiagnostic.Factory.instance(context));
   142         context.put(logKey, this);
   143         this.errWriter = errWriter;
   144         this.warnWriter = warnWriter;
   145         this.noticeWriter = noticeWriter;
   147         @SuppressWarnings("unchecked") // FIXME
   148         DiagnosticListener<? super JavaFileObject> dl =
   149             context.get(DiagnosticListener.class);
   150         this.diagListener = dl;
   152         messages = JavacMessages.instance(context);
   153         messages.add(Main.javacBundleName);
   155         final Options options = Options.instance(context);
   156         initOptions(options);
   157         options.addListener(new Runnable() {
   158             public void run() {
   159                 initOptions(options);
   160             }
   161         });
   162     }
   163     // where
   164         private void initOptions(Options options) {
   165             this.dumpOnError = options.isSet(DOE);
   166             this.promptOnError = options.isSet(PROMPT);
   167             this.emitWarnings = options.isUnset(XLINT_CUSTOM, "none");
   168             this.suppressNotes = options.isSet("suppressNotes");
   169             this.MaxErrors = getIntOption(options, XMAXERRS, getDefaultMaxErrors());
   170             this.MaxWarnings = getIntOption(options, XMAXWARNS, getDefaultMaxWarnings());
   172             boolean rawDiagnostics = options.isSet("rawDiagnostics");
   173             this.diagFormatter = rawDiagnostics ? new RawDiagnosticFormatter(options) :
   174                                                   new BasicDiagnosticFormatter(options, messages);
   176             String ek = options.get("expectKeys");
   177             if (ek != null)
   178                 expectDiagKeys = new HashSet<String>(Arrays.asList(ek.split(", *")));
   179         }
   181         private int getIntOption(Options options, OptionName optionName, int defaultValue) {
   182             String s = options.get(optionName);
   183             try {
   184                 if (s != null) {
   185                     int n = Integer.parseInt(s);
   186                     return (n <= 0 ? Integer.MAX_VALUE : n);
   187                 }
   188             } catch (NumberFormatException e) {
   189                 // silently ignore ill-formed numbers
   190             }
   191             return defaultValue;
   192         }
   194         /** Default value for -Xmaxerrs.
   195          */
   196         protected int getDefaultMaxErrors() {
   197             return 100;
   198         }
   200         /** Default value for -Xmaxwarns.
   201          */
   202         protected int getDefaultMaxWarnings() {
   203             return 100;
   204         }
   206     /** The default writer for diagnostics
   207      */
   208     static PrintWriter defaultWriter(Context context) {
   209         PrintWriter result = context.get(outKey);
   210         if (result == null)
   211             context.put(outKey, result = new PrintWriter(System.err));
   212         return result;
   213     }
   215     /** Construct a log with default settings.
   216      */
   217     protected Log(Context context) {
   218         this(context, defaultWriter(context));
   219     }
   221     /** Construct a log with all output redirected.
   222      */
   223     protected Log(Context context, PrintWriter defaultWriter) {
   224         this(context, defaultWriter, defaultWriter, defaultWriter);
   225     }
   227     /** Get the Log instance for this context. */
   228     public static Log instance(Context context) {
   229         Log instance = context.get(logKey);
   230         if (instance == null)
   231             instance = new Log(context);
   232         return instance;
   233     }
   235     /** The number of errors encountered so far.
   236      */
   237     public int nerrors = 0;
   239     /** The number of warnings encountered so far.
   240      */
   241     public int nwarnings = 0;
   243     /** A set of all errors generated so far. This is used to avoid printing an
   244      *  error message more than once. For each error, a pair consisting of the
   245      *  source file name and source code position of the error is added to the set.
   246      */
   247     private Set<Pair<JavaFileObject, Integer>> recorded = new HashSet<Pair<JavaFileObject,Integer>>();
   249     public boolean hasDiagnosticListener() {
   250         return diagListener != null;
   251     }
   253     public void setEndPosTable(JavaFileObject name, Map<JCTree, Integer> table) {
   254         name.getClass(); // null check
   255         getSource(name).setEndPosTable(table);
   256     }
   258     /** Return current sourcefile.
   259      */
   260     public JavaFileObject currentSourceFile() {
   261         return source == null ? null : source.getFile();
   262     }
   264     /** Get the current diagnostic formatter.
   265      */
   266     public DiagnosticFormatter<JCDiagnostic> getDiagnosticFormatter() {
   267         return diagFormatter;
   268     }
   270     /** Set the current diagnostic formatter.
   271      */
   272     public void setDiagnosticFormatter(DiagnosticFormatter<JCDiagnostic> diagFormatter) {
   273         this.diagFormatter = diagFormatter;
   274     }
   276     public PrintWriter getWriter(WriterKind kind) {
   277         switch (kind) {
   278             case NOTICE:    return noticeWriter;
   279             case WARNING:   return warnWriter;
   280             case ERROR:     return errWriter;
   281             default:        throw new IllegalArgumentException();
   282         }
   283     }
   285     public void setWriter(WriterKind kind, PrintWriter pw) {
   286         pw.getClass();
   287         switch (kind) {
   288             case NOTICE:    noticeWriter = pw;  break;
   289             case WARNING:   warnWriter = pw;    break;
   290             case ERROR:     errWriter = pw;     break;
   291             default:        throw new IllegalArgumentException();
   292         }
   293     }
   295     public void setWriters(PrintWriter pw) {
   296         pw.getClass();
   297         noticeWriter = warnWriter = errWriter = pw;
   298     }
   300     /** Flush the logs
   301      */
   302     public void flush() {
   303         errWriter.flush();
   304         warnWriter.flush();
   305         noticeWriter.flush();
   306     }
   308     public void flush(WriterKind kind) {
   309         getWriter(kind).flush();
   310     }
   312     /** Returns true if an error needs to be reported for a given
   313      * source name and pos.
   314      */
   315     protected boolean shouldReport(JavaFileObject file, int pos) {
   316         if (multipleErrors || file == null)
   317             return true;
   319         Pair<JavaFileObject,Integer> coords = new Pair<JavaFileObject,Integer>(file, pos);
   320         boolean shouldReport = !recorded.contains(coords);
   321         if (shouldReport)
   322             recorded.add(coords);
   323         return shouldReport;
   324     }
   326     /** Prompt user after an error.
   327      */
   328     public void prompt() {
   329         if (promptOnError) {
   330             System.err.println(localize("resume.abort"));
   331             try {
   332                 while (true) {
   333                     switch (System.in.read()) {
   334                     case 'a': case 'A':
   335                         System.exit(-1);
   336                         return;
   337                     case 'r': case 'R':
   338                         return;
   339                     case 'x': case 'X':
   340                         throw new AssertionError("user abort");
   341                     default:
   342                     }
   343                 }
   344             } catch (IOException e) {}
   345         }
   346     }
   348     /** Print the faulty source code line and point to the error.
   349      *  @param pos   Buffer index of the error position, must be on current line
   350      */
   351     private void printErrLine(int pos, PrintWriter writer) {
   352         String line = (source == null ? null : source.getLine(pos));
   353         if (line == null)
   354             return;
   355         int col = source.getColumnNumber(pos, false);
   357         printRawLines(writer, line);
   358         for (int i = 0; i < col - 1; i++) {
   359             writer.print((line.charAt(i) == '\t') ? "\t" : " ");
   360         }
   361         writer.println("^");
   362         writer.flush();
   363     }
   365     public void printNewline() {
   366         noticeWriter.println();
   367     }
   369     public void printNewline(WriterKind wk) {
   370         getWriter(wk).println();
   371     }
   373     public void printLines(String key, Object... args) {
   374         printRawLines(noticeWriter, localize(key, args));
   375     }
   377     public void printLines(PrefixKind pk, String key, Object... args) {
   378         printRawLines(noticeWriter, localize(pk, key, args));
   379     }
   381     public void printLines(WriterKind wk, String key, Object... args) {
   382         printRawLines(getWriter(wk), localize(key, args));
   383     }
   385     public void printLines(WriterKind wk, PrefixKind pk, String key, Object... args) {
   386         printRawLines(getWriter(wk), localize(pk, key, args));
   387     }
   389     /** Print the text of a message, translating newlines appropriately
   390      *  for the platform.
   391      */
   392     public void printRawLines(String msg) {
   393         printRawLines(noticeWriter, msg);
   394     }
   396     /** Print the text of a message, translating newlines appropriately
   397      *  for the platform.
   398      */
   399     public void printRawLines(WriterKind kind, String msg) {
   400         printRawLines(getWriter(kind), msg);
   401     }
   403     /** Print the text of a message, translating newlines appropriately
   404      *  for the platform.
   405      */
   406     public static void printRawLines(PrintWriter writer, String msg) {
   407         int nl;
   408         while ((nl = msg.indexOf('\n')) != -1) {
   409             writer.println(msg.substring(0, nl));
   410             msg = msg.substring(nl+1);
   411         }
   412         if (msg.length() != 0) writer.println(msg);
   413     }
   415     /**
   416      * Print the localized text of a "verbose" message to the
   417      * noticeWriter stream.
   418      */
   419     public void printVerbose(String key, Object... args) {
   420         printRawLines(noticeWriter, localize("verbose." + key, args));
   421     }
   423     protected void directError(String key, Object... args) {
   424         printRawLines(errWriter, localize(key, args));
   425         errWriter.flush();
   426     }
   428     /** Report a warning that cannot be suppressed.
   429      *  @param pos    The source position at which to report the warning.
   430      *  @param key    The key for the localized warning message.
   431      *  @param args   Fields of the warning message.
   432      */
   433     public void strictWarning(DiagnosticPosition pos, String key, Object ... args) {
   434         writeDiagnostic(diags.warning(source, pos, key, args));
   435         nwarnings++;
   436     }
   438     /** Report all deferred diagnostics, and clear the deferDiagnostics flag. */
   439     public void reportDeferredDiagnostics() {
   440         reportDeferredDiagnostics(EnumSet.allOf(JCDiagnostic.Kind.class));
   441     }
   443     /** Report selected deferred diagnostics, and clear the deferDiagnostics flag. */
   444     public void reportDeferredDiagnostics(Set<JCDiagnostic.Kind> kinds) {
   445         deferDiagnostics = false;
   446         JCDiagnostic d;
   447         while ((d = deferredDiagnostics.poll()) != null) {
   448             if (kinds.contains(d.getKind()))
   449                 report(d);
   450         }
   451     }
   453     /**
   454      * Common diagnostic handling.
   455      * The diagnostic is counted, and depending on the options and how many diagnostics have been
   456      * reported so far, the diagnostic may be handed off to writeDiagnostic.
   457      */
   458     public void report(JCDiagnostic diagnostic) {
   459         if (deferDiagnostics) {
   460             deferredDiagnostics.add(diagnostic);
   461             return;
   462         }
   464         if (expectDiagKeys != null)
   465             expectDiagKeys.remove(diagnostic.getCode());
   467         switch (diagnostic.getType()) {
   468         case FRAGMENT:
   469             throw new IllegalArgumentException();
   471         case NOTE:
   472             // Print out notes only when we are permitted to report warnings
   473             // Notes are only generated at the end of a compilation, so should be small
   474             // in number.
   475             if ((emitWarnings || diagnostic.isMandatory()) && !suppressNotes) {
   476                 writeDiagnostic(diagnostic);
   477             }
   478             break;
   480         case WARNING:
   481             if (emitWarnings || diagnostic.isMandatory()) {
   482                 if (nwarnings < MaxWarnings) {
   483                     writeDiagnostic(diagnostic);
   484                     nwarnings++;
   485                 }
   486             }
   487             break;
   489         case ERROR:
   490             if (nerrors < MaxErrors
   491                 && shouldReport(diagnostic.getSource(), diagnostic.getIntPosition())) {
   492                 writeDiagnostic(diagnostic);
   493                 nerrors++;
   494             }
   495             break;
   496         }
   497     }
   499     /**
   500      * Write out a diagnostic.
   501      */
   502     protected void writeDiagnostic(JCDiagnostic diag) {
   503         if (diagListener != null) {
   504             diagListener.report(diag);
   505             return;
   506         }
   508         PrintWriter writer = getWriterForDiagnosticType(diag.getType());
   510         printRawLines(writer, diagFormatter.format(diag, messages.getCurrentLocale()));
   512         if (promptOnError) {
   513             switch (diag.getType()) {
   514             case ERROR:
   515             case WARNING:
   516                 prompt();
   517             }
   518         }
   520         if (dumpOnError)
   521             new RuntimeException().printStackTrace(writer);
   523         writer.flush();
   524     }
   526     @Deprecated
   527     protected PrintWriter getWriterForDiagnosticType(DiagnosticType dt) {
   528         switch (dt) {
   529         case FRAGMENT:
   530             throw new IllegalArgumentException();
   532         case NOTE:
   533             return noticeWriter;
   535         case WARNING:
   536             return warnWriter;
   538         case ERROR:
   539             return errWriter;
   541         default:
   542             throw new Error();
   543         }
   544     }
   546     /** Find a localized string in the resource bundle.
   547      *  Because this method is static, it ignores the locale.
   548      *  Use localize(key, args) when possible.
   549      *  @param key    The key for the localized string.
   550      *  @param args   Fields to substitute into the string.
   551      */
   552     public static String getLocalizedString(String key, Object ... args) {
   553         return JavacMessages.getDefaultLocalizedString(PrefixKind.COMPILER_MISC.key(key), args);
   554     }
   556     /** Find a localized string in the resource bundle.
   557      *  @param key    The key for the localized string.
   558      *  @param args   Fields to substitute into the string.
   559      */
   560     public String localize(String key, Object... args) {
   561         return localize(PrefixKind.COMPILER_MISC, key, args);
   562     }
   564     /** Find a localized string in the resource bundle.
   565      *  @param key    The key for the localized string.
   566      *  @param args   Fields to substitute into the string.
   567      */
   568     public String localize(PrefixKind pk, String key, Object... args) {
   569         if (useRawMessages)
   570             return pk.key(key);
   571         else
   572             return messages.getLocalizedString(pk.key(key), args);
   573     }
   574     // where
   575         // backdoor hook for testing, should transition to use -XDrawDiagnostics
   576         private static boolean useRawMessages = false;
   578 /***************************************************************************
   579  * raw error messages without internationalization; used for experimentation
   580  * and quick prototyping
   581  ***************************************************************************/
   583     /** print an error or warning message:
   584      */
   585     private void printRawError(int pos, String msg) {
   586         if (source == null || pos == Position.NOPOS) {
   587             printRawLines(errWriter, "error: " + msg);
   588         } else {
   589             int line = source.getLineNumber(pos);
   590             JavaFileObject file = source.getFile();
   591             if (file != null)
   592                 printRawLines(errWriter,
   593                            file.getName() + ":" +
   594                            line + ": " + msg);
   595             printErrLine(pos, errWriter);
   596         }
   597         errWriter.flush();
   598     }
   600     /** report an error:
   601      */
   602     public void rawError(int pos, String msg) {
   603         if (nerrors < MaxErrors && shouldReport(currentSourceFile(), pos)) {
   604             printRawError(pos, msg);
   605             prompt();
   606             nerrors++;
   607         }
   608         errWriter.flush();
   609     }
   611     /** report a warning:
   612      */
   613     public void rawWarning(int pos, String msg) {
   614         if (nwarnings < MaxWarnings && emitWarnings) {
   615             printRawError(pos, "warning: " + msg);
   616         }
   617         prompt();
   618         nwarnings++;
   619         errWriter.flush();
   620     }
   622     public static String format(String fmt, Object... args) {
   623         return String.format((java.util.Locale)null, fmt, args);
   624     }
   626 }

mercurial