CheckResourceKeys.java revision 3573:c4a18ee691c4
1/* 2 * Copyright (c) 2010, 2016, 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/jdk.javadoc.internal.tool 29 * jdk.jdeps/com.sun.tools.classfile 30 */ 31 32import java.io.*; 33import java.lang.reflect.Layer; 34import java.lang.reflect.Module; 35import java.util.*; 36import javax.tools.*; 37import com.sun.tools.classfile.*; 38 39/** 40 * Compare string constants in javadoc classes against keys in javadoc resource bundles. 41 */ 42public class CheckResourceKeys { 43 /** 44 * Main program. 45 * Options: 46 * -finddeadkeys 47 * look for keys in resource bundles that are no longer required 48 * -findmissingkeys 49 * look for keys in resource bundles that are missing 50 * 51 * @throws Exception if invoked by jtreg and errors occur 52 */ 53 public static void main(String... args) throws Exception { 54 CheckResourceKeys c = new CheckResourceKeys(); 55 if (c.run(args)) 56 return; 57 58 if (is_jtreg()) 59 throw new Exception(c.errors + " errors occurred"); 60 else 61 System.exit(1); 62 } 63 64 static boolean is_jtreg() { 65 return (System.getProperty("test.src") != null); 66 } 67 68 /** 69 * Main entry point. 70 */ 71 boolean run(String... args) throws Exception { 72 boolean findDeadKeys = false; 73 boolean findMissingKeys = false; 74 75 if (args.length == 0) { 76 if (is_jtreg()) { 77 findDeadKeys = true; 78 findMissingKeys = true; 79 } else { 80 System.err.println("Usage: java CheckResourceKeys <options>"); 81 System.err.println("where options include"); 82 System.err.println(" -finddeadkeys find keys in resource bundles which are no longer required"); 83 System.err.println(" -findmissingkeys find keys in resource bundles that are required but missing"); 84 return true; 85 } 86 } else { 87 for (String arg: args) { 88 if (arg.equalsIgnoreCase("-finddeadkeys")) 89 findDeadKeys = true; 90 else if (arg.equalsIgnoreCase("-findmissingkeys")) 91 findMissingKeys = true; 92 else 93 error("bad option: " + arg); 94 } 95 } 96 97 if (errors > 0) 98 return false; 99 100 Set<String> codeKeys = getCodeKeys(); 101 Set<String> resourceKeys = getResourceKeys(); 102 103 System.err.println("found " + codeKeys.size() + " keys in code"); 104 System.err.println("found " + resourceKeys.size() + " keys in resource bundles"); 105 106 if (findDeadKeys) 107 findDeadKeys(codeKeys, resourceKeys); 108 109 if (findMissingKeys) 110 findMissingKeys(codeKeys, resourceKeys); 111 112 usageTests(false); 113 usageTests(true); 114 115 return (errors == 0); 116 } 117 118 void usageTests(boolean xflag) { 119 String[] argarray = { xflag ? "-X" : "-help" }; 120 StringWriter sw = new StringWriter(); 121 PrintWriter pw = new PrintWriter(sw); 122 if (jdk.javadoc.internal.tool.Main.execute(argarray, pw) == 0) { 123 pw.flush(); 124 String s = sw.toString(); 125 if (s.isEmpty()) { 126 error("no javadoc output ?"); 127 return; 128 } 129 if (sw.toString().contains("<MISSING KEY>")) { 130 System.out.println(s); 131 error("missing resources in output ?"); 132 } 133 } else { 134 error("failed to execute javadoc"); 135 } 136 } 137 138 /** 139 * Find keys in resource bundles which are probably no longer required. 140 * A key is required if there is a string in the code that is a resource key, 141 * or if the key is well-known according to various pragmatic rules. 142 */ 143 void findDeadKeys(Set<String> codeKeys, Set<String> resourceKeys) { 144 for (String rk: resourceKeys) { 145 // ignore these synthesized keys, tested by usageTests 146 if (rk.startsWith("doclet.usage.") || rk.startsWith("doclet.xusage")) 147 continue; 148 if (codeKeys.contains(rk)) 149 continue; 150 151 error("Resource key not found in code: '" + rk + '"'); 152 } 153 } 154 155 /** 156 * For all strings in the code that look like they might be 157 * a resource key, verify that a key exists. 158 */ 159 void findMissingKeys(Set<String> codeKeys, Set<String> resourceKeys) { 160 for (String ck: codeKeys) { 161 // ignore these synthesized keys, tested by usageTests 162 if (ck.startsWith("doclet.usage.") || ck.startsWith("doclet.xusage.")) 163 continue; 164 if (resourceKeys.contains(ck)) 165 continue; 166 error("No resource for \"" + ck + "\""); 167 } 168 } 169 170 /** 171 * Get the set of strings from (most of) the javadoc classfiles. 172 */ 173 Set<String> getCodeKeys() throws IOException { 174 Set<String> results = new TreeSet<String>(); 175 JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 176 try (JavaFileManager fm = c.getStandardFileManager(null, null, null)) { 177 JavaFileManager.Location javadocLoc = findJavadocLocation(fm); 178 String[] pkgs = { 179 "jdk.javadoc.internal.doclets", 180 "jdk.javadoc.internal.tool" 181 }; 182 for (String pkg: pkgs) { 183 for (JavaFileObject fo: fm.list(javadocLoc, 184 pkg, EnumSet.of(JavaFileObject.Kind.CLASS), true)) { 185 String name = fo.getName(); 186 // ignore resource files 187 if (name.matches(".*resources.[A-Za-z_0-9]+\\.class.*")) 188 continue; 189 scan(fo, results); 190 } 191 } 192 193 // special handling for code strings synthesized in 194 // com.sun.tools.doclets.internal.toolkit.util.Util.getTypeName 195 String[] extras = { 196 "AnnotationType", "Class", "Enum", "Error", "Exception", "Interface" 197 }; 198 for (String s: extras) { 199 if (results.contains("doclet." + s)) 200 results.add("doclet." + s.toLowerCase()); 201 } 202 203 // special handling for code strings synthesized in 204 // com.sun.tools.javadoc.Messager 205 results.add("javadoc.error.msg"); 206 results.add("javadoc.note.msg"); 207 results.add("javadoc.note.pos.msg"); 208 results.add("javadoc.warning.msg"); 209 210 return results; 211 } 212 } 213 214 // depending on how the test is run, javadoc may be on bootclasspath or classpath 215 JavaFileManager.Location findJavadocLocation(JavaFileManager fm) { 216 JavaFileManager.Location[] locns = 217 { StandardLocation.PLATFORM_CLASS_PATH, StandardLocation.CLASS_PATH }; 218 try { 219 for (JavaFileManager.Location l: locns) { 220 JavaFileObject fo = fm.getJavaFileForInput(l, 221 "jdk.javadoc.internal.tool.Main", JavaFileObject.Kind.CLASS); 222 if (fo != null) { 223 System.err.println("found javadoc in " + l); 224 return l; 225 } 226 } 227 } catch (IOException e) { 228 throw new Error(e); 229 } 230 throw new IllegalStateException("Cannot find javadoc"); 231 } 232 233 /** 234 * Get the set of strings from a class file. 235 * Only strings that look like they might be a resource key are returned. 236 */ 237 void scan(JavaFileObject fo, Set<String> results) throws IOException { 238 //System.err.println("scan " + fo.getName()); 239 InputStream in = fo.openInputStream(); 240 try { 241 ClassFile cf = ClassFile.read(in); 242 for (ConstantPool.CPInfo cpinfo: cf.constant_pool.entries()) { 243 if (cpinfo.getTag() == ConstantPool.CONSTANT_Utf8) { 244 String v = ((ConstantPool.CONSTANT_Utf8_info) cpinfo).value; 245 if (v.matches("(doclet|main|javadoc|tag)\\.[A-Za-z0-9-_.]+")) 246 results.add(v); 247 } 248 } 249 } catch (ConstantPoolException ignore) { 250 } finally { 251 in.close(); 252 } 253 } 254 255 /** 256 * Get the set of keys from the javadoc resource bundles. 257 */ 258 Set<String> getResourceKeys() { 259 Module jdk_javadoc = Layer.boot().findModule("jdk.javadoc").get(); 260 String[] names = { 261 "jdk.javadoc.internal.doclets.formats.html.resources.standard", 262 "jdk.javadoc.internal.doclets.toolkit.resources.doclets", 263 "jdk.javadoc.internal.tool.resources.javadoc", 264 }; 265 Set<String> results = new TreeSet<String>(); 266 for (String name : names) { 267 ResourceBundle b = ResourceBundle.getBundle(name, jdk_javadoc); 268 results.addAll(b.keySet()); 269 } 270 return results; 271 } 272 273 /** 274 * Report an error. 275 */ 276 void error(String msg) { 277 System.err.println("Error: " + msg); 278 errors++; 279 } 280 281 int errors; 282} 283