duke@1: /* xdono@54: * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javac.util; duke@1: duke@1: import java.io.*; duke@1: import java.util.HashSet; duke@1: import java.util.Map; duke@1: import java.util.Set; mcimadamore@83: import java.util.Locale; duke@1: import javax.tools.DiagnosticListener; duke@1: import javax.tools.JavaFileObject; jjg@50: jjg@50: import com.sun.tools.javac.file.JavacFileManager; duke@1: import com.sun.tools.javac.tree.JCTree; mcimadamore@83: import com.sun.tools.javac.api.DiagnosticFormatter; duke@1: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; duke@1: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; duke@1: duke@1: /** A class for error logs. Reports errors and warnings, and duke@1: * keeps track of error numbers and positions. duke@1: * duke@1: *

This is NOT part of any API supported by Sun Microsystems. If duke@1: * you write code that depends on this, you do so at your own risk. duke@1: * This code and its internal interfaces are subject to change or duke@1: * deletion without notice. duke@1: */ jjg@73: public class Log extends AbstractLog { duke@1: /** The context key for the log. */ duke@1: public static final Context.Key logKey duke@1: = new Context.Key(); duke@1: duke@1: /** The context key for the output PrintWriter. */ duke@1: public static final Context.Key outKey = duke@1: new Context.Key(); duke@1: duke@1: //@Deprecated duke@1: public final PrintWriter errWriter; duke@1: duke@1: //@Deprecated duke@1: public final PrintWriter warnWriter; duke@1: duke@1: //@Deprecated duke@1: public final PrintWriter noticeWriter; duke@1: duke@1: /** The maximum number of errors/warnings that are reported. duke@1: */ duke@1: public final int MaxErrors; duke@1: public final int MaxWarnings; duke@1: duke@1: /** Switch: prompt user on each error. duke@1: */ duke@1: public boolean promptOnError; duke@1: duke@1: /** Switch: emit warning messages. duke@1: */ duke@1: public boolean emitWarnings; duke@1: duke@1: /** Print stack trace on errors? duke@1: */ duke@1: public boolean dumpOnError; duke@1: duke@1: /** Print multiple errors for same source locations. duke@1: */ duke@1: public boolean multipleErrors; duke@1: duke@1: /** duke@1: * Diagnostic listener, if provided through programmatic duke@1: * interface to javac (JSR 199). duke@1: */ duke@1: protected DiagnosticListener diagListener; jjg@73: duke@1: /** duke@1: * Formatter for diagnostics duke@1: */ mcimadamore@83: private DiagnosticFormatter diagFormatter; duke@1: duke@1: /** Construct a log with given I/O redirections. duke@1: */ duke@1: @Deprecated duke@1: protected Log(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) { jjg@73: super(JCDiagnostic.Factory.instance(context)); duke@1: context.put(logKey, this); duke@1: this.errWriter = errWriter; duke@1: this.warnWriter = warnWriter; duke@1: this.noticeWriter = noticeWriter; duke@1: duke@1: Options options = Options.instance(context); duke@1: this.dumpOnError = options.get("-doe") != null; duke@1: this.promptOnError = options.get("-prompt") != null; duke@1: this.emitWarnings = options.get("-Xlint:none") == null; duke@1: this.MaxErrors = getIntOption(options, "-Xmaxerrs", 100); duke@1: this.MaxWarnings = getIntOption(options, "-Xmaxwarns", 100); duke@1: mcimadamore@83: boolean rawDiagnostics = options.get("rawDiagnostics") != null; mcimadamore@83: Messages msgs = Messages.instance(context); mcimadamore@83: this.diagFormatter = rawDiagnostics ? new RawDiagnosticFormatter(msgs) : mcimadamore@83: new BasicDiagnosticFormatter(options, msgs); duke@1: @SuppressWarnings("unchecked") // FIXME duke@1: DiagnosticListener diagListener = duke@1: context.get(DiagnosticListener.class); duke@1: this.diagListener = diagListener; duke@1: } duke@1: // where duke@1: private int getIntOption(Options options, String optionName, int defaultValue) { duke@1: String s = options.get(optionName); duke@1: try { duke@1: if (s != null) return Integer.parseInt(s); duke@1: } catch (NumberFormatException e) { duke@1: // silently ignore ill-formed numbers duke@1: } duke@1: return defaultValue; duke@1: } duke@1: duke@1: /** The default writer for diagnostics duke@1: */ duke@1: static final PrintWriter defaultWriter(Context context) { duke@1: PrintWriter result = context.get(outKey); duke@1: if (result == null) duke@1: context.put(outKey, result = new PrintWriter(System.err)); duke@1: return result; duke@1: } duke@1: duke@1: /** Construct a log with default settings. duke@1: */ duke@1: protected Log(Context context) { duke@1: this(context, defaultWriter(context)); duke@1: } duke@1: duke@1: /** Construct a log with all output redirected. duke@1: */ duke@1: protected Log(Context context, PrintWriter defaultWriter) { duke@1: this(context, defaultWriter, defaultWriter, defaultWriter); duke@1: } duke@1: duke@1: /** Get the Log instance for this context. */ duke@1: public static Log instance(Context context) { duke@1: Log instance = context.get(logKey); duke@1: if (instance == null) duke@1: instance = new Log(context); duke@1: return instance; duke@1: } duke@1: duke@1: /** The number of errors encountered so far. duke@1: */ duke@1: public int nerrors = 0; duke@1: duke@1: /** The number of warnings encountered so far. duke@1: */ duke@1: public int nwarnings = 0; duke@1: duke@1: /** A set of all errors generated so far. This is used to avoid printing an duke@1: * error message more than once. For each error, a pair consisting of the duke@1: * source file name and source code position of the error is added to the set. duke@1: */ duke@1: private Set> recorded = new HashSet>(); duke@1: duke@1: public boolean hasDiagnosticListener() { duke@1: return diagListener != null; duke@1: } duke@1: duke@1: public void setEndPosTable(JavaFileObject name, Map table) { jjg@73: name.getClass(); // null check jjg@73: getSource(name).setEndPosTable(table); duke@1: } duke@1: duke@1: /** Return current source name. duke@1: */ duke@1: public JavaFileObject currentSource() { duke@1: return source == null ? null : source.getFile(); duke@1: } duke@1: duke@1: /** Flush the logs duke@1: */ duke@1: public void flush() { duke@1: errWriter.flush(); duke@1: warnWriter.flush(); duke@1: noticeWriter.flush(); duke@1: } duke@1: duke@1: /** Returns true if an error needs to be reported for a given duke@1: * source name and pos. duke@1: */ duke@1: protected boolean shouldReport(JavaFileObject file, int pos) { duke@1: if (multipleErrors || file == null) duke@1: return true; duke@1: duke@1: Pair coords = new Pair(file, pos); duke@1: boolean shouldReport = !recorded.contains(coords); duke@1: if (shouldReport) duke@1: recorded.add(coords); duke@1: return shouldReport; duke@1: } duke@1: duke@1: /** Prompt user after an error. duke@1: */ duke@1: public void prompt() { duke@1: if (promptOnError) { duke@1: System.err.println(getLocalizedString("resume.abort")); duke@1: char ch; duke@1: try { duke@1: while (true) { duke@1: switch (System.in.read()) { duke@1: case 'a': case 'A': duke@1: System.exit(-1); duke@1: return; duke@1: case 'r': case 'R': duke@1: return; duke@1: case 'x': case 'X': duke@1: throw new AssertionError("user abort"); duke@1: default: duke@1: } duke@1: } duke@1: } catch (IOException e) {} duke@1: } duke@1: } duke@1: duke@1: /** Print the faulty source code line and point to the error. duke@1: * @param pos Buffer index of the error position, must be on current line duke@1: */ duke@1: private void printErrLine(int pos, PrintWriter writer) { jjg@73: String line = (source == null ? null : source.getLine(pos)); jjg@73: if (line == null) duke@1: return; jjg@73: int col = source.getColumnNumber(pos); duke@1: jjg@73: printLines(writer, line); jjg@73: for (int i = 0; i < col - 1; i++) { jjg@73: writer.print((line.charAt(i) == '\t') ? "\t" : " "); duke@1: } duke@1: writer.println("^"); duke@1: writer.flush(); duke@1: } duke@1: duke@1: /** Print the text of a message, translating newlines appropriately duke@1: * for the platform. duke@1: */ duke@1: public static void printLines(PrintWriter writer, String msg) { duke@1: int nl; duke@1: while ((nl = msg.indexOf('\n')) != -1) { duke@1: writer.println(msg.substring(0, nl)); duke@1: msg = msg.substring(nl+1); duke@1: } duke@1: if (msg.length() != 0) writer.println(msg); duke@1: } duke@1: jjg@73: protected void directError(String key, Object... args) { jjg@73: printLines(errWriter, getLocalizedString(key, args)); jjg@73: errWriter.flush(); duke@1: } duke@1: duke@1: /** Report a warning that cannot be suppressed. duke@1: * @param pos The source position at which to report the warning. duke@1: * @param key The key for the localized warning message. duke@1: * @param args Fields of the warning message. duke@1: */ duke@1: public void strictWarning(DiagnosticPosition pos, String key, Object ... args) { duke@1: writeDiagnostic(diags.warning(source, pos, key, args)); duke@1: nwarnings++; duke@1: } duke@1: duke@1: /** duke@1: * Common diagnostic handling. duke@1: * The diagnostic is counted, and depending on the options and how many diagnostics have been duke@1: * reported so far, the diagnostic may be handed off to writeDiagnostic. duke@1: */ duke@1: public void report(JCDiagnostic diagnostic) { duke@1: switch (diagnostic.getType()) { duke@1: case FRAGMENT: duke@1: throw new IllegalArgumentException(); duke@1: duke@1: case NOTE: duke@1: // Print out notes only when we are permitted to report warnings duke@1: // Notes are only generated at the end of a compilation, so should be small duke@1: // in number. duke@1: if (emitWarnings || diagnostic.isMandatory()) { duke@1: writeDiagnostic(diagnostic); duke@1: } duke@1: break; duke@1: duke@1: case WARNING: duke@1: if (emitWarnings || diagnostic.isMandatory()) { duke@1: if (nwarnings < MaxWarnings) { duke@1: writeDiagnostic(diagnostic); duke@1: nwarnings++; duke@1: } duke@1: } duke@1: break; duke@1: duke@1: case ERROR: duke@1: if (nerrors < MaxErrors duke@1: && shouldReport(diagnostic.getSource(), diagnostic.getIntPosition())) { duke@1: writeDiagnostic(diagnostic); duke@1: nerrors++; duke@1: } duke@1: break; duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Write out a diagnostic. duke@1: */ duke@1: protected void writeDiagnostic(JCDiagnostic diag) { duke@1: if (diagListener != null) { duke@1: try { duke@1: diagListener.report(diag); duke@1: return; duke@1: } duke@1: catch (Throwable t) { duke@1: throw new ClientCodeException(t); duke@1: } duke@1: } duke@1: duke@1: PrintWriter writer = getWriterForDiagnosticType(diag.getType()); duke@1: mcimadamore@83: printLines(writer, diagFormatter.format(diag, Locale.getDefault())); mcimadamore@83: if (diagFormatter.displaySource(diag)) { duke@1: int pos = diag.getIntPosition(); duke@1: if (pos != Position.NOPOS) { duke@1: JavaFileObject prev = useSource(diag.getSource()); duke@1: printErrLine(pos, writer); duke@1: useSource(prev); duke@1: } duke@1: } duke@1: duke@1: if (promptOnError) { duke@1: switch (diag.getType()) { duke@1: case ERROR: duke@1: case WARNING: duke@1: prompt(); duke@1: } duke@1: } duke@1: duke@1: if (dumpOnError) duke@1: new RuntimeException().printStackTrace(writer); duke@1: duke@1: writer.flush(); duke@1: } duke@1: duke@1: @Deprecated duke@1: protected PrintWriter getWriterForDiagnosticType(DiagnosticType dt) { duke@1: switch (dt) { duke@1: case FRAGMENT: duke@1: throw new IllegalArgumentException(); duke@1: duke@1: case NOTE: duke@1: return noticeWriter; duke@1: duke@1: case WARNING: duke@1: return warnWriter; duke@1: duke@1: case ERROR: duke@1: return errWriter; duke@1: duke@1: default: duke@1: throw new Error(); duke@1: } duke@1: } duke@1: duke@1: /** Find a localized string in the resource bundle. duke@1: * @param key The key for the localized string. duke@1: * @param args Fields to substitute into the string. duke@1: */ duke@1: public static String getLocalizedString(String key, Object ... args) { duke@1: return Messages.getDefaultLocalizedString("compiler.misc." + key, args); duke@1: } duke@1: duke@1: /*************************************************************************** duke@1: * raw error messages without internationalization; used for experimentation duke@1: * and quick prototyping duke@1: ***************************************************************************/ duke@1: jjg@73: /** print an error or warning message: jjg@73: */ duke@1: private void printRawError(int pos, String msg) { jjg@73: if (source == null || pos == Position.NOPOS) { duke@1: printLines(errWriter, "error: " + msg); duke@1: } else { jjg@73: int line = source.getLineNumber(pos); duke@1: JavaFileObject file = currentSource(); duke@1: if (file != null) duke@1: printLines(errWriter, duke@1: JavacFileManager.getJavacFileName(file) + ":" + duke@1: line + ": " + msg); duke@1: printErrLine(pos, errWriter); duke@1: } duke@1: errWriter.flush(); duke@1: } duke@1: jjg@73: /** report an error: jjg@73: */ duke@1: public void rawError(int pos, String msg) { duke@1: if (nerrors < MaxErrors && shouldReport(currentSource(), pos)) { duke@1: printRawError(pos, msg); duke@1: prompt(); duke@1: nerrors++; duke@1: } duke@1: errWriter.flush(); duke@1: } duke@1: jjg@73: /** report a warning: jjg@73: */ duke@1: public void rawWarning(int pos, String msg) { duke@1: if (nwarnings < MaxWarnings && emitWarnings) { duke@1: printRawError(pos, "warning: " + msg); duke@1: } duke@1: prompt(); duke@1: nwarnings++; duke@1: errWriter.flush(); duke@1: } duke@1: duke@1: public static String format(String fmt, Object... args) { duke@1: return String.format((java.util.Locale)null, fmt, args); duke@1: } duke@1: duke@1: }