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