1 /* 2 * Copyright (c) 2010, 2012, 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 7013272 7127924 27 * @summary Automatically generate info about how compiler resource keys are used 28 * @build Example ArgTypeCompilerFactory MessageFile MessageInfo 29 * @run main/othervm MessageInfo 30 */ 31 /* 32 * See CR 7127924 for info on why othervm is used. 33 */ 34 35 import java.io.*; 36 import java.text.SimpleDateFormat; 37 import java.util.*; 38 39 /** 40 * Utility to manipulate compiler.properties, and suggest info comments based 41 * on information derived from running examples. 42 * 43 * Options: 44 * -examples dir location of examples directory 45 * -o file output file 46 * -check just check message file 47 * -ensureNewlines ensure newline after each entry 48 * -fixIndent fix indentation of continuation lines 49 * -sort sort messages 50 * -verbose verbose output 51 * -replace replace comments instead of merging comments 52 * file javac compiler.properties file 53 * 54 */ 55 public class MessageInfo { 56 public static void main(String... args) throws Exception { 57 jtreg = (System.getProperty("test.src") != null); 58 File tmpDir; 59 if (jtreg) { 60 // use standard jtreg scratch directory: the current directory 61 tmpDir = new File(System.getProperty("user.dir")); 62 } else { 63 tmpDir = new File(System.getProperty("java.io.tmpdir"), 64 MessageInfo.class.getName() 65 + (new SimpleDateFormat("yyMMddHHmmss")).format(new Date())); 66 } 67 Example.setTempDir(tmpDir); 68 Example.Compiler.factory = new ArgTypeCompilerFactory(); 69 70 MessageInfo mi = new MessageInfo(); 71 72 try { 73 if (mi.run(args)) 74 return; 75 } finally { 76 /* VERY IMPORTANT NOTE. In jtreg mode, tmpDir is set to the 77 * jtreg scratch directory, which is the current directory. 78 * In case someone is faking jtreg mode, make sure to only 79 * clean tmpDir when it is reasonable to do so. 80 */ 81 if (tmpDir.isDirectory() && 82 tmpDir.getName().startsWith(MessageInfo.class.getName())) { 83 if (clean(tmpDir)) 84 tmpDir.delete(); 85 } 86 } 87 88 if (jtreg) 89 throw new Exception(mi.errors + " errors occurred"); 90 else 91 System.exit(1); 92 } 93 94 void usage() { 95 System.out.println("Usage:"); 96 System.out.println(" java MessageInfo [options] [file]"); 97 System.out.println("where options include"); 98 System.out.println(" -examples dir location of examples directory"); 99 System.out.println(" -o file output file"); 100 System.out.println(" -check just check message file"); 101 System.out.println(" -ensureNewlines ensure newline after each entry"); 102 System.out.println(" -fixIndent fix indentation of continuation lines"); 103 System.out.println(" -sort sort messages"); 104 System.out.println(" -verbose verbose output"); 105 System.out.println(" -replace replace comments instead of merging comments"); 106 System.out.println(" file javac compiler.properties file"); 107 } 108 109 boolean run(String... args) { 110 File testSrc = new File(System.getProperty("test.src", ".")); 111 File examplesDir = new File(testSrc, "examples"); 112 File notYetFile = null; 113 File msgFile = null; 114 File outFile = null; 115 boolean verbose = false; 116 boolean ensureNewlines = false; 117 boolean fixIndent = false; 118 boolean sort = false; 119 boolean replace = false; 120 boolean check = jtreg; // default true in jtreg mode 121 122 for (int i = 0; i < args.length; i++) { 123 String arg = args[i]; 124 if (arg.equals("-examples") && (i + 1) < args.length) 125 examplesDir = new File(args[++i]); 126 else if(arg.equals("-notyet") && (i + 1) < args.length) 127 notYetFile = new File(args[++i]); 128 else if (arg.equals("-ensureNewlines")) 129 ensureNewlines = true; 130 else if (arg.equals("-fixIndent")) 131 fixIndent = true; 132 else if (arg.equals("-sort")) 133 sort = true; 134 else if (arg.equals("-verbose")) 135 verbose = true; 136 else if (arg.equals("-replace")) 137 replace = true; 138 else if (arg.equals("-check")) 139 check = true; 140 else if (arg.equals("-o") && (i + 1) < args.length) 141 outFile = new File(args[++i]); 142 else if (arg.startsWith("-")) { 143 error("unknown option: " + arg); 144 return false; 145 } else if (i == args.length - 1) { 146 msgFile = new File(arg); 147 } else { 148 error("unknown arg: " + arg); 149 return false; 150 } 151 } 152 153 if (!check && outFile == null) { 154 usage(); 155 return true; 156 } 157 158 if ((ensureNewlines || fixIndent || sort) && outFile == null) { 159 error("must set output file for these options"); 160 return false; 161 } 162 163 if (notYetFile == null) { 164 notYetFile = new File(examplesDir.getParentFile(), "examples.not-yet.txt"); 165 } 166 167 if (msgFile == null) { 168 for (File d = testSrc; d != null; d = d.getParentFile()) { 169 if (new File(d, "TEST.ROOT").exists()) { 170 d = d.getParentFile(); 171 File f = new File(d, "src/share/classes/com/sun/tools/javac/resources/compiler.properties"); 172 if (f.exists()) { 173 msgFile = f; 174 break; 175 } 176 } 177 } 178 if (msgFile == null) { 179 if (jtreg) { 180 System.err.println("Warning: no message file available, test skipped"); 181 return true; 182 } 183 error("no message file available"); 184 return false; 185 } 186 } 187 188 MessageFile mf; 189 try { 190 mf = new MessageFile(msgFile); 191 } catch (IOException e) { 192 error("problem reading message file: " + e); 193 return false; 194 } 195 196 Map<String, Set<String>> msgInfo = runExamples(examplesDir, verbose); 197 198 if (ensureNewlines) 199 ensureNewlines(mf); 200 201 if (fixIndent) 202 fixIndent(mf); 203 204 if (sort) 205 sort(mf, true); 206 207 for (Map.Entry<String, Set<String>> e: msgInfo.entrySet()) { 208 String k = e.getKey(); 209 Set<String> suggestions = e.getValue(); 210 MessageFile.Message m = mf.messages.get(k); 211 if (m == null) { 212 error("Can't find message for " + k + " in message file"); 213 continue; 214 } 215 216 MessageFile.Info info = m.getInfo(); 217 Set<Integer> placeholders = m.getPlaceholders(); 218 MessageFile.Info suggestedInfo = new MessageFile.Info(suggestions); 219 suggestedInfo.markUnused(placeholders); 220 221 if (!info.isEmpty()) { 222 if (info.contains(suggestedInfo)) 223 continue; 224 if (!replace) { 225 if (info.fields.size() != suggestedInfo.fields.size()) 226 error("Cannot merge info for " + k); 227 else 228 suggestedInfo.merge(info); 229 } 230 } 231 232 if (outFile == null) { 233 System.err.println("suggest for " + k); 234 System.err.println(suggestedInfo.toComment()); 235 } else 236 m.setInfo(suggestedInfo); 237 } 238 239 if (check) 240 check(mf, notYetFile); 241 242 try { 243 if (outFile != null) 244 mf.write(outFile); 245 } catch (IOException e) { 246 error("problem writing file: " + e); 247 return false; 248 } 249 250 return (errors == 0); 251 } 252 253 void check(MessageFile mf, File notYetFile) { 254 Set<String> notYetList = null; 255 for (Map.Entry<String, MessageFile.Message> e: mf.messages.entrySet()) { 256 String key = e.getKey(); 257 MessageFile.Message m = e.getValue(); 258 if (m.needInfo() && m.getInfo().isEmpty()) { 259 if (notYetList == null) 260 notYetList = getNotYetList(notYetFile); 261 if (notYetList.contains(key)) 262 System.err.println("Warning: no info for " + key); 263 else 264 error("no info for " + key); 265 } 266 } 267 268 } 269 270 void ensureNewlines(MessageFile mf) { 271 for (MessageFile.Message m: mf.messages.values()) { 272 MessageFile.Line l = m.firstLine; 273 while (l.text.endsWith("\\")) 274 l = l.next; 275 if (l.next != null && !l.next.text.isEmpty()) 276 l.insertAfter(""); 277 } 278 } 279 280 void fixIndent(MessageFile mf) { 281 for (MessageFile.Message m: mf.messages.values()) { 282 MessageFile.Line l = m.firstLine; 283 while (l.text.endsWith("\\") && l.next != null) { 284 if (!l.next.text.matches("^ \\S.*")) 285 l.next.text = " " + l.next.text.trim(); 286 l = l.next; 287 } 288 } 289 } 290 291 void sort(MessageFile mf, boolean includePrecedingNewlines) { 292 for (MessageFile.Message m: mf.messages.values()) { 293 for (MessageFile.Line l: m.getLines(includePrecedingNewlines)) { 294 l.remove(); 295 mf.lastLine.insertAfter(l); 296 } 297 } 298 } 299 300 Map<String, Set<String>> runExamples(File examplesDir, boolean verbose) { 301 Map<String, Set<String>> map = new TreeMap<String, Set<String>>(); 302 for (Example e: getExamples(examplesDir)) { 303 StringWriter sw = new StringWriter(); 304 PrintWriter pw = new PrintWriter(sw); 305 e.run(pw, true, verbose); 306 pw.close(); 307 String[] lines = sw.toString().split("\n"); 308 for (String line: lines) { 309 if (!line.startsWith("compiler.")) 310 continue; 311 int colon = line.indexOf(":"); 312 if (colon == -1) 313 continue; 314 String key = line.substring(0, colon); 315 StringBuilder sb = new StringBuilder(); 316 sb.append("# "); 317 int i = 0; 318 String[] descs = line.substring(colon + 1).split(", *"); 319 for (String desc: descs) { 320 if (i > 0) sb.append(", "); 321 sb.append(i++); 322 sb.append(": "); 323 sb.append(desc.trim()); 324 } 325 Set<String> set = map.get(key); 326 if (set == null) 327 map.put(key, set = new TreeSet<String>()); 328 set.add(sb.toString()); 329 } 330 } 331 332 return map; 333 } 334 335 /** 336 * Get the complete set of examples to be checked. 337 */ 338 Set<Example> getExamples(File examplesDir) { 339 Set<Example> results = new TreeSet<Example>(); 340 for (File f: examplesDir.listFiles()) { 341 if (isValidExample(f)) 342 results.add(new Example(f)); 343 } 344 return results; 345 } 346 347 boolean isValidExample(File f) { 348 return (f.isDirectory() && (!jtreg || f.list().length > 0)) || 349 (f.isFile() && f.getName().endsWith(".java")); 350 } 351 352 /** 353 * Get the contents of the "not-yet" list. 354 */ 355 Set<String> getNotYetList(File file) { 356 Set<String> results = new TreeSet<String>(); 357 try { 358 String[] lines = read(file).split("[\r\n]"); 359 for (String line: lines) { 360 int hash = line.indexOf("#"); 361 if (hash != -1) 362 line = line.substring(0, hash).trim(); 363 if (line.matches("[A-Za-z0-9-_.]+")) 364 results.add(line); 365 } 366 } catch (IOException e) { 367 throw new Error(e); 368 } 369 return results; 370 } 371 372 /** 373 * Read the contents of a file. 374 */ 375 String read(File f) throws IOException { 376 byte[] bytes = new byte[(int) f.length()]; 377 DataInputStream in = new DataInputStream(new FileInputStream(f)); 378 try { 379 in.readFully(bytes); 380 } finally { 381 in.close(); 382 } 383 return new String(bytes); 384 } 385 386 /** 387 * Report an error. 388 */ 389 void error(String msg) { 390 System.err.println("Error: " + msg); 391 errors++; 392 } 393 394 static boolean jtreg; 395 396 int errors; 397 398 /** 399 * Clean the contents of a directory. 400 */ 401 static boolean clean(File dir) { 402 boolean ok = true; 403 for (File f: dir.listFiles()) { 404 if (f.isDirectory()) 405 ok &= clean(f); 406 ok &= f.delete(); 407 } 408 return ok; 409 } 410 411 }