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 return (errors == 0); 110 } 111 112 /** 113 * Find keys in resource bundles which are probably no longer required. 114 * A key is required if there is a string in the code that is a resource key, 115 * or if the key is well-known according to various pragmatic rules. 116 */ 117 void findDeadKeys(Set<String> codeKeys, Set<String> resourceKeys) { 118 for (String rk: resourceKeys) { 119 if (codeKeys.contains(rk)) 120 continue; 121 122 error("Resource key not found in code: " + rk); 123 } 124 } 125 126 /** 127 * For all strings in the code that look like they might be 128 * a resource key, verify that a key exists. 129 */ 130 void findMissingKeys(Set<String> codeKeys, Set<String> resourceKeys) { 131 for (String ck: codeKeys) { 132 if (resourceKeys.contains(ck)) 133 continue; 134 error("No resource for \"" + ck + "\""); 135 } 136 } 137 138 /** 139 * Get the set of strings from (most of) the javadoc classfiles. 140 */ 141 Set<String> getCodeKeys() throws IOException { 142 Set<String> results = new TreeSet<String>(); 143 JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 144 try (JavaFileManager fm = c.getStandardFileManager(null, null, null)) { 145 JavaFileManager.Location javadocLoc = findJavadocLocation(fm); 146 String[] pkgs = { 147 "com.sun.tools.doclets", 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 String[] names = { 228 "com.sun.tools.doclets.formats.html.resources.standard", 229 "com.sun.tools.doclets.internal.toolkit.resources.doclets", 230 "com.sun.tools.javadoc.resources.javadoc", 231 }; 232 Set<String> results = new TreeSet<String>(); 233 for (String name : names) { 234 ResourceBundle b = ResourceBundle.getBundle(name); 235 results.addAll(b.keySet()); 236 } 237 return results; 238 } 239 240 /** 241 * Report an error. 242 */ 243 void error(String msg) { 244 System.err.println("Error: " + msg); 245 errors++; 246 } 247 248 int errors; 249 } | 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 } |