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

Wed, 06 Apr 2011 20:33:44 -0700

author
ohair
date
Wed, 06 Apr 2011 20:33:44 -0700
changeset 962
0ff2bbd38f10
parent 929
e2890b8369f7
child 1358
fc123bdeddb8
permissions
-rw-r--r--

7033660: Update copyright year to 2011 on any files changed in 2011
Reviewed-by: dholmes

     1 /*
     2  * Copyright (c) 2005, 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 java.util.Collection;
    29 import java.util.EnumMap;
    30 import java.util.EnumSet;
    31 import java.util.HashMap;
    32 import java.util.Locale;
    33 import java.util.Map;
    34 import java.util.regex.Matcher;
    35 import javax.tools.JavaFileObject;
    37 import com.sun.tools.javac.util.AbstractDiagnosticFormatter.SimpleConfiguration;
    38 import com.sun.tools.javac.util.BasicDiagnosticFormatter.BasicConfiguration;
    40 import static com.sun.tools.javac.api.DiagnosticFormatter.PositionKind.*;
    41 import static com.sun.tools.javac.util.BasicDiagnosticFormatter.BasicConfiguration.*;
    42 import static com.sun.tools.javac.util.LayoutCharacters.*;
    44 /**
    45  * A basic formatter for diagnostic messages.
    46  * The basic formatter will format a diagnostic according to one of three format patterns, depending on whether
    47  * or not the source name and position are set. The formatter supports a printf-like string for patterns
    48  * with the following special characters:
    49  * <ul>
    50  * <li>%b: the base of the source name
    51  * <li>%f: the source name (full absolute path)
    52  * <li>%l: the line number of the diagnostic, derived from the character offset
    53  * <li>%c: the column number of the diagnostic, derived from the character offset
    54  * <li>%o: the character offset of the diagnostic if set
    55  * <li>%p: the prefix for the diagnostic, derived from the diagnostic type
    56  * <li>%t: the prefix as it normally appears in standard diagnostics. In this case, no prefix is
    57  *        shown if the type is ERROR and if a source name is set
    58  * <li>%m: the text or the diagnostic, including any appropriate arguments
    59  * <li>%_: space delimiter, useful for formatting purposes
    60  * </ul>
    61  *
    62  * <p><b>This is NOT part of any supported API.
    63  * If you write code that depends on this, you do so at your own risk.
    64  * This code and its internal interfaces are subject to change or
    65  * deletion without notice.</b>
    66  */
    67 public class BasicDiagnosticFormatter extends AbstractDiagnosticFormatter {
    69     /**
    70      * Create a basic formatter based on the supplied options.
    71      *
    72      * @param opts list of command-line options
    73      * @param msgs JavacMessages object used for i18n
    74      */
    75     public BasicDiagnosticFormatter(Options options, JavacMessages msgs) {
    76         super(msgs, new BasicConfiguration(options));
    77     }
    79     /**
    80      * Create a standard basic formatter
    81      *
    82      * @param msgs JavacMessages object used for i18n
    83      */
    84     public BasicDiagnosticFormatter(JavacMessages msgs) {
    85         super(msgs, new BasicConfiguration());
    86     }
    88     public String formatDiagnostic(JCDiagnostic d, Locale l) {
    89         if (l == null)
    90             l = messages.getCurrentLocale();
    91         String format = selectFormat(d);
    92         StringBuilder buf = new StringBuilder();
    93         for (int i = 0; i < format.length(); i++) {
    94             char c = format.charAt(i);
    95             boolean meta = false;
    96             if (c == '%' && i < format.length() - 1) {
    97                 meta = true;
    98                 c = format.charAt(++i);
    99             }
   100             buf.append(meta ? formatMeta(c, d, l) : String.valueOf(c));
   101         }
   102         if (depth == 0)
   103             return addSourceLineIfNeeded(d, buf.toString());
   104         else
   105             return buf.toString();
   106     }
   108     public String formatMessage(JCDiagnostic d, Locale l) {
   109         int currentIndentation = 0;
   110         StringBuilder buf = new StringBuilder();
   111         Collection<String> args = formatArguments(d, l);
   112         String msg = localize(l, d.getCode(), args.toArray());
   113         String[] lines = msg.split("\n");
   114         if (getConfiguration().getVisible().contains(DiagnosticPart.SUMMARY)) {
   115             currentIndentation += getConfiguration().getIndentation(DiagnosticPart.SUMMARY);
   116             buf.append(indent(lines[0], currentIndentation)); //summary
   117         }
   118         if (lines.length > 1 && getConfiguration().getVisible().contains(DiagnosticPart.DETAILS)) {
   119             currentIndentation += getConfiguration().getIndentation(DiagnosticPart.DETAILS);
   120             for (int i = 1;i < lines.length; i++) {
   121                 buf.append("\n" + indent(lines[i], currentIndentation));
   122             }
   123         }
   124         if (d.isMultiline() && getConfiguration().getVisible().contains(DiagnosticPart.SUBDIAGNOSTICS)) {
   125             currentIndentation += getConfiguration().getIndentation(DiagnosticPart.SUBDIAGNOSTICS);
   126                 for (String sub : formatSubdiagnostics(d, l)) {
   127                     buf.append("\n" + indent(sub, currentIndentation));
   128             }
   129         }
   130         return buf.toString();
   131     }
   133     protected String addSourceLineIfNeeded(JCDiagnostic d, String msg) {
   134         if (!displaySource(d))
   135             return msg;
   136         else {
   137             BasicConfiguration conf = getConfiguration();
   138             int indentSource = conf.getIndentation(DiagnosticPart.SOURCE);
   139             String sourceLine = "\n" + formatSourceLine(d, indentSource);
   140             boolean singleLine = msg.indexOf("\n") == -1;
   141             if (singleLine || getConfiguration().getSourcePosition() == SourcePosition.BOTTOM)
   142                 return msg + sourceLine;
   143             else
   144                 return msg.replaceFirst("\n", Matcher.quoteReplacement(sourceLine) + "\n");
   145         }
   146     }
   148     protected String formatMeta(char c, JCDiagnostic d, Locale l) {
   149         switch (c) {
   150             case 'b':
   151                 return formatSource(d, false, l);
   152             case 'e':
   153                 return formatPosition(d, END, l);
   154             case 'f':
   155                 return formatSource(d, true, l);
   156             case 'l':
   157                 return formatPosition(d, LINE, l);
   158             case 'c':
   159                 return formatPosition(d, COLUMN, l);
   160             case 'o':
   161                 return formatPosition(d, OFFSET, l);
   162             case 'p':
   163                 return formatKind(d, l);
   164             case 's':
   165                 return formatPosition(d, START, l);
   166             case 't': {
   167                 boolean usePrefix;
   168                 switch (d.getType()) {
   169                 case FRAGMENT:
   170                     usePrefix = false;
   171                     break;
   172                 case ERROR:
   173                     usePrefix = (d.getIntPosition() == Position.NOPOS);
   174                     break;
   175                 default:
   176                     usePrefix = true;
   177                 }
   178                 if (usePrefix)
   179                     return formatKind(d, l);
   180                 else
   181                     return "";
   182             }
   183             case 'm':
   184                 return formatMessage(d, l);
   185             case 'L':
   186                 return formatLintCategory(d, l);
   187             case '_':
   188                 return " ";
   189             case '%':
   190                 return "%";
   191             default:
   192                 return String.valueOf(c);
   193         }
   194     }
   196     private String selectFormat(JCDiagnostic d) {
   197         DiagnosticSource source = d.getDiagnosticSource();
   198         String format = getConfiguration().getFormat(BasicFormatKind.DEFAULT_NO_POS_FORMAT);
   199         if (source != null && source != DiagnosticSource.NO_SOURCE) {
   200             if (d.getIntPosition() != Position.NOPOS) {
   201                 format = getConfiguration().getFormat(BasicFormatKind.DEFAULT_POS_FORMAT);
   202             } else if (source.getFile() != null &&
   203                        source.getFile().getKind() == JavaFileObject.Kind.CLASS) {
   204                 format = getConfiguration().getFormat(BasicFormatKind.DEFAULT_CLASS_FORMAT);
   205             }
   206         }
   207         return format;
   208     }
   210     @Override
   211     public BasicConfiguration getConfiguration() {
   212         //the following cast is always safe - see init
   213         return (BasicConfiguration)super.getConfiguration();
   214     }
   216     static public class BasicConfiguration extends SimpleConfiguration {
   218         protected Map<DiagnosticPart, Integer> indentationLevels;
   219         protected Map<BasicFormatKind, String> availableFormats;
   220         protected SourcePosition sourcePosition;
   222         @SuppressWarnings("fallthrough")
   223         public BasicConfiguration(Options options) {
   224             super(options, EnumSet.of(DiagnosticPart.SUMMARY,
   225                             DiagnosticPart.DETAILS,
   226                             DiagnosticPart.SUBDIAGNOSTICS,
   227                             DiagnosticPart.SOURCE));
   228             initFormat();
   229             initIndentation();
   230             if (options.isSet("oldDiags"))
   231                 initOldFormat();
   232             String fmt = options.get("diagsFormat");
   233             if (fmt != null) {
   234                 if (fmt.equals("OLD"))
   235                     initOldFormat();
   236                 else
   237                     initFormats(fmt);
   238             }
   239             String srcPos = null;
   240             if ((((srcPos = options.get("sourcePosition")) != null)) &&
   241                     srcPos.equals("bottom"))
   242                     setSourcePosition(SourcePosition.BOTTOM);
   243             else
   244                 setSourcePosition(SourcePosition.AFTER_SUMMARY);
   245             String indent = options.get("diagsIndentation");
   246             if (indent != null) {
   247                 String[] levels = indent.split("\\|");
   248                 try {
   249                     switch (levels.length) {
   250                         case 5:
   251                             setIndentation(DiagnosticPart.JLS,
   252                                     Integer.parseInt(levels[4]));
   253                         case 4:
   254                             setIndentation(DiagnosticPart.SUBDIAGNOSTICS,
   255                                     Integer.parseInt(levels[3]));
   256                         case 3:
   257                             setIndentation(DiagnosticPart.SOURCE,
   258                                     Integer.parseInt(levels[2]));
   259                         case 2:
   260                             setIndentation(DiagnosticPart.DETAILS,
   261                                     Integer.parseInt(levels[1]));
   262                         default:
   263                             setIndentation(DiagnosticPart.SUMMARY,
   264                                     Integer.parseInt(levels[0]));
   265                     }
   266                 }
   267                 catch (NumberFormatException ex) {
   268                     initIndentation();
   269                 }
   270             }
   271         }
   273         public BasicConfiguration() {
   274             super(EnumSet.of(DiagnosticPart.SUMMARY,
   275                   DiagnosticPart.DETAILS,
   276                   DiagnosticPart.SUBDIAGNOSTICS,
   277                   DiagnosticPart.SOURCE));
   278             initFormat();
   279             initIndentation();
   280         }
   282         private void initFormat() {
   283             initFormats("%f:%l:%_%p%L%m", "%p%L%m", "%f:%_%p%L%m");
   284         }
   286         private void initOldFormat() {
   287             initFormats("%f:%l:%_%t%L%m", "%p%L%m", "%f:%_%t%L%m");
   288         }
   290         private void initFormats(String pos, String nopos, String clazz) {
   291             availableFormats = new EnumMap<BasicFormatKind, String>(BasicFormatKind.class);
   292             setFormat(BasicFormatKind.DEFAULT_POS_FORMAT,    pos);
   293             setFormat(BasicFormatKind.DEFAULT_NO_POS_FORMAT, nopos);
   294             setFormat(BasicFormatKind.DEFAULT_CLASS_FORMAT,  clazz);
   295         }
   297         @SuppressWarnings("fallthrough")
   298         private void initFormats(String fmt) {
   299             String[] formats = fmt.split("\\|");
   300             switch (formats.length) {
   301                 case 3:
   302                     setFormat(BasicFormatKind.DEFAULT_CLASS_FORMAT, formats[2]);
   303                 case 2:
   304                     setFormat(BasicFormatKind.DEFAULT_NO_POS_FORMAT, formats[1]);
   305                 default:
   306                     setFormat(BasicFormatKind.DEFAULT_POS_FORMAT, formats[0]);
   307             }
   308         }
   310         private void initIndentation() {
   311             indentationLevels = new HashMap<DiagnosticPart, Integer>();
   312             setIndentation(DiagnosticPart.SUMMARY, 0);
   313             setIndentation(DiagnosticPart.DETAILS, DetailsInc);
   314             setIndentation(DiagnosticPart.SUBDIAGNOSTICS, DiagInc);
   315             setIndentation(DiagnosticPart.SOURCE, 0);
   316         }
   318         /**
   319          * Get the amount of spaces for a given indentation kind
   320          * @param diagPart the diagnostic part for which the indentation is
   321          * to be retrieved
   322          * @return the amount of spaces used for the specified indentation kind
   323          */
   324         public int getIndentation(DiagnosticPart diagPart) {
   325             return indentationLevels.get(diagPart);
   326         }
   328         /**
   329          * Set the indentation level for various element of a given diagnostic -
   330          * this might lead to more readable diagnostics
   331          *
   332          * @param indentationKind kind of indentation to be set
   333          * @param nSpaces amount of spaces for the specified diagnostic part
   334          */
   335         public void setIndentation(DiagnosticPart diagPart, int nSpaces) {
   336             indentationLevels.put(diagPart, nSpaces);
   337         }
   339         /**
   340          * Set the source line positioning used by this formatter
   341          *
   342          * @param sourcePos a positioning value for source line
   343          */
   344         public void setSourcePosition(SourcePosition sourcePos) {
   345             sourcePosition = sourcePos;
   346         }
   348         /**
   349          * Get the source line positioning used by this formatter
   350          *
   351          * @return the positioning value used by this formatter
   352          */
   353         public SourcePosition getSourcePosition() {
   354             return sourcePosition;
   355         }
   356         //where
   357         /**
   358          * A source positioning value controls the position (within a given
   359          * diagnostic message) in which the source line the diagnostic refers to
   360          * should be displayed (if applicable)
   361          */
   362         public enum SourcePosition {
   363             /**
   364              * Source line is displayed after the diagnostic message
   365              */
   366             BOTTOM,
   367             /**
   368              * Source line is displayed after the first line of the diagnostic
   369              * message
   370              */
   371             AFTER_SUMMARY;
   372         }
   374         /**
   375          * Set a metachar string for a specific format
   376          *
   377          * @param kind the format kind to be set
   378          * @param s the metachar string specifying the format
   379          */
   380         public void setFormat(BasicFormatKind kind, String s) {
   381             availableFormats.put(kind, s);
   382         }
   384         /**
   385          * Get a metachar string for a specific format
   386          *
   387          * @param sourcePos a positioning value for source line
   388          */
   389         public String getFormat(BasicFormatKind kind) {
   390             return availableFormats.get(kind);
   391         }
   392         //where
   393         /**
   394          * This enum contains all the kinds of formatting patterns supported
   395          * by a basic diagnostic formatter.
   396          */
   397         public enum BasicFormatKind {
   398             /**
   399             * A format string to be used for diagnostics with a given position.
   400             */
   401             DEFAULT_POS_FORMAT,
   402             /**
   403             * A format string to be used for diagnostics without a given position.
   404             */
   405             DEFAULT_NO_POS_FORMAT,
   406             /**
   407             * A format string to be used for diagnostics regarding classfiles
   408             */
   409             DEFAULT_CLASS_FORMAT;
   410         }
   411     }
   412 }

mercurial