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