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("&copy;");
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