jjg@610: /* jjg@842: * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. jjg@610: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@610: * jjg@610: * This code is free software; you can redistribute it and/or modify it jjg@610: * under the terms of the GNU General Public License version 2 only, as jjg@610: * published by the Free Software Foundation. jjg@610: * jjg@610: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@610: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@610: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@610: * version 2 for more details (a copy is included in the LICENSE file that jjg@610: * accompanied this code). jjg@610: * jjg@610: * You should have received a copy of the GNU General Public License version jjg@610: * 2 along with this work; if not, write to the Free Software Foundation, jjg@610: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@610: * jjg@610: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jjg@610: * or visit www.oracle.com if you need additional information or have any jjg@610: * questions. jjg@610: */ jjg@610: jjg@610: /** jjg@610: * @test jjg@610: * @bug 6968063 jjg@610: * @summary provide examples of code that generate diagnostics jjg@842: * @build ArgTypeCompilerFactory Example HTMLWriter RunExamples jjg@610: * @run main RunExamples jjg@610: */ jjg@610: jjg@610: import java.io.*; jjg@610: import java.text.SimpleDateFormat; jjg@610: import java.util.*; jjg@610: import java.util.regex.Matcher; jjg@610: import java.util.regex.Pattern; jjg@610: jjg@610: /** jjg@610: * Utility to run selected or all examples, writing results to jjg@610: * stdout, a plain text file or an HTML file. This program can be jjg@610: * run standalone, or as a jtreg test. jjg@610: * jjg@610: * Options: jjg@610: * -examples dir directory of examples. Defaults to ${test.src}/examples jjg@610: * -raw run examples with -XDrawDiagnostics jjg@610: * -showFiles include text of source files in the output jjg@610: * -verbose verbose output jjg@610: * -o file write output to file: format will be HTML if jjg@610: * file has .html extension; otherwise it will be plain text. jjg@610: * default is to stdout jjg@610: * -title string specify a title, only applies to HTML output jjg@610: */ jjg@610: public class RunExamples { jjg@610: public static void main(String... args) throws Exception { mcimadamore@795: jtreg = (System.getProperty("test.src") != null); jjg@610: File tmpDir; jjg@610: if (jtreg) { jjg@610: // use standard jtreg scratch directory: the current directory jjg@610: tmpDir = new File(System.getProperty("user.dir")); jjg@610: } else { jjg@610: tmpDir = new File(System.getProperty("java.io.tmpdir"), jjg@610: RunExamples.class.getName() jjg@610: + (new SimpleDateFormat("yyMMddHHmmss")).format(new Date())); jjg@610: } jjg@610: Example.setTempDir(tmpDir); jjg@610: jjg@610: RunExamples r = new RunExamples(); jjg@610: jjg@610: try { jjg@610: if (r.run(args)) jjg@610: return; jjg@610: } finally { jjg@610: /* VERY IMPORTANT NOTE. In jtreg mode, tmpDir is set to the jjg@610: * jtreg scratch directory, which is the current directory. jjg@610: * In case someone is faking jtreg mode, make sure to only jjg@610: * clean tmpDir when it is reasonable to do so. jjg@610: */ jjg@610: if (tmpDir.isDirectory() && jjg@610: tmpDir.getName().startsWith(RunExamples.class.getName())) { jjg@610: if (clean(tmpDir)) jjg@610: tmpDir.delete(); jjg@610: } jjg@610: } jjg@610: jjg@610: if (jtreg) jjg@610: throw new Exception(r.errors + " errors occurred"); jjg@610: else jjg@610: System.exit(1); jjg@610: } jjg@610: jjg@610: boolean run(String... args) { jjg@610: Set selectedKeys = new TreeSet(); jjg@610: Set selectedExamples = new TreeSet(); jjg@610: File testSrc = new File(System.getProperty("test.src", ".")); jjg@610: File examplesDir = new File(testSrc, "examples"); jjg@610: File outFile = null; jjg@610: boolean raw = false; jjg@610: boolean showFiles = false; jjg@610: boolean verbose = false; jjg@842: boolean argTypes = false; jjg@610: String title = null; jjg@610: jjg@610: for (int i = 0; i < args.length; i++) { jjg@610: String arg = args[i]; jjg@610: if (arg.equals("-k") && (i + 1) < args.length) jjg@610: selectedKeys.add(args[++i]); jjg@610: else if (arg.equals("-examples") && (i + 1) < args.length) jjg@610: examplesDir = new File(args[++i]); jjg@610: else if (arg.equals("-raw")) jjg@610: raw = true; jjg@610: else if (arg.equals("-showFiles")) jjg@610: showFiles = true; jjg@610: else if (arg.equals("-verbose")) jjg@610: verbose = true; jjg@610: else if (arg.equals("-o") && (i + 1) < args.length) jjg@610: outFile = new File(args[++i]); jjg@610: else if (arg.equals("-title") && (i + 1) < args.length) jjg@610: title = args[++i]; jjg@842: else if (arg.equals("-argtypes")) jjg@842: argTypes = true; jjg@610: else if (arg.startsWith("-")) { jjg@610: error("unknown option: " + arg); jjg@610: return false; jjg@610: } else { jjg@610: while (i < args.length) { jjg@610: File f = new File(examplesDir, args[i]); jjg@610: selectedExamples.add(new Example(f)); jjg@610: i++; jjg@610: } jjg@610: } jjg@610: } jjg@610: jjg@842: // special mode to show message keys and the types of the args that jjg@842: // are used. jjg@842: if (argTypes) jjg@842: Example.Compiler.factory = new ArgTypeCompilerFactory(); jjg@842: jjg@610: if (selectedKeys.size() > 0) { jjg@610: Set examples = getExamples(examplesDir); jjg@610: nextKey: jjg@610: for (String k: selectedKeys) { jjg@610: for (Example e: examples) { jjg@610: if (e.getDeclaredKeys().contains(k)) jjg@610: continue nextKey; jjg@610: } jjg@610: error("Key " + k + ": no examples found"); jjg@610: } jjg@610: } else { jjg@842: if (selectedExamples.isEmpty()) jjg@610: selectedExamples = getExamples(examplesDir); jjg@610: } jjg@610: jjg@610: try { jjg@610: Runner r; jjg@610: if (outFile == null) { jjg@610: PrintWriter out = new PrintWriter(System.out); jjg@610: r = new TextRunner(out, showFiles, raw, verbose); jjg@610: } else if (outFile.getName().endsWith(".html")) jjg@610: r = new HTMLRunner(outFile, showFiles, raw, verbose, title); jjg@610: else jjg@610: r = new TextRunner(outFile, showFiles, raw, verbose); jjg@610: r.run(selectedExamples); jjg@610: r.close(); jjg@610: } catch (IOException e) { jjg@610: error("Error writing output: " + e); jjg@610: } jjg@610: jjg@610: return (errors == 0); jjg@610: } jjg@610: jjg@610: /** jjg@610: * Get the complete set of examples to be checked. jjg@610: */ jjg@610: Set getExamples(File examplesDir) { jjg@610: Set results = new TreeSet(); jjg@610: for (File f: examplesDir.listFiles()) { mcimadamore@795: if (isValidExample(f)) jjg@610: results.add(new Example(f)); jjg@610: } jjg@610: return results; jjg@610: } jjg@610: mcimadamore@795: boolean isValidExample(File f) { mcimadamore@795: return (f.isDirectory() && (!jtreg || f.list().length > 0)) || mcimadamore@795: (f.isFile() && f.getName().endsWith(".java")); mcimadamore@795: } mcimadamore@795: jjg@610: /** jjg@610: * Report an error. jjg@610: */ jjg@610: void error(String msg) { jjg@610: System.err.println("Error: " + msg); jjg@610: errors++; jjg@610: } jjg@610: mcimadamore@795: static boolean jtreg; mcimadamore@795: jjg@610: int errors; jjg@610: jjg@610: /** jjg@610: * Clean the contents of a directory. jjg@610: */ jjg@610: static boolean clean(File dir) { jjg@610: boolean ok = true; jjg@610: for (File f: dir.listFiles()) { jjg@610: if (f.isDirectory()) jjg@610: ok &= clean(f); jjg@610: ok &= f.delete(); jjg@610: } jjg@610: return ok; jjg@610: } jjg@610: jjg@610: static abstract class Runner { jjg@610: Runner(boolean showFiles, boolean raw, boolean verbose) { jjg@610: this.showFiles = showFiles; jjg@610: this.raw = raw; jjg@610: this.verbose = verbose; jjg@610: } jjg@610: jjg@610: void close() throws IOException { } jjg@610: jjg@610: void run(Collection examples) throws IOException { jjg@610: for (Example e: examples) { jjg@610: startExample(e); jjg@610: if (showFiles) { jjg@610: showFile(e, e.infoFile); jjg@610: Set srcFiles = new TreeSet(e.srcFiles); jjg@610: srcFiles.remove(e.infoFile); jjg@610: showFiles(e, srcFiles); jjg@610: showFiles(e, e.srcPathFiles); jjg@610: showFiles(e, e.procFiles); jjg@610: showFiles(e, e.supportFiles); jjg@610: } jjg@610: run(e); jjg@610: } jjg@610: } jjg@610: jjg@610: void showFiles(Example e, Collection files) throws IOException { jjg@610: for (File f: files) jjg@610: showFile(e, f); jjg@610: } jjg@610: jjg@610: abstract void startExample(Example e) throws IOException; jjg@610: jjg@610: abstract void showFile(Example e, File f) throws IOException; jjg@610: jjg@610: abstract void run(Example e) throws IOException; jjg@610: jjg@610: protected String read(File f) throws IOException { jjg@610: byte[] bytes = new byte[(int) f.length()]; jjg@610: DataInputStream in = new DataInputStream(new FileInputStream(f)); jjg@610: try { jjg@610: in.readFully(bytes); jjg@610: } finally { jjg@610: in.close(); jjg@610: } jjg@610: return new String(bytes); jjg@610: } jjg@610: jjg@610: protected Pattern copyrightHeaderPat = jjg@610: Pattern.compile("(?s)(/\\*.*?Copyright.*?\\*/\n)\\s*(.*)"); jjg@610: protected Pattern infoHeaderPat = jjg@610: Pattern.compile("(?s)((?://\\s*[a-z]+:[^\n]*\n)+)\\s*(.*)"); jjg@610: jjg@610: protected boolean showFiles; jjg@610: protected boolean raw; jjg@610: protected boolean verbose; jjg@610: } jjg@610: jjg@610: static class TextRunner extends Runner { jjg@610: TextRunner(File file, boolean showFiles, boolean raw, boolean verbose) jjg@610: throws IOException { jjg@610: super(showFiles, raw, verbose); jjg@610: this.file = file; jjg@610: out = new PrintWriter(new FileWriter(file)); jjg@610: } jjg@610: jjg@610: TextRunner(PrintWriter out, boolean showFiles, boolean raw, boolean verbose) jjg@610: throws IOException { jjg@610: super(showFiles, raw, verbose); jjg@610: this.out = out; jjg@610: } jjg@610: jjg@610: @Override jjg@610: void close() { jjg@610: if (file != null) jjg@610: out.close(); jjg@610: } jjg@610: jjg@610: @Override jjg@610: void startExample(Example e) { jjg@610: out.println("----- " + e.getName() + " --------------------"); jjg@610: out.println(); jjg@610: } jjg@610: jjg@610: @Override jjg@610: void showFile(Example e, File f) { jjg@610: out.println("--- " + f); jjg@610: String text; jjg@610: try { jjg@610: text = read(f); jjg@610: } catch (IOException ex) { jjg@610: text = "Error reading " + f + "; " + ex; jjg@610: } jjg@610: Matcher m = copyrightHeaderPat.matcher(text); jjg@610: if (m.matches()) { jjg@610: out.println("(Copyright)"); jjg@610: writeLines(m.group(2)); jjg@610: } else { jjg@610: writeLines(text); jjg@610: } jjg@610: out.println(); jjg@610: } jjg@610: jjg@610: @Override jjg@610: void run(Example e) { jjg@610: // only show Output: header if also showing files jjg@610: if (showFiles) jjg@610: out.println("--- Output:"); jjg@610: e.run(out, raw, verbose); jjg@610: out.println(); jjg@610: } jjg@610: jjg@610: void writeLines(String text) { jjg@610: for (String line: text.split("\n")) jjg@610: out.println(line); jjg@610: } jjg@610: jjg@610: File file; jjg@610: PrintWriter out; jjg@610: } jjg@610: jjg@610: static class HTMLRunner extends Runner { jjg@610: HTMLRunner(File file, boolean showFiles, boolean raw, boolean verbose, String title) jjg@610: throws IOException { jjg@610: super(showFiles, raw, verbose); jjg@610: this.file = file; jjg@610: PrintWriter out = new PrintWriter(new FileWriter(file)); jjg@610: html = new HTMLWriter(out); jjg@610: html.startTag(HTMLWriter.HEAD); jjg@610: if (title != null) { jjg@610: html.startTag(HTMLWriter.TITLE); jjg@610: html.write(title); jjg@610: html.endTag(HTMLWriter.TITLE); jjg@610: } jjg@610: html.startTag(HTMLWriter.STYLE); jjg@610: html.newLine(); jjg@610: html.writeLine("div.file { background-color:#e0ffe0; margin-left:30px; margin-right:30px;\n" jjg@610: + " padding: 3px; border: thin solid silver; }"); jjg@610: html.writeLine("p.file { white-space: pre-wrap; font-family:monospace; margin: 0; }"); jjg@610: html.writeLine("div.output { background-color:#e0e0ff; margin-left:30px; margin-right:30px;\n" jjg@610: + " padding: 3px; border: thin solid silver; }"); jjg@610: html.writeLine("p.output { white-space: pre-wrap; font-family:monospace; margin: 0; }"); jjg@610: html.writeLine("table.index { border: thin solid silver; }"); jjg@610: html.writeLine(".copyright { font-size: x-small }"); jjg@610: html.writeLine(".hidden { display:none }"); jjg@610: html.writeLine(".unhidden { display:block }"); jjg@610: html.writeLine(".odd { background-color: #e0e0e0 }"); jjg@610: html.writeLine(".even { background-color: white }"); jjg@610: html.endTag(HTMLWriter.STYLE); jjg@610: html.startTag(HTMLWriter.SCRIPT); jjg@610: html.writeAttr(HTMLWriter.TYPE, HTMLWriter.TEXT_JAVASCRIPT); jjg@610: html.writeLine("\nfunction unhide(id) {\n" jjg@610: + " var item = document.getElementById(id);\n" jjg@610: + " if (item) {\n" jjg@610: + " item.className=(item.className=='hidden')?'unhidden':'hidden';\n" jjg@610: + " }\n" jjg@610: + "}"); jjg@610: html.endTag(HTMLWriter.SCRIPT); jjg@610: html.endTag(HTMLWriter.HEAD); jjg@610: html.startTag(HTMLWriter.BODY); jjg@610: if (title != null) { jjg@610: html.startTag(TITLE_HEADER); jjg@610: html.write(title); jjg@610: html.endTag(TITLE_HEADER); jjg@610: } jjg@610: } jjg@610: jjg@610: @Override jjg@610: void close() throws IOException { jjg@610: html.endTag(HTMLWriter.BODY); jjg@610: html.newLine(); jjg@610: html.flush(); jjg@610: } jjg@610: jjg@610: @Override jjg@610: void run(Collection examples) throws IOException { jjg@610: if (examples.size() > 1) jjg@610: writeIndex(examples); jjg@610: super.run(examples); jjg@610: } jjg@610: jjg@610: void writeIndex(Collection examples) throws IOException { jjg@610: Map> index = new TreeMap>(); jjg@610: Set initials = new HashSet(); jjg@610: for (Example e: examples) { jjg@610: for (String k: e.getDeclaredKeys()) { jjg@610: Set s = index.get(k); jjg@610: if (s == null) jjg@610: index.put(k, s = new TreeSet()); jjg@610: s.add(e); jjg@610: } jjg@610: initials.add(e.getName().substring(0, 1).toUpperCase()); jjg@610: } jjg@610: jjg@610: jjg@610: if (INDEX_HEADER != null) { jjg@610: html.startTag(INDEX_HEADER); jjg@610: html.write("Index"); jjg@610: html.endTag(INDEX_HEADER); jjg@610: } jjg@610: jjg@610: html.startTag(HTMLWriter.P); jjg@610: html.writeLine("Examples: "); jjg@610: for (char initial = 'A'; initial <= 'Z'; initial++) { jjg@610: String s = String.valueOf(initial); jjg@610: if (initials.contains(s)) { jjg@610: html.writeLink("#" + s, s); jjg@610: } else { jjg@610: html.write(s); jjg@610: } jjg@610: html.newLine(); jjg@610: } jjg@610: html.endTag(HTMLWriter.P); jjg@610: jjg@610: html.startTag(HTMLWriter.TABLE); jjg@610: html.writeAttr(HTMLWriter.CLASS, "index"); jjg@610: html.newLine(); jjg@610: int row = 0; jjg@610: for (Map.Entry> entry: index.entrySet()) { jjg@610: html.startTag(HTMLWriter.TR); jjg@610: html.writeAttr(HTMLWriter.CLASS, jjg@610: (row++ % 2 == 0 ? "even" : "odd")); jjg@610: html.startTag(HTMLWriter.TD); jjg@610: html.writeAttr("valign", "top"); jjg@610: html.write(entry.getKey()); jjg@610: html.endTag(HTMLWriter.TD); jjg@610: html.newLine(); jjg@610: html.startTag(HTMLWriter.TD); jjg@610: html.writeAttr(HTMLWriter.ALIGN, "top"); jjg@610: String sep = ""; jjg@610: for (Example e: entry.getValue()) { jjg@610: html.write(sep); jjg@610: html.writeLink('#' + e.getName(), e.getName()); jjg@610: sep = ", "; jjg@610: } jjg@610: html.endTag(HTMLWriter.TD); jjg@610: html.endTag(HTMLWriter.TR); jjg@610: html.newLine(); jjg@610: } jjg@610: html.endTag(HTMLWriter.TABLE); jjg@610: } jjg@610: jjg@610: @Override jjg@610: void startExample(Example e) throws IOException { jjg@610: String name = e.getName(); jjg@610: String initial = name.substring(0, 1).toUpperCase(); jjg@610: if (!initial.equals(currInitial)) { jjg@610: html.writeLinkDestination(initial, ""); jjg@610: currInitial = initial; jjg@610: } jjg@610: html.writeLinkDestination(name, ""); jjg@610: html.startTag(EXAMPLE_HEADER); jjg@610: html.write(e.getName()); jjg@610: html.endTag(EXAMPLE_HEADER); jjg@610: } jjg@610: jjg@610: @Override jjg@610: void showFile(Example e, File f) throws IOException { jjg@610: String text; jjg@610: try { jjg@610: text = read(f); jjg@610: } catch (IOException ex) { jjg@610: text = "Error reading " + f + ": " + ex; jjg@610: } jjg@610: if (!f.equals(e.file)) { jjg@610: html.startTag(FILE_HEADER); jjg@610: html.write(e.file.toURI().relativize(f.toURI()).toString()); jjg@610: html.endTag(FILE_HEADER); jjg@610: } jjg@610: html.startTag(HTMLWriter.DIV); jjg@610: html.writeAttr(CLASS, FILE); jjg@610: jjg@610: String legalHeader; jjg@610: Matcher m1 = copyrightHeaderPat.matcher(text); jjg@610: if (m1.matches()) { jjg@610: legalHeader = m1.group(1); jjg@610: text = m1.group(2); jjg@610: } else jjg@610: legalHeader = null; jjg@610: jjg@610: String infoHeader; jjg@610: Matcher m2 = infoHeaderPat.matcher(text); jjg@610: if (m2.matches()) { jjg@610: infoHeader = m2.group(1); jjg@610: text = m2.group(2); jjg@610: } else jjg@610: infoHeader = null; jjg@610: jjg@610: String legalId = null, infoId = null; jjg@610: if (legalHeader != null || infoHeader != null) { jjg@610: String sep = ""; jjg@610: html.startTag(HTMLWriter.SPAN); jjg@610: html.writeStyleAttr("float: right"); jjg@610: if (legalHeader != null) { jjg@610: legalId = nextId(); jjg@610: html.startTag(HTMLWriter.A); jjg@610: html.writeAttr(HTMLWriter.HREF, "javascript:unhide('" + legalId + "');"); jjg@610: //html.writeEntity("©"); jjg@610: html.write("Copyright"); jjg@610: html.endTag(HTMLWriter.A); jjg@610: sep = ", "; jjg@610: } jjg@610: if (infoHeader != null) { jjg@610: html.write(sep); jjg@610: infoId = nextId(); jjg@610: html.startTag(HTMLWriter.A); jjg@610: html.writeAttr(HTMLWriter.HREF, "javascript:unhide('" + infoId + "');"); jjg@610: html.write("Info"); jjg@610: html.endTag(HTMLWriter.A); jjg@610: sep = ", "; jjg@610: } jjg@610: html.endTag(HTMLWriter.SPAN); jjg@610: } jjg@610: jjg@610: html.startTag(HTMLWriter.P); jjg@610: html.writeAttr(CLASS, FILE); jjg@610: if (legalHeader != null) { jjg@610: html.startTag(HTMLWriter.SPAN); jjg@610: html.writeAttr(HTMLWriter.CLASS, "hidden"); jjg@610: html.writeAttr(HTMLWriter.ID, legalId); jjg@610: html.write(legalHeader); jjg@610: html.newLine(); jjg@610: html.endTag(HTMLWriter.SPAN); jjg@610: } jjg@610: if (infoHeader != null) { jjg@610: html.startTag(HTMLWriter.SPAN); jjg@610: html.writeAttr(HTMLWriter.CLASS, "hidden"); jjg@610: html.writeAttr(HTMLWriter.ID, infoId); jjg@610: html.write(infoHeader); jjg@610: html.newLine(); jjg@610: html.endTag(HTMLWriter.SPAN); jjg@610: } jjg@610: html.write(text); jjg@610: html.endTag(HTMLWriter.P); jjg@610: jjg@610: html.endTag(HTMLWriter.DIV); jjg@610: } jjg@610: jjg@610: @Override jjg@610: void run(Example e) throws IOException { jjg@610: StringWriter sw = new StringWriter(); jjg@610: PrintWriter pw = new PrintWriter(sw); jjg@610: e.run(pw, raw, verbose); jjg@610: pw.flush(); jjg@610: jjg@610: // only show Output: header if also showing files jjg@610: if (showFiles) { jjg@610: html.startTag(OUTPUT_HEADER); jjg@610: html.write("Output:"); jjg@610: html.endTag(OUTPUT_HEADER); jjg@610: } jjg@610: jjg@610: html.startTag(HTMLWriter.DIV); jjg@610: html.writeAttr(CLASS, OUTPUT); jjg@610: html.startTag(HTMLWriter.P); jjg@610: html.writeAttr(CLASS, OUTPUT); jjg@610: String[] lines = sw.toString().split("\n"); jjg@610: for (String line: lines) { jjg@610: html.write(line); jjg@610: html.newLine(); jjg@610: } jjg@610: html.endTag(HTMLWriter.P); jjg@610: html.endTag(HTMLWriter.DIV); jjg@610: } jjg@610: jjg@610: String nextId() { jjg@610: return "id" + (nextId++); jjg@610: } jjg@610: jjg@610: File file; jjg@610: HTMLWriter html; jjg@610: int nextId; jjg@610: String currInitial = ""; jjg@610: jjg@610: static final String TITLE_HEADER = HTMLWriter.H3; jjg@610: static final String INDEX_HEADER = HTMLWriter.H4; jjg@610: static final String EXAMPLE_HEADER = HTMLWriter.H4; jjg@610: static final String FILE_HEADER = HTMLWriter.H5; jjg@610: static final String OUTPUT_HEADER = HTMLWriter.H5; jjg@610: static final String CLASS = "class"; jjg@610: static final String FILE = "file"; jjg@610: static final String OUTPUT = "output"; jjg@610: } jjg@610: } jjg@610: jjg@610: