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