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

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

author
ohair
date
Wed, 06 Apr 2011 20:33:44 -0700
changeset 962
0ff2bbd38f10
parent 816
7c537f4298fb
child 1347
1408af4cd8b0
permissions
-rw-r--r--

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

     1 /*
     2  * Copyright (c) 2008, 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  */
    25 package com.sun.tools.javac.util;
    27 import java.util.Arrays;
    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.Set;
    34 import javax.tools.JavaFileObject;
    36 import com.sun.tools.javac.api.DiagnosticFormatter;
    37 import com.sun.tools.javac.api.DiagnosticFormatter.Configuration.DiagnosticPart;
    38 import com.sun.tools.javac.api.DiagnosticFormatter.Configuration.MultilineLimit;
    39 import com.sun.tools.javac.api.DiagnosticFormatter.PositionKind;
    40 import com.sun.tools.javac.api.Formattable;
    41 import com.sun.tools.javac.code.Lint.LintCategory;
    42 import com.sun.tools.javac.code.Printer;
    43 import com.sun.tools.javac.code.Symbol;
    44 import com.sun.tools.javac.code.Type;
    45 import com.sun.tools.javac.code.Type.CapturedType;
    47 import com.sun.tools.javac.file.BaseFileObject;
    48 import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*;
    50 /**
    51  * This abstract class provides a basic implementation of the functionalities that should be provided
    52  * by any formatter used by javac. Among the main features provided by AbstractDiagnosticFormatter are:
    53  *
    54  * <ul>
    55  *  <li> Provides a standard implementation of the visitor-like methods defined in the interface DiagnisticFormatter.
    56  *  Those implementations are specifically targeting JCDiagnostic objects.
    57  *  <li> Provides basic support for i18n and a method for executing all locale-dependent conversions
    58  *  <li> Provides the formatting logic for rendering the arguments of a JCDiagnostic object.
    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 abstract class AbstractDiagnosticFormatter implements DiagnosticFormatter<JCDiagnostic> {
    68     /**
    69      * JavacMessages object used by this formatter for i18n.
    70      */
    71     protected JavacMessages messages;
    73     /**
    74      * Configuration object used by this formatter
    75      */
    76     private SimpleConfiguration config;
    78     /**
    79      * Current depth level of the disgnostic being formatted
    80      * (!= 0 for subdiagnostics)
    81      */
    82     protected int depth = 0;
    84     /**
    85      * All captured types that have been encountered during diagnostic formatting.
    86      * This info is used by the FormatterPrinter in order to print friendly unique
    87      * ids for captured types
    88      */
    89     private List<Type> allCaptured = List.nil();
    91     /**
    92      * Initialize an AbstractDiagnosticFormatter by setting its JavacMessages object.
    93      * @param messages
    94      */
    95     protected AbstractDiagnosticFormatter(JavacMessages messages, SimpleConfiguration config) {
    96         this.messages = messages;
    97         this.config = config;
    98     }
   100     public String formatKind(JCDiagnostic d, Locale l) {
   101         switch (d.getType()) {
   102             case FRAGMENT: return "";
   103             case NOTE:     return localize(l, "compiler.note.note");
   104             case WARNING:  return localize(l, "compiler.warn.warning");
   105             case ERROR:    return localize(l, "compiler.err.error");
   106             default:
   107                 throw new AssertionError("Unknown diagnostic type: " + d.getType());
   108         }
   109     }
   111     @Override
   112     public String format(JCDiagnostic d, Locale locale) {
   113         allCaptured = List.nil();
   114         return formatDiagnostic(d, locale);
   115     }
   117     protected abstract String formatDiagnostic(JCDiagnostic d, Locale locale);
   119     public String formatPosition(JCDiagnostic d, PositionKind pk,Locale l) {
   120         Assert.check(d.getPosition() != Position.NOPOS);
   121         return String.valueOf(getPosition(d, pk));
   122     }
   123     //where
   124     private long getPosition(JCDiagnostic d, PositionKind pk) {
   125         switch (pk) {
   126             case START: return d.getIntStartPosition();
   127             case END: return d.getIntEndPosition();
   128             case LINE: return d.getLineNumber();
   129             case COLUMN: return d.getColumnNumber();
   130             case OFFSET: return d.getIntPosition();
   131             default:
   132                 throw new AssertionError("Unknown diagnostic position: " + pk);
   133         }
   134     }
   136     public String formatSource(JCDiagnostic d, boolean fullname, Locale l) {
   137         JavaFileObject fo = d.getSource();
   138         if (fo == null)
   139             throw new IllegalArgumentException(); // d should have source set
   140         if (fullname)
   141             return fo.getName();
   142         else if (fo instanceof BaseFileObject)
   143             return ((BaseFileObject) fo).getShortName();
   144         else
   145             return BaseFileObject.getSimpleName(fo);
   146     }
   148     /**
   149      * Format the arguments of a given diagnostic.
   150      *
   151      * @param d diagnostic whose arguments are to be formatted
   152      * @param l locale object to be used for i18n
   153      * @return a Collection whose elements are the formatted arguments of the diagnostic
   154      */
   155     protected Collection<String> formatArguments(JCDiagnostic d, Locale l) {
   156         ListBuffer<String> buf = new ListBuffer<String>();
   157         for (Object o : d.getArgs()) {
   158            buf.append(formatArgument(d, o, l));
   159         }
   160         return buf.toList();
   161     }
   163     /**
   164      * Format a single argument of a given diagnostic.
   165      *
   166      * @param d diagnostic whose argument is to be formatted
   167      * @param arg argument to be formatted
   168      * @param l locale object to be used for i18n
   169      * @return string representation of the diagnostic argument
   170      */
   171     protected String formatArgument(JCDiagnostic d, Object arg, Locale l) {
   172         if (arg instanceof JCDiagnostic) {
   173             String s = null;
   174             depth++;
   175             try {
   176                 s = formatMessage((JCDiagnostic)arg, l);
   177             }
   178             finally {
   179                 depth--;
   180             }
   181             return s;
   182         }
   183         else if (arg instanceof Iterable<?>) {
   184             return formatIterable(d, (Iterable<?>)arg, l);
   185         }
   186         else if (arg instanceof Type) {
   187             return printer.visit((Type)arg, l);
   188         }
   189         else if (arg instanceof Symbol) {
   190             return printer.visit((Symbol)arg, l);
   191         }
   192         else if (arg instanceof JavaFileObject) {
   193             return ((JavaFileObject)arg).getName();
   194         }
   195         else if (arg instanceof Formattable) {
   196             return ((Formattable)arg).toString(l, messages);
   197         }
   198         else {
   199             return String.valueOf(arg);
   200         }
   201     }
   203     /**
   204      * Format an iterable argument of a given diagnostic.
   205      *
   206      * @param d diagnostic whose argument is to be formatted
   207      * @param it iterable argument to be formatted
   208      * @param l locale object to be used for i18n
   209      * @return string representation of the diagnostic iterable argument
   210      */
   211     protected String formatIterable(JCDiagnostic d, Iterable<?> it, Locale l) {
   212         StringBuilder sbuf = new StringBuilder();
   213         String sep = "";
   214         for (Object o : it) {
   215             sbuf.append(sep);
   216             sbuf.append(formatArgument(d, o, l));
   217             sep = ",";
   218         }
   219         return sbuf.toString();
   220     }
   222     /**
   223      * Format all the subdiagnostics attached to a given diagnostic.
   224      *
   225      * @param d diagnostic whose subdiagnostics are to be formatted
   226      * @param l locale object to be used for i18n
   227      * @return list of all string representations of the subdiagnostics
   228      */
   229     protected List<String> formatSubdiagnostics(JCDiagnostic d, Locale l) {
   230         List<String> subdiagnostics = List.nil();
   231         int maxDepth = config.getMultilineLimit(MultilineLimit.DEPTH);
   232         if (maxDepth == -1 || depth < maxDepth) {
   233             depth++;
   234             try {
   235                 int maxCount = config.getMultilineLimit(MultilineLimit.LENGTH);
   236                 int count = 0;
   237                 for (JCDiagnostic d2 : d.getSubdiagnostics()) {
   238                     if (maxCount == -1 || count < maxCount) {
   239                         subdiagnostics = subdiagnostics.append(formatSubdiagnostic(d, d2, l));
   240                         count++;
   241                     }
   242                     else
   243                         break;
   244                 }
   245             }
   246             finally {
   247                 depth--;
   248             }
   249         }
   250         return subdiagnostics;
   251     }
   253     /**
   254      * Format a subdiagnostics attached to a given diagnostic.
   255      *
   256      * @param parent multiline diagnostic whose subdiagnostics is to be formatted
   257      * @param sub subdiagnostic to be formatted
   258      * @param l locale object to be used for i18n
   259      * @return string representation of the subdiagnostics
   260      */
   261     protected String formatSubdiagnostic(JCDiagnostic parent, JCDiagnostic sub, Locale l) {
   262         return formatMessage(sub, l);
   263     }
   265     /** Format the faulty source code line and point to the error.
   266      *  @param d The diagnostic for which the error line should be printed
   267      */
   268     protected String formatSourceLine(JCDiagnostic d, int nSpaces) {
   269         StringBuilder buf = new StringBuilder();
   270         DiagnosticSource source = d.getDiagnosticSource();
   271         int pos = d.getIntPosition();
   272         if (d.getIntPosition() == Position.NOPOS)
   273             throw new AssertionError();
   274         String line = (source == null ? null : source.getLine(pos));
   275         if (line == null)
   276             return "";
   277         buf.append(indent(line, nSpaces));
   278         int col = source.getColumnNumber(pos, false);
   279         if (config.isCaretEnabled()) {
   280             buf.append("\n");
   281             for (int i = 0; i < col - 1; i++)  {
   282                 buf.append((line.charAt(i) == '\t') ? "\t" : " ");
   283             }
   284             buf.append(indent("^", nSpaces));
   285         }
   286         return buf.toString();
   287     }
   289     protected String formatLintCategory(JCDiagnostic d, Locale l) {
   290         LintCategory lc = d.getLintCategory();
   291         if (lc == null)
   292             return "";
   293         return localize(l, "compiler.warn.lintOption", lc.option);
   294     }
   296     /**
   297      * Converts a String into a locale-dependent representation accordingly to a given locale.
   298      *
   299      * @param l locale object to be used for i18n
   300      * @param key locale-independent key used for looking up in a resource file
   301      * @param args localization arguments
   302      * @return a locale-dependent string
   303      */
   304     protected String localize(Locale l, String key, Object... args) {
   305         return messages.getLocalizedString(l, key, args);
   306     }
   308     public boolean displaySource(JCDiagnostic d) {
   309         return config.getVisible().contains(DiagnosticPart.SOURCE) &&
   310                 d.getType() != FRAGMENT &&
   311                 d.getIntPosition() != Position.NOPOS;
   312     }
   314     public boolean isRaw() {
   315         return false;
   316     }
   318     /**
   319      * Creates a string with a given amount of empty spaces. Useful for
   320      * indenting the text of a diagnostic message.
   321      *
   322      * @param nSpaces the amount of spaces to be added to the result string
   323      * @return the indentation string
   324      */
   325     protected String indentString(int nSpaces) {
   326         String spaces = "                        ";
   327         if (nSpaces <= spaces.length())
   328             return spaces.substring(0, nSpaces);
   329         else {
   330             StringBuilder buf = new StringBuilder();
   331             for (int i = 0 ; i < nSpaces ; i++)
   332                 buf.append(" ");
   333             return buf.toString();
   334         }
   335     }
   337     /**
   338      * Indent a string by prepending a given amount of empty spaces to each line
   339      * of the string.
   340      *
   341      * @param s the string to be indented
   342      * @param nSpaces the amount of spaces that should be prepended to each line
   343      * of the string
   344      * @return an indented string
   345      */
   346     protected String indent(String s, int nSpaces) {
   347         String indent = indentString(nSpaces);
   348         StringBuilder buf = new StringBuilder();
   349         String nl = "";
   350         for (String line : s.split("\n")) {
   351             buf.append(nl);
   352             buf.append(indent + line);
   353             nl = "\n";
   354         }
   355         return buf.toString();
   356     }
   358     public SimpleConfiguration getConfiguration() {
   359         return config;
   360     }
   362     static public class SimpleConfiguration implements Configuration {
   364         protected Map<MultilineLimit, Integer> multilineLimits;
   365         protected EnumSet<DiagnosticPart> visibleParts;
   366         protected boolean caretEnabled;
   368         public SimpleConfiguration(Set<DiagnosticPart> parts) {
   369             multilineLimits = new HashMap<MultilineLimit, Integer>();
   370             setVisible(parts);
   371             setMultilineLimit(MultilineLimit.DEPTH, -1);
   372             setMultilineLimit(MultilineLimit.LENGTH, -1);
   373             setCaretEnabled(true);
   374         }
   376         @SuppressWarnings("fallthrough")
   377         public SimpleConfiguration(Options options, Set<DiagnosticPart> parts) {
   378             this(parts);
   379             String showSource = null;
   380             if ((showSource = options.get("showSource")) != null) {
   381                 if (showSource.equals("true"))
   382                     setVisiblePart(DiagnosticPart.SOURCE, true);
   383                 else if (showSource.equals("false"))
   384                     setVisiblePart(DiagnosticPart.SOURCE, false);
   385             }
   386             String diagOpts = options.get("diags");
   387             if (diagOpts != null) {//override -XDshowSource
   388                 Collection<String> args = Arrays.asList(diagOpts.split(","));
   389                 if (args.contains("short")) {
   390                     setVisiblePart(DiagnosticPart.DETAILS, false);
   391                     setVisiblePart(DiagnosticPart.SUBDIAGNOSTICS, false);
   392                 }
   393                 if (args.contains("source"))
   394                     setVisiblePart(DiagnosticPart.SOURCE, true);
   395                 if (args.contains("-source"))
   396                     setVisiblePart(DiagnosticPart.SOURCE, false);
   397             }
   398             String multiPolicy = null;
   399             if ((multiPolicy = options.get("multilinePolicy")) != null) {
   400                 if (multiPolicy.equals("disabled"))
   401                     setVisiblePart(DiagnosticPart.SUBDIAGNOSTICS, false);
   402                 else if (multiPolicy.startsWith("limit:")) {
   403                     String limitString = multiPolicy.substring("limit:".length());
   404                     String[] limits = limitString.split(":");
   405                     try {
   406                         switch (limits.length) {
   407                             case 2: {
   408                                 if (!limits[1].equals("*"))
   409                                     setMultilineLimit(MultilineLimit.DEPTH, Integer.parseInt(limits[1]));
   410                             }
   411                             case 1: {
   412                                 if (!limits[0].equals("*"))
   413                                     setMultilineLimit(MultilineLimit.LENGTH, Integer.parseInt(limits[0]));
   414                             }
   415                         }
   416                     }
   417                     catch(NumberFormatException ex) {
   418                         setMultilineLimit(MultilineLimit.DEPTH, -1);
   419                         setMultilineLimit(MultilineLimit.LENGTH, -1);
   420                     }
   421                 }
   422             }
   423             String showCaret = null;
   424             if (((showCaret = options.get("showCaret")) != null) &&
   425                 showCaret.equals("false"))
   426                     setCaretEnabled(false);
   427             else
   428                 setCaretEnabled(true);
   429         }
   431         public int getMultilineLimit(MultilineLimit limit) {
   432             return multilineLimits.get(limit);
   433         }
   435         public EnumSet<DiagnosticPart> getVisible() {
   436             return EnumSet.copyOf(visibleParts);
   437         }
   439         public void setMultilineLimit(MultilineLimit limit, int value) {
   440             multilineLimits.put(limit, value < -1 ? -1 : value);
   441         }
   444         public void setVisible(Set<DiagnosticPart> diagParts) {
   445             visibleParts = EnumSet.copyOf(diagParts);
   446         }
   448         public void setVisiblePart(DiagnosticPart diagParts, boolean enabled) {
   449             if (enabled)
   450                 visibleParts.add(diagParts);
   451             else
   452                 visibleParts.remove(diagParts);
   453         }
   455         /**
   456          * Shows a '^' sign under the source line displayed by the formatter
   457          * (if applicable).
   458          *
   459          * @param caretEnabled if true enables caret
   460          */
   461         public void setCaretEnabled(boolean caretEnabled) {
   462             this.caretEnabled = caretEnabled;
   463         }
   465         /**
   466          * Tells whether the caret display is active or not.
   467          *
   468          * @param caretEnabled if true the caret is enabled
   469          */
   470         public boolean isCaretEnabled() {
   471             return caretEnabled;
   472         }
   473     }
   475     public Printer getPrinter() {
   476         return printer;
   477     }
   479     public void setPrinter(Printer printer) {
   480         this.printer = printer;
   481     }
   483     /**
   484      * An enhanced printer for formatting types/symbols used by
   485      * AbstractDiagnosticFormatter. Provides alternate numbering of captured
   486      * types (they are numbered starting from 1 on each new diagnostic, instead
   487      * of relying on the underlying hashcode() method which generates unstable
   488      * output). Also detects cycles in wildcard messages (e.g. if the wildcard
   489      * type referred by a given captured type C contains C itself) which might
   490      * lead to infinite loops.
   491      */
   492     protected Printer printer = new Printer() {
   493         @Override
   494         protected String localize(Locale locale, String key, Object... args) {
   495             return AbstractDiagnosticFormatter.this.localize(locale, key, args);
   496         }
   497         @Override
   498         protected String capturedVarId(CapturedType t, Locale locale) {
   499             return "" + (allCaptured.indexOf(t) + 1);
   500         }
   501         @Override
   502         public String visitCapturedType(CapturedType t, Locale locale) {
   503             if (!allCaptured.contains(t)) {
   504                 allCaptured = allCaptured.append(t);
   505             }
   506             return super.visitCapturedType(t, locale);
   507         }
   508     };
   509 }

mercurial