src/share/classes/com/sun/tools/doclint/Messages.java

Tue, 17 Sep 2013 14:17:13 -0700

author
jjg
date
Tue, 17 Sep 2013 14:17:13 -0700
changeset 2033
fdfbc5f0c4ed
parent 1455
75ab654b5cd5
child 2413
fe033d997ddf
permissions
-rw-r--r--

8024538: -Xdoclint + -Xprefer:source + incremental compilation == FAIL
Reviewed-by: darcy

     1 /*
     2  * Copyright (c) 2012, 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.doclint;
    28 import java.io.PrintWriter;
    29 import java.text.MessageFormat;
    30 import java.util.Comparator;
    31 import java.util.HashMap;
    32 import java.util.Locale;
    33 import java.util.Map;
    34 import java.util.MissingResourceException;
    35 import java.util.ResourceBundle;
    36 import java.util.Set;
    37 import java.util.TreeMap;
    38 import java.util.TreeSet;
    40 import javax.tools.Diagnostic;
    42 import com.sun.source.doctree.DocTree;
    43 import com.sun.source.tree.Tree;
    44 import com.sun.tools.doclint.Env.AccessKind;
    46 /**
    47  * Message reporting for DocLint.
    48  *
    49  * Options are used to filter out messages based on group and access level.
    50  * Support can be enabled for accumulating statistics of different kinds of
    51  * messages.
    52  *
    53  * <p><b>This is NOT part of any supported API.
    54  * If you write code that depends on this, you do so at your own
    55  * risk.  This code and its internal interfaces are subject to change
    56  * or deletion without notice.</b></p>
    57  */
    58 public class Messages {
    59     /**
    60      * Groups used to categorize messages, so that messages in each group
    61      * can be enabled or disabled via options.
    62      */
    63     public enum Group {
    64         ACCESSIBILITY,
    65         HTML,
    66         MISSING,
    67         SYNTAX,
    68         REFERENCE;
    70         String optName() { return name().toLowerCase(); }
    71         String notOptName() { return "-" + optName(); }
    73         static boolean accepts(String opt) {
    74             for (Group g: values())
    75                 if (opt.equals(g.optName())) return true;
    76             return false;
    77         }
    78     };
    80     private final Options options;
    81     private final Stats stats;
    83     ResourceBundle bundle;
    84     Env env;
    86     Messages(Env env) {
    87         this.env = env;
    88         String name = getClass().getPackage().getName() + ".resources.doclint";
    89         bundle = ResourceBundle.getBundle(name, Locale.ENGLISH);
    91         stats = new Stats(bundle);
    92         options = new Options(stats);
    93     }
    95     void error(Group group, DocTree tree, String code, Object... args) {
    96         report(group, Diagnostic.Kind.ERROR, tree, code, args);
    97     }
    99     void warning(Group group, DocTree tree, String code, Object... args) {
   100         report(group, Diagnostic.Kind.WARNING, tree, code, args);
   101     }
   103     void setOptions(String opts) {
   104         options.setOptions(opts);
   105     }
   107     void setStatsEnabled(boolean b) {
   108         stats.setEnabled(b);
   109     }
   111     void reportStats(PrintWriter out) {
   112         stats.report(out);
   113     }
   115     protected void report(Group group, Diagnostic.Kind dkind, DocTree tree, String code, Object... args) {
   116         if (options.isEnabled(group, env.currAccess)) {
   117             String msg = (code == null) ? (String) args[0] : localize(code, args);
   118             env.trees.printMessage(dkind, msg, tree,
   119                     env.currDocComment, env.currPath.getCompilationUnit());
   121             stats.record(group, dkind, code);
   122         }
   123     }
   125     protected void report(Group group, Diagnostic.Kind dkind, Tree tree, String code, Object... args) {
   126         if (options.isEnabled(group, env.currAccess)) {
   127             String msg = localize(code, args);
   128             env.trees.printMessage(dkind, msg, tree, env.currPath.getCompilationUnit());
   130             stats.record(group, dkind, code);
   131         }
   132     }
   134     String localize(String code, Object... args) {
   135         String msg = bundle.getString(code);
   136         if (msg == null) {
   137             StringBuilder sb = new StringBuilder();
   138             sb.append("message file broken: code=").append(code);
   139             if (args.length > 0) {
   140                 sb.append(" arguments={0}");
   141                 for (int i = 1; i < args.length; i++) {
   142                     sb.append(", {").append(i).append("}");
   143                 }
   144             }
   145             msg = sb.toString();
   146         }
   147         return MessageFormat.format(msg, args);
   148     }
   150     // <editor-fold defaultstate="collapsed" desc="Options">
   152     /**
   153      * Handler for (sub)options specific to message handling.
   154      */
   155     static class Options {
   156         Map<String, Env.AccessKind> map = new HashMap<String, Env.AccessKind>();
   157         private final Stats stats;
   159         static boolean isValidOptions(String opts) {
   160             for (String opt: opts.split(",")) {
   161                 if (!isValidOption(opt.trim().toLowerCase()))
   162                     return false;
   163             }
   164             return true;
   165         }
   167         private static boolean isValidOption(String opt) {
   168             if (opt.equals("none") || opt.equals(Stats.OPT))
   169                 return true;
   171             int begin = opt.startsWith("-") ? 1 : 0;
   172             int sep = opt.indexOf("/");
   173             String grp = opt.substring(begin, (sep != -1) ? sep : opt.length());
   174             return ((begin == 0 && grp.equals("all")) || Group.accepts(grp))
   175                     && ((sep == -1) || AccessKind.accepts(opt.substring(sep + 1)));
   176         }
   178         Options(Stats stats) {
   179             this.stats = stats;
   180         }
   182         /** Determine if a message group is enabled for a particular access level. */
   183         boolean isEnabled(Group g, Env.AccessKind access) {
   184             if (map.isEmpty())
   185                 map.put("all", Env.AccessKind.PROTECTED);
   187             Env.AccessKind ak = map.get(g.optName());
   188             if (ak != null && access.compareTo(ak) >= 0)
   189                 return true;
   191             ak = map.get(ALL);
   192             if (ak != null && access.compareTo(ak) >= 0) {
   193                 ak = map.get(g.notOptName());
   194                 if (ak == null || access.compareTo(ak) > 0) // note >, not >=
   195                     return true;
   196             }
   198             return false;
   199         }
   201         void setOptions(String opts) {
   202             if (opts == null)
   203                 setOption(ALL, Env.AccessKind.PRIVATE);
   204             else {
   205                 for (String opt: opts.split(","))
   206                     setOption(opt.trim().toLowerCase());
   207             }
   208         }
   210         private void setOption(String arg) throws IllegalArgumentException {
   211             if (arg.equals(Stats.OPT)) {
   212                 stats.setEnabled(true);
   213                 return;
   214             }
   216             int sep = arg.indexOf("/");
   217             if (sep > 0) {
   218                 Env.AccessKind ak = Env.AccessKind.valueOf(arg.substring(sep + 1).toUpperCase());
   219                 setOption(arg.substring(0, sep), ak);
   220             } else {
   221                 setOption(arg, null);
   222             }
   223         }
   225         private void setOption(String opt, Env.AccessKind ak) {
   226             map.put(opt, (ak != null) ? ak
   227                     : opt.startsWith("-") ? Env.AccessKind.PUBLIC : Env.AccessKind.PRIVATE);
   228         }
   230         private static final String ALL = "all";
   231     }
   233     // </editor-fold>
   235     // <editor-fold defaultstate="collapsed" desc="Statistics">
   237     /**
   238      * Optionally record statistics of different kinds of message.
   239      */
   240     static class Stats {
   241         public static final String OPT = "stats";
   242         public static final String NO_CODE = "";
   243         final ResourceBundle bundle;
   245         // tables only initialized if enabled
   246         int[] groupCounts;
   247         int[] dkindCounts;
   248         Map<String, Integer> codeCounts;
   250         Stats(ResourceBundle bundle) {
   251             this.bundle = bundle;
   252         }
   254         void setEnabled(boolean b) {
   255             if (b) {
   256                 groupCounts = new int[Messages.Group.values().length];
   257                 dkindCounts = new int[Diagnostic.Kind.values().length];
   258                 codeCounts = new HashMap<String, Integer>();
   259             } else {
   260                 groupCounts = null;
   261                 dkindCounts = null;
   262                 codeCounts = null;
   263             }
   264         }
   266         void record(Messages.Group g, Diagnostic.Kind dkind, String code) {
   267             if (codeCounts == null) {
   268                 return;
   269             }
   270             groupCounts[g.ordinal()]++;
   271             dkindCounts[dkind.ordinal()]++;
   272             if (code == null) {
   273                 code = NO_CODE;
   274             }
   275             Integer i = codeCounts.get(code);
   276             codeCounts.put(code, (i == null) ? 1 : i + 1);
   277         }
   279         void report(PrintWriter out) {
   280             if (codeCounts == null) {
   281                 return;
   282             }
   283             out.println("By group...");
   284             Table groupTable = new Table();
   285             for (Messages.Group g : Messages.Group.values()) {
   286                 groupTable.put(g.optName(), groupCounts[g.ordinal()]);
   287             }
   288             groupTable.print(out);
   289             out.println();
   290             out.println("By diagnostic kind...");
   291             Table dkindTable = new Table();
   292             for (Diagnostic.Kind k : Diagnostic.Kind.values()) {
   293                 dkindTable.put(k.toString().toLowerCase(), dkindCounts[k.ordinal()]);
   294             }
   295             dkindTable.print(out);
   296             out.println();
   297             out.println("By message kind...");
   298             Table codeTable = new Table();
   299             for (Map.Entry<String, Integer> e : codeCounts.entrySet()) {
   300                 String code = e.getKey();
   301                 String msg;
   302                 try {
   303                     msg = code.equals(NO_CODE) ? "OTHER" : bundle.getString(code);
   304                 } catch (MissingResourceException ex) {
   305                     msg = code;
   306                 }
   307                 codeTable.put(msg, e.getValue());
   308             }
   309             codeTable.print(out);
   310         }
   312         /**
   313          * A table of (int, String) sorted by decreasing int.
   314          */
   315         private static class Table {
   317             private static final Comparator<Integer> DECREASING = new Comparator<Integer>() {
   319                 public int compare(Integer o1, Integer o2) {
   320                     return o2.compareTo(o1);
   321                 }
   322             };
   323             private final TreeMap<Integer, Set<String>> map = new TreeMap<Integer, Set<String>>(DECREASING);
   325             void put(String label, int n) {
   326                 if (n == 0) {
   327                     return;
   328                 }
   329                 Set<String> labels = map.get(n);
   330                 if (labels == null) {
   331                     map.put(n, labels = new TreeSet<String>());
   332                 }
   333                 labels.add(label);
   334             }
   336             void print(PrintWriter out) {
   337                 for (Map.Entry<Integer, Set<String>> e : map.entrySet()) {
   338                     int count = e.getKey();
   339                     Set<String> labels = e.getValue();
   340                     for (String label : labels) {
   341                         out.println(String.format("%6d: %s", count, label));
   342                     }
   343                 }
   344             }
   345         }
   346     }
   347     // </editor-fold>
   348 }

mercurial