changeset 1201:76b5306fead0

Upgrade to jtreg-4_0-src-b02-15_oct_2008.
author Mark Wielaard <mark@klomp.org>
date Sun, 09 Nov 2008 22:29:59 +0100
parents 93e7061da818
children 688efd120766
files ChangeLog test/jtreg/README test/jtreg/com/sun/javatest/diff/Diff.java test/jtreg/com/sun/javatest/diff/Fault.java test/jtreg/com/sun/javatest/diff/HTMLReporter.java test/jtreg/com/sun/javatest/diff/HTMLWriter.java test/jtreg/com/sun/javatest/diff/Main.java test/jtreg/com/sun/javatest/diff/MultiMap.java test/jtreg/com/sun/javatest/diff/ReportReader.java test/jtreg/com/sun/javatest/diff/StandardDiff.java test/jtreg/com/sun/javatest/diff/SuperDiff.java test/jtreg/com/sun/javatest/diff/WorkDirectoryReader.java test/jtreg/com/sun/javatest/diff/i18n.properties test/jtreg/com/sun/javatest/regtest/Main.java test/jtreg/com/sun/javatest/regtest/MainAction.java test/jtreg/com/sun/javatest/regtest/RegressionSecurityManager.java test/jtreg/com/sun/javatest/regtest/RegressionTestFinder.java test/jtreg/com/sun/javatest/regtest/i18n.properties
diffstat 18 files changed, 1761 insertions(+), 573 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Nov 09 22:27:11 2008 +0100
+++ b/ChangeLog	Sun Nov 09 22:29:59 2008 +0100
@@ -1,3 +1,24 @@
+2008-11-09  Mark Wielaard  <mark@klomp.org>
+
+	* test/jtreg/README,
+	test/jtreg/com/sun/javatest/diff/HTMLReporter.java,
+	test/jtreg/com/sun/javatest/diff/Main.java,
+	test/jtreg/com/sun/javatest/diff/MultiMap.java,
+	test/jtreg/com/sun/javatest/diff/ReportReader.java,
+	test/jtreg/com/sun/javatest/diff/WorkDirectoryReader.java,
+	test/jtreg/com/sun/javatest/diff/i18n.properties,
+	test/jtreg/com/sun/javatest/regtest/Main.java,
+	test/jtreg/com/sun/javatest/regtest/MainAction.java,
+	test/jtreg/com/sun/javatest/regtest/RegressionSecurityManager.java,
+	test/jtreg/com/sun/javatest/regtest/RegressionTestFinder.java,
+	test/jtreg/com/sun/javatest/regtest/i18n.properties,
+	test/jtreg/com/sun/javatest/diff/Diff.java,
+	test/jtreg/com/sun/javatest/diff/Fault.java,
+	test/jtreg/com/sun/javatest/diff/HTMLWriter.java,
+	test/jtreg/com/sun/javatest/diff/StandardDiff.java,
+	test/jtreg/com/sun/javatest/diff/SuperDiff.java,
+	Upgrade to jtreg-4_0-src-b02-15_oct_2008.
+
 2008-11-09  Mark Wielaard  <mark@klomp.org>
 
 	* .hgignore: Add test/check-.*log.
--- a/test/jtreg/README	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/README	Sun Nov 09 22:29:59 2008 +0100
@@ -20,7 +20,7 @@
 This version is based on:
 
 - jtharness-oss-4_1_3a-dev
-- jtreg-4_0-src-b01-29_apr_2008
+- jtreg-4_0-src-b02-15_oct_2008
 
 IcedJTReg is distrubuted under the GPLv2.0, like jtreg and jtharness.
 See the documents in the legal directory included in this release.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/diff/Diff.java	Sun Nov 09 22:29:59 2008 +0100
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.javatest.diff;
+
+import com.sun.javatest.Status;
+import com.sun.javatest.TestResult;
+import com.sun.javatest.TestSuite;
+import com.sun.javatest.WorkDirectory;
+import com.sun.javatest.util.I18NResourceBundle;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+public abstract class Diff {
+
+    public abstract boolean report(File outFile) throws Fault, InterruptedException;
+
+    protected boolean diff(List<File> files, File outFile)
+            throws Fault, InterruptedException {
+        this.outFile = outFile;
+        List<DiffReader> list = new ArrayList<DiffReader>();
+        for (File f: files)
+            list.add(open(f));
+
+        PrintWriter prevOut = out;
+        if (out == null && outFile != null) {
+            try {
+                out = new PrintWriter(new BufferedWriter(new FileWriter(outFile))); // FIXME don't want to use PrintWriter
+            } catch (IOException e) {
+                throw new Fault(i18n, "diff.cantOpenFile", outFile, e);
+            }
+        }
+
+        try {
+            initComparator();
+
+            initReporter();
+            reporter.setTitle(title);
+            reporter.setComparator(comparator);
+            reporter.setReaders(list);
+
+            List<int[]> testCounts = new ArrayList<int[]>();
+            MultiMap<String,TestResult> table = new MultiMap<String,TestResult>();
+            for (DiffReader r: list) {
+                int index = table.addColumn(r.getFile().getPath());
+                int[] counts = new int[Status.NUM_STATES];
+                for (TestResult tr: r) {
+                    table.addRow(index, tr.getTestName(), tr);
+                    counts[tr.getStatus().getType()]++;
+                }
+                testCounts.add(counts);
+            }
+            reporter.setTestCounts(testCounts);
+
+            try {
+                reporter.write(table);
+            } catch (IOException e) {
+                throw new Fault(i18n, "diff.ioError", e);
+            }
+
+            return (reporter.diffs == 0);
+        } finally {
+            if (out != prevOut) {
+//                try {
+                    out.close();
+//                } catch (IOException e) {
+//                    throw new Fault(i18n, "main.ioError", e);
+//                }
+                out = prevOut;
+            }
+        }
+    }
+
+    protected void initFormat() {
+        if (format == null && outFile != null) {
+            String name = outFile.getName();
+            int dot = name.lastIndexOf(".");
+            if (dot != -1)
+                format = name.substring(dot + 1).toLowerCase();
+        }
+    }
+
+    protected void initReporter() throws Fault {
+        if (reporter == null) {
+            try {
+                initFormat();
+                if (format != null && format.equals("html"))
+                    reporter = new HTMLReporter(out);
+                else
+                    reporter = new SimpleReporter(out);
+            } catch (IOException e) {
+                throw new Fault(i18n, "diff.cantOpenReport", e);
+            }
+        }
+    }
+
+    protected void initComparator() {
+        if (comparator == null)
+            comparator = new StatusComparator(includeReason);
+    }
+
+    protected DiffReader open(File f) throws Fault {
+        if (!f.exists())
+            throw new Fault(i18n, "main.cantFindFile", f);
+
+        try {
+            if (WorkDirectoryReader.accepts(f))
+                return new WorkDirectoryReader(f);
+
+            if (ReportReader.accepts(f))
+                return new ReportReader(f);
+
+            throw new Fault(i18n, "main.unrecognizedFile", f);
+
+        } catch (TestSuite.Fault e) {
+            throw new Fault(i18n, "main.cantOpenFile", f, e);
+        } catch (WorkDirectory.Fault e) {
+            throw new Fault(i18n, "main.cantOpenFile", f, e);
+        } catch (IOException e) {
+            throw new Fault(i18n, "main.cantOpenFile", f, e);
+        }
+
+    }
+
+    protected File outFile;
+    protected PrintWriter out;
+    protected Comparator<TestResult> comparator;
+    protected Reporter reporter;
+    protected boolean includeReason;
+    protected String format;
+    protected String title;
+    private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(Diff.class);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/diff/Fault.java	Sun Nov 09 22:29:59 2008 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.javatest.diff;
+
+import com.sun.javatest.util.I18NResourceBundle;
+
+/**
+ * Exception to report a problem while executing in Main.
+ */
+public class Fault extends Exception {
+
+    static final long serialVersionUID = 1607979458544175906L;
+
+    Fault(I18NResourceBundle i18n, String s, Object... args) {
+        super(i18n.getString(s, args));
+    }
+}
--- a/test/jtreg/com/sun/javatest/diff/HTMLReporter.java	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/diff/HTMLReporter.java	Sun Nov 09 22:29:59 2008 +0100
@@ -33,7 +33,6 @@
 
 import com.sun.javatest.TestResult;
 import com.sun.javatest.Status;
-import com.sun.javatest.util.HTMLWriter;
 import com.sun.javatest.util.I18NResourceBundle;
 
 import static com.sun.javatest.util.HTMLWriter.*;
@@ -47,8 +46,8 @@
  * Report differences to an HTML file.
  */
 public class HTMLReporter extends Reporter {
-    
-    /** Creates a new instance of SimpleDiffReporter */
+
+    /** Creates a new instance of HTMLReporter */
     public HTMLReporter(Writer out) throws IOException {
         this.out = new HTMLWriter(out, DOCTYPE);
         this.out.setI18NResourceBundle(i18n);
@@ -56,10 +55,40 @@
 
     public void write(MultiMap<String, TestResult> table) throws IOException {
         this.table = table;
-        
         size = table.getColumns();
-        
+
+        startReport(title);
+
+        out.startTag(H1);
+        if (title == null)
+            out.writeI18N("html.head.notitle");
+        else
+            out.writeI18N("html.head.title", title);
+        out.endTag(H1);
+
+        writeIndexTable();
+        writeMainTable();
+        writeSummary();
+
+        endReport();
+    }
+
+    protected void startReport(String title) throws IOException {
         out.startTag(HTML);
+        writeHead(title);
+        out.startTag(BODY);
+    }
+
+    protected void endReport() throws IOException {
+        out.startTag(HR);
+        out.writeI18N("html.generatedAt", new Date());
+        out.endTag(BODY);
+
+        out.endTag(HTML);
+        out.flush();
+    }
+
+    protected void writeHead(String title) throws IOException {
         out.startTag(HEAD);
         out.startTag(TITLE);
         if (title == null)
@@ -74,40 +103,24 @@
         out.write("tr.head { background-color:#dddddd }");
         out.write("tr.odd  { background-color:#eeeeee }");
         out.write("tr.even { background-color:white } ");
-        out.write("td { padding: 0 1em }");
+        out.write("td { padding: 0 .5em }");
         out.write("td.pass { background-color:#ddffdd } ");
         out.write("td.fail { background-color:#ffdddd } ");
         out.write("td.error { background-color:#ddddff } ");
         out.write("td.notRun { background-color:#dddddd } ");
+        out.write("th { padding: 0 .5em }");
         out.write("hr      { margin-top:30px; }");
         out.write("\n");
         out.endTag(STYLE);
         out.endTag(HEAD);
-        
-        out.startTag(BODY);
-        out.startTag(H1);
-        if (title == null)
-            out.writeI18N("html.head.notitle");
-        else
-            out.writeI18N("html.head.title", title);
-        out.endTag(H1);
-        writeHead();
-        writeBody();
-        writeSummary();
-        
-        out.startTag(HR);
-        out.writeI18N("html.generatedAt", new Date());
-        out.endTag(BODY);
-        
-        out.endTag(HTML);
-        out.flush();
+
     }
-    
-    private void writeHead() throws IOException {
+
+    private void writeIndexTable() throws IOException {
         out.startTag(H2);
         out.writeI18N("html.head.sets");
         out.endTag(H2);
-        
+
         out.startTag(TABLE);
         out.writeAttr(FRAME, BOX);
         out.writeAttr(RULES, GROUPS);
@@ -120,6 +133,7 @@
         out.startTag(TH);
         out.writeI18N("html.th.location");
         out.endTag(TH);
+        writeIndexTableInfoHeadings();
 //        out.startTag(TH);
 //        out.writeI18N("html.th.type");
 //        out.endTag(TH);
@@ -146,16 +160,17 @@
         out.endTag(TH);
         out.endTag(TR);
         out.endTag(THEAD);
-        
+
         out.startTag(TBODY);
         for (int i = 0; i < size; i++) {
             out.startTag(TR);
             out.writeAttr(CLASS, (i % 2 == 0 ? EVEN : ODD));
             out.startTag(TD);
-            out.write(String.valueOf(i));
+            out.write(String.valueOf(i + 1));
             out.endTag(TD);
             out.startTag(TD);
             out.write(table.getColumnName(i));
+            writeIndexTableInfoValues(table.getColumnName(i));
             out.endTag(TD);
 //            out.startTag(TD);
 //            out.write("??");
@@ -166,7 +181,7 @@
                 out.startTag(TD);
                 if (counts[c] > 0)
                     out.write(String.valueOf(counts[c]));
-                else 
+                else
                     out.writeEntity("&nbsp;");
                 total += counts[c];
                 out.endTag(TD);
@@ -179,8 +194,14 @@
         out.endTag(TBODY);
         out.endTag(TABLE);
     }
-    
-    private void writeBody() throws IOException {
+
+    protected void writeIndexTableInfoHeadings() throws IOException {
+    }
+
+    protected void writeIndexTableInfoValues(String name) throws IOException {
+    }
+
+    private void writeMainTable() throws IOException {
         diffs = 0;
         for (Map.Entry<String, MultiMap.Entry<TestResult>> e: table.entrySet()) {
             String testName = e.getKey();
@@ -202,7 +223,10 @@
                 out.endTag(TH);
                 for (int i = 0; i < result.getSize(); i++) {
                     out.startTag(TH);
-                    out.writeI18N("html.th.setN", i);
+                    if (compact)
+                        out.write(String.valueOf(i + 1));
+                    else
+                        out.writeI18N("html.th.setN", i + 1);
                     out.endTag(TH);
                 }
                 out.endTag(TR);
@@ -222,36 +246,24 @@
                     if (wd != null)
                         trFile = new File(wd, tr.getWorkRelativePath());
                 }
+                out.startTag(TD);
                 Status s = (tr == null ? null : tr.getStatus());
-                out.startTag(TD);
-                String classAttr;
-                String text;
-                switch (s == null ? Status.NOT_RUN : s.getType()) {
-                    case Status.PASSED:
-                        classAttr = PASS;
-                        text = i18n.getString("html.pass");
-                        break;
-                    case Status.FAILED:
-                        classAttr = FAIL;
-                        text = i18n.getString("html.fail");
-                        break;
-                    case Status.ERROR:
-                        classAttr = ERROR;
-                        text = i18n.getString("html.error");
-                        break;
-                    default:
-                        classAttr = NOT_RUN;
-                        text = i18n.getString("html.notRun");
-                        break;
-                }
-                out.writeAttr(CLASS, classAttr);
+                out.writeAttr(CLASS, getClassAttr(s));
+                String text = getText(s);
                 if (trFile != null && trFile.exists()) {
                     out.startTag(A);
                     out.writeAttr(HREF, trFile.toURI().toString());
-                    out.write(text);
+                    if (text.startsWith("&"))
+                        out.writeEntity(text);
+                    else
+                        out.write(text);
                     out.endTag(A);
-                } else
-                    out.write(text);
+                } else {
+                    if (text.startsWith("&"))
+                        out.writeEntity(text);
+                    else
+                        out.write(text);
+                }
                 out.endTag(TD);
             }
             out.endTag(TR);
@@ -262,7 +274,7 @@
             out.endTag(TABLE);
         }
     }
-    
+
     private void writeSummary() throws IOException {
         out.startTag(P);
         if (diffs == 0)
@@ -271,27 +283,60 @@
             out.writeI18N("html.diffs.count", diffs);
         out.endTag(P);
     }
-    
+
+    protected String getClassAttr(Status s) {
+        switch (s == null ? Status.NOT_RUN : s.getType()) {
+            case Status.PASSED:
+                return PASS;
+            case Status.FAILED:
+                return FAIL;
+            case Status.ERROR:
+                return ERROR;
+            default:
+                return NOT_RUN;
+        }
+    }
+
+    protected String getText(Status s) {
+        if (statusStrings == null) {
+            statusStrings = new String[Status.NUM_STATES];
+            if (compact) {
+                statusStrings[Status.PASSED] = i18n.getString("html.pass.compact");
+                statusStrings[Status.FAILED] = i18n.getString("html.fail.compact");
+                statusStrings[Status.ERROR] = i18n.getString("html.error.compact");
+                statusStrings[Status.NOT_RUN] = i18n.getString("html.notRun.compact");
+            } else {
+                statusStrings[Status.PASSED] = i18n.getString("html.pass");
+                statusStrings[Status.FAILED] = i18n.getString("html.fail");
+                statusStrings[Status.ERROR] = i18n.getString("html.error");
+                statusStrings[Status.NOT_RUN] = i18n.getString("html.notRun");
+            }
+        }
+        return statusStrings[s == null ? Status.NOT_RUN : s.getType()];
+    }
+
+    private String[] statusStrings;
+
+    protected final HTMLWriter out;
     private MultiMap<String, TestResult> table;
     private int size;
-    private HTMLWriter out;
-    
-    private static final String DOCTYPE = 
+
+    private static final String DOCTYPE =
             "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd\">";
-    
+
     // HTML tags
     private static final String THEAD = "thead";
     private static final String TBODY = "tbody";
-    
+
     // HTML attribute names
     private static final String CLASS = "class";
     private static final String FRAME = "frame";
     private static final String RULES = "rules";
-    
+
     // HTML attribute values
     private static final String BOX = "box";
     private static final String GROUPS = "groups";
-    
+
     // HTML class values
     private static final String HEAD = "head";
     private static final String ODD  = "odd";
@@ -300,6 +345,7 @@
     private static final String FAIL = "fail";
     private static final String ERROR = "error";
     private static final String NOT_RUN = "notRun";
-    
+
+    private boolean compact = Boolean.TRUE.equals(Boolean.getBoolean("jtdiff.html.compact"));
     private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(HTMLReporter.class);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/diff/HTMLWriter.java	Sun Nov 09 22:29:59 2008 +0100
@@ -0,0 +1,558 @@
+/*
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.javatest.diff;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URL;
+import com.sun.javatest.util.I18NResourceBundle;
+
+/**
+ * A class to facilitate writing HTML via a stream.
+ */
+public class HTMLWriter
+{
+    /**
+     * Create an HTMLWriter object, using a default doctype for HTML 3.2.
+     * @param out a Writer to which to write the generated HTML
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public HTMLWriter(Writer out) throws IOException {
+        this(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">");
+    }
+
+    /**
+     * Create an HTMLWriter object, using a specifed doctype header.
+     * @param out a Writer to which to write the generated HTML
+     * @param docType a string containing a doctype header for the HTML to be generetaed
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public HTMLWriter(Writer out, String docType) throws IOException {
+        if (out instanceof BufferedWriter)
+            this.out = (BufferedWriter) out;
+        else
+            this.out = new BufferedWriter(out);
+        this.out.write(docType);
+        this.out.newLine();
+    }
+
+    /**
+     * Create an HTMLWriter object, using a specified bundle for l0calizing messages.
+     * @param out a Writer to which to write the generated HTML
+     * @param i18n a resource bundle to use to localize messages
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public HTMLWriter(Writer out, I18NResourceBundle i18n) throws IOException {
+        this(out);
+        this.i18n = i18n;
+    }
+
+
+    /**
+     * Create an HTMLWriter object, using a specifed doctype header and
+     * using a specified bundle for l0calizing messages.
+     * @param out a Writer to which to write the generated HTML
+     * @param docType a string containing a doctype header for the HTML to be generetaed
+     * @param i18n a resource bundle to use to localize messages
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public HTMLWriter(Writer out, String docType, I18NResourceBundle i18n) throws IOException {
+        this(out, docType);
+        this.i18n = i18n;
+    }
+
+    /**
+     * Set the reource bundle to be used for localizing messages.
+     * @param i18n the resource bundle to be used for localizing messages
+     */
+    public void setI18NResourceBundle(I18NResourceBundle i18n) {
+        this.i18n = i18n;
+    }
+
+    /**
+     * Flush the stream, and the underlying output stream.
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public void flush() throws IOException {
+        out.flush();
+    }
+
+    /**
+     * Close the stream, and the underlying output stream.
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void close() throws IOException {
+        out.close();
+    }
+
+    /**
+     * Write a newline to the underlying output stream.
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public void newLine() throws IOException {
+        out.newLine();
+    }
+
+    /**
+     * Start an HTML tag.  If a prior tag has been started, it will
+     * be closed first. Once a tag has been opened, attributes for the
+     * tag may be written out, followed by body content before finally
+     * ending the tag.
+     * @param tag the tag to be started
+     * @throws IOException if there is a problem writing to the underlying stream
+     * @see #writeAttr
+     * @see #write
+     * @see #endTag
+     */
+    public void startTag(String tag) throws IOException {
+        if (state == IN_TAG) {
+            out.write(">");
+            state = IN_BODY;
+        }
+        newLine();
+        out.write("<");
+        out.write(tag);
+        state = IN_TAG;
+    }
+
+    /**
+     * Finish an HTML tag. It is expected that a call to endTag will match
+     * a corresponding earlier call to startTag, but there is no formal check
+     * for this.
+     * @param tag the tag to be closed.
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public void endTag(String tag) throws IOException {
+        if (state == IN_TAG) {
+            out.write(">");
+            state = IN_BODY;
+            out.newLine();
+        }
+        out.write("</");
+        out.write(tag);
+        out.write(">");
+        //out.newLine();   // PATCHED, jjg
+        state = IN_BODY;
+    }
+
+    /**
+     * Finish an empty element tag, such as a META, BASE or LINK tag.
+     * This is expected to correspond with a startTag.
+     * @param tag the tag which is being closed.  this is only useful for
+     *        validation, it is not written out
+     * @throws IllegalStateException if this call does not follow startTag
+     *         (stream is not currently inside a tag)
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public void endEmptyTag(String tag) throws IOException {
+        if (state != IN_TAG)
+            throw new IllegalStateException();
+
+        out.write(">");
+        state = IN_BODY;
+        out.newLine();
+    }
+
+    /**
+     * Write an attribute for a tag. A tag must previously have been started.
+     * All tag attributes must be written before any body text is written.
+     * The value will be quoted if necessary when writing it to the underlying
+     * stream. No check is made that the attribute is valid for the current tag.
+     * @param name the name of the attribute to be written
+     * @param value the value of the attribute to be written
+     * @throws IllegalStateException if the stream is not in a state to
+     * write attributes -- e.g. if this call does not follow startTag or other
+     * calls of writteAttr
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public void writeAttr(String name, String value) throws IOException {
+        if (state != IN_TAG)
+            throw new IllegalStateException();
+
+        out.write(" ");
+        out.write(name);
+        out.write("=");
+        boolean alpha = true;
+        for (int i = 0; i < value.length() && alpha; i++)
+            alpha = Character.isLetter(value.charAt(i));
+        if (!alpha)
+            out.write("\"");
+        out.write(value);
+        if (!alpha)
+            out.write("\"");
+    }
+
+    /**
+     * Write an attribute for a tag. A tag must previously have been started.
+     * All tag attributes must be written before any body text is written.
+     * The value will be quoted if necessary when writing it to the underlying
+     * stream. No check is made that the attribute is valid for the current tag.
+     * @param name the name of the attribute to be written
+     * @param value the value of the attribute to be written
+     * @throws IllegalStateException if the stream is not in a state to
+     * write attributes -- e.g. if this call does not follow startTag or other
+     * calls of writteAttr
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public void writeAttr(String name, int value) throws IOException {
+        writeAttr(name, Integer.toString(value));
+    }
+
+    /**
+     * Write a line of text, followed by a newline.
+     * The text will be escaped as necessary.
+     * @param text the text to be written.
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeLine(String text) throws IOException {
+        write(text);
+        out.newLine();
+    }
+
+    /**
+     * Write body text, escaping it as necessary.
+     * If this call follows a call of startTag, the open tag will be
+     * closed -- meaning that no more attributes can be written until another
+     * tag is started.  If the text value is null, the current tag will still
+     * be closed, but no other text will be written.
+     * @param text the text to be written, may be null or zero length.
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public void write(String text) throws IOException {
+        if (state == IN_TAG) {
+            out.write(">");
+            state = IN_BODY;
+        }
+
+        if (text == null)
+            return;
+
+        // check to see if there are any special characters
+        boolean specialChars = false;
+        for (int i = 0; i < text.length() && !specialChars; i++) {
+            switch (text.charAt(i)) {
+            case '<': case '>': case '&':
+                specialChars = true;
+            }
+        }
+
+        // if there are special characters write the string character at a time;
+        // otherwise, write it out as is
+        if (specialChars) {
+            for (int i = 0; i < text.length(); i++) {
+                char c = text.charAt(i);
+                switch (c) {
+                case '<': out.write("&lt;"); break;
+                case '>': out.write("&gt;"); break;
+                case '&': out.write("&amp;"); break;
+                default: out.write(c);
+                }
+            }
+        }
+        else
+            out.write(text);
+    }
+
+    /**
+     * Write a basic HTML entity, such as &nbsp; or &#123; .
+     * @param entity the entity to write
+     * @throws IOException if there is a problem writing to the underlying stream
+     */
+    public void writeEntity(String entity) throws IOException {
+        if (state == IN_TAG) {
+            out.write(">");
+            state = IN_BODY;
+        }
+        out.write(entity);
+    }
+
+    /**
+     * Write an image tag, using a specified path for the image source attribute.
+     * @param imagePath the path for the image source
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeImage(String imagePath) throws IOException {
+        startTag(IMAGE);
+        writeAttr(SRC, imagePath);
+    }
+
+    /**
+     * Write an image tag, using a specified path for the image source attribute.
+     * @param imageURL the url for the image source
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeImage(URL imageURL) throws IOException {
+        writeImage(imageURL.toString());
+    }
+
+    /**
+     * Write a hypertext link.
+     * @param anchor the target for the link
+     * @param body the body text for the link
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeLink(String anchor, String body) throws IOException {
+        startTag(A);
+        writeAttr(HREF, anchor);
+        write(body);
+        endTag(A);
+    }
+
+    /**
+     * Write a hypertext link.
+     * @param file the target for the link
+     * @param body the body text for the link
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeLink(File file, String body) throws IOException {
+        startTag(A);
+        StringBuffer sb = new StringBuffer();
+        String path = file.getPath().replace(File.separatorChar, '/');
+        if (file.isAbsolute() && !path.startsWith("/"))
+            sb.append('/');
+        sb.append(path);
+        writeAttr(HREF, sb.toString());
+        write(body);
+        endTag(A);
+    }
+
+    /**
+     * Write a hypertext link.
+     * @param file the target and body for the link
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeLink(File file) throws IOException {
+        writeLink(file, file.getPath());
+    }
+
+    /**
+     * Write a hypertext link.
+     * @param url the target for the link
+     * @param body the body text for the link
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeLink(URL url, String body) throws IOException {
+        startTag(A);
+        writeAttr(HREF, url.toString());
+        write(body);
+        endTag(A);
+    }
+
+    /**
+     * Write the destination marker for a hypertext link.
+     * @param anchor the destination marker for hypertext links
+     * @param body the body text for the marker
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeLinkDestination(String anchor, String body) throws IOException {
+        startTag(A);
+        writeAttr(NAME, anchor);
+        write(body);
+        endTag(A);
+    }
+
+    /**
+     * Write a parameter tag.
+     * @param name the name of the parameter
+     * @param value the value of the parameter
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeParam(String name, String value) throws IOException {
+        startTag(PARAM);
+        writeAttr(NAME, name);
+        writeAttr(VALUE, value);
+    }
+
+    /**
+     * Write a style attribute.
+     * @param value the value for the style atrtribute
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeStyleAttr(String value) throws IOException {
+        writeAttr(STYLE, value);
+    }
+
+    /**
+     * Write a localized message, using a specified resource bundle.
+     * @param i18n the resource bundle used to localize the message
+     * @param key the key for the message to be localized
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void write(I18NResourceBundle i18n, String key) throws IOException {
+        write(i18n.getString(key));
+    }
+
+    /**
+     * Write a localized message, using a specified resource bundle.
+     * @param i18n the resource bundle used to localize the message
+     * @param key the key for the message to be localized
+     * @param arg an argument to be formatted into the localized message
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void write(I18NResourceBundle i18n, String key, Object arg) throws IOException {
+        write(i18n.getString(key, arg));
+    }
+
+    /**
+     * Write a localized message, using a specified resource bundle.
+     * @param i18n the resource bundle used to localize the message
+     * @param key the key for the message to be localized
+     * @param args arguments to be formatted into the localized message
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void write(I18NResourceBundle i18n, String key, Object[] args) throws IOException {
+        write(i18n.getString(key, args));
+    }
+
+    /**
+     * Write a localized message, using the default resource bundle.
+     * @param key the key for the message to be localized
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeI18N(String key) throws IOException {
+        write(i18n.getString(key));
+    }
+
+    /**
+     * Write a localized message, using the default resource bundle.
+     * @param key the key for the message to be localized
+     * @param arg an argument to be formatted into the localized message
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeI18N(String key, Object arg) throws IOException {
+        write(i18n.getString(key, arg));
+    }
+
+    /**
+     * Write a localized message, using the default resource bundle.
+     * @param key the key for the message to be localized
+     * @param args arguments to be formatted into the localized message
+     * @throws IOException if there is a problem closing the underlying stream
+     */
+    public void writeI18N(String key, Object[] args) throws IOException {
+        write(i18n.getString(key, args));
+    }
+
+    /** The HTML "a" tag. */
+    public static final String A = "a";
+    /** The HTML "align" attribute. */
+    public static final String ALIGN = "align";
+    /** The HTML "b" tag. */
+    public static final String B = "b";
+    /** The HTML "body" tag. */
+    public static final String BODY = "body";
+    /** The HTML "border" attribute. */
+    public static final String BORDER = "border";
+    /** The HTML "br" tag. */
+    public static final String BR = "br";
+    /** The HTML "classid" attribute. */
+    public static final String CLASSID  = "classid";
+    /** The HTML "code" tag. */
+    public static final String CODE  = "code";
+    /** The HTML "color" attribte. */
+    public static final String COLOR  = "color";
+    /** The HTML "col" attribute value. */
+    public static final String COL = "col";
+    /** The HTML "font" tag. */
+    public static final String FONT = "font";
+    /** The HTML "h1" tag. */
+    public static final String H1 = "h1";
+    /** The HTML "h2" tag. */
+    public static final String H2 = "h2";
+    /** The HTML "h3" tag. */
+    public static final String H3 = "h3";
+    /** The HTML "h4" tag. */
+    public static final String H4 = "h4";
+    /** The HTML "head" tag. */
+    public static final String HEAD = "head";
+    /** The HTML "href" attribute. */
+    public static final String HREF = "href";
+    /** The HTML "html" tag. */
+    public static final String HTML = "html";
+    /** The HTML "hr" tag. */
+    public static final String HR = "hr";
+    /** The HTML "i" tag. */
+    public static final String I = "i";
+    /** The HTML "image" tag. */
+    public static final String IMAGE = "image";
+    /** The HTML "left" attribute value. */
+    public static final String LEFT = "left";
+    /** The HTML "li" tag. */
+    public static final String LI = "li";
+    /** The HTML "link" tag. */
+    public static final String LINK = "link";
+    /** The HTML "name" attribute. */
+    public static final String NAME = "name";
+    /** The HTML "object" tag. */
+    public static final String OBJECT = "object";
+    /** The HTML "p" tag. */
+    public static final String PARAM = "param";
+    /** The HTML "param" tag. */
+    public static final String P = "p";
+    /** The HTML "rel" attribute value. */
+    public static final String REL = "rel";
+    /** The HTML "right" attribute value. */
+    public static final String RIGHT = "right";
+    /** The HTML "row" attribute value. */
+    public static final String ROW = "row";
+    /** The HTML "small" tag. */
+    public static final String SMALL = "small";
+    /** The HTML "src" attribute. */
+    public static final String SRC = "src";
+    /** The HTML "scope" attribute. */
+    public static final String SCOPE = "scope";
+    /** The HTML "style" attribute. */
+    public static final String STYLE = "style";
+    /** The HTML "table" tag. */
+    public static final String TABLE = "table";
+    /** The HTML "td" tag. */
+    public static final String TD = "td";
+    /** The HTML "title"attribute. */
+    public static final String TITLE = "title";
+    /** The HTML "th" tag. */
+    public static final String TH = "th";
+    /** The HTML "top" attribute value. */
+    public static final String TOP = "top";
+    /** The HTML "tr" tag. */
+    public static final String TR = "tr";
+    /** The HTML "type" attribute. */
+    public static final String TYPE = "type";
+    /** The HTML "ul" tag. */
+    public static final String UL = "ul";
+    /** The HTML "valign" attribute. */
+    public static final String VALIGN = "valign";
+    /** The HTML "value" attribute. */
+    public static final String VALUE = "value";
+
+
+    private BufferedWriter out;
+    private int state;
+    private I18NResourceBundle i18n;
+    private static final int IN_TAG = 1;
+    private static final int IN_BODY = 2;
+}
--- a/test/jtreg/com/sun/javatest/diff/Main.java	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/diff/Main.java	Sun Nov 09 22:29:59 2008 +0100
@@ -33,20 +33,12 @@
 package com.sun.javatest.diff;
 
 import com.sun.javatest.regtest.BadArgs;
-import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Comparator;
 import java.util.List;
 
-import com.sun.javatest.Status;
-import com.sun.javatest.TestResult;
-import com.sun.javatest.TestSuite;
-import com.sun.javatest.WorkDirectory;
 import com.sun.javatest.regtest.AntOptionDecoder;
 import com.sun.javatest.regtest.Option;
 import com.sun.javatest.regtest.OptionDecoder;
@@ -58,29 +50,24 @@
  * Main entry point for jtdiff.
  */
 public class Main {
-    
-    /**
-     * Exception to report a problem while executing in Main.
-     */
-    public static class Fault extends Exception {
-        static final long serialVersionUID = 1607979458544175906L;
-        Fault(I18NResourceBundle i18n, String s, Object... args) {
-            super(i18n.getString(s, args));
-        }
-    }
     //---------- command line option decoding ----------------------------------
-  
+
     private static final String COMPARE = "compare";
     private static final String OUTPUT = "output";
     private static final String DOC = "doc";
     private static final String FILES = "files";
-    
+
     Option[] options = {
-        new Option(NONE, COMPARE, "r", "reason") {
+        new Option(NONE, COMPARE, "r", "r", "reason") {
             public void process(String opt, String arg) {
                 includeReason = true;
             }
         },
+        new Option(NONE, COMPARE, "s", "s", "super") {
+            public void process(String opt, String arg) {
+                superMode = true;
+            }
+        },
         new Option(OLD, OUTPUT, "o", "o", "outFile") {
             public void process(String opt, String arg) {
                 outFile = new File(arg);
@@ -136,7 +123,7 @@
             } finally {
                 out.flush();
             }
-            
+
             if (!ok) {
                 // take care not to exit if GUI might be around,
                 // and take care to ensure JavaTestSecurityManager will
@@ -159,16 +146,16 @@
             exit(3);
         }
     } // main()
-    
+
     public Main() {
         this(new PrintWriter(System.out, true), new PrintWriter(System.err, true));
     }
-    
+
     public Main(PrintWriter out, PrintWriter err) {
         this.out = out;
         this.err = err;
     }
-    
+
     /**
      * Decode command line args and perform the requested operations.
      * @param args An array of args, such as might be supplied on the command line.
@@ -178,141 +165,55 @@
      */
     public final boolean run(String[] args) throws BadArgs, Fault, InterruptedException {
         new OptionDecoder(options).decodeArgs(args);
-            
+
+        if (superMode) {
+            if (fileArgs.size() != 1 || !fileArgs.get(0).isDirectory())
+                throw new Fault(i18n, "main.bad.super.dir");
+            if (format != null)
+                throw new Fault(i18n, "main.bad.super.format");
+        }
+
         return run();
     }
-    
+
     private boolean run() throws Fault, InterruptedException {
-        if (fileArgs.size() == 0 && help == null) {
+        if (fileArgs.size() == 0 && !superMode && help == null) {
             help = new Help(options);
             help.setCommandLineHelpQuery(null);
         }
-        
+
         if (help != null) {
             help.show(out);
             return true;
         }
-        
-        List<DiffReader> list = new ArrayList<DiffReader>();
-        for (File f: fileArgs)
-            list.add(open(f));
-        
-        PrintWriter prevOut = out;
-        if (outFile != null) {
-            try {
-                out = new PrintWriter(new BufferedWriter(new FileWriter(outFile))); // FIXME don't want to use PrintWriter
-            } catch (IOException e) {
-                throw new Fault(i18n, "main.cantOpenFile", outFile, e);
-            }
-        }
-        
-        try {
-            initComparator();  
-            
-            initReporter();  
-            reporter.setTitle(title); 
-            reporter.setComparator(comparator);
-            reporter.setReaders(list);
 
-            List<int[]> testCounts = new ArrayList<int[]>();
-            MultiMap<String,TestResult> table = new MultiMap<String,TestResult>();
-            for (DiffReader r: list) {
-                int index = table.add(r.getFile().getPath());
-                int[] counts = new int[Status.NUM_STATES];
-                for (TestResult tr: r) {
-                    table.add(index, tr.getTestName(), tr);
-                    counts[tr.getStatus().getType()]++;
-                }
-                testCounts.add(counts);
-            }
-
-            reporter.setTestCounts(testCounts);
-            
-            try {
-                reporter.write(table);
-            } catch (IOException e) {
-                throw new Fault(i18n, "main.ioError", e);
-            }
+        Diff d;
+        if (superMode)
+            d = new SuperDiff(fileArgs.get(0));
+        else
+            d = new StandardDiff(fileArgs);
 
-            return (reporter.diffs == 0);
-        } finally {
-            if (out != prevOut) {
-//                try {
-                    out.close();
-//                } catch (IOException e) {
-//                    throw new Fault(i18n, "main.ioError", e);
-//                }
-                out = prevOut;
-            }
-        }
-    }
-    
-    private void initFormat() {
-        if (format == null && outFile != null) {
-            String name = outFile.getName();
-            int dot = name.lastIndexOf(".");
-            if (dot != -1) 
-                format = name.substring(dot + 1).toLowerCase();
-        }
+        d.includeReason = includeReason;
+        d.format = format;
+        d.title = title;
+
+        return d.report(outFile);
     }
-    
-    private void initReporter() throws Fault {
-        if (reporter == null) {
-            try {
-                initFormat();
-                if (format != null && format.equals("html"))
-                    reporter = new HTMLReporter(out);
-                if (reporter == null)
-                    reporter = new SimpleReporter(out); 
-            } catch (IOException e) {
-                throw new Fault(i18n, "main.cantOpenReport", e);
-            }
-        }
-    }
-    
-    private void initComparator() {
-        if (comparator == null)
-            comparator = new StatusComparator(includeReason); 
-    }
-    
-    private DiffReader open(File f) throws Fault {
-        if (!f.exists())
-            throw new Fault(i18n, "main.cantFindFile", f);
-        
-        try {
-            if (WorkDirectoryReader.accepts(f)) 
-                return new WorkDirectoryReader(f);
-            
-            if (ReportReader.accepts(f)) 
-                return new ReportReader(f);
-            
-            throw new Fault(i18n, "main.unrecognizedFile", f);
-            
-        } catch (TestSuite.Fault e) {
-            throw new Fault(i18n, "main.cantOpenFile", f, e);
-        } catch (WorkDirectory.Fault e) {
-            throw new Fault(i18n, "main.cantOpenFile", f, e);
-        } catch (IOException e) {
-            throw new Fault(i18n, "main.cantOpenFile", f, e);
-        }
-        
-    }
-    
+
     private static void exit(int exitCode) {
         System.exit(exitCode);
     }
-    
+
     private PrintWriter out;
     private PrintWriter err;
-    private Comparator<TestResult> comparator;
-    private Reporter reporter;
-    
+
     private boolean includeReason;
     private String format;
     private String title;
     private File outFile;
     private List<File> fileArgs = new ArrayList<File>();
+    private boolean superMode;
     private Help help;
-    
+
     private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(Main.class);
 }
--- a/test/jtreg/com/sun/javatest/diff/MultiMap.java	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/diff/MultiMap.java	Sun Nov 09 22:29:59 2008 +0100
@@ -38,26 +38,26 @@
  */
 public class MultiMap<K,V> implements Map<K, MultiMap.Entry<V>> {
     public static class Entry<V> {
-        
+
         private Entry(MultiMap<?,?> t) {
             table = t;
         }
-        
+
         V get(int index) {
             return (index < list.size() ? list.get(index) : null);
         }
-        
+
         int getSize() {
             return table.getColumns();
         }
-        
+
         void put(int index, V value) {
             if (index >= table.getColumns())
                 throw new IndexOutOfBoundsException();
-            
+
             if (list == null)
                 list = new ArrayList<V>(index);
-            
+
             if (index < list.size())
                 list.set(index, value);
             else {
@@ -66,7 +66,7 @@
                 list.add(value);
             }
         }
-        
+
         boolean allEqual(Comparator<V> c) {
             if (list.size() == 0)
                 return true;
@@ -79,96 +79,95 @@
             }
             return true;
         }
-        
+
         private List<V> list;
         private MultiMap<?,?> table;
     }
-    
-    /** Creates a new instance of DiffTable */
+
+    /** Creates a new instance of MultiMap */
     public MultiMap() {
         names = new ArrayList<String>();
         map = new TreeMap<K, Entry<V>>();
     }
-    
+
     int getColumns() {
         return names.size();
     }
-    
+
     String getColumnName(int index) {
         return names.get(index);
     }
-    
-    int add(String name) {
+
+    int addColumn(String name) {
         names.add(name);
         return names.size() - 1;
     }
-    
-    void add(int index, K k, V v) {
+
+    void addColumn(String name, Map<K, V> map) {
+        addColumn(name, map.entrySet());
+    }
+
+    void addColumn(String name, Iterable<Map.Entry<K,V>> iter) {
+        int index = addColumn(name);
+        for (Map.Entry<K,V> e: iter)
+            addRow(index, e.getKey(), e.getValue());
+    }
+
+    void addRow(int index, K k, V v) {
         Entry<V> de = get(k);
         if (de == null)
             put(k, de = new Entry<V>(this));
         de.put(index, v);
-        
     }
-    
-    void add(String name, Map<K, V> map) {
-        add(name, map.entrySet());
-    }
-    
-    void add(String name, Iterable<Map.Entry<K,V>> iter) {
-        int index = add(name);
-        for (Map.Entry<K,V> e: iter)
-            add(index, e.getKey(), e.getValue());
-    }
-    
+
     public int size() {
         return map.size();
     }
-    
+
     public Entry<V> get(Object path) {
         return map.get(path);
     }
-    
+
     public boolean isEmpty() {
         return map.isEmpty();
     }
-    
+
     public boolean containsKey(Object key) {
         return map.containsKey(key);
     }
-    
+
     public boolean containsValue(Object value) {
         return map.containsValue(value);
     }
-    
+
     public Entry<V> put(K key, Entry<V> value) {
         return map.put(key, value);
     }
-    
+
     public Entry<V> remove(Object key) {
         return map.remove(key);
     }
-    
+
     public void putAll(Map<? extends K, ? extends Entry<V>> t) {
         map.putAll(t);
     }
-    
+
     public void clear() {
         map.clear();
     }
-    
+
     public Set<Map.Entry<K, Entry<V>>> entrySet() {
         return map.entrySet();
     }
-    
+
     public Set<K> keySet() {
         return map.keySet();
     }
-    
+
     public Collection<Entry<V>> values() {
         return map.values();
     }
-    
+
     private List<String> names;
     private TreeMap<K, Entry<V>> map;
 }
--- a/test/jtreg/com/sun/javatest/diff/ReportReader.java	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/diff/ReportReader.java	Sun Nov 09 22:29:59 2008 +0100
@@ -39,28 +39,28 @@
 import java.util.List;
 
 /**
- * Read a set of test results from summary.txt, possibly located in a 
+ * Read a set of test results from summary.txt, possibly located in a
  * report directory.
  */
 public class ReportReader implements DiffReader {
-    private static final String SUMMARY_TXT = "summary.txt"; 
-    
+    private static final String SUMMARY_TXT = "summary.txt";
+
     public static boolean accepts(File f) {
         if (!f.exists())
             return false;
-        
+
         if (f.isFile() && f.getName().equals(SUMMARY_TXT))
             return true;
-        
+
         if (f.isDirectory() && new File(f, SUMMARY_TXT).exists())
             return true;
-        
+
         if (f.isDirectory() && new File(new File(f, "text"), SUMMARY_TXT).exists())
             return true;
-        
+
         return false;
     }
-    
+
     /** Creates a new instance of SummaryReader */
     public ReportReader(File file) {
         this.file = file;
@@ -69,11 +69,7 @@
     public File getFile() {
         return file;
     }
-    
-    public File getWorkDirectory() {
-        return null;
-    }
-    
+
     public String getFileType() {
         if (file != null && file.isDirectory())
             return i18n.getString("report.reportDir");
@@ -81,10 +77,14 @@
             return i18n.getString("report.reportFile");
     }
 
+    public File getWorkDirectory() {
+        return null;
+    }
+
     public Iterator<TestResult> iterator() {
         return readSummary().iterator();
     }
-    
+
     private List<TestResult> readSummary() {
         List<TestResult> list = new ArrayList<TestResult>();
         File root = getRoot();
@@ -97,7 +97,7 @@
             f = new File(new File(file, "text"), SUMMARY_TXT);
         else
             throw new IllegalStateException();
-        
+
         try {
             BufferedReader in = new BufferedReader(new FileReader(f));
             String line;
@@ -113,14 +113,14 @@
         }
         return list;
     }
-    
+
     private File getRoot() {
         return UNKNOWN;
     }
-    
+
     private static File UNKNOWN = new File("unknown");
-    
+
     private File file;
-    
+
     private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(WorkDirectoryReader.class);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/diff/StandardDiff.java	Sun Nov 09 22:29:59 2008 +0100
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.javatest.diff;
+
+import java.io.File;
+import java.util.List;
+
+public class StandardDiff extends Diff {
+    StandardDiff(List<File> files) {
+        this.files = files;
+    }
+
+    @Override
+    public boolean report(File outFile) throws Fault, InterruptedException {
+        return diff(files, outFile);
+    }
+
+    List<File> files;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jtreg/com/sun/javatest/diff/SuperDiff.java	Sun Nov 09 22:29:59 2008 +0100
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.javatest.diff;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import com.sun.javatest.util.I18NResourceBundle;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import static com.sun.javatest.util.HTMLWriter.*;
+
+class SuperDiff extends Diff {
+    SuperDiff(File dir) {
+        table = new SuperTable(dir, resultPath);
+    }
+
+    @Override
+    public boolean report(File outDir) throws Fault, InterruptedException {
+        baseTitle = title;
+        boolean ok = true;
+        for (YearDay yearDay: table.getRecentKeys(historySize))
+            ok &= diffPlatforms(yearDay, outDir);
+        for (String platform : table.platforms) {
+            ok &= diffHistory(platform, outDir);
+        }
+        writeIndex(outDir, baseTitle);
+        return ok;
+    }
+
+    protected boolean diff(List<File> files, File outFile, String title) throws Fault, InterruptedException {
+        this.title = title;
+        reporter = null;
+        return diff(files, outFile);
+    }
+
+    @Override
+    protected void initReporter() throws Fault {
+        try {
+             reporter = new SuperReporter(out);
+        } catch (IOException e) {
+            throw new Fault(i18n, "main.cantOpenReport", e);
+        }
+    }
+
+    private boolean diffPlatforms(YearDay yearDay, File outDir) throws Fault, InterruptedException {
+        Map<String, File> pMap = table.get(yearDay);
+        List<File> pDirs = new ArrayList<File>();
+        for (String platform : table.platforms) {
+            File dir = pMap.get(platform);
+            if (dir != null) {
+                pDirs.add(dir);
+            }
+        }
+        File file = new File(outDir, yearDay.year + "_" + yearDay.dayOfYear + ".html");
+        platformIndex.put(yearDay.toDateString(monthDayFormat), file);
+        String prefix = baseTitle == null ? "" : baseTitle + ": ";
+        return diff(pDirs, file, prefix + yearDay.toDateString(mediumDateFormat)); // I18N a better title?
+    }
+
+    private boolean diffHistory(String platform, File outDir) throws Fault, InterruptedException {
+        List<File> pDirs = new ArrayList<File>();
+        for (YearDay yearDay: table.getRecentKeys(historySize, platform)) {
+            pDirs.add(table.get(yearDay).get(platform));
+        }
+        File file = new File(outDir, platform + ".html");
+        historyIndex.put(platform, file);
+        String prefix = baseTitle == null ? "" : baseTitle + ": ";
+        return diff(pDirs, file, prefix + platform);// I18N a better title?
+    }
+
+    private void writeIndex(File outDir, String title) throws Fault {
+        PrintWriter out;
+        try {
+            out = new PrintWriter(new BufferedWriter(new FileWriter(new File(outDir, "index.html"))));
+        } catch (IOException e) {
+            throw new Fault(i18n, "main.cantOpenReport", e);
+        }
+
+        try {
+            SuperReporter r = new SuperReporter(out);
+            r.writeMainIndex(title);
+        } catch (IOException e) {
+            throw new Fault(i18n, "main.ioError", e);
+        } finally {
+            out.close();
+        }
+    }
+
+    protected String resultPath = System.getProperty("jtdiff.super.testResults", "JTreport/text/summary.txt");
+    protected int historySize = Integer.getInteger("jtdiff.super.history", 21);
+
+    private SuperTable table;
+    private String baseTitle;
+    private Map<String,File> historyIndex = new LinkedHashMap<String,File>();
+    private Map<String,File> platformIndex = new LinkedHashMap<String,File>();
+
+    private static DateFormat monthDayFormat = new SimpleDateFormat("MMM d");
+    private static DateFormat mediumDateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
+    private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(Main.class);
+
+    static class Info {
+        Info(String platform, Date date) {
+            this.platform = platform;
+            this.date = date;
+        }
+
+        final String platform;
+        final Date date;
+    }
+
+    class SuperReporter extends HTMLReporter {
+        SuperReporter(Writer out) throws IOException {
+            super(out);
+        }
+
+        protected void writeIndexTableInfoHeadings() throws IOException {
+            out.startTag(TH);
+            out.writeI18N("super.th.platform");
+            out.endTag(TH);
+            out.startTag(TH);
+            out.writeI18N("super.th.date");
+            out.endTag(TH);
+        }
+
+        protected void writeIndexTableInfoValues(String path) throws IOException {
+            Info info = table.getInfo(path);
+            out.startTag(TD);
+            if (info != null)
+                out.write(info.platform);
+            out.endTag(TD);
+            out.startTag(TD);
+            if (info != null)
+                out.write(monthDayFormat.format(info.date));
+            out.endTag(TH);
+        }
+
+        void writeMainIndex(String title) throws IOException {
+            startReport(title);
+
+            out.startTag(H1);
+            out.write(baseTitle);
+            out.endTag(H1);
+
+            writeMainIndexList(i18n.getString("super.platforms"), platformIndex);
+            writeMainIndexList(i18n.getString("super.history"), historyIndex);
+
+            endReport();
+        }
+
+        void writeMainIndexList(String head, Map<String,File> map) throws IOException {
+            out.startTag(H2);
+            out.write(head);
+            out.endTag(H2);
+            out.startTag(P);
+            String comma = "";
+            for (Map.Entry<String,File> e: map.entrySet()) {
+                out.write(comma);
+                out.startTag(A);
+                out.writeAttr(HREF, e.getValue().getName());
+                String nbsp = "";
+                for (String s: e.getKey().split(" ")) {
+                    out.writeEntity(nbsp);
+                    out.write(s);
+                    nbsp = "&nbsp;";
+                }
+                out.endTag(A);
+                comma = ", ";
+            }
+        }
+    }
+
+    static class SuperTable extends TreeMap<YearDay, Map<String, File>> {
+
+        static final long serialVersionUID = 5933594140534747584L;
+
+        SuperTable(File inDir, String resultPath) {
+            super();
+            for (File pDir : inDir.listFiles()) {
+                if (!pDir.isDirectory()) {
+                    continue;
+                }
+                for (File yDir : pDir.listFiles()) {
+                    if (!yDir.isDirectory()) {
+                        continue;
+                    }
+                    for (File dDir : yDir.listFiles()) {
+                        if (!dDir.isDirectory()) {
+                            continue;
+                        }
+                        File resultDir = new File(dDir, resultPath);
+                        if (resultDir.exists()) {
+                            add(pDir.getName(), yDir.getName(), dDir.getName(), resultDir);
+                        }
+                    }
+                }
+            }
+        }
+
+        private void add(String platform, String year, String day, File dir) {
+            platforms.add(platform);
+            YearDay yd = new YearDay(year, day);
+            Map<String, File> pMap = get(yd);
+            if (pMap == null) {
+                pMap = new HashMap<String, File>();
+                put(yd, pMap);
+            }
+            pMap.put(platform, dir);
+
+            Date date;
+            try {
+                Calendar c = Calendar.getInstance();
+                c.clear();
+                c.set(Calendar.YEAR, Integer.parseInt(year));
+                c.set(Calendar.DAY_OF_YEAR, Integer.parseInt(day));
+                date = c.getTime();
+            } catch (NumberFormatException e) {
+                date = null;
+            }
+            infoTable.put(dir.getPath(), new Info(platform, date));
+        }
+
+        List<YearDay> getRecentKeys(int n) {
+            return getRecentKeys(n, null);
+        }
+
+        List<YearDay> getRecentKeys(int n, String platform) {
+            LinkedList<YearDay> results = new LinkedList<YearDay>();
+            List<YearDay> keys = new ArrayList<YearDay>(keySet());
+            for (ListIterator<YearDay> iter = keys.listIterator(keys.size());
+                    iter.hasPrevious() && results.size() < n; ) {
+                YearDay key = iter.previous();
+                if (platform == null || get(key).get(platform) != null)
+                    results.addFirst(key);
+            }
+            return results;
+        }
+
+        Info getInfo(String path) {
+            return infoTable.get(path);
+        }
+
+        final Set<String> platforms = new TreeSet<String>();
+        final Map<String,Info> infoTable = new HashMap<String, Info>();
+    }
+
+    static class YearDay implements Comparable<YearDay> {
+        YearDay(String year, String dayOfYear) {
+            year.getClass();
+            dayOfYear.getClass();
+            this.year = year;
+            this.dayOfYear = dayOfYear;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof YearDay))
+                return false;
+            YearDay ydo = (YearDay) o;
+            return year.equals(ydo.year) && dayOfYear.equals(ydo.dayOfYear);
+        }
+
+        @Override
+        public int hashCode() {
+            return year.hashCode() * 37 + dayOfYear.hashCode();
+        }
+
+        //@Override
+        public int compareTo(YearDay o) {
+            int c = compare(year, o.year);
+            return (c == 0 ? compare(dayOfYear, o.dayOfYear) : c);
+        }
+
+        public String toString() {
+            return year + ":" + dayOfYear;
+        }
+
+        public Date asDate() {
+            try {
+                Calendar c = Calendar.getInstance();
+                c.clear();
+                c.set(Calendar.YEAR, Integer.parseInt(year));
+                c.set(Calendar.DAY_OF_YEAR, Integer.parseInt(dayOfYear));
+                return c.getTime();
+            } catch (NumberFormatException e) {
+                return null;
+            }
+        }
+
+        public String toDateString(DateFormat f) {
+            Date d = asDate();
+            return (d == null ? toString() : f.format(d));
+        }
+
+        private int compare(String left, String right) {
+            return left.compareTo(right);
+        }
+
+        final String year;
+        final String dayOfYear;
+    }
+
+}
--- a/test/jtreg/com/sun/javatest/diff/WorkDirectoryReader.java	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/diff/WorkDirectoryReader.java	Sun Nov 09 22:29:59 2008 +0100
@@ -48,12 +48,12 @@
     public static boolean accepts(File f) {
         return WorkDirectory.isWorkDirectory(f);
     }
-    
+
     /** Creates a new instance of WorkDirectoryReader */
-    public WorkDirectoryReader(File file) 
+    public WorkDirectoryReader(File file)
             throws FileNotFoundException, WorkDirectory.Fault, TestSuite.Fault {
         this.file = file;
-        
+
         // Because regtest testsuites don't contain testsuite.jtt
         // files, we can't use the standard WorkDirectory.open call.
         File tsp = getTestSuitePath(file);
@@ -63,31 +63,31 @@
         } else
             wd = WorkDirectory.open(file);
     }
-    
+
+    public File getFile() {
+        return file;
+    }
+
     public String getFileType() {
         return i18n.getString("wd.name");
     }
-    
-    public File getFile() {
-        return file;
-    }
-    
+
     public File getWorkDirectory() {
         return wd.getRoot();
     }
-    
+
     @SuppressWarnings("unchecked")
     public Iterator<TestResult> iterator() {
         TestResultTable trt = wd.getTestResultTable();
         trt.waitUntilReady();
         return trt.getIterator();
     }
-    
+
     private static File getTestSuitePath(File workDir) {
         File f = new File(new File(workDir, "jtData"), "testsuite");
-        if (!f.exists()) 
+        if (!f.exists())
             return null;
-          
+
         InputStream in = null;
         try {
             in = new BufferedInputStream(new FileInputStream(f));
@@ -105,9 +105,9 @@
             return null;
         }
     }
-    
+
     private File file;;
     private WorkDirectory wd;
-    
+
     private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(WorkDirectoryReader.class);
 }
--- a/test/jtreg/com/sun/javatest/diff/i18n.properties	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/diff/i18n.properties	Sun Nov 09 22:29:59 2008 +0100
@@ -23,6 +23,10 @@
 # have any questions.
 #
 
+diff.cantOpenFile=Cannot open file {0}: {1}
+diff.cantOpenReport=Cannot open report: {0}
+diff.ioError=Error: {0}
+
 help.cantFindReleaseNotes=
 help.onlineHelp.pleaseWait=Loading online help ...
 help.releaseNotes=
@@ -42,8 +46,21 @@
 
 help.compare.name=Compare Options      
 help.compare.desc=
-help.compare.reason.desc=Include the reason string in the comparison, as well as \
+help.compare.r.desc=Include the reason string in the comparison, as well as \
     the type (i.e. Passed, Failed, etc.)
+help.compare.s.desc=Perform a "super-diff" looking for result sets within \
+    a directory hierarchy. The hierarchy must be arranged as \
+    follows: PATH/PLATFORM/YEAR/DAY_OF_YEAR/TEST_RESULTS, where PATH is \
+    given by the single directory argument, PLATFORM is the name of the \
+    platform on which the tests were run, YEAR and DAY_OF_YEAR are numbers \
+    identifying when the tests were run, and TESTSUITE_RESULTS is a fixed string \
+    identifying when to find the result set in the hierarchy. The default \
+    is "JTreport/text/summary.txt", but it can be set to a different value by \
+    setting the system property "jtdiff.super.testResults". It may identify any \
+    source of results acceptable to a standard invocation of jtdiff. \
+    Set system property jtdiff.html.compact=true for a more output format, with \
+    symbols replacing the words "pass" (check mark), "fail" (cross), "error" \
+    (cross in a circle), and "not run" (horizontal bar).
 
 help.doc.name=Help Options
 help.doc.desc=Options for additional help and information
@@ -76,6 +93,10 @@
 html.fail=fail
 html.notRun=not run
 html.pass=pass
+html.error.compact=&otimes;
+html.fail.compact=&times;
+html.notRun.compact=&mdash;
+html.pass.compact=&radic;
 
 html.head.differences=Differences
 html.head.notitle=jtdiff results
@@ -94,6 +115,8 @@
 html.th.type=Type
 
 main.badArgs=Error: {0}
+main.bad.super.dir=Bad directory argument for "super" mode
+main.bad.super.format=Cannot specify format in "super" mode
 main.cantFindFile=Cannot find file: {0}
 main.cantOpenFile=Cannot open file {0}: {1}
 main.cantOpenReport=Cannot open report: {0}
@@ -123,3 +146,8 @@
     {6,choice,0#|0<not run: {6,number}}
 
 wd.name=Work Directory
+
+super.history=Comparison of recent results by platform
+super.platforms=Comparison across platforms by date 
+super.th.date=Date
+super.th.platform=Platform
--- a/test/jtreg/com/sun/javatest/regtest/Main.java	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/regtest/Main.java	Sun Nov 09 22:29:59 2008 +0100
@@ -83,7 +83,7 @@
  * JavaTest entry point to be used to access regression extensions.
  */
 public class Main {
-    
+
     /**
      * Exception to report a problem while executing in Main.
      */
@@ -93,14 +93,14 @@
             super(i18n.getString(s, args));
         }
     }
-    
+
     public static final String MAIN = "main";           // main set of options
     public static final String SELECT = "select";       // test selection options
     public static final String JDK = "jdk";             // specify JDK to use
     public static final String MODE = "mode";           // sameVM or otherVM
     public static final String VERBOSE = "verbose";     // verbose controls
     public static final String DOC = "doc";             // help or doc info
-    
+
     Option[] options = {
         new Option(OPT, VERBOSE, "verbose", "v", "verbose") {
             @Override
@@ -122,49 +122,49 @@
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, VERBOSE, "verbose", "v1") {
             public void process(String opt, String arg) {
                 verbose = Verbose.SUMMARY;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, VERBOSE, "verbose", "va") {
             public void process(String opt, String arg) {
                 verbose = Verbose.ALL;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, VERBOSE, "verbose", "vp") {
             public void process(String opt, String arg) {
                 verbose = Verbose.PASS;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, VERBOSE, "verbose", "vf") {
             public void process(String opt, String arg) {
                 verbose = Verbose.FAIL;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, VERBOSE, "verbose", "ve") {
             public void process(String opt, String arg) {
                 verbose = Verbose.ERROR;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, VERBOSE, "verbose", "vt") {
             public void process(String opt, String arg) {
                 verbose = Verbose.TIME;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, DOC, "", "t", "tagspec") {
             public void process(String opt, String arg) {
                 if (help == null)
@@ -172,7 +172,7 @@
                 help.setTagSpec(true);
             }
         },
-        
+
         new Option(NONE, DOC, "", "n", "relnote") {
             public void process(String opt, String arg) {
                 if (help == null)
@@ -180,15 +180,15 @@
                 help.setReleaseNotes(true);
             }
         },
-        
+
         new Option(OLD, MAIN, "", "w", "workDir") {
             public void process(String opt, String arg) {
-                
+
                 workDirArg = new File(arg);
                 childArgs.add("-w:" + workDirArg.getAbsolutePath());
             }
         },
-        
+
         new Option(OPT, MAIN, "", "retain") {
             @Override
             public String[] getChoices() {
@@ -204,34 +204,40 @@
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(OLD, MAIN, "", "r", "reportDir") {
             public void process(String opt, String arg) {
                 reportDirArg = new File(arg);
                 childArgs.add("-r:" + reportDirArg.getAbsolutePath());
             }
         },
-        
-        new Option(NONE, MAIN, null, "ro", "reportOnly") {
+
+        new Option(NONE, MAIN, "ro-nr", "ro", "reportOnly") {
             public void process(String opt, String arg) {
                 reportOnlyFlag = true;
             }
         },
-        
+
+        new Option(NONE, MAIN, "ro-nr", "nr", "noreport") {
+            public void process(String opt, String arg) {
+                noReportFlag = true;
+            }
+        },
+
         new Option(STD, MAIN, "", "timeout", "timeoutFactor") {
             public void process(String opt, String arg) {
                 timeoutFactorArg = arg;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(STD, MAIN, "", "dir") {
             public void process(String opt, String arg) {
                 baseDirArg = new File(arg);
                 childArgs.add("-dir:" + baseDirArg.getAbsolutePath());
             }
         },
-        
+
         new Option(STD, SELECT, "", "status") {
             @Override
             public String[] getChoices() {
@@ -242,29 +248,29 @@
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(STD, SELECT, "", "exclude", "Xexclude") {
             public void process(String opt, String arg) {
                 File f = new File(arg);
                 excludeListArgs.add(f);
-                childArgs.add(f.getAbsolutePath());
+                childArgs.add("-exclude:" + f.getAbsolutePath());
             }
         },
-        
+
         new Option(NONE, MAIN, null, "startHttpd") {
             public void process(String opt, String arg) {
                 httpdFlag = true;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(OLD, MAIN, "", "o", "observer") {
             public void process(String opt, String arg) {
                 observerClassName = arg;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(OLD, MAIN, "", "od", "observerDir", "op", "observerPath") {
             public void process(String opt, String arg) {
                 arg = arg.trim();
@@ -279,20 +285,20 @@
                 childArgs.add("-op:" + filesToAbsolutePath(pathToFiles(arg)));
             }
         },
-        
+
         new Option(NONE, MAIN, null, "g", "gui") {
             public void process(String opt, String arg) {
                 guiFlag = true;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, MAIN, null, "c", "check") {
             public void process(String opt, String arg) {
                 checkFlag = true;
             }
         },
-        
+
         // deprecated
         new Option(NONE, MAIN, "ignore", "noignore") {
             public void process(String opt, String arg) {
@@ -300,7 +306,7 @@
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(STD, MAIN, "ignore", "ignore") {
             @Override
             public String[] getChoices() {
@@ -323,69 +329,69 @@
                 throw new BadArgs(i18n, "main.unknownIgnore", arg);
             }
         },
-        
+
         new Option(NONE, SELECT, "a-m", "a", "automatic", "automagic") {
             public void process(String opt, String arg) {
                 keywordsExprArg = combineKeywords(keywordsExprArg, AUTOMATIC);
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, SELECT, "a-m", "m", "manual") {
             public void process(String opt, String arg) {
                 keywordsExprArg = combineKeywords(keywordsExprArg, MANUAL);
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, SELECT, "shell-noshell", "shell") {
             public void process(String opt, String arg) {
                 keywordsExprArg = combineKeywords(keywordsExprArg, "shell");
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, SELECT, "shell-noshell", "noshell") {
             public void process(String opt, String arg) {
                 keywordsExprArg = combineKeywords(keywordsExprArg, "!shell");
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(STD, SELECT, null, "bug") {
             public void process(String opt, String arg) {
                 keywordsExprArg = combineKeywords(keywordsExprArg, "bug" + arg);
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(STD, SELECT, null, "k", "keywords") {
             public void process(String opt, String arg) {
                 keywordsExprArg = combineKeywords(keywordsExprArg, "(" + arg + ")");
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, MODE, "svm-ovm", "ovm", "othervm") {
             public void process(String opt, String arg) {
                 sameJVMFlag = false;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(NONE, MODE, "svm-ovm", "s", "svm", "samevm") {
             public void process(String opt, String arg) {
                 sameJVMFlag = true;
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(OLD, JDK, "", "jdk", "testjdk") {
             public void process(String opt, String arg) {
                 jdk = new JDK(arg);
             }
         },
-        
+
         new Option(STD, JDK, "", "cpa", "classpathappend") {
             public void process(String opt, String arg) {
                 arg = arg.trim();
@@ -398,73 +404,73 @@
                 }
             }
         },
-        
+
         new Option(NONE, JDK, "jit-nojit", "jit") {
             public void process(String opt, String arg) {
                 jitFlag = true;
             }
         },
-        
+
         new Option(NONE, JDK, "jit-nojit", "nojit") {
             public void process(String opt, String arg) {
                 jitFlag = false;
             }
         },
-        
+
         new Option(WILDCARD, JDK, null, "Xrunjcov") {
             public void process(String opt, String arg) {
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(NONE, JDK, null, "classic", "green", "native", "hotspot", "client", "server", "d32", "d64") {
             public void process(String opt, String arg) {
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(OPT, JDK, null, "enableassertions", "ea", "disableassertions", "da") {
             public void process(String opt, String arg) {
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(NONE, JDK, null, "enablesystemassertions", "esa", "disablesystemassertions", "dsa") {
             public void process(String opt, String arg) {
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(WILDCARD, JDK, null, "XX", "Xms", "Xmx") {
             public void process(String opt, String arg) {
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(WILDCARD, JDK, null, "Xint", "Xmixed", "Xcomp") {
             public void process(String opt, String arg) {
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(STD, JDK, null, "Xbootclasspath") {
             public void process(String opt, String arg) {
                 testVMOpts.add("-Xbootclasspath:" + filesToAbsolutePath(pathToFiles(arg)));
             }
         },
-        
+
         new Option(STD, JDK, null, "Xbootclasspath/a") {
             public void process(String opt, String arg) {
                 testVMOpts.add("-Xbootclasspath/a:" + filesToAbsolutePath(pathToFiles(arg)));
             }
         },
-        
+
         new Option(STD, JDK, null, "Xbootclasspath/p") {
             public void process(String opt, String arg) {
                 testVMOpts.add("-Xbootclasspath/p:" + filesToAbsolutePath(pathToFiles(arg)));
             }
         },
-        
+
         new Option(WILDCARD, JDK, null, "X") {
             public void process(String opt, String arg) {
                 // This is a change in spec. Previously. -X was used to tunnel
@@ -473,20 +479,20 @@
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(WILDCARD, JDK, null, "D") {
             public void process(String opt, String arg) {
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(STD, JDK, null, "vmoption") {
             public void process(String opt, String arg) {
                 if (arg.length() > 0)
                     testVMOpts.add(arg);
             }
         },
-        
+
         new Option(STD, JDK, null, "vmoptions") {
             public void process(String opt, String arg) {
                 arg = arg.trim();
@@ -495,7 +501,7 @@
                 testVMOpts.addAll(Arrays.asList(arg.split("\\s+")));
             }
         },
-        
+
         new Option(OLD, JDK, null, "e") {
             public void process(String opt, String arg) {
                 arg = arg.trim();
@@ -504,25 +510,25 @@
                 envVarArgs.addAll(Arrays.asList(arg.split(",")));
             }
         },
-        
+
         new Option(STD, JDK, null, "agentlib") {
             public void process(String opt, String arg) {
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(STD, JDK, null, "agentpath") {
             public void process(String opt, String arg) {
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(STD, JDK, null, "javaagent") {
             public void process(String opt, String arg) {
                 testVMOpts.add(opt);
             }
         },
-        
+
         new Option(STD, JDK, null, "javacoption") {
             public void process(String opt, String arg) {
                 arg = arg.trim();
@@ -532,7 +538,7 @@
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(STD, JDK, null, "javacoptions") {
             public void process(String opt, String arg) {
                 arg = arg.trim();
@@ -542,7 +548,7 @@
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(STD, JDK, null, "javaoption") {
             public void process(String opt, String arg) {
                 arg = arg.trim();
@@ -552,7 +558,7 @@
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(STD, JDK, null, "javaoptions") {
             public void process(String opt, String arg) {
                 arg = arg.trim();
@@ -562,7 +568,7 @@
                 childArgs.add(opt);
             }
         },
-        
+
         new Option(REST, DOC, "help", "h", "help", "usage") {
             public void process(String opt, String arg) {
                 if (help == null)
@@ -570,7 +576,7 @@
                 help.setCommandLineHelpQuery(arg);
             }
         },
-        
+
         new Option(REST, DOC, "help", "onlineHelp") {
             public void process(String opt, String arg) {
                 if (help == null)
@@ -578,7 +584,7 @@
                 help.setOnlineHelpQuery(arg);
             }
         },
-        
+
         new Option(NONE, DOC, "help", "version") {
             public void process(String opt, String arg) {
                 if (help == null)
@@ -586,7 +592,7 @@
                 help.setVersionFlag(true);
             }
         },
-        
+
         new Option(FILE, MAIN, null) {
             public void process(String opt, String arg) {
                 File f= new File(arg);
@@ -610,7 +616,7 @@
     public static final int EXIT_FAULT = 4;
     /** Unexpected exception occurred. */
     public static final int EXIT_EXCEPTION = 5;
-    
+
     /**
      * Standard entry point. Only returns if GUI mode is initiated; otherwise, it calls System.exit
      * with an appropriate exit code.
@@ -628,7 +634,7 @@
                 out.flush();
                 err.flush();
             }
-            
+
             if (!(m.guiFlag && rc == EXIT_OK)) {
                 // take care not to exit if GUI might be around,
                 // and take care to ensure JavaTestSecurityManager will
@@ -654,21 +660,21 @@
             exit(EXIT_EXCEPTION);
         }
     } // main()
-    
+
     public Main() {
         this(new PrintWriter(System.out, true), new PrintWriter(System.err, true));
     }
-    
+
     public Main(PrintWriter out, PrintWriter err) {
         this.out = out;
         this.err = err;
-        
+
         // FIXME: work around bug 6466752
         File javatest_jar = findJar("javatest.jar", "lib/javatest.jar", com.sun.javatest.Harness.class);
         if (javatest_jar != null)
             System.setProperty("javatestClassDir", javatest_jar.getPath());
     }
-    
+
     /**
      * Decode command line args and perform the requested operations.
      * @param args An array of args, such as might be supplied on the command line.
@@ -681,7 +687,7 @@
         new OptionDecoder(options).decodeArgs(expandAtFiles(args));
         return run();
     }
-    
+
     private int run() throws BadArgs, Fault, Harness.Fault, InterruptedException {
         if (help != null) {
             guiFlag = help.show(out);
@@ -690,7 +696,7 @@
 
         if (sameJVMFlag && !testJavaOpts.isEmpty())
             throw new Fault(i18n, "main.cant.mix.samevm.java.options");
-        
+
         if (jdk == null) {
             String s = null;
             if (!sameJVMFlag)
@@ -706,23 +712,23 @@
                 f = f.getParentFile();
             jdk = new JDK(f);
         }
-        
+
         if (jitFlag == false) {
             if (sameJVMFlag)
                 testVMOpts.add("-Djava.compiler=");
             else
                 envVarArgs.add("JAVA_COMPILER=");
         }
-        
+
         if (classPathAppendArg.size() > 0) {
             // TODO: store this separately in RegressionParameters, instead of in envVars
             if (!sameJVMFlag)
                 envVarArgs.add("CPAPPEND=" + filesToAbsolutePath(classPathAppendArg));
         }
-        
+
         if (!jdk.exists())
             throw new Fault(i18n, "main.jdk.not.found", jdk);
-        
+
         File baseDir;
         if (baseDirArg == null) {
             baseDir = new File(System.getProperty("user.dir"));
@@ -731,9 +737,9 @@
                 throw new Fault(i18n, "main.cantFindFile", baseDirArg);
             baseDir = baseDirArg.getAbsoluteFile();
         }
-        
+
         List<File> absTestFileArgs = new ArrayList<File>();
-        
+
         for (File t: testFileArgs) {
             if (!t.isAbsolute())
                 t = new File(baseDir, t.getPath());
@@ -741,13 +747,13 @@
                 throw new Fault(i18n, "main.cantFindFile", t);
             absTestFileArgs.add(t);
         }
-        
+
         testFileArgs = absTestFileArgs;
-        
+
         String antFileList = System.getProperty(JAVATEST_ANT_FILE_LIST);
         if (antFileList != null)
             antFileArgs.addAll(readFileList(new File(antFileList)));
-        
+
         if (testSuiteArg == null) {
             File t;
             if (testFileArgs.size() > 0)
@@ -756,49 +762,57 @@
                 t = antFileArgs.iterator().next();
             else
                 throw new BadArgs(i18n, "main.noTestSuiteOrTests");
-            
+
             testSuiteArg = getTestSuite(t);
             if (testSuiteArg == null)
                 throw new Fault(i18n, "main.cantDetermineTestSuite", t);
         }
-        
+
         if (workDirArg == null) {
             workDirArg = new File("JTwork");
             childArgs.add(0, "-w:" + workDirArg.getAbsolutePath());
         }
-        
-        if (reportDirArg == null) {
+
+        if (reportDirArg == null && !noReportFlag) {
             reportDirArg = new File("JTreport");
             childArgs.add(0, "-r:" + reportDirArg.getAbsolutePath());
         }
-        
-        makeDir(reportDirArg);
+
+        if (!noReportFlag)
+            makeDir(reportDirArg);
+
         makeDir(workDirArg);
         makeDir(new File(workDirArg, "scratch"));
-        
+
         if (!isThisVMOK())
             return execChild();
-        
+
         RegressionParameters params = createParameters();
-        
+
         checkLockFiles(params.getWorkDirectory().getRoot(), "start");
-        
+
         Harness.setClassDir(ProductInfo.getJavaTestClassDir());
-        
+
         // Allow keywords to begin with a numeric
         Keywords.setAllowNumericKeywords(true);
-        
+
         // Before we install our own security manager (which will restrict access
         // to the system properties), take a copy of the system properties.
         TestEnvironment.addDefaultPropTable("(system properties)", System.getProperties());
-        
+
         // TODO: take SecurityManager into account for isThisVMOK
-        if (sameJVMFlag)
+        if (sameJVMFlag) {
             RegressionSecurityManager.install();
-        
+            SecurityManager sc = System.getSecurityManager();
+            if (sc instanceof RegressionSecurityManager) {
+                // experimental
+                ((RegressionSecurityManager) sc).setAllowSetIO(true);
+            }
+        }
+
         if (httpdFlag)
             startHttpServer();
-        
+
         if (guiFlag) {
             showTool(params);
             return EXIT_OK;
@@ -810,7 +824,7 @@
             }
         }
     }
-    
+
     /**
      * Process Win32-style command files for the specified command line
      * arguments and return the resulting arguments. A command file argument
@@ -839,7 +853,7 @@
         }
         return newArgs.toArray(new String[newArgs.size()]);
     }
-    
+
     private static void loadCmdFile(String name, List<String> args)
     throws Fault {
         Reader r;
@@ -871,7 +885,7 @@
             }
         }
     }
-    
+
     private static List<File> readFileList(File file)
     throws Fault {
         BufferedReader r;
@@ -898,7 +912,7 @@
             }
         }
     }
-    
+
     private static void writeFileList(File file, List<File> list) throws Fault {
         try {
             BufferedWriter out = new BufferedWriter(new FileWriter(file));
@@ -912,21 +926,21 @@
             throw new Fault(i18n, "main.cantWrite", file, e);
         }
     }
-    
+
     public int[] getTestStats() {
         return testStats;
     }
-    
+
     private boolean isThisVMOK() {
         if (reportOnlyFlag || checkFlag || !sameJVMFlag)
             return true;
-        
+
         // sameVM tests can use this VM if
         // - the current directory is the required scratch directory
         // - the current VM is the required test VM
         // - there are no outstanding VM options
         // - there is no classpath append
-        
+
         File scratchDir = canon(new File(workDirArg, "scratch"));
         File currDir = canon(new File(""));
         if (!currDir.equals(scratchDir)) {
@@ -934,7 +948,7 @@
                 System.err.println("dir mismatch: " + currDir + " " + scratchDir);
             return false;
         }
-        
+
         File currJDKHome = canon(new File(System.getProperty("java.home")));
         if (currJDKHome.getName().toLowerCase().equals("jre"))
             currJDKHome = currJDKHome.getParentFile();
@@ -943,53 +957,53 @@
                 System.err.println("jdk mismatch: " + currJDKHome + " " + jdk + " (" + jdk.getCanonicalFile() + ")");
             return false;
         }
-        
+
         if (System.getProperty("javatest.child") == null && !testVMOpts.isEmpty()) {
             if (debugChild)
                 System.err.println("need VM opts: " + testVMOpts);
             return false;
         }
-        
+
         if (classPathAppendArg.size() > 0) {
             if (debugChild)
                 System.err.println("need classPathAppend: " + classPathAppendArg);
             return false;
         }
-        
+
         return true;
     }
-    
+
     // TODO use @file for args?
     private int execChild() throws Fault {
         if (System.getProperty("javatest.child") != null)
             throw new AssertionError();
-        
+
         File javatest_jar = findJar("javatest.jar", "lib/javatest.jar", com.sun.javatest.Harness.class);
         if (javatest_jar == null)
             throw new Fault(i18n, "main.cantFind.javatest.jar");
-        
+
         File jtreg_jar = findJar("jtreg.jar", "lib/jtreg.jar", getClass());
         if (jtreg_jar == null)
             throw new Fault(i18n, "main.cantFind.jtreg.jar");
-        
+
         File childJDKHome = jdk.getAbsoluteFile();
         File childJava = new File(new File(childJDKHome, "bin"), "java");
         File childTools  = new File(new File(childJDKHome, "lib"), "tools.jar");
-        
+
         File scratchDir = canon(new File(workDirArg, "scratch"));
-        
+
         List<String> c = new ArrayList<String>();
         c.add(childJava.getPath());
-        
+
         c.add("-classpath");
         List<File> classpath = new ArrayList<File>();
         classpath.add(jtreg_jar);
         classpath.add(childTools);
         classpath.addAll(classPathAppendArg);
         c.add(filesToAbsolutePath(classpath));
-        
+
         c.addAll(testVMOpts);
-        
+
         // Tunnel Ant file args separately from command line tests, so that
         // they can be treated specially in the child VM:  invalid files
         // specified by the user on the command line give an error;
@@ -1003,39 +1017,39 @@
                 throw new Fault(i18n, "main.cantWriteTempFile", e);
             }
         }
-        
+
         for (Map.Entry<?,?> e: System.getProperties().entrySet()) {
             String name = (String) e.getKey();
             if (name.startsWith("javatest."))
                 c.add("-D" + name + "=" + e.getValue());
         }
-        
+
         c.add("-Djavatest.child=true");
-        
+
         c.add(Main.class.getName());
-        
+
         for (String o: testVMOpts)
             c.add("-vmoption:" + o);
-        
+
         if (baseDirArg == null)
             c.add("-dir:" + System.getProperty("user.dir"));
-        
+
         c.addAll(childArgs);
-        
+
         String[] cmd = c.toArray(new String[c.size()]);
         File execDir = scratchDir;
-        
+
         if (debugChild) {
             System.err.println("Starting JavaTest child");
             System.err.println("Dir " + execDir + "; Command " + c);
         }
-        
+
         Runtime r = Runtime.getRuntime();
         Process p = null;
-        
+
         try {
             try {
-                
+
                 // strictly speaking, we do not need to set the CLASSPATH for the child VM,
                 // but we do it to maximize the consistency between sameVM and otherVM env.
                 // See similar code in MainAction for otherVM tests.
@@ -1043,46 +1057,46 @@
                 // because it will not have (and cannot have) the test-specific values.
                 String cp = "CLASSPATH=" + javatest_jar + PATHSEP + jtreg_jar
                         + PATHSEP + jdk.getToolsJar();
-                
+
                 String[] env = getEnvVars();
                 String[] env_cp = new String[env.length + 1];
                 System.arraycopy(env, 0, env_cp, 0, env.length);
                 env_cp[env_cp.length - 1] = cp;
-                
+
                 p = r.exec(cmd, env_cp, execDir);
             } catch (IOException e) {
                 err.println("cannot start child VM");
                 return EXIT_FAULT;
             }
-            
+
             InputStream childOut = p.getInputStream(); // output stream from process
             StreamCopier childOutCopier = new StreamCopier(childOut, out);
             childOutCopier.start();
             InputStream childErr = p.getErrorStream();
             StreamCopier childErrCopier = new StreamCopier(childErr, err);
             childErrCopier.start();
-            
+
             OutputStream childIn = p.getOutputStream();  // input stream to process
             if (childIn != null)
                 childIn.close();
-            
+
             // wait for the stream copiers to complete
             childOutCopier.waitUntilDone();
             childErrCopier.waitUntilDone();
-            
+
             // wait for the process to complete;
             int exitCode = p.waitFor();
             p = null;
-            
+
             if (debugChild) {
                 System.err.println("JavaTest child process: rc=" + exitCode);
             }
-            
+
             childOut.close();
             childErr.close();
-            
+
             return exitCode;
-            
+
         } catch (IOException e) {
             // TODO handle exception
             return EXIT_EXCEPTION;
@@ -1094,7 +1108,7 @@
                 p.destroy();
         }
     }
-    
+
     /**
      * A thread to copy an input stream to an output stream
      */
@@ -1109,7 +1123,7 @@
             in = new BufferedReader(new InputStreamReader(from));
             out = to;
         }
-        
+
         /**
          * Set the thread going.
          */
@@ -1130,32 +1144,32 @@
                 notifyAll();
             }
         }
-        
+
         public synchronized boolean isDone() {
             return done;
         }
-        
+
         /**
          * Blocks until the copy is complete, or until the thread is interrupted
          */
         public synchronized void waitUntilDone() throws InterruptedException {
             boolean interrupted = false;
-            
+
             // poll interrupted flag, while waiting for copy to complete
             while (!(interrupted = Thread.interrupted()) && !done)
                 wait(1000);
-            
+
             if (interrupted)
                 throw new InterruptedException();
         }
-        
+
         private BufferedReader in;
         private PrintWriter out;
         private boolean done;
         private static int serial;
-        
+
     }
-    
+
     private File getTestSuite(File test) {
         File f = canon(test);
         if (f.isFile())
@@ -1168,7 +1182,7 @@
         // TODO try and default from work directory
         return null;
     }
-    
+
     private String getEnvVar(String name) {
         for (String arg: envVarArgs) {
             if (arg.startsWith(name + "="))
@@ -1176,7 +1190,7 @@
         }
         return null;
     }
-    
+
     private void makeDir(File dir) throws Fault {
         // FIXME: I18N
         if (dir.isDirectory())
@@ -1187,7 +1201,7 @@
             throw new Fault(i18n, "main.cantCreateDir", dir);
         }
     }
-    
+
     private static List<File> pathToFiles(String path) {
         List<File> files = new ArrayList<File>();
         for (String f: path.split(File.pathSeparator)) {
@@ -1196,7 +1210,7 @@
         }
         return files;
     }
-    
+
     private static String filesToAbsolutePath(List<File> files) {
         StringBuffer sb = new StringBuffer();
         for (File f: files) {
@@ -1206,7 +1220,7 @@
         }
         return sb.toString();
     }
-    
+
     private static String join(Iterator<?> iter, String sep) {
         StringBuilder sb = new StringBuilder();
         while (iter.hasNext()) {
@@ -1216,7 +1230,7 @@
         }
         return sb.toString();
     }
-    
+
     /**
      * Create a RegressionParameters object based on the values set up by decodeArgs.
      * @return a RegressionParameters object
@@ -1224,26 +1238,26 @@
     private RegressionParameters createParameters() throws BadArgs, Fault {
         File ts = testSuiteArg;
         File wd = workDirArg;
-        
+
         try {
             // create a canonTestFile suite and work dir.
             RegressionTestSuite testSuite = new RegressionTestSuite(ts);
             RegressionParameters rp = (RegressionParameters) (testSuite.createInterview());
-            
+
             WorkDirectory workDir;
             if (WorkDirectory.isWorkDirectory(wd))
                 workDir = WorkDirectory.open(wd, testSuite);
             else
                 workDir = WorkDirectory.convert(wd, testSuite);
             rp.setWorkDirectory(workDir);
-            
+
             rp.setRetainArgs(retainArgs);
-            
+
             // set up the tests mode, and if specified tests are used, pass in the canonTestFile list.
             // ensure the tests parameters are root-relative
             File root = testSuite.getRoot();
             List<String> tests = new ArrayList<String>();
-            
+
             if (testFileArgs != null) {
                 // In the command line, the canonTestFile args are filenames, probably absolute,
                 // and possibly with non-canonical file separators (e.g. / on Windows).
@@ -1261,7 +1275,7 @@
                     }
                 }
             }
-            
+
             // no need to scan ant tests if all test suite selected (i.e. tests == null)
             if (tests != null && antFileArgs != null && antFileArgs.size() > 0) {
                 TestResultTable trt = workDir.getTestResultTable();
@@ -1279,10 +1293,10 @@
                     }
                 }
             }
-            
+
             if (tests != null && tests.size() > 0)
                 rp.setTests(tests);
-            
+
             if (keywordsExprArg != null)
                 rp.setKeywordsExpr(keywordsExprArg);
             rp.setExcludeLists(excludeListArgs.toArray(new File[excludeListArgs.size()]));
@@ -1296,7 +1310,7 @@
                 b[Status.NOT_RUN] = (priorStatusValuesArg.indexOf("notr") != -1);
                 rp.setPriorStatusValues(b);
             }
-            
+
             if (concurrencyArg != null) {
                 try {
                     rp.setConcurrency(Integer.parseInt(concurrencyArg));
@@ -1304,7 +1318,7 @@
                     throw new BadArgs(i18n, "main.badConcurrency");
                 }
             }
-            
+
             if (timeoutFactorArg != null) {
                 try {
                     rp.setTimeoutFactor(Integer.parseInt(timeoutFactorArg));
@@ -1312,10 +1326,10 @@
                     throw new BadArgs(i18n, "main.badTimeoutFactor");
                 }
             }
-            
+
             if (!rp.isValid())
                 throw new Fault(i18n, "main.badParams", rp.getErrorMessage());
-            
+
             for (String o: testVMOpts) {
                 if (o.startsWith("-Xrunjcov")) {
                     if (!testVMOpts.contains("-XX:+EnableJVMPIInstructionStartEvent"))
@@ -1323,23 +1337,23 @@
                     break;
                 }
             }
-            
+
             if (testVMOpts.size() > 0)
                 rp.setTestVMOptions(testVMOpts);
-            
+
             if (testCompilerOpts.size() > 0)
                 rp.setTestCompilerOptions(testCompilerOpts);
-            
+
             if (testJavaOpts.size() > 0)
                 rp.setTestJavaOptions(testJavaOpts);
-            
+
             rp.setCheck(checkFlag);
             rp.setSameJVM(sameJVMFlag);
             rp.setEnvVars(getEnvVars());
             rp.setJDK(jdk);
             if (ignoreKind != null)
                 rp.setIgnoreKind(ignoreKind);
-            
+
             return rp;
         } catch (TestSuite.Fault f) {
             f.printStackTrace();
@@ -1359,7 +1373,7 @@
             return file.getAbsoluteFile();
         }
     }
-    
+
     private String getRelativePath(File base, File f) {
         StringBuilder sb = new StringBuilder();
         for ( ; f != null; f = f.getParentFile()) {
@@ -1371,18 +1385,18 @@
         }
         return null;
     }
-    
+
     /**
      * Initialize the harness.  If we are in verbose mode, add our own observer.
      */
     private Harness createHarness() throws Fault {
-        
+
         // Set backup parameters; in time this might become more versatile.
         BackupPolicy backupPolicy = createBackupPolicy();
-        
+
         Harness h = new Harness();
         h.setBackupPolicy(backupPolicy);
-        
+
         if (observerClassName != null) {
             try {
                 Class observerClass;
@@ -1413,16 +1427,16 @@
                 throw new Fault(i18n, "main.obsvrFault", e);
             }
         }
-        
+
         // add our own observer for verbose
         if (verbose != null) {
             Harness.Observer observer = new RegressionObserver(verbose, out, err);
             h.addObserver(observer);
         }
-        
+
         return h;
     } // createHarness()
-    
+
     /**
      * Run the harness in batch mode, using the specified parameters.
      */
@@ -1430,7 +1444,7 @@
     throws Fault, Harness.Fault, InterruptedException {
         try {
             boolean ok;
-            
+
             if (reportOnlyFlag) {
                 testStats = new int[Status.NUM_STATES];
                 for (Iterator iter = getResultsIterator(params); iter.hasNext(); ) {
@@ -1443,15 +1457,27 @@
                 harness.addObserver(new BatchObserver());
                 ok = harness.batch(params);
             }
-            
+
             showResultStats(testStats);
-            
-            boolean reportRequired = !Boolean.getBoolean("javatest.noReportRequired");
+
+            boolean reportRequired =
+                    !noReportFlag && !Boolean.getBoolean("javatest.noReportRequired");
+            List<String> reportKinds =
+                    Arrays.asList(System.getProperty("javatest.report.kinds", "html text").split("[ ,]+"));
             if (reportRequired) {
                 try {
                     Report r = new Report();
                     Report.Settings s = new Report.Settings(params);
-                    s.setHtmlMainReport(true, true);
+                    if (reportKinds.contains("html")) {
+                        s.setEnableHtmlReport(true);
+                        s.setHtmlMainReport(true, true);
+                    }
+                    if (reportKinds.contains("text")) {
+                        s.setEnablePlainReport(true);
+                    }
+                    if (reportKinds.contains("xml")) {
+                        s.setEnableXmlReport(true);
+                    }
                     s.setFilter(new CompositeFilter(params.getFilters()));
                     r.writeReport(s, reportDirArg);
                     File report = new File(reportDirArg, "report.html"); // std through version 3.*
@@ -1463,15 +1489,15 @@
                     out.println("Error while writing report: " + e);
                 }
             }
-            
+
             if (!reportOnlyFlag)
                 out.println("Results written to " + params.getWorkDirectory().getPath());
-            
+
             // report a brief msg to System.err as well, in case System.out has
             // been redirected.
             if (!ok)
                 err.println(i18n.getString("main.testsFailed"));
-            
+
             return (testStats[Status.ERROR] > 0 ? EXIT_TEST_ERROR :
                 testStats[Status.FAILED] > 0 ? EXIT_TEST_FAILED :
                     EXIT_OK);
@@ -1480,11 +1506,11 @@
             err.flush();
         }
     }
-    
+
     private Iterator getResultsIterator(InterviewParameters params) {
         TestResultTable trt = params.getWorkDirectory().getTestResultTable();
         trt.waitUntilReady();
-        
+
         String[] tests = params.getTests();
         TestFilter[] filters = params.getFilters();
         if (tests == null)
@@ -1492,10 +1518,10 @@
         else
             return trt.getIterator(tests, filters);
     }
-    
+
     private void showTool(final InterviewParameters params) throws BadArgs {
         Startup startup = new Startup();
-        
+
         try {
             EventQueue.invokeLater(new Runnable() {
                 public void run() {
@@ -1512,13 +1538,13 @@
             startup.disposeLater();
         }
     } // showTool()
-    
+
     private void showResultStats(int[] stats) {
         int p = stats[Status.PASSED];
         int f = stats[Status.FAILED];
         int e = stats[Status.ERROR];
         int nr = stats[Status.NOT_RUN];
-        
+
         String msg;
         if (p + f + e + nr == 0)
             msg = i18n.getString("main.noTests");
@@ -1536,7 +1562,7 @@
         }
         out.println(msg);
     }
-    
+
     private BackupPolicy createBackupPolicy() {
         return new BackupPolicy() {
             public int getNumBackupsToKeep(File file) {
@@ -1555,7 +1581,7 @@
             private String[] ignoreExtns = StringArray.split(System.getProperty("javatest.backup.ignore", ".jtr"));
         };
     }
-    
+
     private void startHttpServer() {
         // start the http server
         // do this as early as possible, since objects may check
@@ -1563,9 +1589,9 @@
         // register their handlers
         HttpdServer server = new HttpdServer();
         Thread thr = new Thread(server);
-        
+
         PageGenerator.setSWName(ProductInfo.getName());
-        
+
         // format the date for i18n
         DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
         Date dt = ProductInfo.getBuildDate();
@@ -1574,15 +1600,15 @@
             date = df.format(dt);
         else
             date = i18n.getString("main.nobDate");
-        
+
         PageGenerator.setSWBuildDate(date);
         PageGenerator.setSWVersion(ProductInfo.getVersion());
-        
+
         thr.start();
     }
-    
+
     private String[] getEnvVars() {
-        
+
         Map<String,String> envVars = new TreeMap<String,String>();
         String osName = System.getProperty("os.name").toLowerCase();
         if (osName.startsWith("windows")) {
@@ -1594,22 +1620,22 @@
             addEnvVars(envVars, "PATH=/bin:/usr/bin");
         }
         addEnvVars(envVars, envVarArgs);
-        
+
         return envVars.values().toArray(new String[envVars.size()]);
     }
-    
+
     private void addEnvVars(Map<String,String> table, String list) {
         addEnvVars(table, list.split(","));
     }
-    
+
     private void addEnvVars(Map<String,String> table, String[] list) {
         addEnvVars(table, Arrays.asList(list));
     }
-    
+
     private void addEnvVars(Map<String,String> table, List<String> list) {
         if (list == null)
             return;
-        
+
         for (String s: list) {
             s = s.trim();
             if (s.length() == 0)
@@ -1625,24 +1651,24 @@
             }
         }
     }
-    
+
     private static String combineKeywords(String kw1, String kw2) {
         return (kw1 == null ? kw2 : kw1 + " & " + kw2);
     }
-    
+
     private File findJar(String jarProp, String pathFromHome, Class<?> c) {
         if (jarProp != null) {
             String v = System.getProperty(jarProp);
             if (v != null)
                 return new File(v);
         }
-        
+
         if (pathFromHome != null) {
             String v = System.getProperty("jtreg.home");
             if (v != null)
                 return new File(v, pathFromHome);
         }
-        
+
         if (c != null)  {
             try {
                 String className = c.getName().replace(".", "/") + ".class";
@@ -1659,10 +1685,10 @@
                 ignore.printStackTrace();
             }
         }
-        
+
         return null;
     }
-    
+
     /**
      * Call System.exit, taking care to get permission from the
      * JavaTestSecurityManager, if it is installed.
@@ -1675,7 +1701,7 @@
             ((JavaTestSecurityManager) sc).setAllowExit(true);
         System.exit(exitCode);
     }
-    
+
     // This is almost completely dead code; testStats appears unused
     // so the only use here is error handling, which perhaps can be
     // folded into RegressionObserver
@@ -1683,24 +1709,24 @@
         public void startingTestRun(Parameters params) {
             testStats = new int[Status.NUM_STATES];
         }
-        
+
         public void startingTest(TestResult tr) { }
-        
+
         public void finishedTest(TestResult tr) {
             testStats[tr.getStatus().getType()]++;
         }
-        
+
         public void stoppingTestRun() { }
-        
+
         public void finishedTesting() { }
-        
+
         public void finishedTestRun(boolean allOK) { }
-        
+
         public void error(String msg) {
             err.println(i18n.getString("main.error", msg));
         }
     }
-    
+
     private void checkLockFiles(File workDir, String msg) {
 //      String jc = System.getProperty("javatest.child");
 //      File jtData = new File(workDir, "jtData");
@@ -1712,12 +1738,12 @@
 //          }
 //      }
     }
-    
+
     //----------member variables-----------------------------------------------
-    
+
     private PrintWriter out;
     private PrintWriter err;
-    
+
     // this first group of args are the "standard" JavaTest args
     private File testSuiteArg;
     private File workDirArg;
@@ -1732,13 +1758,14 @@
     // TODO: consider making this a "pathset" to detect redundant specification
     // of directories and paths within them.
     private List<File> antFileArgs = new ArrayList<File>();
-    
+
     // these args are jtreg extras
     private File baseDirArg;
     private boolean sameJVMFlag;
     private JDK jdk;
     private boolean guiFlag;
     private boolean reportOnlyFlag;
+    private boolean noReportFlag;
     private static Verbose  verbose;
     private boolean httpdFlag;
     private String observerClassName;
@@ -1752,28 +1779,28 @@
     private List<File> classPathAppendArg = new ArrayList<File>();
     private boolean jitFlag = true;
     private Help help;
-    
-    
+
+
     // the list of args to be passed down to a  child VM
     private List<String> childArgs = new ArrayList<String>();
-    
+
     private int[] testStats;
-    
+
     private static final String AUTOMATIC = "!manual";
     private static final String MANUAL    = "manual";
-    
+
     private static final String[] DEFAULT_UNIX_ENV_VARS = {
         "DISPLAY", "HOME", "LANG", "LC_ALL", "LC_TYPE", "LPDEST", "PRINTER", "TZ", "XMODIFIERS"
     };
-    
+
     private static final String[] DEFAULT_WINDOWS_ENV_VARS = {
         "SystemDrive", "SystemRoot", "windir", "TMP", "TEMP"
     };
-    
+
     private static final String JAVATEST_ANT_FILE_LIST = "javatest.ant.file.list";
-    
+
     private static boolean debugChild = Boolean.getBoolean("javatest.regtest.debugChild");
     private static final String PATHSEP  = System.getProperty("path.separator");
-    
+
     private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(Main.class);
 }
--- a/test/jtreg/com/sun/javatest/regtest/MainAction.java	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/regtest/MainAction.java	Sun Nov 09 22:29:59 2008 +0100
@@ -151,7 +151,7 @@
                 throw new ParseException(PARSE_SECURE_OTHERVM);
         }
     } // init()
-    
+
     @Override
     public File[] getSourceFiles() {
         List<File> l = new ArrayList<File>();
@@ -244,11 +244,11 @@
         // available to main and applet actions via the system properties
         // "test.src" and "test.classes", respectively"
         List<String> command = new ArrayList<String>(6);
-        
+
         // some tests are inappropriately relying on the CLASSPATH environment
         // variable being set, so force the use here.
         final boolean useCLASSPATH = true;
-        
+
         if (useCLASSPATH || script.isJDK11()) {
             command.add("CLASSPATH=" + script.getJavaTestClassPath() +
                         PATHSEP + script.testClassPath());
@@ -263,10 +263,10 @@
 
         command.add("-Dtest.src=" + script.absTestSrcDir());
         command.add("-Dtest.classes=" + script.absTestClsDir());
-        command.add("-Dtest.vm.opts=" + script.getTestVMOptions());
-        command.add("-Dtest.tool.vm.opts=" + script.getTestToolVMOptions());
-        command.add("-Dtest.javac.opts=" + script.getTestCompilerOptions());
-        command.add("-Dtest.java.opts=" + script.getTestJavaOptions());
+        command.add("-Dtest.vm.opts=" + join(script.getTestVMOptions()));
+        command.add("-Dtest.tool.vm.opts=" + join(script.getTestToolVMOptions()));
+        command.add("-Dtest.javac.opts=" + join(script.getTestCompilerOptions()));
+        command.add("-Dtest.java.opts=" + join(script.getTestJavaOptions()));
 
         String newPolicyFN;
         if (policyFN != null) {
@@ -364,7 +364,7 @@
     } // runOtherJVM()
 
     private static Hashtable savedSystemProperties;
-    
+
     private Status runSameJVM() throws TestRunException {
         // TAG-SPEC:  "The source and class directories of a test are made
         // available to main and applet actions via the system properties
@@ -376,6 +376,11 @@
                 Properties p = System.getProperties();
                 if (savedSystemProperties == null)
                     savedSystemProperties = copyProperties(p);
+                p.put("java.class.path",
+                        script.absTestClsDir() + PATHSEP +
+                        script.absTestSrcDir() + PATHSEP +
+                        script.absClsLibListStr() + PATHSEP +
+                        p.getProperty("java.class.path"));
                 p.put("test.src", script.absTestSrcDir().getPath());
                 p.put("test.classes", script.absTestClsDir().getPath());
                 p.put("test.vm.opts", StringUtils.join(script.getTestVMOptions(), " "));
@@ -390,18 +395,18 @@
                 //return Status.error(MAIN_SECMGR_BAD);
             }
         }
-        
+
         ByteArrayOutputStream newOut = new ByteArrayOutputStream();
         ByteArrayOutputStream newErr = new ByteArrayOutputStream();
         PrintStream psOut = new PrintStream(newOut);
         PrintStream psErr = new PrintStream(newErr);
-        
+
         Status status;
         PrintStream saveOut = System.out;
         PrintStream saveErr = System.err;
         try {
             status = Status.passed(EXEC_PASS);
-            
+
             String[] classpath = StringArray.splitSeparator(PATHSEP, script.testClassPath());
             List<URL> urls = new ArrayList<URL>();
             for (int i = 0; i < classpath.length; i++) {
@@ -417,18 +422,18 @@
             Class<?> c = loader.loadClass(buildFN);
             Class<?>[] argTypes = { String[].class };
             Method method = c.getMethod("main", argTypes);
-            
+
             // XXX 4/1 possible to use splitSeparator instead?
             String[] tmpArgs = StringArray.splitWS(mainArgs);
             Object[] runArgs = {tmpArgs};
-            
+
             Status stat = redirectOutput(psOut, psErr);
             if (!stat.isPassed()) {
                 return stat;
             }
-            
+
             // RUN JAVA IN ANOTHER THREADGROUP
-            
+
             SameVMThreadGroup tg = new SameVMThreadGroup();
             SameVMThread svmt = new SameVMThread(method, runArgs, psErr);
             Thread t = new Thread(tg, svmt, "SameVMThread");
@@ -443,7 +448,7 @@
                 }
             }
             tg.cleanup();
-            
+
             if (((svmt.t != null) || (tg.uncaughtThrowable != null)) && (error == null)) {
                 if (svmt.t == null)
                     error = tg.uncaughtThrowable;
@@ -451,12 +456,12 @@
                     error = svmt.t;
                 status = Status.failed(MAIN_THREW_EXCEPT + error.toString());
             }
-            
+
             // EVALUATE RESULTS
             if (status.getReason().endsWith("java.lang.SecurityException: System.exit() forbidden by JavaTest")) {
                 status = Status.failed(UNEXPECT_SYS_EXIT);
             } else {
-                
+
                 boolean ok = status.isPassed();
                 int st   = status.getType();
                 String sr;
@@ -503,19 +508,20 @@
                     System.setProperties(newProperties(savedSystemProperties));
 //                    System.err.println("reset properties");
                 } else {
+                    System.setProperty("java.class.path", (String) savedSystemProperties.get("java.class.path"));
 //                    System.err.println("no need to reset properties");
                 }
                 rsm.setAllowPropertiesAccess(false);
             }
-            
+
             Status stat = redirectOutput(saveOut, saveErr);
             if (!stat.isPassed()) {
                 return stat;
             }
-            
+
             psOut.close();
             psErr.close();
-            
+
             String outString = newOut.toString();
             String errString = newErr.toString();
             PrintWriter sysOut = section.createOutput("System.out");
@@ -528,7 +534,7 @@
                 if (sysErr != null) sysErr.close();
             }
         }
-        
+
         return status;
     } // runSameJVM()
 
@@ -539,7 +545,7 @@
             value = "novalue";
         return value;
     } // parseMainManual()
-    
+
     private static Hashtable<Object,Object> copyProperties(Properties p) {
         Hashtable<Object,Object> h = new Hashtable<Object,Object>();
         for (Enumeration<?> e = p.propertyNames(); e.hasMoreElements(); ) {
@@ -548,7 +554,7 @@
         }
         return h;
     }
-    
+
     private static Properties newProperties(Hashtable<?,?> h) {
         Properties p = new Properties();
         for (Enumeration<?> e = h.keys(); e.hasMoreElements(); ) {
@@ -556,7 +562,17 @@
             p.put(key, h.get(key));
         }
         return p;
-        
+
+    }
+
+    private String join(List<String> list) {
+        StringBuffer sb = new StringBuffer();
+        for (String s: list) {
+            if (sb.length() > 0)
+                sb.append(" ");
+            sb.append(s);
+        }
+        return sb.toString();
     }
 
     //----------internal classes------------------------------------------------
@@ -630,27 +646,27 @@
 
         private void cleanup() {
             cleanMode = true;
-            
+
             final int CLEANUP_ROUNDS = 4;
             final long MAX_CLEANUP_TIME_MILLIS = 2 * 60 * 1000;
             final long CLEANUP_MILLIS_PER_ROUND = MAX_CLEANUP_TIME_MILLIS / CLEANUP_ROUNDS;
             final long NANOS_PER_MILLI = 1000L * 1000L;
-            
+
             long startCleanupTime = System.nanoTime();
-            
+
             for (int i = 1; i <= CLEANUP_ROUNDS; i++) {
                 long deadline = startCleanupTime + i * CLEANUP_MILLIS_PER_ROUND * NANOS_PER_MILLI;
                 List<Thread> liveThreads = liveThreads();
-                if (liveThreads.isEmpty()) { 
+                if (liveThreads.isEmpty()) {
                     // nothing left to cleanup
                     cleanupOK = true;
                     return;
                 }
-                
+
                 // kick the remaining live threads
                 for (Thread thread : liveThreads)
                     thread.interrupt();
-                
+
                 // try joining as many threads as possible before
                 // the round times out
                 for (Thread thread : liveThreads) {
@@ -663,10 +679,10 @@
                     }
                 }
             }
-            
+
             cleanupOK = liveThreads().isEmpty();
         } // cleanup()
-        
+
         /**
          * Gets all the "interesting" threads in the thread group.
          * @see ThreadGroup#enumerate(Thread[])
--- a/test/jtreg/com/sun/javatest/regtest/RegressionSecurityManager.java	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/regtest/RegressionSecurityManager.java	Sun Nov 09 22:29:59 2008 +0100
@@ -25,7 +25,6 @@
 
 package com.sun.javatest.regtest;
 
-import java.lang.RuntimePermission;
 import java.security.Permission;
 import java.util.PropertyPermission;
 
@@ -55,7 +54,7 @@
                 System.err.println("execution of sameJVM tests");
                 System.err.println();
             }
-            else 
+            else
                 System.setSecurityManager(new RegressionSecurityManager());
         }
         catch (SecurityException e) {
@@ -104,13 +103,13 @@
         // allow most stuff, but limit as appropriate
         if (perm instanceof RuntimePermission) {
             if (perm.getName().equals("setIO")) {
-                if (!allowSetIO) 
+                if (!allowSetIO)
                     // is this right or should we really restrict this more?
                     super.checkPermission(new java.lang.RuntimePermission("setIO"));
             }
             else if (perm.getName().equals("exitVM"))
                 checkExit(0);
-            else if (perm.getName().equals("createSecurityManager")) 
+            else if (perm.getName().equals("createSecurityManager"))
                 super.checkPermission(new java.lang.RuntimePermission("createSecurityManager"));
         }
         else if (perm instanceof PropertyPermission) {
@@ -118,21 +117,21 @@
                 checkPropertiesAccess();
         }
     }
-    
+
     private boolean propertiesAccessed;
-    
+
     public synchronized void checkPropertiesAccess() {
         super.checkPropertiesAccess();
         propertiesAccessed = true;
     }
-    
+
     boolean isPropertiesAccessed() {
         return propertiesAccessed;
     }
-    
+
     void resetPropertiesAccessed() {
         propertiesAccessed = false;
-    } 
+    }
 
     public boolean setAllowSetIO(boolean bool) {
         boolean prev = allowSetIO;
--- a/test/jtreg/com/sun/javatest/regtest/RegressionTestFinder.java	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/regtest/RegressionTestFinder.java	Sun Nov 09 22:29:59 2008 +0100
@@ -40,6 +40,7 @@
 import com.sun.javatest.finder.TagTestFinder;
 import com.sun.javatest.finder.HTMLCommentStream;
 import com.sun.javatest.finder.ShScriptCommentStream;
+import java.util.regex.Pattern;
 
 /**
   * This is a specific implementation of the TagTestFinder which is to be used
@@ -169,36 +170,33 @@
         }
 
         // force more key words based on actions
-        String name;
         String value = newTagValues.get("run");
 
         String origKeywords = newTagValues.get("keywords");
         String addKeywords  = "";
 
-        if (value.indexOf(name = "othervm") != -1)
-            addKeywords += "othervm ";
+        if (match(value, OTHERVM_OPTION))
+            addKeywords += " othervm";
 
-        if (value.indexOf(name = "manual") != -1)
-            addKeywords += "manual ";
+        if (match(value, MANUAL_OPTION))
+            addKeywords += " manual";
 
-        if (value.indexOf(name = "shell") != -1)
-            addKeywords += "shell ";
+        if (match(value, SHELL_ACTION))
+            addKeywords += " shell";
 
-        if (value.indexOf(name = "ignore") != -1)
-            addKeywords += "ignore ";
+        if (match(value, IGNORE_ACTION))
+            addKeywords += " ignore";
 
         if (!addKeywords.equals("")) {
             if (origKeywords == null)
                 newTagValues.put("keywords", addKeywords.trim());
             else
-                newTagValues.put("keywords", origKeywords + " " + addKeywords.trim());
+                newTagValues.put("keywords", origKeywords + addKeywords);
         }
 
         /*
-        for (Enumeration e = newTagValues.keys(); e.hasMoreElements(); ) {
-            name  = (String) e.nextElement();
-            value = (String) newTagValues.get(name);
-            System.out.println("NAME: " + name + " VALUE: " + value);
+        for (Map.Entry<String,String> e: newTagValues.entrySet()) {
+            System.out.println("NAME: " + e.getKey() + " VALUE: " + e.getValue());
 //          if (name.equals("keywords"))
 //              System.out.println(currFile + " " + "`" + value + "'");
         }
@@ -206,6 +204,10 @@
 
         return newTagValues;
     }
+    
+    private static boolean match(CharSequence cs, Pattern p) {
+        return p.matcher(cs).matches();
+    }
 
     /**
      * Make sure that the provide name-value pair is of the proper format as
@@ -221,7 +223,6 @@
     {
         Map<String,String> tagValues = (Map<String,String>) tv;
         
-        File currFile = getCurrentFile();
         // check for valid tag name, don't produce error message for the
         // the SCCS sequence '%' 'W' '%'
         if (name.startsWith("(#)"))
@@ -552,6 +553,12 @@
     
     private static final boolean allowLocalKeys = 
             Boolean.parseBoolean(System.getProperty("javatest.regtest.allowLocalKeys", "true"));
+    
+    private static final Pattern 
+        OTHERVM_OPTION = Pattern.compile(".*/othervm[/ \t].*",    Pattern.DOTALL),
+        MANUAL_OPTION  = Pattern.compile(".*/manual[/= \t].*",    Pattern.DOTALL),
+        SHELL_ACTION   = Pattern.compile(".*[ \t]shell[/ \t].*",  Pattern.DOTALL),
+        IGNORE_ACTION  = Pattern.compile(".*[ \t]ignore[/ \t].*", Pattern.DOTALL);
 
     //----------member variables------------------------------------------------
 
--- a/test/jtreg/com/sun/javatest/regtest/i18n.properties	Sun Nov 09 22:27:11 2008 +0100
+++ b/test/jtreg/com/sun/javatest/regtest/i18n.properties	Sun Nov 09 22:29:59 2008 +0100
@@ -176,6 +176,7 @@
     re-run any tests.  A work directory containing the results of the executed \
     tests must be provided.  The default location is "./JTwork". To specify an \
     alternate directory, use -workDir.
+help.main.nr.desc=Do not generate a final report.
 help.main.startHttpd.desc=Start the http server to view test results
 help.main.timeout.desc=A scaling factor to extend the default timeout of all \
     tests.  Typically used when running on slow file systems.