mcimadamore@83: /* ohair@798: * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. mcimadamore@83: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. mcimadamore@83: * mcimadamore@83: * This code is free software; you can redistribute it and/or modify it mcimadamore@83: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this mcimadamore@83: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. mcimadamore@83: * mcimadamore@83: * This code is distributed in the hope that it will be useful, but WITHOUT mcimadamore@83: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or mcimadamore@83: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License mcimadamore@83: * version 2 for more details (a copy is included in the LICENSE file that mcimadamore@83: * accompanied this code). mcimadamore@83: * mcimadamore@83: * You should have received a copy of the GNU General Public License version mcimadamore@83: * 2 along with this work; if not, write to the Free Software Foundation, mcimadamore@83: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. mcimadamore@83: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. mcimadamore@83: */ mcimadamore@83: mcimadamore@83: package com.sun.tools.javac.util; mcimadamore@83: mcimadamore@221: import java.util.Collection; mcimadamore@221: import java.util.EnumSet; mcimadamore@83: import java.util.HashMap; mcimadamore@83: import java.util.Locale; mcimadamore@83: import java.util.Map; mcimadamore@221: import java.util.regex.Matcher; mcimadamore@83: import javax.tools.JavaFileObject; mcimadamore@83: mcimadamore@221: import com.sun.tools.javac.util.AbstractDiagnosticFormatter.SimpleConfiguration; mcimadamore@221: import com.sun.tools.javac.util.BasicDiagnosticFormatter.BasicConfiguration; mcimadamore@221: mcimadamore@83: import static com.sun.tools.javac.api.DiagnosticFormatter.PositionKind.*; mcimadamore@221: import static com.sun.tools.javac.util.BasicDiagnosticFormatter.BasicConfiguration.*; mcimadamore@221: import static com.sun.tools.javac.util.LayoutCharacters.*; mcimadamore@83: mcimadamore@83: /** mcimadamore@83: * A basic formatter for diagnostic messages. mcimadamore@83: * The basic formatter will format a diagnostic according to one of three format patterns, depending on whether mcimadamore@83: * or not the source name and position are set. The formatter supports a printf-like string for patterns mcimadamore@83: * with the following special characters: mcimadamore@83: * jjg@333: * jjg@581: *

This is NOT part of any supported API. jjg@333: * If you write code that depends on this, you do so at your own risk. jjg@333: * This code and its internal interfaces are subject to change or jjg@333: * deletion without notice. mcimadamore@83: */ mcimadamore@83: public class BasicDiagnosticFormatter extends AbstractDiagnosticFormatter { mcimadamore@83: mcimadamore@83: /** mcimadamore@83: * Create a basic formatter based on the supplied options. mcimadamore@83: * mcimadamore@83: * @param opts list of command-line options mcimadamore@136: * @param msgs JavacMessages object used for i18n mcimadamore@83: */ mcimadamore@221: public BasicDiagnosticFormatter(Options options, JavacMessages msgs) { mcimadamore@221: super(msgs, new BasicConfiguration(options)); mcimadamore@83: } mcimadamore@83: mcimadamore@83: /** mcimadamore@83: * Create a standard basic formatter mcimadamore@83: * mcimadamore@136: * @param msgs JavacMessages object used for i18n mcimadamore@83: */ mcimadamore@136: public BasicDiagnosticFormatter(JavacMessages msgs) { mcimadamore@221: super(msgs, new BasicConfiguration()); mcimadamore@83: } mcimadamore@83: mcimadamore@238: public String formatDiagnostic(JCDiagnostic d, Locale l) { mcimadamore@136: if (l == null) mcimadamore@136: l = messages.getCurrentLocale(); mcimadamore@83: String format = selectFormat(d); mcimadamore@83: StringBuilder buf = new StringBuilder(); mcimadamore@83: for (int i = 0; i < format.length(); i++) { mcimadamore@83: char c = format.charAt(i); mcimadamore@83: boolean meta = false; mcimadamore@83: if (c == '%' && i < format.length() - 1) { mcimadamore@83: meta = true; mcimadamore@83: c = format.charAt(++i); mcimadamore@83: } mcimadamore@83: buf.append(meta ? formatMeta(c, d, l) : String.valueOf(c)); mcimadamore@83: } mcimadamore@221: if (depth == 0) mcimadamore@221: return addSourceLineIfNeeded(d, buf.toString()); mcimadamore@221: else mcimadamore@221: return buf.toString(); mcimadamore@221: } mcimadamore@221: mcimadamore@221: public String formatMessage(JCDiagnostic d, Locale l) { mcimadamore@689: int currentIndentation = 0; mcimadamore@689: StringBuilder buf = new StringBuilder(); mcimadamore@689: Collection args = formatArguments(d, l); mcimadamore@689: String msg = localize(l, d.getCode(), args.toArray()); mcimadamore@689: String[] lines = msg.split("\n"); mcimadamore@689: if (getConfiguration().getVisible().contains(DiagnosticPart.SUMMARY)) { mcimadamore@689: currentIndentation += getConfiguration().getIndentation(DiagnosticPart.SUMMARY); mcimadamore@689: buf.append(indent(lines[0], currentIndentation)); //summary mcimadamore@689: } mcimadamore@689: if (lines.length > 1 && getConfiguration().getVisible().contains(DiagnosticPart.DETAILS)) { mcimadamore@689: currentIndentation += getConfiguration().getIndentation(DiagnosticPart.DETAILS); mcimadamore@689: for (int i = 1;i < lines.length; i++) { mcimadamore@689: buf.append("\n" + indent(lines[i], currentIndentation)); mcimadamore@221: } mcimadamore@689: } mcimadamore@689: if (d.isMultiline() && getConfiguration().getVisible().contains(DiagnosticPart.SUBDIAGNOSTICS)) { mcimadamore@689: currentIndentation += getConfiguration().getIndentation(DiagnosticPart.SUBDIAGNOSTICS); mcimadamore@689: for (String sub : formatSubdiagnostics(d, l)) { mcimadamore@689: buf.append("\n" + indent(sub, currentIndentation)); mcimadamore@221: } mcimadamore@137: } mcimadamore@689: return buf.toString(); mcimadamore@221: } mcimadamore@221: mcimadamore@221: protected String addSourceLineIfNeeded(JCDiagnostic d, String msg) { mcimadamore@221: if (!displaySource(d)) mcimadamore@221: return msg; mcimadamore@221: else { mcimadamore@221: BasicConfiguration conf = getConfiguration(); mcimadamore@221: int indentSource = conf.getIndentation(DiagnosticPart.SOURCE); mcimadamore@221: String sourceLine = "\n" + formatSourceLine(d, indentSource); mcimadamore@221: boolean singleLine = msg.indexOf("\n") == -1; mcimadamore@221: if (singleLine || getConfiguration().getSourcePosition() == SourcePosition.BOTTOM) mcimadamore@221: return msg + sourceLine; mcimadamore@221: else mcimadamore@221: return msg.replaceFirst("\n", Matcher.quoteReplacement(sourceLine) + "\n"); mcimadamore@221: } mcimadamore@83: } mcimadamore@83: mcimadamore@83: protected String formatMeta(char c, JCDiagnostic d, Locale l) { mcimadamore@83: switch (c) { mcimadamore@83: case 'b': mcimadamore@100: return formatSource(d, false, l); mcimadamore@83: case 'e': mcimadamore@83: return formatPosition(d, END, l); mcimadamore@83: case 'f': mcimadamore@100: return formatSource(d, true, l); mcimadamore@83: case 'l': mcimadamore@83: return formatPosition(d, LINE, l); mcimadamore@83: case 'c': mcimadamore@83: return formatPosition(d, COLUMN, l); mcimadamore@83: case 'o': mcimadamore@83: return formatPosition(d, OFFSET, l); mcimadamore@83: case 'p': mcimadamore@83: return formatKind(d, l); mcimadamore@83: case 's': mcimadamore@83: return formatPosition(d, START, l); mcimadamore@83: case 't': { mcimadamore@83: boolean usePrefix; mcimadamore@83: switch (d.getType()) { mcimadamore@83: case FRAGMENT: mcimadamore@83: usePrefix = false; mcimadamore@83: break; mcimadamore@83: case ERROR: mcimadamore@83: usePrefix = (d.getIntPosition() == Position.NOPOS); mcimadamore@83: break; mcimadamore@83: default: mcimadamore@83: usePrefix = true; mcimadamore@83: } mcimadamore@83: if (usePrefix) mcimadamore@83: return formatKind(d, l); mcimadamore@83: else mcimadamore@83: return ""; mcimadamore@83: } mcimadamore@83: case 'm': mcimadamore@83: return formatMessage(d, l); jjg@612: case 'L': jjg@612: return formatLintCategory(d, l); mcimadamore@83: case '_': mcimadamore@83: return " "; mcimadamore@83: case '%': mcimadamore@83: return "%"; mcimadamore@83: default: mcimadamore@83: return String.valueOf(c); mcimadamore@83: } mcimadamore@83: } mcimadamore@83: mcimadamore@83: private String selectFormat(JCDiagnostic d) { mcimadamore@83: DiagnosticSource source = d.getDiagnosticSource(); mcimadamore@221: String format = getConfiguration().getFormat(BasicFormatKind.DEFAULT_NO_POS_FORMAT); jjg@519: if (source != null && source != DiagnosticSource.NO_SOURCE) { mcimadamore@83: if (d.getIntPosition() != Position.NOPOS) { mcimadamore@221: format = getConfiguration().getFormat(BasicFormatKind.DEFAULT_POS_FORMAT); mcimadamore@83: } else if (source.getFile() != null && mcimadamore@83: source.getFile().getKind() == JavaFileObject.Kind.CLASS) { mcimadamore@221: format = getConfiguration().getFormat(BasicFormatKind.DEFAULT_CLASS_FORMAT); mcimadamore@83: } mcimadamore@83: } mcimadamore@83: return format; mcimadamore@83: } mcimadamore@83: mcimadamore@221: @Override mcimadamore@221: public BasicConfiguration getConfiguration() { mcimadamore@288: //the following cast is always safe - see init mcimadamore@221: return (BasicConfiguration)super.getConfiguration(); mcimadamore@221: } mcimadamore@221: mcimadamore@221: static public class BasicConfiguration extends SimpleConfiguration { mcimadamore@221: mcimadamore@221: protected Map indentationLevels; mcimadamore@221: protected Map availableFormats; mcimadamore@221: protected SourcePosition sourcePosition; mcimadamore@221: mcimadamore@221: @SuppressWarnings("fallthrough") mcimadamore@221: public BasicConfiguration(Options options) { mcimadamore@221: super(options, EnumSet.of(DiagnosticPart.SUMMARY, mcimadamore@221: DiagnosticPart.DETAILS, mcimadamore@221: DiagnosticPart.SUBDIAGNOSTICS, mcimadamore@221: DiagnosticPart.SOURCE)); mcimadamore@221: initFormat(); mcimadamore@221: initIndentation(); mcimadamore@221: String fmt = options.get("diagsFormat"); mcimadamore@221: if (fmt != null) { mcimadamore@221: String[] formats = fmt.split("\\|"); mcimadamore@221: switch (formats.length) { mcimadamore@221: case 3: mcimadamore@221: setFormat(BasicFormatKind.DEFAULT_CLASS_FORMAT, formats[2]); mcimadamore@221: case 2: mcimadamore@221: setFormat(BasicFormatKind.DEFAULT_NO_POS_FORMAT, formats[1]); mcimadamore@221: default: mcimadamore@221: setFormat(BasicFormatKind.DEFAULT_POS_FORMAT, formats[0]); mcimadamore@221: } mcimadamore@221: } jjg@612: String srcPos = null; jjg@612: if ((((srcPos = options.get("sourcePosition")) != null)) && jjg@612: srcPos.equals("bottom")) mcimadamore@221: setSourcePosition(SourcePosition.BOTTOM); mcimadamore@221: else mcimadamore@221: setSourcePosition(SourcePosition.AFTER_SUMMARY); mcimadamore@221: String indent = options.get("diagsIndentation"); mcimadamore@221: if (indent != null) { mcimadamore@221: String[] levels = indent.split("\\|"); mcimadamore@221: try { mcimadamore@221: switch (levels.length) { mcimadamore@221: case 5: mcimadamore@221: setIndentation(DiagnosticPart.JLS, mcimadamore@221: Integer.parseInt(levels[4])); mcimadamore@221: case 4: mcimadamore@221: setIndentation(DiagnosticPart.SUBDIAGNOSTICS, mcimadamore@221: Integer.parseInt(levels[3])); mcimadamore@221: case 3: mcimadamore@221: setIndentation(DiagnosticPart.SOURCE, mcimadamore@221: Integer.parseInt(levels[2])); mcimadamore@221: case 2: mcimadamore@221: setIndentation(DiagnosticPart.DETAILS, mcimadamore@221: Integer.parseInt(levels[1])); mcimadamore@221: default: mcimadamore@221: setIndentation(DiagnosticPart.SUMMARY, mcimadamore@221: Integer.parseInt(levels[0])); mcimadamore@221: } mcimadamore@221: } mcimadamore@221: catch (NumberFormatException ex) { mcimadamore@221: initIndentation(); mcimadamore@221: } mcimadamore@221: } mcimadamore@221: } mcimadamore@221: mcimadamore@221: public BasicConfiguration() { mcimadamore@221: super(EnumSet.of(DiagnosticPart.SUMMARY, mcimadamore@221: DiagnosticPart.DETAILS, mcimadamore@221: DiagnosticPart.SUBDIAGNOSTICS, mcimadamore@221: DiagnosticPart.SOURCE)); mcimadamore@221: initFormat(); mcimadamore@221: initIndentation(); mcimadamore@221: } mcimadamore@221: //where mcimadamore@221: private void initFormat() { mcimadamore@221: availableFormats = new HashMap(); jjg@612: setFormat(BasicFormatKind.DEFAULT_POS_FORMAT, "%f:%l:%_%t%L%m"); jjg@612: setFormat(BasicFormatKind.DEFAULT_NO_POS_FORMAT, "%p%L%m"); jjg@612: setFormat(BasicFormatKind.DEFAULT_CLASS_FORMAT, "%f:%_%t%L%m"); mcimadamore@221: } mcimadamore@221: //where mcimadamore@221: private void initIndentation() { mcimadamore@221: indentationLevels = new HashMap(); mcimadamore@221: setIndentation(DiagnosticPart.SUMMARY, 0); mcimadamore@221: setIndentation(DiagnosticPart.DETAILS, DetailsInc); mcimadamore@221: setIndentation(DiagnosticPart.SUBDIAGNOSTICS, DiagInc); mcimadamore@221: setIndentation(DiagnosticPart.SOURCE, 0); mcimadamore@221: } mcimadamore@221: mcimadamore@83: /** mcimadamore@221: * Get the amount of spaces for a given indentation kind mcimadamore@221: * @param diagPart the diagnostic part for which the indentation is mcimadamore@221: * to be retrieved mcimadamore@221: * @return the amount of spaces used for the specified indentation kind mcimadamore@221: */ mcimadamore@221: public int getIndentation(DiagnosticPart diagPart) { mcimadamore@221: return indentationLevels.get(diagPart); mcimadamore@221: } mcimadamore@221: mcimadamore@83: /** mcimadamore@221: * Set the indentation level for various element of a given diagnostic - mcimadamore@221: * this might lead to more readable diagnostics mcimadamore@221: * mcimadamore@221: * @param indentationKind kind of indentation to be set mcimadamore@221: * @param nSpaces amount of spaces for the specified diagnostic part mcimadamore@221: */ mcimadamore@221: public void setIndentation(DiagnosticPart diagPart, int nSpaces) { mcimadamore@221: indentationLevels.put(diagPart, nSpaces); mcimadamore@221: } mcimadamore@221: mcimadamore@83: /** mcimadamore@221: * Set the source line positioning used by this formatter mcimadamore@221: * mcimadamore@221: * @param sourcePos a positioning value for source line mcimadamore@221: */ mcimadamore@221: public void setSourcePosition(SourcePosition sourcePos) { mcimadamore@221: sourcePosition = sourcePos; mcimadamore@221: } mcimadamore@221: mcimadamore@221: /** mcimadamore@221: * Get the source line positioning used by this formatter mcimadamore@221: * mcimadamore@221: * @return the positioning value used by this formatter mcimadamore@221: */ mcimadamore@221: public SourcePosition getSourcePosition() { mcimadamore@221: return sourcePosition; mcimadamore@221: } mcimadamore@221: //where mcimadamore@221: /** mcimadamore@221: * A source positioning value controls the position (within a given mcimadamore@221: * diagnostic message) in which the source line the diagnostic refers to mcimadamore@221: * should be displayed (if applicable) mcimadamore@221: */ mcimadamore@221: public enum SourcePosition { mcimadamore@221: /** mcimadamore@221: * Source line is displayed after the diagnostic message mcimadamore@221: */ mcimadamore@221: BOTTOM, mcimadamore@221: /** mcimadamore@221: * Source line is displayed after the first line of the diagnostic mcimadamore@221: * message mcimadamore@221: */ mcimadamore@221: AFTER_SUMMARY; mcimadamore@221: } mcimadamore@221: mcimadamore@221: /** mcimadamore@221: * Set a metachar string for a specific format mcimadamore@221: * mcimadamore@221: * @param kind the format kind to be set mcimadamore@221: * @param s the metachar string specifying the format mcimadamore@221: */ mcimadamore@221: public void setFormat(BasicFormatKind kind, String s) { mcimadamore@221: availableFormats.put(kind, s); mcimadamore@221: } mcimadamore@221: mcimadamore@221: /** mcimadamore@221: * Get a metachar string for a specific format mcimadamore@221: * mcimadamore@221: * @param sourcePos a positioning value for source line mcimadamore@221: */ mcimadamore@221: public String getFormat(BasicFormatKind kind) { mcimadamore@221: return availableFormats.get(kind); mcimadamore@221: } mcimadamore@221: //where mcimadamore@221: /** mcimadamore@221: * This enum contains all the kinds of formatting patterns supported mcimadamore@221: * by a basic diagnostic formatter. mcimadamore@221: */ mcimadamore@221: public enum BasicFormatKind { mcimadamore@221: /** mcimadamore@221: * A format string to be used for diagnostics with a given position. mcimadamore@221: */ mcimadamore@221: DEFAULT_POS_FORMAT, mcimadamore@221: /** mcimadamore@221: * A format string to be used for diagnostics without a given position. mcimadamore@221: */ mcimadamore@221: DEFAULT_NO_POS_FORMAT, mcimadamore@221: /** mcimadamore@221: * A format string to be used for diagnostics regarding classfiles mcimadamore@221: */ mcimadamore@221: DEFAULT_CLASS_FORMAT; mcimadamore@221: } mcimadamore@83: } mcimadamore@83: }