CheckResourceKeys.java revision 3573:c4a18ee691c4
150764Smarkm/* 250764Smarkm * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 3233294Sstas * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4233294Sstas * 5233294Sstas * This code is free software; you can redistribute it and/or modify it 650764Smarkm * under the terms of the GNU General Public License version 2 only, as 7233294Sstas * published by the Free Software Foundation. 8233294Sstas * 9233294Sstas * This code is distributed in the hope that it will be useful, but WITHOUT 1050764Smarkm * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11233294Sstas * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12233294Sstas * version 2 for more details (a copy is included in the LICENSE file that 1350764Smarkm * accompanied this code). 14233294Sstas * 15233294Sstas * You should have received a copy of the GNU General Public License version 16233294Sstas * 2 along with this work; if not, write to the Free Software Foundation, 1750764Smarkm * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18233294Sstas * 19233294Sstas * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20233294Sstas * or visit www.oracle.com if you need additional information or have any 2150764Smarkm * questions. 22233294Sstas */ 23233294Sstas 24233294Sstas/* 25233294Sstas * @test 26233294Sstas * @bug 8000612 27233294Sstas * @summary need test program to validate javadoc resource bundles 28233294Sstas * @modules jdk.jdeps/com.sun.tools.classfile 29233294Sstas */ 30233294Sstas 31233294Sstasimport java.io.*; 32233294Sstasimport java.lang.reflect.Layer; 3350764Smarkmimport java.lang.reflect.Module; 3450764Smarkmimport java.util.*; 3550764Smarkmimport javax.tools.*; 36127807Snectarimport com.sun.tools.classfile.*; 3750764Smarkm 3850764Smarkm/** 39127807Snectar * Compare string constants in javadoc classes against keys in javadoc resource bundles. 4050764Smarkm */ 4150764Smarkmpublic class CheckResourceKeys { 4250764Smarkm /** 4350764Smarkm * Main program. 4450764Smarkm * Options: 4550764Smarkm * -finddeadkeys 4650764Smarkm * look for keys in resource bundles that are no longer required 4750764Smarkm * -findmissingkeys 4850764Smarkm * look for keys in resource bundles that are missing 49233294Sstas * 50233294Sstas * @throws Exception if invoked by jtreg and errors occur 51233294Sstas */ 5250764Smarkm public static void main(String... args) throws Exception { 5350764Smarkm CheckResourceKeys c = new CheckResourceKeys(); 5450764Smarkm if (c.run(args)) 5550764Smarkm return; 5650764Smarkm 5750764Smarkm if (is_jtreg()) 5850764Smarkm throw new Exception(c.errors + " errors occurred"); 5950764Smarkm else 6050764Smarkm System.exit(1); 6150764Smarkm } 6250764Smarkm 6350764Smarkm static boolean is_jtreg() { 6450764Smarkm return (System.getProperty("test.src") != null); 65233294Sstas } 6650764Smarkm 6750764Smarkm /** 6850764Smarkm * Main entry point. 6950764Smarkm */ 7050764Smarkm boolean run(String... args) throws Exception { 7150764Smarkm boolean findDeadKeys = false; 7250764Smarkm boolean findMissingKeys = false; 7350764Smarkm 7450764Smarkm if (args.length == 0) { 7550764Smarkm if (is_jtreg()) { 7650764Smarkm findDeadKeys = true; 7750764Smarkm findMissingKeys = true; 7850764Smarkm } else { 7950764Smarkm System.err.println("Usage: java CheckResourceKeys <options>"); 8050764Smarkm System.err.println("where options include"); 81178846Sdfr System.err.println(" -finddeadkeys find keys in resource bundles which are no longer required"); 82178846Sdfr System.err.println(" -findmissingkeys find keys in resource bundles that are required but missing"); 8350764Smarkm return true; 8450764Smarkm } 8550764Smarkm } else { 8650764Smarkm for (String arg: args) { 87178846Sdfr if (arg.equalsIgnoreCase("-finddeadkeys")) 88178846Sdfr findDeadKeys = true; 8950764Smarkm else if (arg.equalsIgnoreCase("-findmissingkeys")) 9050764Smarkm findMissingKeys = true; 9150764Smarkm else 9250764Smarkm error("bad option: " + arg); 9350764Smarkm } 9450764Smarkm } 9550764Smarkm 9650764Smarkm if (errors > 0) 9750764Smarkm return false; 98233294Sstas 9950764Smarkm Set<String> codeKeys = getCodeKeys(); 10050764Smarkm Set<String> resourceKeys = getResourceKeys(); 10150764Smarkm 10250764Smarkm System.err.println("found " + codeKeys.size() + " keys in code"); 10350764Smarkm System.err.println("found " + resourceKeys.size() + " keys in resource bundles"); 104178846Sdfr 105178846Sdfr if (findDeadKeys) 106178846Sdfr findDeadKeys(codeKeys, resourceKeys); 107178846Sdfr 10850764Smarkm if (findMissingKeys) 10950764Smarkm findMissingKeys(codeKeys, resourceKeys); 11050764Smarkm 11150764Smarkm return (errors == 0); 11250764Smarkm } 113178846Sdfr 114178846Sdfr /** 11550764Smarkm * Find keys in resource bundles which are probably no longer required. 11650764Smarkm * A key is required if there is a string in the code that is a resource key, 11750764Smarkm * or if the key is well-known according to various pragmatic rules. 11850764Smarkm */ 11950764Smarkm void findDeadKeys(Set<String> codeKeys, Set<String> resourceKeys) { 120233294Sstas for (String rk: resourceKeys) { 121178846Sdfr if (codeKeys.contains(rk)) 122178846Sdfr continue; 12350764Smarkm 12450764Smarkm error("Resource key not found in code: '" + rk + "'"); 12550764Smarkm } 12650764Smarkm } 12750764Smarkm 128178846Sdfr /** 129178846Sdfr * For all strings in the code that look like they might be 13050764Smarkm * a resource key, verify that a key exists. 13150764Smarkm */ 13250764Smarkm void findMissingKeys(Set<String> codeKeys, Set<String> resourceKeys) { 13350764Smarkm for (String ck: codeKeys) { 13450764Smarkm if (resourceKeys.contains(ck)) 13550764Smarkm continue; 13650764Smarkm error("No resource for \"" + ck + "\""); 13750764Smarkm } 13850764Smarkm } 13950764Smarkm 14050764Smarkm /** 14150764Smarkm * Get the set of strings from (most of) the javadoc classfiles. 14250764Smarkm */ 14350764Smarkm Set<String> getCodeKeys() throws IOException { 14450764Smarkm Set<String> results = new TreeSet<String>(); 145127807Snectar JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 14650764Smarkm try (JavaFileManager fm = c.getStandardFileManager(null, null, null)) { 14750764Smarkm JavaFileManager.Location javadocLoc = findJavadocLocation(fm); 14850764Smarkm String[] pkgs = { 149178846Sdfr "com.sun.tools.doclets", 15050764Smarkm "com.sun.tools.javadoc" 15150764Smarkm }; 15250764Smarkm for (String pkg: pkgs) { 15350764Smarkm for (JavaFileObject fo: fm.list(javadocLoc, 15450764Smarkm pkg, EnumSet.of(JavaFileObject.Kind.CLASS), true)) { 15550764Smarkm String name = fo.getName(); 15650764Smarkm // ignore resource files 15750764Smarkm if (name.matches(".*resources.[A-Za-z_0-9]+\\.class.*")) 15850764Smarkm continue; 15950764Smarkm scan(fo, results); 16050764Smarkm } 16150764Smarkm } 162178846Sdfr 16350764Smarkm // special handling for code strings synthesized in 164178846Sdfr // com.sun.tools.doclets.internal.toolkit.util.Util.getTypeName 165178846Sdfr String[] extras = { 166178846Sdfr "AnnotationType", "Class", "Enum", "Error", "Exception", "Interface" 167178846Sdfr }; 16850764Smarkm for (String s: extras) { 16950764Smarkm if (results.contains("doclet." + s)) 17050764Smarkm results.add("doclet." + s.toLowerCase()); 17150764Smarkm } 17250764Smarkm 173233294Sstas // special handling for code strings synthesized in 17450764Smarkm // com.sun.tools.javadoc.Messager 175 results.add("javadoc.error.msg"); 176 results.add("javadoc.note.msg"); 177 results.add("javadoc.note.pos.msg"); 178 results.add("javadoc.warning.msg"); 179 180 return results; 181 } 182 } 183 184 // depending on how the test is run, javadoc may be on bootclasspath or classpath 185 JavaFileManager.Location findJavadocLocation(JavaFileManager fm) { 186 JavaFileManager.Location[] locns = 187 { StandardLocation.PLATFORM_CLASS_PATH, StandardLocation.CLASS_PATH }; 188 try { 189 for (JavaFileManager.Location l: locns) { 190 JavaFileObject fo = fm.getJavaFileForInput(l, 191 "com.sun.tools.javadoc.Main", JavaFileObject.Kind.CLASS); 192 if (fo != null) { 193 System.err.println("found javadoc in " + l); 194 return l; 195 } 196 } 197 } catch (IOException e) { 198 throw new Error(e); 199 } 200 throw new IllegalStateException("Cannot find javadoc"); 201 } 202 203 /** 204 * Get the set of strings from a class file. 205 * Only strings that look like they might be a resource key are returned. 206 */ 207 void scan(JavaFileObject fo, Set<String> results) throws IOException { 208 //System.err.println("scan " + fo.getName()); 209 InputStream in = fo.openInputStream(); 210 try { 211 ClassFile cf = ClassFile.read(in); 212 for (ConstantPool.CPInfo cpinfo: cf.constant_pool.entries()) { 213 if (cpinfo.getTag() == ConstantPool.CONSTANT_Utf8) { 214 String v = ((ConstantPool.CONSTANT_Utf8_info) cpinfo).value; 215 if (v.matches("(doclet|main|javadoc|tag)\\.[A-Za-z0-9-_.]+")) 216 results.add(v); 217 } 218 } 219 } catch (ConstantPoolException ignore) { 220 } finally { 221 in.close(); 222 } 223 } 224 225 /** 226 * Get the set of keys from the javadoc resource bundles. 227 */ 228 Set<String> getResourceKeys() { 229 Module jdk_javadoc = Layer.boot().findModule("jdk.javadoc").get(); 230 String[] names = { 231 "com.sun.tools.doclets.formats.html.resources.standard", 232 "com.sun.tools.doclets.internal.toolkit.resources.doclets", 233 "com.sun.tools.javadoc.resources.javadoc", 234 }; 235 Set<String> results = new TreeSet<String>(); 236 for (String name : names) { 237 ResourceBundle b = ResourceBundle.getBundle(name, jdk_javadoc); 238 results.addAll(b.keySet()); 239 } 240 return results; 241 } 242 243 /** 244 * Report an error. 245 */ 246 void error(String msg) { 247 System.err.println("Error: " + msg); 248 errors++; 249 } 250 251 int errors; 252} 253