1 /* 2 * Copyright (c) 2010, 2013, 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 6968063 7127924 27 * @summary provide examples of code that generate diagnostics 28 * @build ArgTypeCompilerFactory Example HTMLWriter RunExamples DocCommentProcessor 29 * @run main/othervm RunExamples 30 */ 31 /* 32 * See CR 7127924 for info on why othervm is used. 33 */ 34 35 import java.io.*; 36 import java.nio.file.*; 37 import java.nio.file.attribute.BasicFileAttributes; 38 import java.util.*; 39 import java.util.regex.Matcher; 40 import java.util.regex.Pattern; 41 42 /** 43 * Utility to run selected or all examples, writing results to 44 * stdout, a plain text file or an HTML file. This program can be 45 * run standalone, or as a jtreg test. 46 * 47 * Options: 48 * -examples dir directory of examples. Defaults to ${test.src}/examples 49 * -raw run examples with -XDrawDiagnostics 50 * -showFiles include text of source files in the output 51 * -verbose verbose output 52 * -o file write output to file: format will be HTML if 53 * file has .html extension; otherwise it will be plain text. 54 * default is to stdout 55 * -title string specify a title, only applies to HTML output 56 */ 57 public class RunExamples { main(String... args)58 public static void main(String... args) throws Exception { 59 jtreg = (System.getProperty("test.src") != null); 60 Path tmpDir; 61 boolean deleteOnExit; 62 if (jtreg) { 63 // use standard jtreg scratch directory: the current directory 64 tmpDir = Paths.get(System.getProperty("user.dir")); 65 deleteOnExit = false; 66 } else { 67 tmpDir = Files.createTempDirectory(Paths.get(System.getProperty("java.io.tmpdir")), 68 RunExamples.class.getName()); 69 deleteOnExit = true; 70 } 71 Example.setTempDir(tmpDir.toFile()); 72 73 RunExamples r = new RunExamples(); 74 75 try { 76 if (r.run(args)) 77 return; 78 } finally { 79 if (deleteOnExit) { 80 clean(tmpDir); 81 } 82 } 83 84 if (jtreg) 85 throw new Exception(r.errors + " errors occurred"); 86 else 87 System.exit(1); 88 } 89 run(String... args)90 boolean run(String... args) { 91 Set<String> selectedKeys = new TreeSet<String>(); 92 Set<Example> selectedExamples = new TreeSet<Example>(); 93 File testSrc = new File(System.getProperty("test.src", ".")); 94 File examplesDir = new File(testSrc, "examples"); 95 File outFile = null; 96 boolean raw = false; 97 boolean showFiles = false; 98 boolean verbose = false; 99 boolean argTypes = false; 100 String title = null; 101 102 for (int i = 0; i < args.length; i++) { 103 String arg = args[i]; 104 if (arg.equals("-k") && (i + 1) < args.length) 105 selectedKeys.add(args[++i]); 106 else if (arg.equals("-examples") && (i + 1) < args.length) 107 examplesDir = new File(args[++i]); 108 else if (arg.equals("-raw")) 109 raw = true; 110 else if (arg.equals("-showFiles")) 111 showFiles = true; 112 else if (arg.equals("-verbose")) 113 verbose = true; 114 else if (arg.equals("-o") && (i + 1) < args.length) 115 outFile = new File(args[++i]); 116 else if (arg.equals("-title") && (i + 1) < args.length) 117 title = args[++i]; 118 else if (arg.equals("-argtypes")) 119 argTypes = true; 120 else if (arg.startsWith("-")) { 121 error("unknown option: " + arg); 122 return false; 123 } else { 124 while (i < args.length) { 125 File f = new File(examplesDir, args[i]); 126 selectedExamples.add(new Example(f)); 127 i++; 128 } 129 } 130 } 131 132 // special mode to show message keys and the types of the args that 133 // are used. 134 if (argTypes) 135 Example.Compiler.factory = new ArgTypeCompilerFactory(); 136 137 if (selectedKeys.size() > 0) { 138 Set<Example> examples = getExamples(examplesDir); 139 nextKey: 140 for (String k: selectedKeys) { 141 for (Example e: examples) { 142 if (e.getDeclaredKeys().contains(k)) 143 continue nextKey; 144 } 145 error("Key " + k + ": no examples found"); 146 } 147 } else { 148 if (selectedExamples.isEmpty()) 149 selectedExamples = getExamples(examplesDir); 150 } 151 152 try { 153 Runner r; 154 if (outFile == null) { 155 PrintWriter out = new PrintWriter(System.out); 156 r = new TextRunner(out, showFiles, raw, verbose); 157 } else if (outFile.getName().endsWith(".html")) 158 r = new HTMLRunner(outFile, showFiles, raw, verbose, title); 159 else 160 r = new TextRunner(outFile, showFiles, raw, verbose); 161 r.run(selectedExamples); 162 r.close(); 163 } catch (IOException e) { 164 error("Error writing output: " + e); 165 } 166 167 return (errors == 0); 168 } 169 170 /** 171 * Get the complete set of examples to be checked. 172 */ getExamples(File examplesDir)173 Set<Example> getExamples(File examplesDir) { 174 Set<Example> results = new TreeSet<Example>(); 175 for (File f: examplesDir.listFiles()) { 176 if (isValidExample(f)) 177 results.add(new Example(f)); 178 } 179 return results; 180 } 181 isValidExample(File f)182 boolean isValidExample(File f) { 183 return (f.isDirectory() && (!jtreg || f.list().length > 0)) || 184 (f.isFile() && f.getName().endsWith(".java")); 185 } 186 187 /** 188 * Report an error. 189 */ error(String msg)190 void error(String msg) { 191 System.err.println("Error: " + msg); 192 errors++; 193 } 194 195 static boolean jtreg; 196 197 int errors; 198 199 /** 200 * Clean the contents of a directory. 201 */ clean(Path dir)202 static void clean(Path dir) throws IOException { 203 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { 204 @Override 205 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 206 Files.delete(file); 207 return super.visitFile(file, attrs); 208 } 209 210 @Override 211 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { 212 if (exc == null) Files.delete(dir); 213 return super.postVisitDirectory(dir, exc); 214 } 215 }); 216 } 217 218 static abstract class Runner { Runner(boolean showFiles, boolean raw, boolean verbose)219 Runner(boolean showFiles, boolean raw, boolean verbose) { 220 this.showFiles = showFiles; 221 this.raw = raw; 222 this.verbose = verbose; 223 } 224 close()225 void close() throws IOException { } 226 run(Collection<Example> examples)227 void run(Collection<Example> examples) throws IOException { 228 for (Example e: examples) { 229 startExample(e); 230 if (showFiles) { 231 showFile(e, e.infoFile); 232 Set<File> srcFiles = new TreeSet<File>(e.srcFiles); 233 srcFiles.remove(e.infoFile); 234 showFiles(e, srcFiles); 235 showFiles(e, e.srcPathFiles); 236 showFiles(e, e.procFiles); 237 showFiles(e, e.supportFiles); 238 } 239 run(e); 240 } 241 } 242 showFiles(Example e, Collection<File> files)243 void showFiles(Example e, Collection<File> files) throws IOException { 244 for (File f: files) 245 showFile(e, f); 246 } 247 startExample(Example e)248 abstract void startExample(Example e) throws IOException; 249 showFile(Example e, File f)250 abstract void showFile(Example e, File f) throws IOException; 251 run(Example e)252 abstract void run(Example e) throws IOException; 253 read(File f)254 protected String read(File f) throws IOException { 255 byte[] bytes = new byte[(int) f.length()]; 256 DataInputStream in = new DataInputStream(new FileInputStream(f)); 257 try { 258 in.readFully(bytes); 259 } finally { 260 in.close(); 261 } 262 return new String(bytes); 263 } 264 265 protected Pattern copyrightHeaderPat = 266 Pattern.compile("(?s)(/\\*.*?Copyright.*?\\*/\n)\\s*(.*)"); 267 protected Pattern infoHeaderPat = 268 Pattern.compile("(?s)((?://\\s*[a-z]+:[^\n]*\n)+)\\s*(.*)"); 269 270 protected boolean showFiles; 271 protected boolean raw; 272 protected boolean verbose; 273 } 274 275 static class TextRunner extends Runner { TextRunner(File file, boolean showFiles, boolean raw, boolean verbose)276 TextRunner(File file, boolean showFiles, boolean raw, boolean verbose) 277 throws IOException { 278 super(showFiles, raw, verbose); 279 this.file = file; 280 out = new PrintWriter(new FileWriter(file)); 281 } 282 TextRunner(PrintWriter out, boolean showFiles, boolean raw, boolean verbose)283 TextRunner(PrintWriter out, boolean showFiles, boolean raw, boolean verbose) 284 throws IOException { 285 super(showFiles, raw, verbose); 286 this.out = out; 287 } 288 289 @Override close()290 void close() { 291 if (file != null) 292 out.close(); 293 } 294 295 @Override startExample(Example e)296 void startExample(Example e) { 297 out.println("----- " + e.getName() + " --------------------"); 298 out.println(); 299 } 300 301 @Override showFile(Example e, File f)302 void showFile(Example e, File f) { 303 out.println("--- " + f); 304 String text; 305 try { 306 text = read(f); 307 } catch (IOException ex) { 308 text = "Error reading " + f + "; " + ex; 309 } 310 Matcher m = copyrightHeaderPat.matcher(text); 311 if (m.matches()) { 312 out.println("(Copyright)"); 313 writeLines(m.group(2)); 314 } else { 315 writeLines(text); 316 } 317 out.println(); 318 } 319 320 @Override run(Example e)321 void run(Example e) { 322 // only show Output: header if also showing files 323 if (showFiles) 324 out.println("--- Output:"); 325 e.run(out, raw, verbose); 326 out.println(); 327 } 328 writeLines(String text)329 void writeLines(String text) { 330 for (String line: text.split("\n")) 331 out.println(line); 332 } 333 334 File file; 335 PrintWriter out; 336 } 337 338 static class HTMLRunner extends Runner { HTMLRunner(File file, boolean showFiles, boolean raw, boolean verbose, String title)339 HTMLRunner(File file, boolean showFiles, boolean raw, boolean verbose, String title) 340 throws IOException { 341 super(showFiles, raw, verbose); 342 this.file = file; 343 PrintWriter out = new PrintWriter(new FileWriter(file)); 344 html = new HTMLWriter(out); 345 html.startTag(HTMLWriter.HEAD); 346 if (title != null) { 347 html.startTag(HTMLWriter.TITLE); 348 html.write(title); 349 html.endTag(HTMLWriter.TITLE); 350 } 351 html.startTag(HTMLWriter.STYLE); 352 html.newLine(); 353 html.writeLine("div.file { background-color:#e0ffe0; margin-left:30px; margin-right:30px;\n" 354 + " padding: 3px; border: thin solid silver; }"); 355 html.writeLine("p.file { white-space: pre-wrap; font-family:monospace; margin: 0; }"); 356 html.writeLine("div.output { background-color:#e0e0ff; margin-left:30px; margin-right:30px;\n" 357 + " padding: 3px; border: thin solid silver; }"); 358 html.writeLine("p.output { white-space: pre-wrap; font-family:monospace; margin: 0; }"); 359 html.writeLine("table.index { border: thin solid silver; }"); 360 html.writeLine(".copyright { font-size: x-small }"); 361 html.writeLine(".hidden { display:none }"); 362 html.writeLine(".unhidden { display:block }"); 363 html.writeLine(".odd { background-color: #e0e0e0 }"); 364 html.writeLine(".even { background-color: white }"); 365 html.endTag(HTMLWriter.STYLE); 366 html.startTag(HTMLWriter.SCRIPT); 367 html.writeAttr(HTMLWriter.TYPE, HTMLWriter.TEXT_JAVASCRIPT); 368 html.writeLine("\nfunction unhide(id) {\n" 369 + " var item = document.getElementById(id);\n" 370 + " if (item) {\n" 371 + " item.className=(item.className=='hidden')?'unhidden':'hidden';\n" 372 + " }\n" 373 + "}"); 374 html.endTag(HTMLWriter.SCRIPT); 375 html.endTag(HTMLWriter.HEAD); 376 html.startTag(HTMLWriter.BODY); 377 if (title != null) { 378 html.startTag(TITLE_HEADER); 379 html.write(title); 380 html.endTag(TITLE_HEADER); 381 } 382 } 383 384 @Override close()385 void close() throws IOException { 386 html.endTag(HTMLWriter.BODY); 387 html.newLine(); 388 html.flush(); 389 } 390 391 @Override run(Collection<Example> examples)392 void run(Collection<Example> examples) throws IOException { 393 if (examples.size() > 1) 394 writeIndex(examples); 395 super.run(examples); 396 } 397 writeIndex(Collection<Example> examples)398 void writeIndex(Collection<Example> examples) throws IOException { 399 Map<String, Set<Example>> index = new TreeMap<String, Set<Example>>(); 400 Set<String> initials = new HashSet<String>(); 401 for (Example e: examples) { 402 for (String k: e.getDeclaredKeys()) { 403 Set<Example> s = index.get(k); 404 if (s == null) 405 index.put(k, s = new TreeSet<Example>()); 406 s.add(e); 407 } 408 initials.add(e.getName().substring(0, 1).toUpperCase()); 409 } 410 411 412 if (INDEX_HEADER != null) { 413 html.startTag(INDEX_HEADER); 414 html.write("Index"); 415 html.endTag(INDEX_HEADER); 416 } 417 418 html.startTag(HTMLWriter.P); 419 html.writeLine("Examples: "); 420 for (char initial = 'A'; initial <= 'Z'; initial++) { 421 String s = String.valueOf(initial); 422 if (initials.contains(s)) { 423 html.writeLink("#" + s, s); 424 } else { 425 html.write(s); 426 } 427 html.newLine(); 428 } 429 html.endTag(HTMLWriter.P); 430 431 html.startTag(HTMLWriter.TABLE); 432 html.writeAttr(HTMLWriter.CLASS, "index"); 433 html.newLine(); 434 int row = 0; 435 for (Map.Entry<String, Set<Example>> entry: index.entrySet()) { 436 html.startTag(HTMLWriter.TR); 437 html.writeAttr(HTMLWriter.CLASS, 438 (row++ % 2 == 0 ? "even" : "odd")); 439 html.startTag(HTMLWriter.TD); 440 html.writeAttr("valign", "top"); 441 html.write(entry.getKey()); 442 html.endTag(HTMLWriter.TD); 443 html.newLine(); 444 html.startTag(HTMLWriter.TD); 445 html.writeAttr(HTMLWriter.ALIGN, "top"); 446 String sep = ""; 447 for (Example e: entry.getValue()) { 448 html.write(sep); 449 html.writeLink('#' + e.getName(), e.getName()); 450 sep = ", "; 451 } 452 html.endTag(HTMLWriter.TD); 453 html.endTag(HTMLWriter.TR); 454 html.newLine(); 455 } 456 html.endTag(HTMLWriter.TABLE); 457 } 458 459 @Override startExample(Example e)460 void startExample(Example e) throws IOException { 461 String name = e.getName(); 462 String initial = name.substring(0, 1).toUpperCase(); 463 if (!initial.equals(currInitial)) { 464 html.writeLinkDestination(initial, ""); 465 currInitial = initial; 466 } 467 html.writeLinkDestination(name, ""); 468 html.startTag(EXAMPLE_HEADER); 469 html.write(e.getName()); 470 html.endTag(EXAMPLE_HEADER); 471 } 472 473 @Override showFile(Example e, File f)474 void showFile(Example e, File f) throws IOException { 475 String text; 476 try { 477 text = read(f); 478 } catch (IOException ex) { 479 text = "Error reading " + f + ": " + ex; 480 } 481 if (!f.equals(e.file)) { 482 html.startTag(FILE_HEADER); 483 html.write(e.file.toURI().relativize(f.toURI()).toString()); 484 html.endTag(FILE_HEADER); 485 } 486 html.startTag(HTMLWriter.DIV); 487 html.writeAttr(CLASS, FILE); 488 489 String legalHeader; 490 Matcher m1 = copyrightHeaderPat.matcher(text); 491 if (m1.matches()) { 492 legalHeader = m1.group(1); 493 text = m1.group(2); 494 } else 495 legalHeader = null; 496 497 String infoHeader; 498 Matcher m2 = infoHeaderPat.matcher(text); 499 if (m2.matches()) { 500 infoHeader = m2.group(1); 501 text = m2.group(2); 502 } else 503 infoHeader = null; 504 505 String legalId = null, infoId = null; 506 if (legalHeader != null || infoHeader != null) { 507 String sep = ""; 508 html.startTag(HTMLWriter.SPAN); 509 html.writeStyleAttr("float: right"); 510 if (legalHeader != null) { 511 legalId = nextId(); 512 html.startTag(HTMLWriter.A); 513 html.writeAttr(HTMLWriter.HREF, "javascript:unhide('" + legalId + "');"); 514 //html.writeEntity("©"); 515 html.write("Copyright"); 516 html.endTag(HTMLWriter.A); 517 sep = ", "; 518 } 519 if (infoHeader != null) { 520 html.write(sep); 521 infoId = nextId(); 522 html.startTag(HTMLWriter.A); 523 html.writeAttr(HTMLWriter.HREF, "javascript:unhide('" + infoId + "');"); 524 html.write("Info"); 525 html.endTag(HTMLWriter.A); 526 sep = ", "; 527 } 528 html.endTag(HTMLWriter.SPAN); 529 } 530 531 html.startTag(HTMLWriter.P); 532 html.writeAttr(CLASS, FILE); 533 if (legalHeader != null) { 534 html.startTag(HTMLWriter.SPAN); 535 html.writeAttr(HTMLWriter.CLASS, "hidden"); 536 html.writeAttr(HTMLWriter.ID, legalId); 537 html.write(legalHeader); 538 html.newLine(); 539 html.endTag(HTMLWriter.SPAN); 540 } 541 if (infoHeader != null) { 542 html.startTag(HTMLWriter.SPAN); 543 html.writeAttr(HTMLWriter.CLASS, "hidden"); 544 html.writeAttr(HTMLWriter.ID, infoId); 545 html.write(infoHeader); 546 html.newLine(); 547 html.endTag(HTMLWriter.SPAN); 548 } 549 html.write(text); 550 html.endTag(HTMLWriter.P); 551 552 html.endTag(HTMLWriter.DIV); 553 } 554 555 @Override run(Example e)556 void run(Example e) throws IOException { 557 StringWriter sw = new StringWriter(); 558 PrintWriter pw = new PrintWriter(sw); 559 e.run(pw, raw, verbose); 560 pw.flush(); 561 562 // only show Output: header if also showing files 563 if (showFiles) { 564 html.startTag(OUTPUT_HEADER); 565 html.write("Output:"); 566 html.endTag(OUTPUT_HEADER); 567 } 568 569 html.startTag(HTMLWriter.DIV); 570 html.writeAttr(CLASS, OUTPUT); 571 html.startTag(HTMLWriter.P); 572 html.writeAttr(CLASS, OUTPUT); 573 String[] lines = sw.toString().split("\n"); 574 for (String line: lines) { 575 html.write(line); 576 html.newLine(); 577 } 578 html.endTag(HTMLWriter.P); 579 html.endTag(HTMLWriter.DIV); 580 } 581 nextId()582 String nextId() { 583 return "id" + (nextId++); 584 } 585 586 File file; 587 HTMLWriter html; 588 int nextId; 589 String currInitial = ""; 590 591 static final String TITLE_HEADER = HTMLWriter.H3; 592 static final String INDEX_HEADER = HTMLWriter.H4; 593 static final String EXAMPLE_HEADER = HTMLWriter.H4; 594 static final String FILE_HEADER = HTMLWriter.H5; 595 static final String OUTPUT_HEADER = HTMLWriter.H5; 596 static final String CLASS = "class"; 597 static final String FILE = "file"; 598 static final String OUTPUT = "output"; 599 } 600 } 601 602 603