test/langtools/tools/javadoc/CheckResourceKeys.java
changeset 47216 71c04702a3d5
parent 45742 36bf0f2436ad
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2010, 2017, 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.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @bug 8000612
       
    27  * @summary need test program to validate javadoc resource bundles
       
    28  * @modules jdk.javadoc/com.sun.tools.javadoc.resources:open
       
    29  *          jdk.jdeps/com.sun.tools.classfile
       
    30  */
       
    31 
       
    32 import java.io.*;
       
    33 import java.util.*;
       
    34 import javax.tools.*;
       
    35 import com.sun.tools.classfile.*;
       
    36 
       
    37 /**
       
    38  * Compare string constants in javadoc classes against keys in javadoc resource bundles.
       
    39  */
       
    40 public class CheckResourceKeys {
       
    41     /**
       
    42      * Main program.
       
    43      * Options:
       
    44      * -finddeadkeys
       
    45      *      look for keys in resource bundles that are no longer required
       
    46      * -findmissingkeys
       
    47      *      look for keys in resource bundles that are missing
       
    48      *
       
    49      * @throws Exception if invoked by jtreg and errors occur
       
    50      */
       
    51     public static void main(String... args) throws Exception {
       
    52         CheckResourceKeys c = new CheckResourceKeys();
       
    53         if (c.run(args))
       
    54             return;
       
    55 
       
    56         if (is_jtreg())
       
    57             throw new Exception(c.errors + " errors occurred");
       
    58         else
       
    59             System.exit(1);
       
    60     }
       
    61 
       
    62     static boolean is_jtreg() {
       
    63         return (System.getProperty("test.src") != null);
       
    64     }
       
    65 
       
    66     /**
       
    67      * Main entry point.
       
    68      */
       
    69     boolean run(String... args) throws Exception {
       
    70         boolean findDeadKeys = false;
       
    71         boolean findMissingKeys = false;
       
    72 
       
    73         if (args.length == 0) {
       
    74             if (is_jtreg()) {
       
    75                 findDeadKeys = true;
       
    76                 findMissingKeys = true;
       
    77             } else {
       
    78                 System.err.println("Usage: java CheckResourceKeys <options>");
       
    79                 System.err.println("where options include");
       
    80                 System.err.println("  -finddeadkeys      find keys in resource bundles which are no longer required");
       
    81                 System.err.println("  -findmissingkeys   find keys in resource bundles that are required but missing");
       
    82                 return true;
       
    83             }
       
    84         } else {
       
    85             for (String arg: args) {
       
    86                 if (arg.equalsIgnoreCase("-finddeadkeys"))
       
    87                     findDeadKeys = true;
       
    88                 else if (arg.equalsIgnoreCase("-findmissingkeys"))
       
    89                     findMissingKeys = true;
       
    90                 else
       
    91                     error("bad option: " + arg);
       
    92             }
       
    93         }
       
    94 
       
    95         if (errors > 0)
       
    96             return false;
       
    97 
       
    98         Set<String> codeKeys = getCodeKeys();
       
    99         Set<String> resourceKeys = getResourceKeys();
       
   100 
       
   101         System.err.println("found " + codeKeys.size() + " keys in code");
       
   102         System.err.println("found " + resourceKeys.size() + " keys in resource bundles");
       
   103 
       
   104         if (findDeadKeys)
       
   105             findDeadKeys(codeKeys, resourceKeys);
       
   106 
       
   107         if (findMissingKeys)
       
   108             findMissingKeys(codeKeys, resourceKeys);
       
   109 
       
   110         return (errors == 0);
       
   111     }
       
   112 
       
   113     /**
       
   114      * Find keys in resource bundles which are probably no longer required.
       
   115      * A key is required if there is a string in the code that is a resource key,
       
   116      * or if the key is well-known according to various pragmatic rules.
       
   117      */
       
   118     void findDeadKeys(Set<String> codeKeys, Set<String> resourceKeys) {
       
   119         for (String rk: resourceKeys) {
       
   120             if (codeKeys.contains(rk))
       
   121                 continue;
       
   122 
       
   123             error("Resource key not found in code: '" + rk + "'");
       
   124         }
       
   125     }
       
   126 
       
   127     /**
       
   128      * For all strings in the code that look like they might be
       
   129      * a resource key, verify that a key exists.
       
   130      */
       
   131     void findMissingKeys(Set<String> codeKeys, Set<String> resourceKeys) {
       
   132         for (String ck: codeKeys) {
       
   133             if (resourceKeys.contains(ck))
       
   134                 continue;
       
   135             error("No resource for \"" + ck + "\"");
       
   136         }
       
   137     }
       
   138 
       
   139     /**
       
   140      * Get the set of strings from (most of) the javadoc classfiles.
       
   141      */
       
   142     Set<String> getCodeKeys() throws IOException {
       
   143         Set<String> results = new TreeSet<String>();
       
   144         JavaCompiler c = ToolProvider.getSystemJavaCompiler();
       
   145         try (JavaFileManager fm = c.getStandardFileManager(null, null, null)) {
       
   146             JavaFileManager.Location javadocLoc = findJavadocLocation(fm);
       
   147             String[] pkgs = {
       
   148                 "com.sun.tools.javadoc"
       
   149             };
       
   150             for (String pkg: pkgs) {
       
   151                 for (JavaFileObject fo: fm.list(javadocLoc,
       
   152                         pkg, EnumSet.of(JavaFileObject.Kind.CLASS), true)) {
       
   153                     String name = fo.getName();
       
   154                     // ignore resource files
       
   155                     if (name.matches(".*resources.[A-Za-z_0-9]+\\.class.*"))
       
   156                         continue;
       
   157                     scan(fo, results);
       
   158                 }
       
   159             }
       
   160 
       
   161             // special handling for code strings synthesized in
       
   162             // com.sun.tools.doclets.internal.toolkit.util.Util.getTypeName
       
   163             String[] extras = {
       
   164                 "AnnotationType", "Class", "Enum", "Error", "Exception", "Interface"
       
   165             };
       
   166             for (String s: extras) {
       
   167                 if (results.contains("doclet." + s))
       
   168                     results.add("doclet." + s.toLowerCase());
       
   169             }
       
   170 
       
   171             // special handling for code strings synthesized in
       
   172             // com.sun.tools.javadoc.Messager
       
   173             results.add("javadoc.error.msg");
       
   174             results.add("javadoc.note.msg");
       
   175             results.add("javadoc.note.pos.msg");
       
   176             results.add("javadoc.warning.msg");
       
   177 
       
   178             return results;
       
   179         }
       
   180     }
       
   181 
       
   182     // depending on how the test is run, javadoc may be on bootclasspath or classpath
       
   183     JavaFileManager.Location findJavadocLocation(JavaFileManager fm) {
       
   184         JavaFileManager.Location[] locns =
       
   185             { StandardLocation.PLATFORM_CLASS_PATH, StandardLocation.CLASS_PATH };
       
   186         try {
       
   187             for (JavaFileManager.Location l: locns) {
       
   188                 JavaFileObject fo = fm.getJavaFileForInput(l,
       
   189                     "com.sun.tools.javadoc.Main", JavaFileObject.Kind.CLASS);
       
   190                 if (fo != null) {
       
   191                     System.err.println("found javadoc in " + l);
       
   192                     return l;
       
   193                 }
       
   194             }
       
   195         } catch (IOException e) {
       
   196             throw new Error(e);
       
   197         }
       
   198         throw new IllegalStateException("Cannot find javadoc");
       
   199     }
       
   200 
       
   201     /**
       
   202      * Get the set of strings from a class file.
       
   203      * Only strings that look like they might be a resource key are returned.
       
   204      */
       
   205     void scan(JavaFileObject fo, Set<String> results) throws IOException {
       
   206         //System.err.println("scan " + fo.getName());
       
   207         InputStream in = fo.openInputStream();
       
   208         try {
       
   209             ClassFile cf = ClassFile.read(in);
       
   210             for (ConstantPool.CPInfo cpinfo: cf.constant_pool.entries()) {
       
   211                 if (cpinfo.getTag() == ConstantPool.CONSTANT_Utf8) {
       
   212                     String v = ((ConstantPool.CONSTANT_Utf8_info) cpinfo).value;
       
   213                     if (v.matches("(doclet|main|javadoc|tag)\\.[A-Za-z0-9-_.]+"))
       
   214                         results.add(v);
       
   215                 }
       
   216             }
       
   217         } catch (ConstantPoolException ignore) {
       
   218         } finally {
       
   219             in.close();
       
   220         }
       
   221     }
       
   222 
       
   223     /**
       
   224      * Get the set of keys from the javadoc resource bundles.
       
   225      */
       
   226     Set<String> getResourceKeys() {
       
   227         Module jdk_javadoc = ModuleLayer.boot().findModule("jdk.javadoc").get();
       
   228         String[] names = {
       
   229                 "com.sun.tools.javadoc.resources.javadoc",
       
   230         };
       
   231         Set<String> results = new TreeSet<String>();
       
   232         for (String name : names) {
       
   233             ResourceBundle b = ResourceBundle.getBundle(name, jdk_javadoc);
       
   234             results.addAll(b.keySet());
       
   235         }
       
   236         return results;
       
   237     }
       
   238 
       
   239     /**
       
   240      * Report an error.
       
   241      */
       
   242     void error(String msg) {
       
   243         System.err.println("Error: " + msg);
       
   244         errors++;
       
   245     }
       
   246 
       
   247     int errors;
       
   248 }