changeset 0:49376e5d18f1

Initial load
author duke
date Fri, 21 Jun 2013 16:34:42 -0700
parents
children bda8a64ff19c
files build.xml nbproject/project.xml src/buildLogFilter/Main.java src/buildLogWarnSummary/Fault.java src/buildLogWarnSummary/HTMLReporter.java src/buildLogWarnSummary/HTMLWriter.java src/buildLogWarnSummary/Main.java src/buildLogWarnSummary/Messages.java src/buildLogWarnSummary/Reporter.java src/buildLogWarnSummary/SimpleReporter.java src/buildLogWarnSummary/Tables.java src/javacLintSummary/Main.java
diffstat 12 files changed, 3438 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/build.xml	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2013, Oracle and/or its affiliates. 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.
+
+ 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ or visit www.oracle.com if you need additional information or have any
+ questions.
+-->
+<project name="fridayStats" default="build">
+    <!-- javacLintSummary requires JDK 8 because it recompiles source code
+    in order to analyze the requisite diagnostics. The other utilities
+    just require JDK 7 or better.
+    You can override this path on the ant command line. -->
+    <property name="jdk" location="/opt/jdk/1.8.0"/>
+
+    <target name="build" depends="buildLogFilter,buildLogWarnSummary,javacLintSummary"/>
+
+    <target name="buildLogFilter" depends="compile-classes">
+        <jar destfile="dist/buildLogFilter.jar" basedir="build/classes"
+            includes="buildLogFilter/**">
+            <manifest>
+                <attribute name="Main-Class" value="buildLogFilter.Main"/>
+            </manifest>
+        </jar>
+    </target>
+
+    <target name="buildLogWarnSummary" depends="compile-classes">
+        <jar destfile="dist/buildLogWarnSummary.jar" basedir="build/classes"
+            includes="buildLogWarnSummary/**">
+            <manifest>
+                <attribute name="Main-Class" value="buildLogWarnSummary.Main"/>
+            </manifest>
+        </jar>
+    </target>
+
+    <target name="javacLintSummary" depends="compile-classes">
+        <jar destfile="dist/javacLintSummary.jar" basedir="build/classes"
+            includes="javacLintSummary/**">
+            <manifest>
+                <attribute name="Main-Class" value="javacLintSummary.Main"/>
+            </manifest>
+        </jar>
+    </target>
+
+    <target name="compile-classes">
+        <mkdir dir="build/classes"/>
+        <javac fork="true" executable="${jdk}/bin/javac"
+            destdir="build/classes"
+            srcdir="src"
+            classpath="${jdk}/lib/tools.jar"
+            debug="true"
+            includeantruntime="false"/>
+    </target>
+
+    <target name="clean">
+        <delete dir="build"/>
+        <delete dir="dist"/>
+    </target>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nbproject/project.xml	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2013, Oracle and/or its affiliates. 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.
+
+ 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ or visit www.oracle.com if you need additional information or have any
+ questions.
+-->
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.ant.freeform</type>
+    <configuration>
+        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
+            <!-- Do not use Project Properties customizer when editing this file manually. -->
+            <name>fridayStats</name>
+            <properties>
+                <property name="jdk">/opt/jdk/1.8.0</property>
+            </properties>
+            <folders>
+                <source-folder>
+                    <label>src</label>
+                    <type>java</type>
+                    <location>src</location>
+                </source-folder>
+            </folders>
+            <ide-actions>
+                <action name="build">
+                    <script>nbproject/nbjdk.xml</script>
+                    <target>build</target>
+                </action>
+                <action name="clean">
+                    <script>nbproject/nbjdk.xml</script>
+                    <target>clean</target>
+                </action>
+                <action name="run">
+                    <script>nbproject/nbjdk.xml</script>
+                    <target>run</target>
+                </action>
+                <action name="rebuild">
+                    <script>nbproject/nbjdk.xml</script>
+                    <target>clean</target>
+                    <target>build</target>
+                </action>
+            </ide-actions>
+            <export>
+                <type>folder</type>
+                <location>build/classes</location>
+                <build-target>build</build-target>
+            </export>
+            <view>
+                <items>
+                    <source-folder style="packages">
+                        <label>src</label>
+                        <location>src</location>
+                    </source-folder>
+                    <source-file>
+                        <location>build.xml</location>
+                    </source-file>
+                </items>
+                <context-menu>
+                    <ide-action name="build"/>
+                    <ide-action name="rebuild"/>
+                    <ide-action name="clean"/>
+                    <ide-action name="run"/>
+                </context-menu>
+            </view>
+            <subprojects/>
+        </general-data>
+        <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3">
+            <compilation-unit>
+                <package-root>src</package-root>
+                <classpath mode="compile">${jdk}/lib/tools.jar</classpath>
+                <built-to>build/classes</built-to>
+                <source-level>1.7</source-level>
+            </compilation-unit>
+        </java-data>
+    </configuration>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buildLogFilter/Main.java	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2011,2013, Oracle and/or its affiliates. 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.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package buildLogFilter;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.regex.Pattern;
+
+
+/**
+ * Simple program that filters out the informative/chatty messages from a
+ * standard (new)JDK build log, leaving any diagnostic messages that need to be
+ * addressed.  A line count summary is provided.
+ *
+ * Usage:
+ *  java -jar BuildLogFilter.jar options logfile
+ *
+ * Options:
+ *  -q --quiet      Do not show the filtered log file; just show the
+ *                  line count stats
+ *  -xml file       Specify a file for an XML report
+ *
+ * @author jjg
+ */
+public class Main {
+
+    /**
+     * Command-line entry point.
+     * @param args command line args
+     */
+    public static void main(String[] args) {
+        try {
+            Main m = new Main();
+            boolean ok = m.run(args);
+            if (!ok)
+                System.exit(1);
+        } catch (IOException e) {
+            System.err.println("IO error: " + e);
+            System.exit(2);
+        }
+    }
+
+    public void usage(PrintWriter out) {
+        out.println(Main.class.getPackage().getName() + ":");
+        out.println("  Filters out the informative/chatty messages from a standard" );
+        out.println("  (new) JDK build log, leaving any diagnostic messages that need");
+        out.println("  to be addressed.");
+        out.println();
+        out.println("Usage:");
+        out.println("  java -jar BuildLogFilter.jar options logfile");
+        out.println();
+        out.println("Options:");
+        out.println("  -q --quiet      Do not show the filtered log file; just show the ");
+        out.println("                  line count stats");
+        out.println("  -xml file       Specify file for XML report.");
+        out.println();
+        out.println("logfile   logfile from JDK build at default log level");
+    }
+
+    /**
+     * API entry point.
+     * @param args command line args
+     * @return true if operation completed successfully
+     * @throws IOException if an IO error occurs during execution
+     */
+    public boolean run(String... args) throws IOException {
+        PrintWriter out = new PrintWriter(System.out);
+        try {
+            return run(out, args);
+        } finally {
+            out.flush();
+        }
+    }
+
+    /**
+     * API entry point.
+     * @param args command line args
+     * @return true if operation completed successfully
+     * @throws IOException if an IO error occurs during execution
+     */
+    public boolean run(PrintWriter out, String... args) throws IOException {
+
+        processArgs(args);
+        if (errors > 0)
+            return false;
+
+        if (help) {
+            usage(out);
+            return true;
+        }
+
+        int expected = 0, total = 0;
+
+        try (BufferedReader r = new BufferedReader(new FileReader(inFile))) {
+            String line;
+            while ((line = r.readLine()) != null) {
+                total++;
+                if (isExpected(line))
+                    expected++;
+                else {
+                    if (!quiet)
+                        out.println(line);
+                }
+            }
+        }
+
+        if (total == 0) {
+            out.println("Log is empty");
+        } else {
+            int expected_percent = Math.round((expected * 100.f) / total);
+            out.println(String.format("Expected/info lines   %5d (%3d%%)",
+                    expected, expected_percent));
+            out.println(String.format("Unexpected/diag lines %5d (%3d%%)",
+                    (total - expected), (100 - expected_percent)));
+            out.println(String.format("(TOTAL)               %5d",
+                    total));
+        }
+
+        if (xmlFile != null)
+            new XMLReportWriter().write(xmlFile, expected, total);
+
+        return true;
+    }
+
+    boolean isExpected(String line) {
+        if (line.isEmpty())
+            return true;
+        for (Pattern p: expectedPatterns) {
+            if (p.matcher(line).matches())
+                return true;
+        }
+        return false;
+    }
+
+    private static final Pattern[] expectedPatterns = {
+        Pattern.compile("^(Assembling|Compiling|Copying|Creating|Generating|Importing|Linking|Making|Running|Updating|Using|Verifying) .*"),
+        Pattern.compile("^(Aliases|Cache|Classes|INFO): .*"),
+        Pattern.compile("^All done\\..*"),
+        Pattern.compile("^(## |---*).*"),
+        Pattern.compile("^(Start|End) +[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9][0-9]:[0-9][0-9]:[0-9][0-9]"),
+        Pattern.compile("^[0-9][0-9]:[0-9][0-9]:[0-9][0-9] [A-Za-z0-9]+"),
+    };
+
+    /**
+     * Process command-line arguments.
+     * @param args the arguments to be processed
+     */
+    void processArgs(String... args) {
+        if (args.length == 0)
+            help = true;
+
+        for (int i = 0; i < args.length; i++) {
+            String arg = args[i];
+            if (arg.equals("-q") || arg.equals("--quiet"))
+                quiet = true;
+            else if (arg.equals("-xml") && i + 1 < args.length)
+                xmlFile = new File(args[++i]);
+            else if (arg.matches("-h|-help|--help"))
+                help = true;
+            else if (arg.startsWith("-"))
+                error("Unrecognized option: " + arg);
+            else {
+                if (inFile != null)
+                    error("Unexpected argument");
+                inFile = new File(arg);
+            }
+        }
+
+        if (help)
+            return;
+
+        if (inFile == null)
+            error("no build log specified");
+        else if (!inFile.exists())
+            error("can't find " + inFile);
+        else if (!inFile.canRead())
+            error("can't read " + inFile);
+    }
+
+    boolean help = false;
+    boolean quiet = false;
+    File inFile;
+    File xmlFile;
+
+    /**
+     * Record an error message.
+     * @param msg the message
+     */
+    void error(String msg) {
+        System.err.println(msg);
+        errors++;
+    }
+
+
+    /** The number of errors that have been reported. */
+    int errors;
+
+    static class XMLReportWriter {
+        void write(File xmlFile, int expected, int total) throws IOException {
+            try (PrintWriter out = new PrintWriter(new FileWriter(xmlFile))) {
+                out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
+                out.println("<buildLogFilter>");
+                out.println("<expected>" + expected + "</expected>");
+                out.println("<unexpected>" + (total - expected) + "</unexpected>");
+                out.println("</buildLogFilter>");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buildLogWarnSummary/Fault.java	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. 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.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package buildLogWarnSummary;
+
+/**
+ *
+ * @author jjg
+ */
+public class Fault extends Exception {
+    Fault(String msg) {
+        super(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buildLogWarnSummary/HTMLReporter.java	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. 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.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package buildLogWarnSummary;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Date;
+
+import static buildLogWarnSummary.HTMLWriter.*;
+import static buildLogWarnSummary.Reporter.TableType.*;
+import static buildLogWarnSummary.Messages.*;
+
+/**
+ *
+ * @author jjg
+ */
+public class HTMLReporter extends Reporter {
+    @Override
+    protected void startReport() throws IOException {
+        if (outFile == null)
+            outFile = new File(System.getProperty("user.dir"));
+        if (outFile.isDirectory())
+            outFile = new File(outFile, "report.html");
+        Writer w = new BufferedWriter(new FileWriter(outFile));
+        out = new HTMLWriter(w);
+        out.startTag(HTML);
+        out.startTag(HEAD);
+        out.startTag(TITLE);
+        out.write("JDK Build Warnings");
+        out.endTag(TITLE);
+        out.startTag(STYLE);
+        out.writeAttr("type", "text/css");
+        out.write("");
+        out.newLine();
+        out.write("table { border: 1px solid grey; }");
+        out.newLine();
+        out.write("tr.odd { background-color: white; }");
+        out.newLine();
+        out.write("tr.even { background-color: #f0f0f0; }");
+        out.newLine();
+        out.endTag(STYLE);
+        out.endTag(HEAD);
+        out.startTag(BODY);
+        out.startTag(H1);
+        out.write("JDK Build Warnings");
+        out.endTag(H1);
+        if (title != null) {
+            out.startTag(H2);
+            out.write(title);
+            out.endTag(H2);
+        }
+//        if (tables.firstLine != null) {
+//            out.startTag(H3);
+//            out.write(tables.firstLine);
+//            out.endTag(H3);
+//        }
+        out.startTag(HR);
+        out.writeAttr(ALIGN, LEFT);
+        out.writeAttr(WIDTH, "50%");
+        writeIndex();
+        out.startTag(HR);
+        out.writeAttr(ALIGN, LEFT);
+        out.writeAttr(WIDTH, "50%");
+    }
+
+    @Override
+    protected void endReport() throws IOException {
+        out.startTag(HR);
+        out.startTag(SPAN);
+        out.writeStyleAttr("font-size: smaller");
+        out.write("Generated on " + (new Date()));
+        out.endTag(SPAN);
+        out.endTag(BODY);
+        out.endTag(HTML);
+        out.close();
+    }
+
+    void writeIndex() throws IOException {
+        out.startTag(UL);
+        if (!refTables.isEmpty()) {
+            writeIndexEntryStart("Comparison against reference results");
+            out.startTag(UL);
+            writeIndexEntry(REF_DELTA_LOCNS);
+            out.endTag(UL);
+            writeIndexEntryEnd();
+        }
+
+        if (showLocations) {
+            writeIndexEntryStart("Warnings categorized by location");
+            out.startTag(UL);
+            writeIndexEntry(LOCN_COUNTS_ALPHA);
+            writeIndexEntry(LOCN_COUNTS_FREQ);
+            writeIndexEntry(LOCN_DIR_COUNTS_ALPHA);
+            writeIndexEntry(LOCN_DIR_COUNTS_FREQ);
+            writeIndexEntry(LOCN_EXTN_COUNTS_ALPHA);
+            writeIndexEntry(LOCN_EXTN_COUNTS_FREQ);
+            Collection<Message> unknownLocns = tables.pathTable.get(Message.Location.UNKNOWN);
+            if (unknownLocns != null)
+                writeIndexEntry(LOCN_UNKNOWN);
+            out.endTag(UL);
+            writeIndexEntryEnd();
+        }
+
+        if (showTools) {
+            writeIndexEntryStart("Warnings categorized by tool");
+            out.startTag(UL);
+            writeIndexEntry(TOOL_COUNTS_ALPHA);
+            writeIndexEntry(TOOL_COUNTS_FREQ);
+            out.endTag(UL);
+            writeIndexEntryEnd();
+        }
+
+        if (showKinds) {
+            writeIndexEntryStart("Warnings categorized by kind");
+            out.startTag(UL);
+            writeIndexEntry(KIND_COUNTS_ALPHA);
+            writeIndexEntry(KIND_COUNTS_FREQ);
+            Collection<Message> unknownKinds = tables.messageKindTable.get(Message.Kind.UNKNOWN);
+            if (unknownKinds != null)
+                writeIndexEntry(KIND_UNKNOWN);
+            out.endTag(UL);
+            writeIndexEntryEnd();
+        }
+        out.endTag(UL);
+    }
+
+    void writeIndexEntry(String text) throws IOException {
+        writeIndexEntryStart(text);
+        writeIndexEntryEnd();
+    }
+
+    void writeIndexEntryStart(String text) throws IOException {
+        out.startTag(LI);
+        out.write(text);
+    }
+
+    void writeIndexEntry(TableType type) throws IOException {
+        writeIndexEntryStart(type);
+        writeIndexEntryEnd();
+    }
+
+    void writeIndexEntryStart(TableType type) throws IOException {
+        out.startTag(LI);
+        out.startTag(A);
+        out.writeAttr(HREF, "#" + type);
+        out.write(type.title);
+        out.endTag(A);
+    }
+
+    void writeIndexEntryEnd() throws IOException {
+        out.endTag(LI);
+    }
+
+//    @Override
+//    protected void writeTable(Collection<? extends Map.Entry<?,?>> entries) throws IOException {
+//        if (entries.isEmpty()) {
+//            out.write("No entries");
+//        } else {
+//            out.startTag(TABLE);
+//            int count = 0;
+//            for (Map.Entry<?,?> e: entries) {
+//                out.startTag(TR);
+//                out.writeAttr("class", ((count++ % 2 == 0) ? "even" : "odd"));
+//                writeTableCell(e.getKey());
+//                writeTableCell(e.getValue());
+//                out.endTag(TR);
+//            }
+//            out.endTag(TABLE);
+//        }
+//    }
+
+    @Override
+    protected void writeTableRows(Collection<? extends Collection<?>> rows) throws IOException {
+        if (rows.isEmpty()) {
+            out.write("No entries");
+        } else {
+            out.startTag(TABLE);
+            int count = 0;
+            for (Collection<?> row: rows) {
+                out.startTag(TR);
+                out.writeAttr("class", ((count++ % 2 == 0) ? "even" : "odd"));
+                for (Object item: row)
+                    writeTableCell(item);
+                out.endTag(TR);
+            }
+            out.endTag(TABLE);
+        }
+    }
+
+    void writeTableCell(Object o) throws IOException {
+        out.startTag(TD);
+        if (o instanceof Number)
+            out.writeAttr(ALIGN, RIGHT);
+        out.write(String.valueOf(o));
+        out.endTag(TD);
+    }
+
+    @Override
+    protected void writeTableHead(String head) throws IOException {
+        out.startTag(H3);
+        out.write(head);
+        out.endTag(H3);
+    }
+
+    @Override
+    protected void writeTableHead(TableType type) throws IOException {
+        out.startTag(H3);
+        out.startTag(A);
+        out.writeAttr(NAME, type.toString());
+        out.endTag(A);
+        out.write(type.title);
+        out.endTag(H3);
+    }
+
+    @Override
+    protected <T> void writeList(Collection<T> list) throws IOException {
+        out.startTag(UL);
+        for (T t: list) {
+            out.startTag(LI);
+            out.write(String.valueOf(t));
+            out.endTag(LI);
+        }
+        out.endTag(UL);
+    }
+
+    HTMLWriter out;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buildLogWarnSummary/HTMLWriter.java	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 1996, 2011, Oracle and/or its affiliates. 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.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package buildLogWarnSummary;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URL;
+
+/**
+ * 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();
+    }
+
+    /**
+     * 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);
+        StringBuilder sb = new StringBuilder();
+        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);
+    }
+
+    /** 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 "span" tag. */
+    public static final String SPAN = "span";
+    /** 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";
+    /** The HTML "width" attribute. */
+    public static final String WIDTH = "width";
+
+
+    private BufferedWriter out;
+    private int state;
+    private static final int IN_TAG = 1;
+    private static final int IN_BODY = 2;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buildLogWarnSummary/Main.java	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. 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.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package buildLogWarnSummary;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Utility to analyze the warnings and other diagnostics generated during a
+ * JDK build.
+ *
+ * @author jjg
+ */
+public class Main {
+    static abstract class Option implements Comparable<Option> {
+        static void processAll(Option[] options, String... args) throws Fault {
+            for (Iterator<String> iter = Arrays.asList(args).iterator(); iter.hasNext(); ) {
+                String arg = iter.next();
+                process(options, arg, iter);
+            }
+        }
+
+        private static void process(Option[] options, String arg, Iterator<String> rest) throws Fault {
+            for (Option o: options) {
+                if (o.matches(arg)) {
+                    if (o.hasArg) {
+                        if (rest.hasNext())
+                            o.process(arg, rest);
+                        else
+                            throw new Fault("no value given for " + arg);
+                    } else
+                        o.process(arg, rest);
+                    return;
+                }
+            }
+            throw new Fault("unrecognized option " + arg);
+        }
+        Option(String name, boolean hasArg, String help) {
+            this.name = name;
+            this.hasArg = hasArg;
+            this.help = help;
+        }
+
+        boolean matches(String opt) {
+            return name.equals(opt);
+        }
+
+        abstract void process(String opt, Iterator<String> args) throws Fault;
+
+        @Override
+        public int compareTo(Option other) {
+            return name.compareTo(other.name);
+        }
+
+        @Override
+        public String toString() {
+            return "Option[" + name +"]";
+        }
+
+        final String name;
+        final boolean hasArg;
+        final String help;
+    }
+
+    Option[] options = {
+        new Option("-help", false, "show this help message") {
+            @Override
+            void process(String opt, Iterator<String> args) {
+                Main.this.help = true;
+            }
+        },
+        new Option("-usage", false, "show this help message") {
+            @Override
+            void process(String opt, Iterator<String> args) {
+                Main.this.help = true;
+            }
+        },
+        new Option("-h", false, "generate HTML report (defaults from output file extn") {
+            @Override
+            void process(String opt, Iterator<String> args) {
+                format = Format.HTML;
+            }
+        },
+        new Option("-l", false, "show warnings categorized by location") {
+            @Override
+            void process(String opt, Iterator<String> args) {
+                showLocations = true;
+            }
+        },
+        new Option("-o", true, "output file") {
+            @Override
+            void process(String opt, Iterator<String> args) {
+                outFile = new File(args.next());
+            }
+        },
+        new Option("-k", false, "show warnings categorized by kind") {
+            @Override
+            void process(String opt, Iterator<String> args) {
+                showKinds = true;
+            }
+        },
+        new Option("-t", false, "show warnings categorized by tool") {
+            @Override
+            void process(String opt, Iterator<String> args) {
+                showTools = true;
+            }
+        },
+        new Option("-title", true, "title for report") {
+            @Override
+            void process(String opt, Iterator<String> args) {
+                title = args.next();
+            }
+        },
+        new Option("-r", true, "reference log file(s)") {
+            @Override
+            void process(String opt, Iterator<String> args) {
+                refFiles.add(new File(args.next()));
+            }
+        },
+        new Option("<files>", false, "log files to be analyzed") {
+            @Override
+            boolean matches(String opt) {
+                return (!opt.startsWith("-"));
+            }
+            @Override
+            void process(String opt, Iterator<String> args) {
+                inFiles.add(new File(opt));
+                while (args.hasNext())
+                    inFiles.add(new File(args.next()));
+            }
+        }
+    };
+
+    enum Format { HTML, SIMPLE };
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new Main().run(args);
+        } catch (Fault e) {
+            System.err.println(e);
+        } catch (IOException e) {
+            System.err.println(e);
+            e.printStackTrace(System.err);
+        }
+    }
+
+    /**
+     * API entry point.
+     * @param args command line args
+     * @return true if operation completed successfully
+     * @throws IOException if an IO error occurs during execution
+     */
+    public void run(String... args) throws IOException, Fault {
+        PrintWriter out = new PrintWriter(System.out);
+        try {
+            run(out, args);
+        } finally {
+            out.flush();
+        }
+    }
+
+    public void run(PrintWriter out, String... args) throws IOException, Fault {
+        Option.processAll(options, args);
+
+        if (help) {
+            showHelp(out);
+            if (inFiles.isEmpty())
+                return;
+        }
+
+        if (!showLocations && !showKinds && !showTools && refFiles.isEmpty()) {
+            showKinds = true;
+            showLocations = true;
+            showTools = true;
+        }
+
+        Tables ref = new Tables(refFiles);
+        Tables t = new Tables(inFiles);
+
+
+        Reporter r = createReporter();
+        if (outFile != null)
+            r.setOutput(outFile);
+        if (title != null)
+            r.setTitle(title);
+        r.setShowLocations(showLocations);
+        r.setShowKinds(showKinds);
+        r.setShowTools(showTools);
+        r.setReference(ref);
+        r.report(t);
+    }
+
+    Reporter createReporter() {
+        Format f = format;
+        if (f == null && outFile != null) {
+            String fileName = outFile.getName();
+            int lastDot = fileName.lastIndexOf('.');
+            String extn = fileName.substring(lastDot);
+            if (extn.equals(".html"))
+                f = Format.HTML;
+        }
+        if (f == null)
+            f = Format.SIMPLE;
+
+        switch (f) {
+            case HTML:
+                return new HTMLReporter();
+            default:
+                return new SimpleReporter();
+        }
+    }
+
+    void showHelp(PrintWriter out) {
+        out.println(Main.class.getPackage().getName() + ":");
+        out.println("  Analyze the warnings and other diagnostics generated during a JDK build.");
+        out.println();
+        out.println("Usage:");
+        out.println("  java -jar " + findJar(Main.class).getName() + " [options...] files...");
+        out.println();
+        out.println("Options:");
+        List<Option> opts = new ArrayList<>(Arrays.asList(options));
+        Collections.sort(opts);
+        for (Option o: opts) {
+            String s = (o.hasArg ? o.name + " <arg>" : o.name);
+            out.println(String.format("  %-18s %s", s, o.help));
+        }
+    }
+
+    private File findJar(Class<?> c) {
+        try {
+            String className = c.getName().replace(".", "/") + ".class";
+            // use URI to avoid encoding issues, e.g. Program%20Files
+            URI uri = getClass().getClassLoader().getResource(className).toURI();
+            if (uri.getScheme().equals("jar")) {
+                String ssp = uri.getRawSchemeSpecificPart();
+                int sep = ssp.lastIndexOf("!");
+                uri = new URI(ssp.substring(0, sep));
+                if (uri.getScheme().equals("file"))
+                    return new File(uri.getPath());
+            }
+        } catch (URISyntaxException ignore) {
+            ignore.printStackTrace(System.err);
+        }
+
+        return null;
+    }
+
+    boolean help;
+    File outFile;
+    Format format;
+    String title;
+    boolean showLocations;
+    boolean showKinds;
+    boolean showTools;
+    List<File> inFiles = new ArrayList<>();
+    List<File> refFiles = new ArrayList<>();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buildLogWarnSummary/Messages.java	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,590 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. 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.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package buildLogWarnSummary;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @author jjg
+ */
+public class Messages {
+
+    static class Message {
+        static class Kind implements Comparable<Kind> {
+            Tool tool;
+            boolean hasLocation;
+            Pattern msgPattern;
+
+            Kind(Tool tool, boolean hasLocation, Pattern msgPattern) {
+                this.tool = tool;
+                this.hasLocation = hasLocation;
+                this.msgPattern = msgPattern;
+            }
+
+            boolean hasLocation() {
+                return hasLocation;
+            }
+
+            boolean matches(String line) {
+                return msgPattern.matcher(line).matches();
+            }
+
+            public int compareTo(Kind o) {
+                int result = tool.name.compareTo(o.tool.name);
+                if (result == 0)
+                    return msgPattern.pattern().compareTo(o.msgPattern.pattern());
+                else
+                    return result;
+            }
+
+            @Override
+            public String toString() {
+                String p = msgPattern.pattern();
+                int start = (p.startsWith(".*") ? 2 : 0);
+                int end = p.length() - (p.endsWith(".*") ? 2 : 0);
+                return p.substring(start, end);
+            }
+
+            static final Kind UNKNOWN = new Kind(new Tool("unknown"), false, Pattern.compile(""));
+        }
+
+        static class Location implements Comparable<Location> {
+            String file;
+            String path;
+
+            Location(String file, String path) {
+                this.file = file;
+                this.path = path;
+            }
+
+            String getExtension() {
+                int dot = file.lastIndexOf(".");
+                return (dot == -1 ? "" : file.substring(dot + 1));
+            }
+
+            String getPathDirectory() {
+                int sep = path.replace("\\", "/").lastIndexOf("/");
+                return sep == -1 ? path : path.substring(0, sep);
+            }
+
+            @Override
+            public int compareTo(Location o) {
+                return path.compareTo(o.path);
+            }
+
+            @Override
+            public String toString() {
+                return path;
+            }
+
+            static final Location UNKNOWN = new Location("", "");
+        }
+
+        Message(Kind kind, String line) {
+            this.kind = kind;
+            this.line = line;
+            String l = line.trim().replace("\\", "/");
+            Matcher ddm;
+            while ((ddm = dotdot.matcher(l)).matches())
+                l = ddm.group(1) + ddm.group(2);
+
+            if (kind.hasLocation) {
+                for (Pattern p: kind.tool.locnPatterns) {
+                    Matcher m = p.matcher(l);
+                    if (m.matches()) {
+                        this.location = new Location(m.group(1), m.group(2));
+                        return;
+                    }
+                }
+            }
+        }
+
+        public String toString() {
+            return kind + ": " + line;
+        }
+
+        private static Pattern dotdot = Pattern.compile("(.*/)[^/.]+/\\.\\./(.*)");
+
+        String line;
+        Location location;
+        Kind kind;
+    }
+
+    boolean isWarning(String line) {
+        if (!warningPattern.matcher(line).matches())
+            return false;
+
+        for (Pattern p: notWarningPatterns) {
+            if (p.matcher(line).matches())
+                return false;
+        }
+
+        return true;
+    }
+
+    Pattern warningPattern = Pattern.compile("(?i).*\\bwarning\\b.*");
+    Pattern[] notWarningPatterns = {
+        Pattern.compile(" *(\\[[^ ]+\\])? *[0-9]+ warning(s?|\\(s\\))"),
+        Pattern.compile(" *(\\[[^ ]+\\])? *[0-9]+ Warning\\(s\\) detected\\."),
+        Pattern.compile(".*warning[^. /\\\\]*\\.(gif|png).*"),
+        Pattern.compile("Dialog Warning for language .* built"),
+        Pattern.compile(".* - [0-9]+ error\\(s\\), [0-9]+ warning\\(s\\)"),
+        Pattern.compile(".*com.sun.java.util.jar.pack.Utils\\$Pack200Logger warning.*")
+    };
+
+    Message getMessage(String line) {
+        for (Tool t: tools) {
+            for (Message.Kind k: t.kinds) {
+                if (k.matches(line)) {
+                    return new Message(k, line);
+                }
+            }
+        }
+        return null;
+    }
+
+    static class Tool implements Comparable<Tool> {
+        final String name;
+        final List<Pattern> locnPatterns = new ArrayList<Pattern>();
+        final List<Message.Kind> kinds = new ArrayList<Message.Kind>();
+
+        protected Tool(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public int compareTo(Tool other) {
+            return name.compareTo(other.name);
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+        protected void location(String regex) {
+            locnPatterns.add(Pattern.compile(regex));
+        }
+
+        protected void kind(boolean hasLocation, String regex) {
+            kinds.add(new Message.Kind(this, hasLocation,
+                    Pattern.compile(".*" + regex + ".*")));
+        }
+    }
+
+    Tool[] tools = {
+        new Tool("adlc") {
+            {
+                kind(false, "ASSERT is undefined, assertions disabled\\.");
+            }
+        },
+
+        new Tool("cc") {
+            {
+                location(".*\"([^\"]+/src/share/native/([^ ]+\\.(?:h|c|cpp)))\".*");
+                location(".*\"([^\"]+/src/solaris/native/([^ ]+\\.(?:h|c|cpp)))\".*");
+                location(".*\"([^\"]+/src/([^ ]+\\.(?:h|c|cpp)))\".*");
+                location("^\"(([^ ]+\\.(?:h|c|cpp)))\".*");
+
+                kind(false, "-xarch=.* is deprecated, use -m64 to create 64-bit programs");
+                kind(true, ".* hides the same name in an outer scope.");
+                kind(true, ".* hides the virtual function .*\\.");
+                kind(true, "Assigning .* to .*\\.");
+                kind(true, "Attempt to redefine .* without using #undef.");
+                kind(true, "Comparing different enum types \".*\" and \".*\".");
+                kind(true, "Conversion of 64 bit type value to .* causes truncation");
+                kind(true, "Formal argument .* of type .* in call to .* is being passed .*\\.");
+                kind(true, "Formal argument func of type .* is being passed .*\\.");
+                kind(true, "Function has no return statement : .*");
+                kind(true, "Identifier expected instead of \".*\".");
+                kind(true, "Printf conversion specification \".*\" and argument type \".*\" are incompatible.");
+                kind(true, "String literal converted to char\\* in assignment.");
+                kind(true, "String literal converted to char\\* in formal argument .* in call to .*\\.");
+                kind(true, "String literal converted to char\\* in initialization\\.");
+                kind(true, "The option .* was seen twice\\.");
+                kind(true, "argument .* is incompatible with prototype");
+                kind(true, "asm\\(\\) statement disables optimization within function");
+                kind(true, "assignment type mismatch");
+                kind(true, "constant promoted to unsigned long");
+                kind(true, "declaration can not follow a statement");
+                kind(true, "empty translation unit");
+                kind(true, "end-of-loop code not reached");
+                kind(false, "illegal option .*");
+                kind(true, "implicit function declaration: .*");
+                kind(true, "implicitly declaring function to return int: .*");
+                kind(true, "improper pointer/integer combination: .*");
+                kind(true, "integer overflow detected: op \".*\"");
+                kind(true, "keyword \".*\" is being redefined\\.");
+                kind(true, "macro redefined: .*");
+                kind(true, "non-constant initializer: .*");
+                kind(true, "pointer arithmetic overflow detected");
+                kind(true, "statement not reached");
+                kind(true, "storage class after type is obsolescent");
+                kind(true, "static function called but not defined: .*");
+                kind(true, "wvarhidemem: .* hides .*");
+            }
+        },
+
+        new Tool("cl") {
+            {
+                kind(false, "ignoring unknown option '.*'");
+                kind(false, "option '.*' has been deprecated and will be removed in a future release");
+                kind(false, "overriding '.*' with '.*'");
+                kind(false, "use 'RTC1' instead of 'GZ'");
+            }
+        },
+
+        new Tool("createSymbols") {
+            {
+                kind(false, "package .* does not exist");
+            }
+        },
+
+        new Tool("gcc") {
+            {
+                location(".*([^ ]+/src/closed/share/classes/([^ ]+\\.(?:h|c|cpp))):.*");
+                location(".*([^ ]+/src/closed/share/native/([^ ]+\\.(?:h|c|cpp))):.*");
+                location(".*([^ ]+/src/solaris/([^ ]+\\.(?:h|c|cpp))):.*");
+                location(".*([^ ]+/src/share/native/([^ ]+\\.(?:h|c|cpp))):.*");
+                location(".*([^ ]+/src/share/demo/([^ ]+\\.(?:h|c|cpp))):.*");
+                location(".*([^ ]+/src/share/([^ ]+\\.(?:h|c|cpp))):.*");
+                location(".*([^ ]+/src/common/unix/native/([^ ]+\\.(?:h|c|cpp))):.*");
+                location(".*([^ ]+/src/([^ ]+\\.(?:h|c|cpp))):.*");
+                location("^(([^ ]+\\.(?:h|c|cpp))):.*");
+
+                kind(false, "this is the location of the previous definition");
+                kind(true, ".*\\[-Wclobbered\\]");
+                kind(true, ".*\\[-Wdeprecated-declarations\\]");
+                kind(true, ".*\\[-Wformat\\]");
+                kind(true, ".*\\[-Wformat-security\\]");
+                kind(true, ".*\\[-Wimplicit-function-declaration\\]");
+                kind(true, ".*\\[-Wimplicit-int\\]");
+                kind(true, ".*\\[-Wmissing-field-initializers\\]");
+                kind(true, ".*\\[-Wpointer-sign\\]");
+                kind(true, ".*\\[-Wreorder\\]");
+                kind(true, ".*\\[-Wswitch\\]");
+                kind(true, ".*\\[-Wuninitialized\\]");
+                kind(true, ".*\\[-Wunused-result\\]");
+                kind(true, "'.*' is deprecated \\(declared at .*\\)");
+                kind(true, "\".*\" redefined");
+                kind(true, "'.*' initialized and declared 'extern'");
+                kind(true, "'.*' may be used uninitialized in this function");
+                kind(true, "'.*' used but never defined");
+                kind(true, "'return' with no value, in function returning non-void");
+                kind(true, "'static' is not at beginning of declaration");
+                kind(true, "\\(near initialization for '.*'\\)");
+                kind(true, "\\(perhaps the '.*' macro was used incorrectly\\)");
+                kind(true, "\\Q\"/*\"\\E within comment");
+                kind(true, "`.*' initialized and declared `extern'");
+                kind(true, "`.*' might be used uninitialized in this function");
+                kind(true, "`class .*' has virtual functions but non-virtual destructor");
+                kind(true, "argument '.*' might be clobbered by 'longjmp' or 'vfork'");
+                kind(true, "array subscript has type '.*'");
+                kind(true, "assignment discards qualifiers from pointer target type");
+                kind(true, "assignment from incompatible pointer type");
+                kind(true, "assignment makes integer from pointer without a cast");
+                kind(true, "assuming signed overflow does not occur when assuming that \\Q(X + c) < X\\E is always false");
+                kind(true, "cast from pointer to integer of different size");
+                kind(true, "cast to pointer from integer of different size");
+                kind(true, "comparison between pointer and integer");
+                kind(true, "comparison between signed and unsigned");
+                kind(true, "comparison is always false due to limited range of data type");
+                kind(true, "comparison of distinct pointer types lacks a cast");
+                kind(true, "comparison of unsigned expression < 0 is always false");
+                kind(true, "comparison of unsigned expression >= 0 is always true");
+                kind(true, "comparison with string literal results in unspecified behavior");
+                kind(true, "conflicting types for built-in function '.*'");
+                kind(true, "control reaches end of non-void function");
+                kind(true, "deprecated conversion from string constant to 'char\\*'");
+                kind(true, "dereferencing type-punned pointer will break strict-aliasing rules");
+                kind(true, "enumeration value '.*' not handled in switch");
+                kind(true, "extra tokens at end of #endif directive");
+                kind(true, "format '.*' expects type '.*', but argument .* has type '.*'");
+                kind(true, "ignoring #pragma ident");
+                kind(true, "implicit declaration of function '.*'");
+                kind(true, "incompatible implicit declaration of built-in function '.*'");
+                kind(true, "incompatible implicit declaration of built-in function '.*'");
+                kind(true, "initialization makes integer from pointer without a cast");
+                kind(true, "integer constant is too large for 'long' type");
+                kind(true, "invalid access to non-static data member '.*' of NULL object");
+                kind(true, "likely type-punning may break strict-aliasing rules");
+                kind(true, "missing braces around initializer");
+                kind(true, "missing initializer");
+                kind(true, "missing sentinel in function call");
+                kind(true, "operation on '.*' may be undefined");
+                kind(true, "passing argument .* of '.*' discards qualifiers from pointer target type");
+                kind(true, "passing argument .* of '.*' from incompatible pointer type");
+                kind(true, "passing argument .* of '.*' makes integer from pointer without a cast");
+                kind(true, "passing argument .* of '.*' makes pointer from integer without a cast");
+                kind(true, "pointer targets in initialization differ in signedness");
+                kind(true, "pointer targets in passing argument .* of '.*' differ in signedness");
+                kind(true, "return makes integer from pointer without a cast");
+                kind(true, "signed and unsigned type in conditional expression");
+                kind(true, "suggest a space before ';' or explicit braces around empty body in 'while' statement");
+                kind(true, "suggest braces around empty body in 'do' statement");
+                kind(true, "suggest braces around empty body in an 'if' statement");
+                kind(true, "type defaults to 'int' in declaration of '.*'");
+                kind(true, "universal character names are only valid in C\\+\\+ and C99");
+                kind(true, "unmappable character for encoding ASCII");
+                kind(true, "unnamed struct/union that defines no instances");
+                kind(true, "unsigned int format, long unsigned int arg \\(arg .*\\)");
+                kind(true, "variable '.*' might be clobbered by 'longjmp' or 'vfork'");
+                kind(true, "incompatible implicit declaration of built-in function ‘.*’ \\[enabled by default\\]");
+                kind(true, "passing argument .* of ‘.*’ discards ‘.*’ qualifier from pointer target type \\[enabled by default\\]");
+            }
+        },
+
+        new Tool("HotSpot") {
+            {
+                kind(false, "Java HotSpot\\(TM\\) .* VM warning: increase O_BUFLEN in ostream.hpp -- output truncated");
+            }
+        },
+
+        new Tool("ISCmdBld") {
+            {
+                kind(false, "The property ALLUSERS is defined in the property table.  This may result is a setup that is always installed per-machine when it has been advertised as a per-user install.");
+                kind(false, "One or more of the project's components contain \\.NET properties that require the \\.NET Framework.");
+                kind(false, "A condition for feature '.*' may possibly set the InstallLevel for this feature to zero at runtime. If this feature gets enabled on install, you must author similar logic to ensure that it is also enabled in maintenance mode, otherwise in an upgrade the feature will be ignored.");
+                kind(false, "The Custom Action .* in the InstallExecuteSequence table is run from an installed file. To run the custom action successfully, you may need to have a condition that checks if the source file is installed locally\\.");
+
+            }
+        },
+
+        new Tool("javac") {
+            {
+                location(".*([^ ]+/src/share/classes/([^ ]+\\.java)):.*");
+                location(".*([^ ]+/src/([^ ]+\\.java)):.*");
+
+                kind(false, "cast to .* for a non-varargs call and to suppress this warning");
+                kind(false, "\\[options\\] bootstrap class path not set in conjunction with -source .*");
+                kind(true, "\\[deprecation\\] .* in .* has been deprecated");
+                kind(true, "\\[overrides\\] Class .* overrides equals, but neither it nor any superclass overrides hashCode method");
+                kind(true, "\\[serial\\] serializable class .* has no definition of serialVersionUID");
+                kind(true, "change obsolete notation for MethodHandle invocations from .* to .*");
+                kind(true, "non-varargs call of varargs method with inexact argument type for last parameter");
+            }
+        },
+
+        new Tool("javadoc") {
+            {
+                location(".*([^ ]+/impsrc/([^ ]+\\.(?:java|html))):.*");
+                location(".*([^ ]+/src/share/classes/([^ ]+\\.java)):.*");
+                location(".*([^ ]+/src/([^ ]+\\.java)):.*");
+
+                kind(true, "Tag @link: reference not found: .*");
+                kind(true, ".* is an unknown tag.");
+                kind(true, "@param argument \".*\" is not a parameter name\\.");
+                kind(true, "@see tag has no arguments\\.");
+                kind(true, "Tag @link: can't find .* in .*");
+                kind(true, "Tag @link: missing '#': .*");
+                kind(true, "Tag @linkplain: reference not found: .*");
+                kind(true, "Tag @return cannot be used in constructor documentation.");
+                kind(true, "Tag @return cannot be used in field documentation.");
+                kind(true, "Tag @see: can't find .* in .*");
+                kind(true, "Tag @see: missing '.*': \".*\"");
+                kind(true, "Tag @see: reference not found: .*");
+                kind(true, "\\} missing for possible See Tag in comment string:.*");
+            }
+        },
+
+        new Tool("javazic") {
+            {
+                kind(false, "found last rules for .* inconsistent");
+            }
+        },
+
+        new Tool("jdwpgen") {
+            {
+                kind(false, "Generated jvmti file does not exist: ");
+            }
+        },
+
+        new Tool("junit") {
+            {
+                kind(false, "\\[junit\\] +class .* ignored in headless mode\\.");
+            }
+        },
+
+        new Tool("jvmtiEnvFill") {
+            {
+
+                kind(false, "function .*: filled and stub arguments differ");
+
+            }
+        },
+
+        new Tool("ld") {
+            {
+                kind(false, "option .* appears more than once, first setting taken");
+                kind(false, "`.*' does not appear in file `.*'");
+                kind(false, "section `.*' does not appear in any input file");
+                kind(false, "symbol `.*' has differing types");
+            }
+        },
+
+        new Tool("link") {
+            {
+                kind(false, "all references to '.*' discarded by /OPT:REF");
+                kind(false, "defaultlib '.*' conflicts with use of other libs; use /NODEFAULTLIB:library");
+                kind(false, "export '.*' specified multiple times; using first specification");
+                kind(false, "exported symbol '.*' should not be assigned an ordinal");
+                kind(false, "no public symbols found; archive member will be inaccessible");
+                kind(false, "unrecognized option '.*'; ignored");
+                kind(false, "object specified more than once; extras ignored");
+                kind(false, "ignoring '/EDITANDCONTINUE' due to '/INCREMENTAL:NO' specification");
+                kind(false, ".* directive in .* differs from output filename '.*'; ignoring directive");
+                kind(false, ".* is no longer supported;  ignored");
+                kind(false, "/MACHINE not specified; defaulting to X86");
+                kind(false, "# statement not supported for the target platform; ignored");
+                kind(false, "locally defined symbol .* imported in function .*");
+                kind(false, "/DELAYLOAD:.* ignored; no imports found from .*");
+            }
+        },
+
+        new Tool("make") {
+            {
+                location(".*([^ ]+(make/[^ ]+(?:\\.gmk|Makefile))):.*");
+                location("[./]*(([^ ]*(?:\\.gmk|Makefile))):.*");
+
+                kind(false, "File was not built with a mapfile: .*");
+                kind(false, "LicenseeSourceScan found .* patterns, see .*");
+                kind(false, "The file jvmti.h is not the same interface as the VM version.");
+                kind(false, "The official builds on windows use .*. You appear to be using .*");
+                kind(false, "The version of ant being used is older than");
+                kind(false, "The windows compiler is not version .*");
+                kind(false, "This build does not include running javadoc.");
+                kind(false, "This machine appears to only have .* of physical memory");
+                kind(false, "To build Java 2 SDK .* you need :");
+                kind(false, "You are not building the DEPLOY sources\\.");
+                kind(false, "junit.jar is not found");
+                kind(false, "awk:.*\\Qescape sequence `\\%' treated as plain `%'\\E");
+                kind(false, "zip warning.*name not matched: .*");
+                kind(true, "Value of .* cannot be empty, check or set .*");
+                kind(true, "ignoring old commands for target `.*'");
+                kind(true, "overriding commands for target `.*'");
+                kind(false, "Value of .* cannot be empty, will use '.*'");
+                kind(false, "The combo jre installer is not built since the 64-bit Installer (?:path )?is not (?:defined|found)");
+                kind(false, "-jN forced in submake: disabling jobserver mode.");
+            }
+        },
+
+        new Tool("msival2") {
+            {
+                kind(false, "Row '.*' in table '.*' has bits set in the '.*' column that are reserved. They should be 0 to ensure compat[ia]bility with future installer versions.");
+            }
+        },
+
+        new Tool("pack200") {
+            {
+                kind(false, "skipping .* bytes of StackMapTable attribute in .*");
+            }
+        },
+
+        new Tool("rc") {
+            {
+                kind(false, "'.*' : redefinition");
+            }
+        },
+
+        new Tool("tar") {
+            {
+                kind(false, "file \".*\": Warning! File larger than 2 gigabytes will be successfully archived; but other de-archivers may have problems reading the resulting archive.");
+            }
+        },
+
+        new Tool("unknown") {
+            {
+                kind(false, "<.*> entry is missing!!!");
+                kind(false, "Java HotSpot\\(TM\\) Server VM warning: Performance bug: SystemDictionary lookup_count=.* lookup_length=.* average=.* load=.*");
+                kind(false, "Option .* passed to ld, if ld is invoked, ignored otherwise");
+                kind(false, "Path does not exist as file or directory:");
+                kind(false, "Possible HotSpot VM interface conflict.");
+                kind(false, "parameter .* set to \".*\"");
+                kind(false, "the use of `.*' is dangerous, better use `.*'");
+                kind(false, "file .* has not been placed into a bundle");
+                kind(false, ".* does not end with a trailing slash.  This build instance will add the slash as it is required to allow proper evaluation of the .*\\.");
+            }
+        },
+
+        new Tool("vs") {
+            {
+                location("(?:2>)?(.*(/VC/INCLUDE/.*\\.(?:h|hpp|c|cpp|inl)))\\([0-9]+\\).*");
+                location("(?:2>)?(.*/src/windows/native/(.*\\.(?:h|hpp|c|cpp|inl)))\\([0-9]+\\).*");
+                location("(?:2>)?(.*/src/windows/(.*\\.(?:h|hpp|c|cpp|inl)))\\([0-9]+\\).*");
+                location("(?:2>)?(.*/src/closed/share/native/(.*\\.(?:h|hpp|c|cpp|inl)))\\([0-9]+\\).*");
+                location("(?:2>)?(.*/src/(.*\\.(?:h|hpp|c|cpp|inl)))\\([0-9]+\\).*");
+                location("(?:2>)?(.*/jdk7/(.*\\.(?:h|c|cpp|hpp|inl)))\\([0-9]+\\).*");
+                location("(?:2>)?((.*\\.(?:h|c|cpp|hpp|inl)))\\([0-9]+\\).*");
+                kind(true, "'.*' : conversion from '.*' to '.*', possible loss of data");
+                kind(true, "'.*': name was marked as #pragma deprecated");
+                kind(true, "'.*' : different 'const' qualifiers");
+                kind(true, "'.*' : unsafe mix of type 'BOOL' and type 'bool' in operation");
+                kind(true, "'.*' : inconsistent dll linkage");
+                kind(true, "'.*' : not all control paths return a value");
+                kind(true, "'.*' : pointer mismatch for actual parameter .*");
+                kind(true, "'.*' : signed/unsigned mismatch");
+                kind(true, "'.*' : unrecognized character escape sequence");
+                kind(true, "'.*' : unreferenced label");
+                kind(true, "'.*' : unreferenced local variable");
+                kind(true, "'.*' differs in levels of indirection from '.*'");
+                kind(true, "'.*' undefined; assuming extern returning int");
+                kind(true, "'.*': .* has been superseded by .* and .*");
+                kind(true, "'.*': .* has been superseded by .*");
+                kind(true, "'.*': This function or variable has been superceded by newer library or operating system functionality\\.");
+                kind(true, "'.*': This function or variable may be unsafe. Consider using .* instead\\.");
+                kind(true, "'.*': identifier in type library '.*' is already a macro; use the 'rename' qualifier");
+                kind(true, "'.*' was declared deprecated");
+                kind(true, "#ident ignored;");
+                kind(true, "C\\+\\+ exception handler used, but unwind semantics are not enabled\\.");
+                kind(true, "The POSIX name for this item is deprecated. Instead, use the ISO C\\+\\+ conformant name: .*\\.");
+                kind(true, "_STATIC_CPPLIB is deprecated");
+                kind(true, "automatically excluding '.*' while importing type library '.*'");
+                kind(true, "benign redefinition of type");
+                kind(true, "different 'volatile' qualifiers");
+                kind(true, "different types for formal and actual parameter .*");
+                kind(true, "forcing value to bool 'true' or 'false' \\(performance warning\\)");
+                kind(true, "incompatible types - from '.*' to '.*'");
+                kind(true, "local variable '.*' used without having been initialized");
+                kind(true, "macro redefinition");
+                kind(true, "nonstandard extension used: '.*' uses .* and '.*' has destructor");
+                kind(true, "nonstandard extension used: enum '.*' used in qualified name");
+                kind(true, "obsolete declaration style: please use '.*' instead");
+                kind(true, "returning address of local variable or temporary");
+                kind(true, "shift count negative or too big, undefined behavior");
+                kind(true, "too many actual parameters for macro '.*'");
+                kind(true, "truncation from '.*' to '.*'");
+                kind(true, "unary minus operator applied to unsigned type, result still unsigned");
+                kind(true, "unexpected tokens following preprocessor directive - expected a newline");
+                kind(true, "uninitialized local variable '.*' used");
+                kind(true, "'.*': .* has been changed to conform with the ISO C standard, adding an extra character count parameter.");
+                kind(true, "'ocscpy': ocscpy is not safe. Intead, use ocscpy_s");
+                kind(true, "'type cast' : conversion from '.*' to '.*' of greater size");
+            }
+        }
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buildLogWarnSummary/Reporter.java	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. 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.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package buildLogWarnSummary;
+
+import java.util.regex.Pattern;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import static buildLogWarnSummary.Messages.*;
+import static buildLogWarnSummary.Reporter.TableType.*;
+
+public abstract class Reporter {
+    protected enum TableType {
+        REF_DELTA_LOCNS("New warnings not found in reference files"),
+        LOCN_COUNTS_ALPHA("Warning counts, sorted alphabetically by location"),
+        LOCN_COUNTS_FREQ("Warning counts, sorted by frequency of location"),
+        LOCN_DIR_COUNTS_ALPHA("Warning counts, sorted alphabetically by location directory"),
+        LOCN_DIR_COUNTS_FREQ("Warning counts, sorted by frequency of location directory"),
+        LOCN_EXTN_COUNTS_ALPHA("Warning counts, sorted alphabetically by location extension"),
+        LOCN_EXTN_COUNTS_FREQ("Warning counts, sorted by frequency of location extension"),
+        LOCN_UNKNOWN("Warnings with unrecognized locations"),
+        KIND_COUNTS_ALPHA("Warning counts, sorted alphabetically by kind"),
+        KIND_COUNTS_FREQ("Warning counts, sorted by frequency of kind"),
+        KIND_UNKNOWN("Warnings with unrecognized kinds"),
+        TOOL_COUNTS_ALPHA("Warning counts, sorted alphabetically by tool"),
+        TOOL_COUNTS_FREQ("Warning counts, sorted by frequency of tool");
+        TableType(String title) {
+            this.title = title;
+        }
+        final String title;
+    }
+
+    protected Tables tables;
+    protected Tables refTables;
+    protected Map<String,Collection<String>> locnDeltas;
+    protected File outFile;
+    protected String title;
+    protected boolean showLocations;
+    protected boolean showKinds;
+    protected boolean showTools;
+    protected Tables referenceTables;
+
+
+    public void setOutput(File file) {
+        outFile = file;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public void setShowLocations(boolean b) {
+        showLocations = b;
+    }
+
+    public void setShowKinds(boolean b) {
+        showKinds = b;
+    }
+
+    public void setShowTools(boolean b) {
+        showTools = b;
+    }
+
+    public void setReference(Tables refTables) {
+        this.refTables = refTables;
+    }
+
+    public void report(Tables t) throws IOException {
+        this.tables = t;
+//        if (refTables != null) {
+//            locnDeltas = getDifference(tables.getLocationMap(), refTables.getLocationMap());
+//        }
+
+        startReport();
+
+        writeTableHead("Log files analyzed");
+        writeList(tables.files);
+        writeTable(tables.getStatistics());
+        if (!refTables.isEmpty()) {
+            writeTableHead("Reference files analyzed");
+            writeList(refTables.files);
+            writeTable(refTables.getStatistics());
+//            writeTable(REF_DELTA_LOCNS, count(locnDeltas));
+        }
+
+        if (showLocations) {
+            Map<Message.Location,Integer> locationCountMap = count(t.pathTable);
+            writeTable(LOCN_COUNTS_ALPHA, locationCountMap);
+            writeTable(LOCN_COUNTS_FREQ, flip(locationCountMap, decreasing));
+
+            Map<String,Integer> locationDirCountMap = count(byDirectory(t.pathTable));
+            writeTable(LOCN_DIR_COUNTS_ALPHA, locationDirCountMap);
+            writeTable(LOCN_DIR_COUNTS_FREQ, flip(locationDirCountMap, decreasing));
+
+            Map<String,Integer> locationExtnCountMap = count(byExtension(t.pathTable));
+            writeTable(LOCN_EXTN_COUNTS_ALPHA, locationExtnCountMap);
+            writeTable(LOCN_EXTN_COUNTS_FREQ, flip(locationExtnCountMap, decreasing));
+
+            Collection<Message> unknownLocns = t.pathTable.get(Message.Location.UNKNOWN);
+            if (unknownLocns != null)
+                writeList(LOCN_UNKNOWN, unknownLocns);
+        }
+
+        if (showTools) {
+            Map<Tool,Integer> toolCountMap = count(t.toolTable);
+            writeTable(TOOL_COUNTS_ALPHA, toolCountMap);
+            writeTable(TOOL_COUNTS_FREQ, flip(toolCountMap, decreasing));
+        }
+
+        if (showKinds) {
+            Map<Message.Kind,Integer> typeCountMap = count(t.messageKindTable);
+            writeTableRows(KIND_COUNTS_ALPHA, getKindRows1(typeCountMap.entrySet()));
+            writeTableRows(KIND_COUNTS_FREQ, getKindRows2(flip(typeCountMap, decreasing)));
+
+            Collection<Message> unknownTypes = t.messageKindTable.get(Message.Kind.UNKNOWN);
+            if (unknownTypes != null)
+                writeList(KIND_UNKNOWN, unknownTypes);
+        }
+
+        endReport();
+    }
+
+    private Collection<? extends Collection<?>> getKindRows1(Collection<? extends Map.Entry<Message.Kind,Integer>> entries) throws IOException {
+        List<List<Object>> rows = new ArrayList<List<Object>>();
+        for (Map.Entry<Message.Kind,Integer> e: entries) {
+            Message.Kind kind = e.getKey();
+            Integer count = e.getValue();
+            List<Object> row = Arrays.asList((Object)kind.tool.name, trimPattern(kind.msgPattern), count);
+            rows.add(row);
+        }
+        return rows;
+    }
+
+    private Collection<? extends Collection<?>> getKindRows2(Collection<? extends Map.Entry<Integer,Message.Kind>> entries) throws IOException {
+        List<List<?>> rows = new ArrayList<List<?>>();
+        for (Map.Entry<Integer,Message.Kind> e: entries) {
+            Integer count = e.getKey();
+            Message.Kind kind = e.getValue();
+            List<Object> row = Arrays.asList(count, (Object)kind.tool.name, trimPattern(kind.msgPattern));
+            rows.add(row);
+        }
+        return rows;
+    }
+
+    String trimPattern(Pattern p) {
+        String s = p.pattern();
+        int start = (s.startsWith(".*") ? 2 : 0);
+        int end = s.length() - (s.endsWith(".*") ? 2 : 0);
+        return s.substring(start, end);
+    }
+
+    protected void startReport() throws IOException { }
+
+    protected void endReport() throws IOException { }
+
+    protected <T> void writeList(TableType type, Collection<T> list) throws IOException {
+        writeTableHead(type);
+        writeList(list);
+    }
+
+    protected abstract <T> void writeList(Collection<T> list) throws IOException;
+
+    protected void writeTable(TableType type, Map<?,?> table) throws IOException {
+        writeTable(type, table.entrySet());
+    }
+
+    protected void writeTable(TableType type, Collection<? extends Map.Entry<?,?>> entries) throws IOException {
+        writeTableHead(type);
+        writeTableRows(getRows(entries));
+    }
+
+    protected void writeTableRows(TableType type, Collection<? extends Collection<?>> rows) throws IOException {
+        writeTableHead(type);
+        writeTableRows(rows);
+    }
+
+    protected void writeTable(Map<?,?> table) throws IOException {
+        writeTableRows(getRows(table.entrySet()));
+    }
+
+    private Collection<? extends Collection<?>> getRows(Collection<? extends Map.Entry<?,?>> entries) throws IOException {
+        List<List<?>> rows = new ArrayList<List<?>>();
+        for (Map.Entry<?,?> e: entries) {
+            rows.add(Arrays.asList(e.getKey(), e.getValue()));
+        }
+        return rows;
+    }
+
+    protected abstract void writeTableHead(String s) throws IOException;
+    protected abstract void writeTableHead(TableType type) throws IOException;
+//    protected abstract void writeTable(Collection<? extends Map.Entry<?,?>> entries) throws IOException;
+    protected abstract void writeTableRows(Collection<? extends Collection<?>> rows) throws IOException;
+
+    Map<String,Collection<String>> getDifference(
+            Map<String, Collection<String>> a,
+            Map<String, Collection<String>> b) {
+        Map<String,Collection<String>> results = new LinkedHashMap<String,Collection<String>>();
+        for (Map.Entry<String,Collection<String>> e: a.entrySet()) {
+            Collection<String> d = getDifference(e.getValue(), b.get(e.getKey()));
+            if (d != null)
+                results.put(e.getKey(), d);
+        }
+        return results;
+    }
+
+    /* return strings in a, which are not in b, allowing for differences in coord info */
+    Collection<String> getDifference(Collection<String> a, Collection<String> b) {
+        if (b == null)
+            return a;
+
+        /* create a normalized version of b, with all digits removed. */
+        Collection<String> nb = new HashSet<String>();
+        for (String s: b)
+            nb.add(s.replaceAll("[0-9]+", ""));
+
+        /* build list by comparing normalized strings in a against normalized set b */
+        Collection<String> results = null;
+        for (String s: a) {
+            if (!nb.contains(s.replaceAll("[0-9]+", ""))) {
+                if (results == null)
+                    results = new ArrayList<String>();
+                results.add(s);
+            }
+        }
+
+        return results;
+    }
+
+//    Collection<String> getDifference(Collection<String> a, Collection<String> b) {
+//        if (b == null)
+//            return a;
+//
+//        Collection<String> results = null;
+//    nextEntry:
+//        for (String ae : a) {
+//            if (!containsIgnoreLine(b, ae)) {
+//                if (results == null)
+//                    results = new ArrayList<String>();
+//                results.add(ae);
+//            }
+//        }
+//        return results;
+//    }
+//
+//    boolean containsIgnoreLine(Collection<String> c, String s) {
+//        if (c.contains(s))
+//            return true;
+//        String sNoLine = removeLine(s);
+//        for (String cs: c) {
+//            if (removeLine(cs).equals(sNoLine))
+//                return true;
+//        }
+//        return false;
+//    }
+//
+//    String removeLine(String warning) {
+//        Matcher m = ignoreLine.matcher(warning);
+//        if (m.matches())
+//            return m.group(1) + m.group(2);
+//        else
+//            return warning;
+//    }
+//
+//    private final Pattern ignoreLine = Pattern.compile("([^:]+:)[0-9:]+:(.*)");
+
+    <T> Map<String,Collection<T>> byExtension(Map<Message.Location,Collection<T>> map) {
+        Map<String,Collection<T>> results = new TreeMap<String,Collection<T>>();
+        for (Map.Entry<Message.Location,Collection<T>> e: map.entrySet()) {
+            String extn = e.getKey().getExtension();
+            Collection<T> dest = results.get(extn);
+            if (dest == null) {
+                dest = new LinkedHashSet<T>();
+                results.put(extn, dest);
+            }
+            dest.addAll(e.getValue());
+        }
+        return results;
+    }
+
+    <T> Map<String,Collection<T>> byDirectory(Map<Message.Location,Collection<T>> map) {
+        Map<String,Collection<T>> results = new TreeMap<String,Collection<T>>();
+        for (Map.Entry<Message.Location,Collection<T>> e: map.entrySet()) {
+            String dir = e.getKey().getPathDirectory();
+            Collection<T> dest = results.get(dir);
+            if (dest == null) {
+                dest = new ArrayList<T>();
+                results.put(dir, dest);
+            }
+            dest.addAll(e.getValue());
+        }
+        return results;
+    }
+
+    <K> Map<K, Integer> count(Map<K,? extends Collection<?>> map) {
+        Map<K,Integer> results = new TreeMap<K,Integer>();
+        for (Map.Entry<K,? extends Collection<?>> e: map.entrySet())
+            results.put(e.getKey(), e.getValue().size());
+        return results;
+    }
+
+    <K,V> Collection<? extends Map.Entry<V,K>> flip(Map<K,V> map, Comparator<Map.Entry<V,?>> c) {
+        List<Map.Entry<V,K>> result = new ArrayList<Map.Entry<V,K>>();
+        for (Map.Entry<K,V> e: map.entrySet())
+            result.add(new SimpleMapEntry<V,K>(e.getValue(), e.getKey()));
+        Collections.sort(result, c);
+        return result;
+    }
+
+    protected static Comparator<Map.Entry<Integer,?>> decreasing = new Comparator<Map.Entry<Integer,?>>() {
+        public int compare(Map.Entry<Integer, ?> o1, Map.Entry<Integer, ?> o2) {
+            int i1 = o1.getKey();
+            int i2 = o2.getKey();
+            return (i1 > i2 ? -1 : i1 == i2 ? 0 : 1);
+        }
+    };
+
+    protected static class SimpleMapEntry<K,V> implements Map.Entry<K,V> {
+        SimpleMapEntry(K k, V v) {
+            key = k;
+            value = v;
+        }
+
+        public K getKey() {
+            return key;
+        }
+
+        public V getValue() {
+            return value;
+        }
+
+        public V setValue(V value) {
+            throw new UnsupportedOperationException();
+        }
+
+        final K key;
+        final V value;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buildLogWarnSummary/SimpleReporter.java	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. 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.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package buildLogWarnSummary;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Collection;
+
+/**
+ *
+ * @author jjg
+ */
+public class SimpleReporter extends Reporter {
+    @Override
+    protected void startReport() throws IOException {
+        if (outFile == null)
+            out = new PrintWriter(new OutputStreamWriter(System.out));
+        else {
+            if (outFile.isDirectory())
+                outFile = new File(outFile, "report.txt");
+            out = new PrintWriter(new BufferedWriter(new FileWriter(outFile)));
+        }
+    }
+
+    @Override
+    protected void endReport() throws IOException {
+        out.flush();
+        if (outFile != null)
+            out.close();
+    }
+
+    @Override
+    protected void writeTableHead(String head) throws IOException {
+        out.println(head);
+    }
+
+    @Override
+    protected void writeTableHead(TableType type) throws IOException {
+        out.println(type.title);
+    }
+
+//    @Override
+//    protected void writeTable(Collection<? extends Map.Entry<?,?>> entries) {
+//        if (entries.isEmpty())
+//            out.println("(No entries)");
+//        else {
+//            for (Map.Entry<?,?> e: entries) {
+//                out.println(e.getKey() + ": " + e.getValue());
+//            }
+//        }
+//    }
+
+    @Override
+    protected void writeTableRows(Collection<? extends Collection<?>> rows) {
+        if (rows.isEmpty())
+            out.println("(No entries)");
+        else {
+            for (Collection<?> row: rows) {
+                String sep = "";
+                for (Object item: row) {
+                    out.print(sep);
+                    out.print(item);
+                    if (sep.isEmpty())
+                        sep = ": ";
+                    else if (sep.equals(": "))
+                        sep = ", ";
+                }
+                out.println();
+            }
+        }
+    }
+
+    @Override
+    protected <T> void writeList(Collection<T> list) throws IOException {
+        for (T t: list)
+            out.println(t);
+    }
+
+    PrintWriter out;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buildLogWarnSummary/Tables.java	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. 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.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package buildLogWarnSummary;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import static buildLogWarnSummary.Messages.*;
+
+public final class Tables {
+    public Tables() { }
+
+    public Tables(Iterable<File> files) throws IOException {
+        for (File f: files) {
+            this.files.add(f);
+            read(f);
+        }
+    }
+
+    boolean isEmpty() {
+        return files.isEmpty();
+    }
+
+    Map<String,Integer> getStatistics() {
+        Map<String,Integer> stats = new LinkedHashMap<String,Integer>();
+        stats.put("total lines read", lines);
+        stats.put("total warnings found", warnings);
+        stats.put("unique warnings found", uniqueWarnings.size());
+        return stats;
+    }
+
+    Map<Message.Kind, Collection<Message>> getMessageTable() {
+        return messageKindTable;
+    }
+
+    public void read(File f) throws IOException {
+        System.err.println("read " + f);
+        BufferedReader in = new BufferedReader(new FileReader(f));
+        try {
+            read(in);
+        } finally {
+            in.close();
+        }
+    }
+
+    public void read(BufferedReader in) throws IOException {
+        String line;
+        while ((line = in.readLine()) != null) {
+            readLine(line);
+            if (unmatchedMessages + unmatchedLocations > 100)
+                return;
+        }
+    }
+
+    void readLine(String line) {
+        lines++;
+
+        if (!msgs.isWarning(line))
+            return;
+
+        warnings++;
+        uniqueWarnings.add(line);
+
+        Message m = msgs.getMessage(line);
+        if (m == null) {
+            System.err.println("unmatched message: " + line);
+            unmatchedMessages++;
+            m = new Message(Message.Kind.UNKNOWN, line);
+        }
+
+        Message.Kind kind = m.kind;
+        add(messageKindTable, kind, m);
+
+        if (kind.hasLocation()) {
+            if (m.location == null) {
+                System.err.println("unmatched location: " + line);
+                unmatchedLocations++;
+            } else {
+                add(pathTable, m.location, m);
+            }
+        }
+
+        add(toolTable, kind.tool, m);
+    }
+
+    <T> void add(Map<T, Collection<Message>> map, T t, Message m) {
+        Collection<Message> c = map.get(t);
+        if (c == null)
+            map.put(t, c = new HashSet<Message>());
+        c.add(m);
+    }
+
+    int lines;
+    int warnings;
+    int unmatchedLocations;
+    int unmatchedMessages;
+
+    List<File> files = new ArrayList<File>();
+    Set<String> uniqueWarnings = new TreeSet<String>();
+
+    Map<Message.Kind, Collection<Message>> messageKindTable =
+            new TreeMap<Message.Kind, Collection<Message>>();
+
+    Map<Message.Location, Collection<Message>> pathTable =
+            new TreeMap<Message.Location, Collection<Message>>();
+
+    Map<Tool, Collection<Message>> toolTable =
+            new TreeMap<Tool, Collection<Message>>();
+
+    Messages msgs = new Messages();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/javacLintSummary/Main.java	Fri Jun 21 16:34:42 2013 -0700
@@ -0,0 +1,790 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. 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.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javacLintSummary;
+
+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.Collection;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+import javax.lang.model.SourceVersion;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+import com.sun.source.util.JavacTask;
+import com.sun.tools.doclint.DocLint;
+import com.sun.tools.doclint.Messages;
+import com.sun.tools.javac.api.ClientCodeWrapper;
+import com.sun.tools.javac.util.JCDiagnostic;
+
+/**
+ * Simple program that prints a summary of how many of each type of warning
+ * are generated in files and packages.
+ *
+ * Usage:
+ *  java -jar javacLintSummary.jar options files-or-packages
+ *
+ * Options:
+ *  -htmlDir dir        specifies directory for HTML (no HTML if not provided)
+ *  -xml file           specifies XML output file (no XML if not provided)
+ *  -title string       plain text title for report
+ *  -bootclasspath, -classpath, -sourcepath
+ *                      all as for javac
+ *  -Xlint -Xlint:opts -Xdoclint and -Xdoclint:opts
+ *                      all as for javac; default is -Xlint
+ *
+ * Files-or-packages
+ *  Files are recognized by ending in .java
+ *  Packages can be given as p.q or p.q.**.  They are expanded using the
+ *  value of -sourcepath. p.q expands to the set of compilation units in
+ *  package p.q; p.q.** expands to the set of compilation units in package
+ *  p.q and all subpackages.  Take care to quote the wildcard form when
+ *  necessary, e.g. on a shell command line.
+ *
+ * @author jjg
+ */
+public class Main {
+
+    /**
+     * Command-line entry point.
+     * @param args command line args
+     */
+    public static void main(String[] args) {
+        try {
+            Main m = new Main();
+            boolean ok = m.run(args);
+            if (!ok)
+                System.exit(1);
+        } catch (IOException e) {
+            System.err.println("IO error: " + e);
+            System.exit(2);
+        }
+    }
+
+    public void usage(PrintWriter out) {
+        out.println(Main.class.getPackage().getName() + ":");
+        out.println("  Prints a summary of how many of each type of lint or doclint");
+        out.println("  message are generated in the given files and packages.");
+        out.println();
+        out.println("Usage:");
+        out.println("  java -jar javacLintSummary.jar options files-or-packages");
+        out.println();
+        out.println("Options:");
+        out.println("  -bootclasspath, -classpath, -sourcepath");
+        out.println("                     all as for javac");
+        out.println("  -Xlint -Xlint:opts -Xdoclint and -Xdoclint:opts ");
+        out.println("                     all as for javac; default is -Xlint");
+        out.println("  -htmlDir dir       specifies directory for HTML report (no HTML if not provided)");
+        out.println("  -xml file          specifies file for XML report (no XML if not provided)");
+        out.println("  -title string      plain text title for report");
+        out.println();
+        out.println("Files-or-packages");
+        out.println("  Files are recognized by ending in .java");
+        out.println("  Packages can be given as p.q or p.q.**.  They are expanded using the");
+        out.println("  value of -sourcepath. p.q expands to the set of compilation units in");
+        out.println("  package p.q; p.q.** expands to the set of compilation units in package");
+        out.println("  p.q and all subpackages.  Take care to quote the wildcard form when");
+        out.println("  necessary, e.g. on a shell command line.");
+    }
+
+    /**
+     * API entry point.
+     * @param args command line args
+     * @return true if operation completed successfully
+     * @throws IOException if an IO error occurs during execution
+     */
+    public boolean run(String... args) throws IOException {
+        PrintWriter out = new PrintWriter(System.out);
+        try {
+            return run(out, args);
+        } finally {
+            out.flush();
+        }
+    }
+
+    /**
+     * API entry point.
+     * @param args command line args
+     * @return true if operation completed successfully
+     * @throws IOException if an IO error occurs during execution
+     */
+    public boolean run(PrintWriter out, String... args) throws IOException {
+
+        processArgs(args);
+        if (errors > 0)
+            return false;
+
+        if (help) {
+            usage(out);
+            return true;
+        }
+
+        JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
+        StandardJavaFileManager fm = javac.getStandardFileManager(null, null, null);
+        if (bootclasspath != null)
+            fm.setLocation(StandardLocation.PLATFORM_CLASS_PATH, pathToFiles(bootclasspath));
+        if (classpath != null)
+            fm.setLocation(StandardLocation.CLASS_PATH, pathToFiles(classpath));
+        if (sourcepath != null)
+            fm.setLocation(StandardLocation.SOURCE_PATH, pathToFiles(sourcepath));
+        List<? extends JavaFileObject> files = getFiles(fm, items);
+//        fm.setLocation(StandardLocation.SOURCE_PATH, null);
+        List<String> opts = new ArrayList<>();
+        if (lintOpts.isEmpty())
+            opts.add("-Xlint:all");
+        else
+            opts.addAll(lintOpts);
+        opts.addAll(Arrays.asList("-Xmaxerrs", "9999"));
+        opts.addAll(Arrays.asList("-Xmaxwarns", "9999"));
+        Table t =  new Table(fm, files);
+        out.println("Analyzing " + files.size() + " files");
+        JavacTask task = (JavacTask) javac.getTask(null, fm, t, opts, null, files);
+        fixupDocLint(task, opts);
+        task.analyze();
+
+        new SimpleTableWriter().write(title, t, out);
+        if (htmlDir != null)
+            new HtmlTableWriter().write(title, t, htmlDir, args);
+        if (xmlFile != null)
+            new XMLTableWriter().write(title, t, xmlFile, args);
+        return true;
+    }
+
+    void fixupDocLint(JavacTask t, List<String> javacOpts) {
+        boolean doclint = false;
+        List<String> docLintOpts = new ArrayList<>();
+        for (String opt: javacOpts) {
+            if (opt.startsWith("-Xdoclint")) {
+                if (!opt.equals("-Xdoclint:none"))
+                    doclint = true;
+                docLintOpts.add(opt.replace("-Xdoclint", "-Xmsgs"));
+            }
+        }
+
+        if (doclint) {
+            // standard doclet normally generates H1, H2
+            docLintOpts.add(DocLint.XIMPLICIT_HEADERS + "2");
+            DocLint l = new DocLint();
+            l.init(t, docLintOpts.toArray(new String[docLintOpts.size()]), true);
+        }
+    }
+
+    /**
+     * Expand the set of command line items into a list of JavaFileObjects.
+     * @param fm the file manager to use
+     * @param items a list of command line items to be expanded
+     * @return a list of JavaFileObjects
+     * @throws IOException if an error occurs
+     */
+    List<JavaFileObject> getFiles(StandardJavaFileManager fm, List<String> items)
+            throws IOException {
+        List<JavaFileObject> files = new ArrayList<>();
+        for (String item: items) {
+            if (item.endsWith(".java"))
+                addAll(files, fm.getJavaFileObjects(item));
+            else {
+                boolean recursive = false;
+                if (item.endsWith(".**")) {
+                    item = item.substring(0, item.length() - 3);
+                    recursive=true;
+                }
+                addAll(files, fm.list(StandardLocation.SOURCE_PATH,
+                        item,
+                        EnumSet.of(JavaFileObject.Kind.SOURCE),
+                        recursive));
+            }
+        }
+        //System.err.println("files: " + files.size() + " " + files);
+        return files;
+    }
+
+    /**
+     * Utility method to add all members of an iterable to a Collection.
+     * @param <T> The type of the each item
+     * @param dest The collection to which to add the items
+     * @param items The source of items to be added to the collection
+     */
+    static <T> void addAll(Collection<T> dest, Iterable<? extends T> items) {
+        for (T item: items)
+            dest.add(item);
+    }
+
+    /**
+     * Convert a path option to a list of files, ignoring entries
+     * which do not exist or cannot be read.
+     * @param path the path value to be split
+     * @return a list of files
+     */
+    List<File> pathToFiles(String path) {
+        List<File> files = new ArrayList<>();
+        for (String p: path.split(File.pathSeparator)) {
+            File f = new File(p);
+            if (f.canRead())
+                files.add(f);
+        }
+        return files;
+    }
+
+    /**
+     * Process command-line arguments.
+     * @param args the arguments to be processed
+     */
+    void processArgs(String... args) {
+        if (args.length == 0)
+            help = true;
+
+        for (int i = 0; i < args.length; i++) {
+            String arg = args[i];
+            if (arg.equals("-bootclasspath") && i + 1 < args.length)
+                bootclasspath = args[++i];
+            else if (arg.equals("-classpath") && i + 1 < args.length)
+                classpath = args[++i];
+            else if (arg.equals("-sourcepath") && i + 1 < args.length)
+                sourcepath = args[++i];
+            else if (arg.startsWith("-Xlint") || arg.startsWith("-Xdoclint"))
+                lintOpts.add(arg);
+            else if (arg.equals("-html") && i + 1 < args.length)
+                htmlDir = new File(args[++i]);
+            else if (arg.equals("-xml") && i + 1 < args.length)
+                xmlFile = new File(args[++i]);
+            else if (arg.equals("-title") && i + 1 < args.length)
+                title = args[++i];
+            else if (arg.matches("-h|-help|--help"))
+                help = true;
+            else if (arg.startsWith("-"))
+                error("Unrecognized option: " + arg);
+            else if (arg.endsWith(".java") && new File(arg).exists())
+                items.add(arg);
+            else if (SourceVersion.isName(arg)
+                    || arg.endsWith(".**") && SourceVersion.isName(arg.substring(0, arg.length() - 3)))
+                items.add(arg);
+            else
+                error("Unrecognized argument: " + arg);
+        }
+
+        if (htmlDir != null) {
+            if (htmlDir.exists()) {
+                if (!(htmlDir.isDirectory() && htmlDir.canWrite()))
+                    error("bad HTML directory");
+            } else {
+                if (!htmlDir.mkdirs())
+                    error("Could not create HTML output directory");
+            }
+        }
+
+        if (title == null)
+            title = "Lint Report";
+    }
+
+    /**
+     * Record an error message.
+     * @param msg the message
+     */
+    void error(String msg) {
+        System.err.println(msg);
+        errors++;
+    }
+
+    boolean help;
+    String bootclasspath;
+    String classpath;
+    String sourcepath;
+    Set<String> lintOpts = new LinkedHashSet<>();
+    List<String> items = new ArrayList<>();
+    File htmlDir;
+    File xmlFile;
+    String title;
+
+    /** The number of errors that have been reported. */
+    int errors;
+
+    /**
+     * Main data structure recording the number of each type of warning
+     * encountered in each package.
+     */
+    static class Table implements DiagnosticListener<JavaFileObject> {
+
+        static class Row {
+            Map<String, Cell> counts = new HashMap<>();
+            void inc(String group, JCDiagnostic d) {
+                Cell c = counts.get(group);
+                if (c == null) counts.put(group, c = new Cell());
+                c.count++;
+                if (d != null) {
+                    if (c.diags == null) c.diags = new ArrayList<>();
+                    c.diags.add(d);
+                }
+            }
+            int getCount(String group) {
+                Cell c = counts.get(group);
+                return c == null ? 0 : c.count;
+            }
+            List<JCDiagnostic> getDiags(String group) {
+                Cell c = counts.get(group);
+                return c == null ? null : c.diags;
+            }
+        }
+
+        static class Cell {
+            int count;
+            List<JCDiagnostic> diags;
+        }
+
+        JavaFileManager fm;
+        Set<JavaFileObject> files;
+        Set<String> headings = new TreeSet<>();
+        Map<String, Row> map = new TreeMap<>();
+        Row totals = new Row();
+        Map<Diagnostic.Kind, Integer> kindCounts
+                = new EnumMap<>(Diagnostic.Kind.class);
+        DocLintManager docLintManager = new DocLintManager();
+
+        Table(JavaFileManager fm, Collection<? extends JavaFileObject> files) {
+            this.fm = fm;
+            this.files = new HashSet<>(files);
+        }
+
+        void inc(String pkg, String group, JCDiagnostic d) {
+            Row row = map.get(pkg);
+            if (row == null)
+                map.put(pkg, row = new Row());
+            headings.add(group);
+            row.inc(group, d);
+            totals.inc(group, d);
+        }
+
+        @Override
+        public void report(Diagnostic<? extends JavaFileObject> d) {
+            if (d.getCode().equals("compiler.warn.sun.proprietary"))
+                return;
+
+            if (!files.contains(d.getSource()))
+                return;
+
+            Integer kc = kindCounts.get(d.getKind());
+            kindCounts.put(d.getKind(), kc == null ? 1 : kc + 1);
+
+            JCDiagnostic jd = ((ClientCodeWrapper.DiagnosticSourceUnwrapper) d).d;
+            String group;
+            if (jd.getLintCategory() != null) {
+                group = jd.getLintCategory().toString().toLowerCase();
+            } else if (docLintManager.isDocLintMessage(jd)) {
+                group = docLintManager.getGroup(jd);
+            } else {
+                switch (d.getKind()) {
+                    case ERROR:
+                        System.err.println(d.getSource().getName() + ": " + d.getMessage(null));
+                        return;
+                    case NOTE:
+                        return;
+                }
+                group = "default";
+            }
+
+            JavaFileObject f = d.getSource();
+            String binaryName = fm.inferBinaryName(StandardLocation.SOURCE_PATH, f);
+            int lastDot = binaryName.lastIndexOf(".");
+            String pkgName = (lastDot == -1) ? "" : binaryName.substring(0, lastDot);
+            //System.err.println(d.getCode() + " " + binaryName + " " + pkgName + " " + d.getMessage(null));
+            inc(pkgName, group, jd);
+        }
+    }
+
+    /**
+     * Write out results in plain text, for easy reporting to the console.
+     */
+    static class SimpleTableWriter {
+        Table t;
+
+        void write(String title, Table table, PrintWriter out) {
+            t = table;
+
+            if (title != null)
+                System.err.println(title);
+
+            if (t.map.isEmpty()) {
+                System.err.println("No warnings found");
+                return;
+            }
+
+            writeHeadings(out);
+            for (Map.Entry<String, Table.Row> e: t.map.entrySet()) {
+                writeRow(out, e.getKey(), e.getValue());
+            }
+            writeRow(out, "(total)", t.totals);
+
+            out.println();
+            String sep = "";
+            for (Diagnostic.Kind k: Diagnostic.Kind.values()) {
+                Integer count = t.kindCounts.get(k);
+                if (count != null) {
+                    out.print(sep + k.toString().toLowerCase() + ": " + count);
+                    sep = ", ";
+                }
+            }
+            out.println();
+        }
+
+        void writeHeadings(PrintWriter out) {
+            int col1w = 0;
+            for (String pkg: t.map.keySet())
+                col1w = Math.max(col1w, pkg.length());
+            col1f = "%-" + col1w + "s";
+            out.print(String.format(col1f, ""));
+            for (String w: t.headings) {
+                String head = w.length() <= 8 ? w : w.substring(0, 8);
+                out.print(String.format(" %8s", head));
+            }
+            out.print(String.format(" %8s", "(total)"));
+            out.println();
+        }
+
+        void writeRow(PrintWriter out, String pkg, Table.Row row) {
+            int total = 0;
+            out.print(String.format(col1f, pkg));
+            for (String w: t.headings) {
+                int v = row.getCount(w);
+                out.print(String.format(" %8d", v));
+                total += v;
+            }
+            out.print(String.format(" %8d", total));
+            out.println();
+        }
+
+        String col1f;
+    }
+
+    /**
+     * Write out results in HTML, for easy browsing.
+     */
+    static class HtmlTableWriter {
+        File htmlDir;
+        Table table;
+
+        void write(String title, Table table, File htmlDir, String[] args) throws IOException {
+            this.table = table;
+            this.htmlDir = htmlDir;
+
+            File indexFile = new File(htmlDir, "index.html");
+            try (PrintWriter index = new PrintWriter(new FileWriter(indexFile))) {
+                writeFile(title, index, args);
+            }
+        }
+
+        void writeFile(String title, PrintWriter out, String[] args) {
+            out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">");
+            out.println("<html>");
+            out.println("<head>");
+            out.println("<title>" + escape(title) + "</title>");
+            out.println("<style type=\"text/css\">");
+            out.println("table { border: 1px solid black; border-collapse:collapse }");
+            out.println("td { font-family: monospace; padding: 3px 6px }");
+            out.println("td.num { text-align:right }");
+            out.println("td.pkg { border-right: solid black 1px }");
+            out.println("td.total { text-align:right; border-left: 1px solid black }");
+            out.println("th { background-color: lightgray; border: 1px solid black; padding: 3px 6px }");
+            out.println("tr.odd { background-color:white }");
+            out.println("tr.even { background-color:#f0f0f0 }");
+            out.println("tr.total { background-color:white; border-top: 1px solid black }");
+            out.println("</style>");
+            out.println("</head>");
+            out.println("<body>");
+            out.println("<h1>" + escape(title) + "</h1>");
+            out.print("<p><b>Args:</b><span style=\"font-family:monospace\"> ");
+            for (int i = 0; i < Math.min(args.length, 32); i++) {
+                out.print(escape(args[i]));
+                out.print(" ");
+            }
+            if (args.length > 32)
+                out.print("...");
+            out.println();
+            out.println("</span></p>");
+            if (table.map.isEmpty()) {
+                out.println("<span style=\"padding:3px; background-color: palegreen; color: green; font-size:larger\">");
+                out.println("No warnings found");
+                out.println("</span>");
+            } else {
+                out.println("<table>");
+                writeHeadings(out);
+                for (Map.Entry<String, Table.Row> e: table.map.entrySet()) {
+                    writeRow(out, e.getKey(), e.getValue());
+                }
+                writeRow(out, "(total)", table.totals);
+                out.println("</table>");
+                out.println("<p>");
+                String sep = "";
+                for (Diagnostic.Kind k: Diagnostic.Kind.values()) {
+                    Integer count = table.kindCounts.get(k);
+                    if (count != null) {
+                        out.print(sep + k.toString().toLowerCase() + ": " + count);
+                        sep = ", ";
+                    }
+                }
+                out.println();
+                out.println("</p>");
+            }
+            out.println("</body>");
+            out.println("</html>");
+
+        }
+
+        void writeHeadings(PrintWriter out) {
+            out.print("<tr class=\"head\">");
+            out.print("<th></th>");
+            for (String w: table.headings) {
+                out.print("<th>" + w + "</th>");
+            }
+            out.print("<th style=\"border-left: 1px solid black\">(total)</th>");
+            out.println("</tr>");
+        }
+
+        void writeRow(PrintWriter out, String pkg, Table.Row row) {
+            String c = pkg.equals("(total)") ? "total" : (rowNo++ % 2 == 0) ? "even" : "odd";
+            out.print("<tr class=\"" + c + "\">");
+            int total = 0;
+            out.print("<td class=\"pkg\">" + pkg + "</td>");
+            for (String g: table.headings) {
+                int v = row.getCount(g);
+                out.print("<td class=\"num\">");
+                if (v != 0) {
+                    File list = writeDiags(pkg, g, row.getDiags(g));
+                    if (list == null)
+                        out.print(v);
+                    else
+                        out.print("<a href=\"" + list.getName() + "\">" + v + "</a>");
+                }
+                out.print("</td>");
+                total += v;
+            }
+            out.print("<td class=\"total\">" + total + "</td>");
+            out.println();
+            out.println("</tr>");
+        }
+
+        int rowNo = 0;
+
+        File writeDiags(String pkg, String group, List<JCDiagnostic> diags) {
+            if (diags == null)
+                return null;
+
+            File txt = new File(htmlDir, pkg.replaceAll("[^A-Za-z0-9_$]+", "_") + "-" + group + ".txt");
+            try (PrintWriter out = new PrintWriter(new FileWriter(txt))) {
+                for (JCDiagnostic d : diags)
+                    out.println(d);
+            } catch (IOException e) {
+                return null;
+            }
+            return txt;
+        }
+
+        String escape(String text) {
+            return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&lt;");
+        }
+    }
+
+    /**
+     * Write out results in XML, for use by Hudson/Jenkins Plot Plugin.
+     */
+    static class XMLTableWriter {
+        void write(String title, Table table, File xmlFile, String[] args) throws IOException {
+            try (PrintWriter out = new PrintWriter(new FileWriter(xmlFile))) {
+                out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
+                out.println("<javacLintSummary>");
+                out.println("<title>" + escape(title) + "</table>");
+                out.println("<args>");
+                for (String arg: args) {
+                    out.println("<arg>" + escape(arg) + "</arg>");
+                }
+                out.println("</args>");
+                for (Map.Entry<String, Table.Row> e: table.map.entrySet()) {
+                    String pkg = e.getKey();
+                    Table.Row row = e.getValue();
+                    out.println("<package name=\"" + pkg + "\">");
+                    for (String g: table.headings) {
+                        out.println("<group name=\"" + g + "\">" + row.getCount(g) + "</group>");
+                    }
+                    out.println("</package>");
+                }
+                out.println("</javacLintSummary>");
+            }
+        }
+
+        String escape(String s) {
+            return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
+        }
+    }
+
+    /**
+     * This class reverse engineers the doclint message group from the message.
+     * You cannot (currently) get at the original unlocalized info, so we have
+     * to pattern match against the messages in the resource bundles. Uugh.
+     */
+    static class DocLintManager {
+        private static final String ACCESSIBILITY = Messages.Group.ACCESSIBILITY.name().toLowerCase();
+        private static final String HTML = Messages.Group.HTML.name().toLowerCase();
+        private static final String MISSING = Messages.Group.MISSING.name().toLowerCase();
+        private static final String REFERENCE = Messages.Group.REFERENCE.name().toLowerCase();
+        private static final String SYNTAX = Messages.Group.SYNTAX.name().toLowerCase();
+
+        private static final String DOCLINT_MAIN = "doclint:main"; // should not happen
+
+        final Map<String,String> strings = new HashMap<>();
+        final Map<Pattern,String> patterns = new HashMap<>();
+
+        DocLintManager() {
+            // Currently, it is not possible to easily identify the group from
+            // the name of the diag key, so for now, we use manually constructed tables.
+
+            ResourceBundle rb = ResourceBundle.getBundle("com.sun.tools.doclint.resources.doclint");
+            if (rb == null) {
+                System.err.println("Warning: Cannot locate doclint resource bundle");
+            } else {
+                add(rb, "dc.anchor.already.defined", HTML);
+                add(rb, "dc.anchor.value.missing", HTML);
+                add(rb, "dc.attr.lacks.value", HTML);
+                add(rb, "dc.attr.not.number", HTML);
+                add(rb, "dc.attr.obsolete", ACCESSIBILITY);
+                add(rb, "dc.attr.obsolete.use.css", ACCESSIBILITY);
+                add(rb, "dc.attr.repeated", HTML);
+                add(rb, "dc.attr.unknown", HTML);
+                add(rb, "dc.bad.option", DOCLINT_MAIN);
+                add(rb, "dc.bad.value.for.option", DOCLINT_MAIN);
+                add(rb, "dc.empty", SYNTAX);
+                add(rb, "dc.entity.invalid", HTML);
+                add(rb, "dc.exception.not.thrown", REFERENCE);
+                add(rb, "dc.invalid.anchor", HTML);
+                add(rb, "dc.invalid.param", REFERENCE);
+                add(rb, "dc.invalid.return", REFERENCE);
+                add(rb, "dc.invalid.throws", REFERENCE);
+                add(rb, "dc.invalid.uri", HTML);
+                add(rb, "dc.no.alt.attr.for.image", ACCESSIBILITY);
+                add(rb, "dc.no.summary.or.caption.for.table", ACCESSIBILITY);
+                add(rb, "dc.main.ioerror", DOCLINT_MAIN);
+                add(rb, "dc.main.no.files.given", DOCLINT_MAIN);
+                add(rb, "dc.main.usage", DOCLINT_MAIN);
+                add(rb, "dc.missing.comment", MISSING);
+                add(rb, "dc.missing.param", MISSING);
+                add(rb, "dc.missing.return", MISSING);
+                add(rb, "dc.missing.throws", MISSING);
+                add(rb, "dc.param.name.not.found", REFERENCE);
+                add(rb, "dc.ref.not.found", REFERENCE);
+                add(rb, "dc.tag.code.within.code", HTML);
+                add(rb, "dc.tag.empty", HTML);
+                add(rb, "dc.tag.end.not.permitted", HTML);
+                add(rb, "dc.tag.end.unexpected", HTML);
+                add(rb, "dc.tag.header.sequence.1", ACCESSIBILITY);
+                add(rb, "dc.tag.header.sequence.2", ACCESSIBILITY);
+                add(rb, "dc.tag.nested.not.allowed", HTML);
+                add(rb, "dc.tag.not.allowed.here", HTML);
+                add(rb, "dc.tag.not.allowed", HTML);
+                add(rb, "dc.tag.not.allowed.inline.element", HTML);
+                add(rb, "dc.tag.not.allowed.inline.tag", HTML);
+                add(rb, "dc.tag.not.allowed.inline.other", HTML);
+                add(rb, "dc.tag.not.closed", HTML);
+                add(rb, "dc.tag.p.in.pre", HTML);
+                add(rb, "dc.tag.self.closing", HTML);
+                add(rb, "dc.tag.start.unmatched", HTML);
+                add(rb, "dc.tag.unknown", HTML);
+                add(rb, "dc.text.not.allowed", HTML);
+            }
+
+
+            rb = ResourceBundle.getBundle("com.sun.tools.javac.resources.compiler");
+            if (rb == null) {
+                System.err.println("Warning: Cannot locate javac resource bundle");
+            } else {
+                add(rb, "compiler.err.dc.bad.entity", SYNTAX);
+                add(rb, "compiler.err.dc.bad.gt", SYNTAX);
+                add(rb, "compiler.err.dc.bad.inline.tag", SYNTAX);
+                add(rb, "compiler.err.dc.identifier.expected", SYNTAX);
+                add(rb, "compiler.err.dc.malformed.html", SYNTAX);
+                add(rb, "compiler.err.dc.missing.semicolon", SYNTAX);
+                add(rb, "compiler.err.dc.no.content", SYNTAX);
+                add(rb, "compiler.err.dc.no.tag.name", SYNTAX);
+                add(rb, "compiler.err.dc.gt.expected", SYNTAX);
+                add(rb, "compiler.err.dc.ref.bad.parens", SYNTAX);
+                add(rb, "compiler.err.dc.ref.syntax.error", SYNTAX);
+                add(rb, "compiler.err.dc.ref.unexpected.input", SYNTAX);
+                add(rb, "compiler.err.dc.unexpected.content", SYNTAX);
+                add(rb, "compiler.err.dc.unterminated.inline.tag", SYNTAX);
+                add(rb, "compiler.err.dc.unterminated.signature", SYNTAX);
+                add(rb, "compiler.err.dc.unterminated.string", SYNTAX);
+            }
+        }
+
+        private void add(ResourceBundle rb, String code, String group) {
+            try {
+                String msg = rb.getString(code).replace("''", "'");
+                if (msg.matches(".*\\{[0-9]\\}.*"))
+                    patterns.put(Pattern.compile(msg.replaceAll("\\{[0-9]\\}", ".*")), group);
+                else
+                    strings.put(msg, group);
+            } catch (MissingResourceException e) {
+                System.err.println("Warning: cannot find doclint message " + code);
+            }
+        }
+
+        boolean isDocLintMessage(JCDiagnostic d) {
+            return d.getCode().endsWith(".proc.messager");
+        }
+
+        String getGroup(JCDiagnostic d) {
+            Object[] args = d.getArgs();
+            if (args.length == 1 && args[0] instanceof String) {
+                String arg = (String) args[0];
+                String g = strings.get(arg);
+                if (g != null)
+                    return g;
+                for (Map.Entry<Pattern,String> e: patterns.entrySet()) {
+                    if (e.getKey().matcher(arg).matches())
+                        return e.getValue();
+                }
+            }
+            System.err.println("can't analyze " + d);
+            return "unknown";
+        }
+    }
+}