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

Wed, 02 Jul 2008 12:56:02 -0700

author
xdono
date
Wed, 02 Jul 2008 12:56:02 -0700
changeset 54
eaf608c64fec
parent 50
b9bcea8bbe24
child 62
07c916ecfc71
permissions
-rw-r--r--

6719955: Update copyright year
Summary: Update copyright year for files that have been modified in 2008
Reviewed-by: ohair, tbell

     1 /*
     2  * Copyright 1999-2008 Sun Microsystems, Inc.  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.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.tools.javac.util;
    28 import java.io.*;
    29 import java.nio.CharBuffer;
    30 import java.util.HashMap;
    31 import java.util.HashSet;
    32 import java.util.Map;
    33 import java.util.Set;
    34 import javax.tools.DiagnosticListener;
    35 import javax.tools.JavaFileObject;
    37 import com.sun.tools.javac.code.Source;
    38 import com.sun.tools.javac.file.JavacFileManager;
    39 import com.sun.tools.javac.tree.JCTree;
    40 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
    41 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
    42 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
    44 import static com.sun.tools.javac.util.LayoutCharacters.*;
    46 /** A class for error logs. Reports errors and warnings, and
    47  *  keeps track of error numbers and positions.
    48  *
    49  *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
    50  *  you write code that depends on this, you do so at your own risk.
    51  *  This code and its internal interfaces are subject to change or
    52  *  deletion without notice.</b>
    53  */
    54 public class Log {
    55     /** The context key for the log. */
    56     public static final Context.Key<Log> logKey
    57         = new Context.Key<Log>();
    59     /** The context key for the output PrintWriter. */
    60     public static final Context.Key<PrintWriter> outKey =
    61         new Context.Key<PrintWriter>();
    63     //@Deprecated
    64     public final PrintWriter errWriter;
    66     //@Deprecated
    67     public final PrintWriter warnWriter;
    69     //@Deprecated
    70     public final PrintWriter noticeWriter;
    72     /** The maximum number of errors/warnings that are reported.
    73      */
    74     public final int MaxErrors;
    75     public final int MaxWarnings;
    77     /** Whether or not to display the line of source containing a diagnostic.
    78      */
    79     private final boolean showSourceLine;
    81     /** Switch: prompt user on each error.
    82      */
    83     public boolean promptOnError;
    85     /** Switch: emit warning messages.
    86      */
    87     public boolean emitWarnings;
    89     /** Enforce mandatory warnings.
    90      */
    91     private boolean enforceMandatoryWarnings;
    93     /** Print stack trace on errors?
    94      */
    95     public boolean dumpOnError;
    97     /** Print multiple errors for same source locations.
    98      */
    99     public boolean multipleErrors;
   101     /**
   102      * Diagnostic listener, if provided through programmatic
   103      * interface to javac (JSR 199).
   104      */
   105     protected DiagnosticListener<? super JavaFileObject> diagListener;
   106     /**
   107      * Formatter for diagnostics
   108      */
   109     private DiagnosticFormatter diagFormatter;
   111     /**
   112      * Factory for diagnostics
   113      */
   114     private JCDiagnostic.Factory diags;
   117     /** Construct a log with given I/O redirections.
   118      */
   119     @Deprecated
   120     protected Log(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) {
   121         context.put(logKey, this);
   122         this.errWriter = errWriter;
   123         this.warnWriter = warnWriter;
   124         this.noticeWriter = noticeWriter;
   126         this.diags = JCDiagnostic.Factory.instance(context);
   128         Options options = Options.instance(context);
   129         this.dumpOnError = options.get("-doe") != null;
   130         this.promptOnError = options.get("-prompt") != null;
   131         this.emitWarnings = options.get("-Xlint:none") == null;
   132         this.MaxErrors = getIntOption(options, "-Xmaxerrs", 100);
   133         this.MaxWarnings = getIntOption(options, "-Xmaxwarns", 100);
   134         this.showSourceLine = options.get("rawDiagnostics") == null;
   136         this.diagFormatter = DiagnosticFormatter.instance(context);
   137         @SuppressWarnings("unchecked") // FIXME
   138         DiagnosticListener<? super JavaFileObject> diagListener =
   139             context.get(DiagnosticListener.class);
   140         this.diagListener = diagListener;
   142         Source source = Source.instance(context);
   143         this.enforceMandatoryWarnings = source.enforceMandatoryWarnings();
   144     }
   145     // where
   146         private int getIntOption(Options options, String optionName, int defaultValue) {
   147             String s = options.get(optionName);
   148             try {
   149                 if (s != null) return Integer.parseInt(s);
   150             } catch (NumberFormatException e) {
   151                 // silently ignore ill-formed numbers
   152             }
   153             return defaultValue;
   154         }
   156     /** The default writer for diagnostics
   157      */
   158     static final PrintWriter defaultWriter(Context context) {
   159         PrintWriter result = context.get(outKey);
   160         if (result == null)
   161             context.put(outKey, result = new PrintWriter(System.err));
   162         return result;
   163     }
   165     /** Construct a log with default settings.
   166      */
   167     protected Log(Context context) {
   168         this(context, defaultWriter(context));
   169     }
   171     /** Construct a log with all output redirected.
   172      */
   173     protected Log(Context context, PrintWriter defaultWriter) {
   174         this(context, defaultWriter, defaultWriter, defaultWriter);
   175     }
   177     /** Get the Log instance for this context. */
   178     public static Log instance(Context context) {
   179         Log instance = context.get(logKey);
   180         if (instance == null)
   181             instance = new Log(context);
   182         return instance;
   183     }
   185     /** The file that's currently translated.
   186      */
   187     protected JCDiagnostic.DiagnosticSource source;
   189     /** The number of errors encountered so far.
   190      */
   191     public int nerrors = 0;
   193     /** The number of warnings encountered so far.
   194      */
   195     public int nwarnings = 0;
   197     /** A set of all errors generated so far. This is used to avoid printing an
   198      *  error message more than once. For each error, a pair consisting of the
   199      *  source file name and source code position of the error is added to the set.
   200      */
   201     private Set<Pair<JavaFileObject, Integer>> recorded = new HashSet<Pair<JavaFileObject,Integer>>();
   203     private Map<JavaFileObject, Map<JCTree, Integer>> endPosTables;
   205     /** The buffer containing the file that's currently translated.
   206      */
   207     private char[] buf = null;
   209     /** The length of useful data in buf
   210      */
   211     private int bufLen = 0;
   213     /** The position in the buffer at which last error was reported
   214      */
   215     private int bp;
   217     /** number of the current source line; first line is 1
   218      */
   219     private int line;
   221     /**  buffer index of the first character of the current source line
   222      */
   223     private int lineStart;
   225     public boolean hasDiagnosticListener() {
   226         return diagListener != null;
   227     }
   229     public void setEndPosTable(JavaFileObject name, Map<JCTree, Integer> table) {
   230         if (endPosTables == null)
   231             endPosTables = new HashMap<JavaFileObject, Map<JCTree, Integer>>();
   232         endPosTables.put(name, table);
   233     }
   235     /** Re-assign source, returning previous setting.
   236      */
   237     public JavaFileObject useSource(final JavaFileObject name) {
   238         JavaFileObject prev = currentSource();
   239         if (name != prev) {
   240             source = new JCDiagnostic.DiagnosticSource() {
   241                     public JavaFileObject getFile() {
   242                         return name;
   243                     }
   244                     public CharSequence getName() {
   245                         return JavacFileManager.getJavacBaseFileName(getFile());
   246                     }
   247                     public int getLineNumber(int pos) {
   248                         return Log.this.getLineNumber(pos);
   249                     }
   250                     public int getColumnNumber(int pos) {
   251                         return Log.this.getColumnNumber(pos);
   252                     }
   253                     public Map<JCTree, Integer> getEndPosTable() {
   254                         return (endPosTables == null ? null : endPosTables.get(name));
   255                     }
   256                 };
   257             buf = null;
   258         }
   259         return prev;
   260     }
   262     /** Re-assign source buffer for existing source name.
   263      */
   264     protected void setBuf(char[] newBuf) {
   265         buf = newBuf;
   266         bufLen = buf.length;
   267         bp = 0;
   268         lineStart = 0;
   269         line = 1;
   270     }
   272     protected char[] getBuf() {
   273         return buf;
   274     }
   276     /** Return current source name.
   277      */
   278     public JavaFileObject currentSource() {
   279         return source == null ? null : source.getFile();
   280     }
   282     /** Flush the logs
   283      */
   284     public void flush() {
   285         errWriter.flush();
   286         warnWriter.flush();
   287         noticeWriter.flush();
   288     }
   290     /** Returns true if an error needs to be reported for a given
   291      * source name and pos.
   292      */
   293     protected boolean shouldReport(JavaFileObject file, int pos) {
   294         if (multipleErrors || file == null)
   295             return true;
   297         Pair<JavaFileObject,Integer> coords = new Pair<JavaFileObject,Integer>(file, pos);
   298         boolean shouldReport = !recorded.contains(coords);
   299         if (shouldReport)
   300             recorded.add(coords);
   301         return shouldReport;
   302     }
   304     /** Prompt user after an error.
   305      */
   306     public void prompt() {
   307         if (promptOnError) {
   308             System.err.println(getLocalizedString("resume.abort"));
   309             char ch;
   310             try {
   311                 while (true) {
   312                     switch (System.in.read()) {
   313                     case 'a': case 'A':
   314                         System.exit(-1);
   315                         return;
   316                     case 'r': case 'R':
   317                         return;
   318                     case 'x': case 'X':
   319                         throw new AssertionError("user abort");
   320                     default:
   321                     }
   322                 }
   323             } catch (IOException e) {}
   324         }
   325     }
   327     /** Print the faulty source code line and point to the error.
   328      *  @param pos   Buffer index of the error position, must be on current line
   329      */
   330     private void printErrLine(int pos, PrintWriter writer) {
   331         if (!findLine(pos))
   332             return;
   334         int lineEnd = lineStart;
   335         while (lineEnd < bufLen && buf[lineEnd] != CR && buf[lineEnd] != LF)
   336             lineEnd++;
   337         if (lineEnd - lineStart == 0)
   338             return;
   339         printLines(writer, new String(buf, lineStart, lineEnd - lineStart));
   340         for (bp = lineStart; bp < pos; bp++) {
   341             writer.print((buf[bp] == '\t') ? "\t" : " ");
   342         }
   343         writer.println("^");
   344         writer.flush();
   345     }
   347     protected void initBuf(JavaFileObject fileObject) throws IOException {
   348         CharSequence cs = fileObject.getCharContent(true);
   349         if (cs instanceof CharBuffer) {
   350             CharBuffer cb = (CharBuffer) cs;
   351             buf = JavacFileManager.toArray(cb);
   352             bufLen = cb.limit();
   353         } else {
   354             buf = cs.toString().toCharArray();
   355             bufLen = buf.length;
   356         }
   357     }
   359     /** Find the line in the buffer that contains the current position
   360      * @param pos      Character offset into the buffer
   361      */
   362     private boolean findLine(int pos) {
   363         if (pos == Position.NOPOS || currentSource() == null)
   364             return false;
   365         try {
   366             if (buf == null) {
   367                 initBuf(currentSource());
   368                 lineStart = 0;
   369                 line = 1;
   370             } else if (lineStart > pos) { // messages don't come in order
   371                 lineStart = 0;
   372                 line = 1;
   373             }
   374             bp = lineStart;
   375             while (bp < bufLen && bp < pos) {
   376                 switch (buf[bp++]) {
   377                 case CR:
   378                     if (bp < bufLen && buf[bp] == LF) bp++;
   379                     line++;
   380                     lineStart = bp;
   381                     break;
   382                 case LF:
   383                     line++;
   384                     lineStart = bp;
   385                     break;
   386                 }
   387             }
   388             return bp <= bufLen;
   389         } catch (IOException e) {
   390             //e.printStackTrace();
   391             // FIXME: include e.getLocalizedMessage() in error message
   392             printLines(errWriter, getLocalizedString("source.unavailable"));
   393             errWriter.flush();
   394             buf = new char[0];
   395         }
   396         return false;
   397     }
   399     /** Print the text of a message, translating newlines appropriately
   400      *  for the platform.
   401      */
   402     public static void printLines(PrintWriter writer, String msg) {
   403         int nl;
   404         while ((nl = msg.indexOf('\n')) != -1) {
   405             writer.println(msg.substring(0, nl));
   406             msg = msg.substring(nl+1);
   407         }
   408         if (msg.length() != 0) writer.println(msg);
   409     }
   411     /** Report an error, unless another error was already reported at same
   412      *  source position.
   413      *  @param key    The key for the localized error message.
   414      *  @param args   Fields of the error message.
   415      */
   416     public void error(String key, Object ... args) {
   417         report(diags.error(source, null, key, args));
   418     }
   420     /** Report an error, unless another error was already reported at same
   421      *  source position.
   422      *  @param pos    The source position at which to report the error.
   423      *  @param key    The key for the localized error message.
   424      *  @param args   Fields of the error message.
   425      */
   426     public void error(DiagnosticPosition pos, String key, Object ... args) {
   427         report(diags.error(source, pos, key, args));
   428     }
   430     /** Report an error, unless another error was already reported at same
   431      *  source position.
   432      *  @param pos    The source position at which to report the error.
   433      *  @param key    The key for the localized error message.
   434      *  @param args   Fields of the error message.
   435      */
   436     public void error(int pos, String key, Object ... args) {
   437         report(diags.error(source, wrap(pos), key, args));
   438     }
   440     /** Report a warning, unless suppressed by the  -nowarn option or the
   441      *  maximum number of warnings has been reached.
   442      *  @param pos    The source position at which to report the warning.
   443      *  @param key    The key for the localized warning message.
   444      *  @param args   Fields of the warning message.
   445      */
   446     public void warning(String key, Object ... args) {
   447         report(diags.warning(source, null, key, args));
   448     }
   450     /** Report a warning, unless suppressed by the  -nowarn option or the
   451      *  maximum number of warnings has been reached.
   452      *  @param pos    The source position at which to report the warning.
   453      *  @param key    The key for the localized warning message.
   454      *  @param args   Fields of the warning message.
   455      */
   456     public void warning(DiagnosticPosition pos, String key, Object ... args) {
   457         report(diags.warning(source, pos, key, args));
   458     }
   460     /** Report a warning, unless suppressed by the  -nowarn option or the
   461      *  maximum number of warnings has been reached.
   462      *  @param pos    The source position at which to report the warning.
   463      *  @param key    The key for the localized warning message.
   464      *  @param args   Fields of the warning message.
   465      */
   466     public void warning(int pos, String key, Object ... args) {
   467         report(diags.warning(source, wrap(pos), key, args));
   468     }
   470     /** Report a warning.
   471      *  @param pos    The source position at which to report the warning.
   472      *  @param key    The key for the localized warning message.
   473      *  @param args   Fields of the warning message.
   474      */
   475     public void mandatoryWarning(DiagnosticPosition pos, String key, Object ... args) {
   476         if (enforceMandatoryWarnings)
   477             report(diags.mandatoryWarning(source, pos, key, args));
   478         else
   479             report(diags.warning(source, pos, key, args));
   480     }
   482     /** Report a warning that cannot be suppressed.
   483      *  @param pos    The source position at which to report the warning.
   484      *  @param key    The key for the localized warning message.
   485      *  @param args   Fields of the warning message.
   486      */
   487     public void strictWarning(DiagnosticPosition pos, String key, Object ... args) {
   488         writeDiagnostic(diags.warning(source, pos, key, args));
   489         nwarnings++;
   490     }
   492     /** Provide a non-fatal notification, unless suppressed by the -nowarn option.
   493      *  @param key    The key for the localized notification message.
   494      *  @param args   Fields of the notification message.
   495      */
   496     public void note(String key, Object ... args) {
   497         report(diags.note(source, null, key, args));
   498     }
   500     /** Provide a non-fatal notification, unless suppressed by the -nowarn option.
   501      *  @param key    The key for the localized notification message.
   502      *  @param args   Fields of the notification message.
   503      */
   504     public void note(DiagnosticPosition pos, String key, Object ... args) {
   505         report(diags.note(source, pos, key, args));
   506     }
   508     /** Provide a non-fatal notification, unless suppressed by the -nowarn option.
   509      *  @param key    The key for the localized notification message.
   510      *  @param args   Fields of the notification message.
   511      */
   512     public void note(int pos, String key, Object ... args) {
   513         report(diags.note(source, wrap(pos), key, args));
   514     }
   516     /** Provide a non-fatal notification, unless suppressed by the -nowarn option.
   517      *  @param key    The key for the localized notification message.
   518      *  @param args   Fields of the notification message.
   519      */
   520     public void mandatoryNote(final JavaFileObject file, String key, Object ... args) {
   521         JCDiagnostic.DiagnosticSource wrapper = null;
   522         if (file != null) {
   523             wrapper = new JCDiagnostic.DiagnosticSource() {
   524                     public JavaFileObject getFile() {
   525                         return file;
   526                     }
   527                     public CharSequence getName() {
   528                         return JavacFileManager.getJavacBaseFileName(getFile());
   529                     }
   530                     public int getLineNumber(int pos) {
   531                         return Log.this.getLineNumber(pos);
   532                     }
   533                     public int getColumnNumber(int pos) {
   534                         return Log.this.getColumnNumber(pos);
   535                     }
   536                     public Map<JCTree, Integer> getEndPosTable() {
   537                         return (endPosTables == null ? null : endPosTables.get(file));
   538                     }
   539                 };
   540         }
   541         if (enforceMandatoryWarnings)
   542             report(diags.mandatoryNote(wrapper, key, args));
   543         else
   544             report(diags.note(wrapper, null, key, args));
   545     }
   547     private DiagnosticPosition wrap(int pos) {
   548         return (pos == Position.NOPOS ? null : new SimpleDiagnosticPosition(pos));
   549     }
   551     /**
   552      * Common diagnostic handling.
   553      * The diagnostic is counted, and depending on the options and how many diagnostics have been
   554      * reported so far, the diagnostic may be handed off to writeDiagnostic.
   555      */
   556     public void report(JCDiagnostic diagnostic) {
   557         switch (diagnostic.getType()) {
   558         case FRAGMENT:
   559             throw new IllegalArgumentException();
   561         case NOTE:
   562             // Print out notes only when we are permitted to report warnings
   563             // Notes are only generated at the end of a compilation, so should be small
   564             // in number.
   565             if (emitWarnings || diagnostic.isMandatory()) {
   566                 writeDiagnostic(diagnostic);
   567             }
   568             break;
   570         case WARNING:
   571             if (emitWarnings || diagnostic.isMandatory()) {
   572                 if (nwarnings < MaxWarnings) {
   573                     writeDiagnostic(diagnostic);
   574                     nwarnings++;
   575                 }
   576             }
   577             break;
   579         case ERROR:
   580             if (nerrors < MaxErrors
   581                 && shouldReport(diagnostic.getSource(), diagnostic.getIntPosition())) {
   582                 writeDiagnostic(diagnostic);
   583                 nerrors++;
   584             }
   585             break;
   586         }
   587     }
   589     /**
   590      * Write out a diagnostic.
   591      */
   592     protected void writeDiagnostic(JCDiagnostic diag) {
   593         if (diagListener != null) {
   594             try {
   595                 diagListener.report(diag);
   596                 return;
   597             }
   598             catch (Throwable t) {
   599                 throw new ClientCodeException(t);
   600             }
   601         }
   603         PrintWriter writer = getWriterForDiagnosticType(diag.getType());
   605         printLines(writer, diagFormatter.format(diag));
   606         if (showSourceLine) {
   607             int pos = diag.getIntPosition();
   608             if (pos != Position.NOPOS) {
   609                 JavaFileObject prev = useSource(diag.getSource());
   610                 printErrLine(pos, writer);
   611                 useSource(prev);
   612             }
   613         }
   615         if (promptOnError) {
   616             switch (diag.getType()) {
   617             case ERROR:
   618             case WARNING:
   619                 prompt();
   620             }
   621         }
   623         if (dumpOnError)
   624             new RuntimeException().printStackTrace(writer);
   626         writer.flush();
   627     }
   629     @Deprecated
   630     protected PrintWriter getWriterForDiagnosticType(DiagnosticType dt) {
   631         switch (dt) {
   632         case FRAGMENT:
   633             throw new IllegalArgumentException();
   635         case NOTE:
   636             return noticeWriter;
   638         case WARNING:
   639             return warnWriter;
   641         case ERROR:
   642             return errWriter;
   644         default:
   645             throw new Error();
   646         }
   647     }
   649     /** Find a localized string in the resource bundle.
   650      *  @param key    The key for the localized string.
   651      *  @param args   Fields to substitute into the string.
   652      */
   653     public static String getLocalizedString(String key, Object ... args) {
   654         return Messages.getDefaultLocalizedString("compiler.misc." + key, args);
   655     }
   657 /***************************************************************************
   658  * raw error messages without internationalization; used for experimentation
   659  * and quick prototyping
   660  ***************************************************************************/
   662 /** print an error or warning message:
   663  */
   664     private void printRawError(int pos, String msg) {
   665         if (!findLine(pos)) {
   666             printLines(errWriter, "error: " + msg);
   667         } else {
   668             JavaFileObject file = currentSource();
   669             if (file != null)
   670                 printLines(errWriter,
   671                            JavacFileManager.getJavacFileName(file) + ":" +
   672                            line + ": " + msg);
   673             printErrLine(pos, errWriter);
   674         }
   675         errWriter.flush();
   676     }
   678 /** report an error:
   679  */
   680     public void rawError(int pos, String msg) {
   681         if (nerrors < MaxErrors && shouldReport(currentSource(), pos)) {
   682             printRawError(pos, msg);
   683             prompt();
   684             nerrors++;
   685         }
   686         errWriter.flush();
   687     }
   689 /** report a warning:
   690  */
   691     public void rawWarning(int pos, String msg) {
   692         if (nwarnings < MaxWarnings && emitWarnings) {
   693             printRawError(pos, "warning: " + msg);
   694         }
   695         prompt();
   696         nwarnings++;
   697         errWriter.flush();
   698     }
   700     /** Return the one-based line number associated with a given pos
   701      * for the current source file.  Zero is returned if no line exists
   702      * for the given position.
   703      */
   704     protected int getLineNumber(int pos) {
   705         if (findLine(pos))
   706             return line;
   707         return 0;
   708     }
   710     /** Return the one-based column number associated with a given pos
   711      * for the current source file.  Zero is returned if no column exists
   712      * for the given position.
   713      */
   714     protected int getColumnNumber(int pos) {
   715         if (findLine(pos)) {
   716             int column = 0;
   717             for (bp = lineStart; bp < pos; bp++) {
   718                 if (bp >= bufLen)
   719                     return 0;
   720                 if (buf[bp] == '\t')
   721                     column = (column / TabInc * TabInc) + TabInc;
   722                 else
   723                     column++;
   724             }
   725             return column + 1; // positions are one-based
   726         }
   727         return 0;
   728     }
   730     public static String format(String fmt, Object... args) {
   731         return String.format((java.util.Locale)null, fmt, args);
   732     }
   734 }

mercurial