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

Thu, 10 Jun 2010 16:08:01 -0700

author
jjg
date
Thu, 10 Jun 2010 16:08:01 -0700
changeset 581
f2fdd52e4e87
parent 554
9d9f26857129
child 612
d1bd93028447
permissions
-rw-r--r--

6944312: Potential rebranding issues in openjdk/langtools repository sources
Reviewed-by: darcy

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

mercurial