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

Tue, 17 Dec 2013 10:55:59 +0100

author
jlahoda
date
Tue, 17 Dec 2013 10:55:59 +0100
changeset 2413
fe033d997ddf
parent 1455
75ab654b5cd5
child 2525
2eb010b6cb22
permissions
-rw-r--r--

8029800: Flags.java uses String.toLowerCase without specifying Locale
Summary: Introducing StringUtils.toLowerCase/toUpperCase independent on the default locale, converting almost all usages of String.toLowerCase/toUpperCase to use the new methods.
Reviewed-by: jjg, bpatel

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

mercurial