diff --git a/build/build.py b/build/build.py
index 1a03b4c..7a99cf4 100755
--- a/build/build.py
+++ b/build/build.py
@@ -168,6 +168,7 @@ runDependencyJars = [
   "jetty-util-ajax-9.2.9.v20150224.jar",
   "log4j-1.2.17.jar",
   "rhino-1.7R5.jar",
+  "iri.jar"
 ]
 
 buildOnlyDependencyJars = [
@@ -641,6 +642,7 @@ def buildEmitters():
     '-d "%s"' % classDir,
     '-encoding UTF-8',
   ]
+  return
   if javaVersion != "":
     args.append('-target ' + javaVersion)
     args.append('-source ' + javaVersion)
diff --git a/htmlparser b/htmlparser
deleted file mode 160000
index 543cc3e..0000000
--- a/htmlparser
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 543cc3e7d442874c10ed40e114317115bcff1ca5
diff --git a/jing-trang b/jing-trang
deleted file mode 160000
index 35eb11b..0000000
--- a/jing-trang
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 35eb11b84a230ee4d7168f12f98a28bf40e940aa
diff --git a/src/nu/validator/client/TestRunner.java b/src/nu/validator/client/TestRunner.java
deleted file mode 100644
index 551df39..0000000
--- a/src/nu/validator/client/TestRunner.java
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
- * Copyright (c) 2013-2015 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.client;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.net.MalformedURLException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-import org.eclipse.jetty.util.ajax.JSON;
-
-import org.relaxng.datatype.DatatypeException;
-import com.thaiopensource.relaxng.exceptions.BadAttributeValueException;
-
-import nu.validator.datatype.Html5DatatypeException;
-
-import nu.validator.validation.SimpleDocumentValidator;
-
-public class TestRunner implements ErrorHandler {
-
-    private boolean inError = false;
-
-    private boolean emitMessages = false;
-
-    private boolean exceptionIsWarning = false;
-
-    private boolean expectingError = false;
-
-    private Exception exception = null;
-
-    private SimpleDocumentValidator validator;
-
-    private PrintWriter err;
-
-    private PrintWriter out;
-
-    private String schema = "http://s.validator.nu/html5-all.rnc";
-
-    private boolean failed = false;
-
-    private static File messagesFile;
-
-    private static String[] ignoreList = null;
-
-    private static boolean writeMessages;
-
-    private static boolean verbose;
-
-    private String baseDir = null;
-
-    private Map<String, String> expectedMessages;
-
-    private Map<String, String> reportedMessages;
-
-    public TestRunner() throws IOException {
-        reportedMessages = new LinkedHashMap<String, String>();
-        validator = new SimpleDocumentValidator();
-        try {
-            this.err = new PrintWriter(new OutputStreamWriter(System.err,
-                    "UTF-8"));
-            this.out = new PrintWriter(new OutputStreamWriter(System.out,
-                    "UTF-8"));
-        } catch (Exception e) {
-            // If this happens, the JDK is too broken anyway
-            throw new RuntimeException(e);
-        }
-    }
-
-    private void checkHtmlFile(File file) throws IOException, SAXException {
-        if (!file.exists()) {
-            if (verbose) {
-                out.println(String.format("\"%s\": warning: File not found.",
-                        file.toURI().toURL().toString()));
-                out.flush();
-            }
-            return;
-        }
-        if (verbose) {
-            out.println(file);
-            out.flush();
-        }
-        if (isHtml(file)) {
-            validator.checkHtmlFile(file, true);
-        } else if (isXhtml(file)) {
-            validator.checkXmlFile(file);
-        } else {
-            if (verbose) {
-                out.println(String.format(
-                        "\"%s\": warning: File was not checked."
-                                + " Files must have a .html, .xhtml, .htm,"
-                                + " or .xht extension.",
-                        file.toURI().toURL().toString()));
-                out.flush();
-            }
-        }
-    }
-
-    private boolean isXhtml(File file) {
-        String name = file.getName();
-        return name.endsWith(".xhtml") || name.endsWith(".xht");
-    }
-
-    private boolean isHtml(File file) {
-        String name = file.getName();
-        return name.endsWith(".html") || name.endsWith(".htm");
-    }
-
-    private boolean isCheckableFile(File file) {
-        return file.isFile() && (isHtml(file) || isXhtml(file));
-    }
-
-    private void recurseDirectory(File directory) throws SAXException,
-            IOException {
-        File[] files = directory.listFiles();
-        for (int i = 0; i < files.length; i++) {
-            File file = files[i];
-            if (file.isDirectory()) {
-                recurseDirectory(file);
-            } else {
-                checkHtmlFile(file);
-            }
-        }
-    }
-
-    private boolean isIgnorable(File file) throws IOException {
-        String testPathname = file.getAbsolutePath().substring(
-                baseDir.length() + 1);
-        if (ignoreList != null) {
-            for (String substring : ignoreList) {
-                if (testPathname.contains(substring)) {
-                    if (verbose) {
-                        out.println(String.format(
-                                "\"%s\": warning: File ignored.",
-                                file.toURI().toURL().toString()));
-                        out.flush();
-                    }
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private void checkFiles(List<File> files) throws IOException {
-        for (File file : files) {
-            if (isIgnorable(file)) {
-                continue;
-            }
-            reset();
-            emitMessages = true;
-            try {
-                if (file.isDirectory()) {
-                    recurseDirectory(file);
-                } else {
-                    checkHtmlFile(file);
-                }
-            } catch (IOException e) {
-            } catch (SAXException e) {
-            }
-            if (inError) {
-                failed = true;
-            }
-        }
-    }
-
-    private boolean messageMatches(String testFilename) {
-        // p{C} = Other = Control+Format+Private_Use+Surrogate+Unassigned
-        // http://www.regular-expressions.info/unicode.html#category
-        // http://www.unicode.org/reports/tr18/#General_Category_Property
-        String messageReported = exception.getMessage().replaceAll("\\p{C}",
-                "?");
-        String messageExpected = expectedMessages.get(testFilename).replaceAll(
-                "\\p{C}", "?");
-        // FIXME: The string replacements below are a hack to "normalize"
-        // error messages reported for bad values of the ins/del datetime
-        // attribute, to work around the fact that in Java 8, parts of
-        // those error messages don't always get emitted in the same order
-        // that they do in Java 7 and earlier.
-        Pattern p;
-        p = Pattern.compile("(Bad datetime with timezone: .+) (Bad date: .+)");
-        messageExpected = p.matcher(messageExpected).replaceAll("$2 $1");
-        messageReported = p.matcher(messageReported).replaceAll("$2 $1");
-        return messageReported.equals(messageExpected);
-    }
-
-    private void checkInvalidFiles(List<File> files) throws IOException {
-        String testFilename;
-        expectingError = true;
-        for (File file : files) {
-            if (isIgnorable(file)) {
-                continue;
-            }
-            reset();
-            try {
-                if (file.isDirectory()) {
-                    recurseDirectory(file);
-                } else {
-                    checkHtmlFile(file);
-                }
-            } catch (IOException e) {
-            } catch (SAXException e) {
-            }
-            if (exception != null) {
-                testFilename = file.getAbsolutePath().substring(
-                        baseDir.length() + 1);
-                if (writeMessages) {
-                    reportedMessages.put(testFilename, exception.getMessage());
-                } else if (expectedMessages != null
-                        && expectedMessages.get(testFilename) == null) {
-                    try {
-                        err.println(String.format(
-                                "\"%s\": warning: No expected message in"
-                                        + " messages file.",
-                                file.toURI().toURL().toString()));
-                        err.flush();
-                    } catch (MalformedURLException e) {
-                        throw new RuntimeException(e);
-                    }
-                } else if (expectedMessages != null
-                        && !messageMatches(testFilename)) {
-                    failed = true;
-                    try {
-                        err.println(String.format(
-                                "\"%s\": error: Expected \"%s\""
-                                        + " but instead encountered \"%s\".",
-                                file.toURI().toURL().toString(),
-                                expectedMessages.get(testFilename),
-                                exception.getMessage()));
-                        err.flush();
-                    } catch (MalformedURLException e) {
-                        throw new RuntimeException(e);
-                    }
-                }
-            }
-            if (!inError) {
-                failed = true;
-                try {
-                    err.println(String.format(
-                            "\"%s\": error: Expected an error but did not"
-                                    + " encounter any.",
-                            file.toURI().toURL().toString()));
-                    err.flush();
-                } catch (MalformedURLException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        }
-    }
-
-    private void checkHasWarningFiles(List<File> files) throws IOException {
-        String testFilename;
-        expectingError = false;
-        for (File file : files) {
-            if (isIgnorable(file)) {
-                continue;
-            }
-            reset();
-            try {
-                if (file.isDirectory()) {
-                    recurseDirectory(file);
-                } else {
-                    checkHtmlFile(file);
-                }
-            } catch (IOException e) {
-            } catch (SAXException e) {
-            }
-            if (exception != null) {
-                testFilename = file.getAbsolutePath().substring(
-                        baseDir.length() + 1);
-                if (writeMessages) {
-                    reportedMessages.put(testFilename, exception.getMessage());
-                } else if (expectedMessages != null
-                        && expectedMessages.get(testFilename) == null) {
-                    try {
-                        err.println(String.format(
-                                "\"%s\": warning: No expected message in"
-                                        + " messages file.",
-                                file.toURI().toURL().toString()));
-                        err.flush();
-                    } catch (MalformedURLException e) {
-                        throw new RuntimeException(e);
-                    }
-                } else if (expectedMessages != null
-                        && !messageMatches(testFilename)) {
-                    try {
-                        err.println(String.format(
-                                "\"%s\": error: Expected \"%s\""
-                                        + " but instead encountered \"%s\".",
-                                file.toURI().toURL().toString(),
-                                expectedMessages.get(testFilename),
-                                exception.getMessage()));
-                        err.flush();
-                    } catch (MalformedURLException e) {
-                        throw new RuntimeException(e);
-                    }
-                }
-            }
-            if (inError) {
-                failed = true;
-                try {
-                    err.println(String.format(
-                            "\"%s\": error: Expected a warning but encountered"
-                                    + " an error first.",
-                            file.toURI().toURL().toString()));
-                    err.flush();
-                } catch (MalformedURLException e) {
-                    throw new RuntimeException(e);
-                }
-            } else if (!exceptionIsWarning) {
-                try {
-                    err.println(String.format(
-                            "\"%s\": error: Expected a warning but did not"
-                                    + " encounter any.",
-                            file.toURI().toURL().toString()));
-                    err.flush();
-                } catch (MalformedURLException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-            if (inError) {
-                failed = true;
-                try {
-                    err.println(String.format(
-                            "\"%s\": error: Expected a warning only but"
-                                    + " encountered at least one error.",
-                            file.toURI().toURL().toString()));
-                    err.flush();
-                } catch (MalformedURLException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        }
-    }
-
-    private enum State {
-        EXPECTING_INVALID_FILES, EXPECTING_VALID_FILES, EXPECTING_ANYTHING
-    }
-
-    private void checkTestDirectoryAgainstSchema(File directory,
-            String schemaUrl) throws SAXException, Exception {
-        validator.setUpMainSchema(schemaUrl, this);
-        checkTestFiles(directory, State.EXPECTING_ANYTHING);
-    }
-
-    private void checkTestFiles(File directory, State state)
-            throws SAXException, IOException {
-        File[] files = directory.listFiles();
-        List<File> validFiles = new ArrayList<File>();
-        List<File> invalidFiles = new ArrayList<File>();
-        List<File> hasWarningFiles = new ArrayList<File>();
-        if (files == null) {
-            if (verbose) {
-                try {
-                    out.println(String.format(
-                            "\"%s\": warning: No files found in directory.",
-                            directory.toURI().toURL().toString()));
-                    out.flush();
-                } catch (MalformedURLException mue) {
-                    throw new RuntimeException(mue);
-                }
-            }
-            return;
-        }
-        for (int i = 0; i < files.length; i++) {
-            File file = files[i];
-            if (file.isDirectory()) {
-                if (state != State.EXPECTING_ANYTHING) {
-                    checkTestFiles(file, state);
-                } else if ("invalid".equals(file.getName())) {
-                    checkTestFiles(file, State.EXPECTING_INVALID_FILES);
-                } else if ("valid".equals(file.getName())) {
-                    checkTestFiles(file, State.EXPECTING_VALID_FILES);
-                } else {
-                    checkTestFiles(file, State.EXPECTING_ANYTHING);
-                }
-            } else if (isCheckableFile(file)) {
-                if (state == State.EXPECTING_INVALID_FILES) {
-                    invalidFiles.add(file);
-                } else if (state == State.EXPECTING_VALID_FILES) {
-                    validFiles.add(file);
-                } else if (file.getPath().indexOf("novalid") > 0) {
-                    invalidFiles.add(file);
-                } else if (file.getPath().indexOf("haswarn") > 0) {
-                    hasWarningFiles.add(file);
-                } else {
-                    validFiles.add(file);
-                }
-            }
-        }
-        if (validFiles.size() > 0) {
-            validator.setUpValidatorAndParsers(this, false, false);
-            checkFiles(validFiles);
-        }
-        if (invalidFiles.size() > 0) {
-            validator.setUpValidatorAndParsers(this, false, false);
-            checkInvalidFiles(invalidFiles);
-        }
-        if (hasWarningFiles.size() > 0) {
-            validator.setUpValidatorAndParsers(this, false, false);
-            checkHasWarningFiles(hasWarningFiles);
-        }
-        if (writeMessages) {
-            OutputStreamWriter out = new OutputStreamWriter(
-                    new FileOutputStream(messagesFile), "utf-8");
-            BufferedWriter bw = new BufferedWriter(out);
-            bw.write(JSON.toString(reportedMessages));
-            bw.close();
-        }
-    }
-
-    public boolean runTestSuite() throws SAXException, Exception {
-        if (messagesFile != null) {
-            baseDir = messagesFile.getAbsoluteFile().getParent();
-            FileInputStream fis = new FileInputStream(messagesFile);
-            InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
-            expectedMessages = (HashMap<String, String>) JSON.parse(reader);
-        } else {
-            baseDir = System.getProperty("user.dir");
-        }
-        for (File directory : new File(baseDir).listFiles()) {
-            if (directory.isDirectory()) {
-                if (directory.getName().contains("rdfalite")) {
-                    checkTestDirectoryAgainstSchema(directory,
-                            "http://s.validator.nu/html5-rdfalite.rnc");
-                } else if (directory.getName().contains("xhtml")) {
-                    checkTestDirectoryAgainstSchema(directory,
-                            "http://s.validator.nu/xhtml5-all.rnc");
-                } else {
-                    checkTestDirectoryAgainstSchema(directory, schema);
-                }
-            }
-        }
-        if (verbose) {
-            if (failed) {
-                out.println("Failure!");
-                out.flush();
-            } else {
-                out.println("Success!");
-                out.flush();
-            }
-        }
-        return !failed;
-    }
-
-    private void emitMessage(SAXParseException e, String messageType) {
-        String systemId = e.getSystemId();
-        err.write((systemId == null) ? "" : '\"' + systemId + '\"');
-        err.write(":");
-        err.write(Integer.toString(e.getLineNumber()));
-        err.write(":");
-        err.write(Integer.toString(e.getColumnNumber()));
-        err.write(": ");
-        err.write(messageType);
-        err.write(": ");
-        err.write(e.getMessage());
-        err.write("\n");
-        err.flush();
-    }
-
-    public void warning(SAXParseException e) throws SAXException {
-        if (emitMessages) {
-            emitMessage(e, "warning");
-        } else if (exception == null && !expectingError) {
-            exception = e;
-            exceptionIsWarning = true;
-        }
-    }
-
-    public void error(SAXParseException e) throws SAXException {
-        if (emitMessages) {
-            emitMessage(e, "error");
-        } else if (exception == null) {
-            exception = e;
-            if (e instanceof BadAttributeValueException) {
-                BadAttributeValueException ex = (BadAttributeValueException) e;
-                Map<String, DatatypeException> datatypeErrors = ex.getExceptions();
-                for (Map.Entry<String, DatatypeException> entry : datatypeErrors.entrySet()) {
-                    DatatypeException dex = entry.getValue();
-                    if (dex instanceof Html5DatatypeException) {
-                        Html5DatatypeException ex5 = (Html5DatatypeException) dex;
-                        if (ex5.isWarning()) {
-                            exceptionIsWarning = true;
-                            return;
-                        }
-                    }
-                }
-            }
-        }
-        inError = true;
-    }
-
-    public void fatalError(SAXParseException e) throws SAXException {
-        inError = true;
-        if (emitMessages) {
-            emitMessage(e, "fatal error");
-            return;
-        } else if (exception == null) {
-            exception = e;
-        }
-    }
-
-    public void reset() {
-        exception = null;
-        inError = false;
-        emitMessages = false;
-        exceptionIsWarning = false;
-    }
-
-    public static void main(String[] args) throws SAXException, Exception {
-        if (args.length < 1) {
-            usage();
-            System.exit(0);
-        }
-        verbose = false;
-        String messagesFilename = null;
-        System.setProperty("nu.validator.datatype.warn", "true");
-        for (int i = 0; i < args.length; i++) {
-            if ("--verbose".equals(args[i])) {
-                verbose = true;
-            } else if ("--errors-only".equals(args[i])) {
-                System.setProperty("nu.validator.datatype.warn", "false");
-            } else if ("--write-messages".equals(args[i])) {
-                writeMessages = true;
-            } else if (args[i].startsWith("--ignore=")) {
-                ignoreList = args[i].substring(9, args[i].length()).split(",");
-            } else if (args[i].startsWith("--")) {
-                System.out.println(String.format(
-                        "\nError: There is no option \"%s\".", args[i]));
-                usage();
-                System.exit(1);
-            } else {
-                if (args[i].endsWith(".json")) {
-                    messagesFilename = args[i];
-                } else {
-                    System.out.println("\nError: Expected the name of a messages"
-                            + " file with a .json extension.");
-                    usage();
-                    System.exit(1);
-                }
-            }
-        }
-        if (messagesFilename != null) {
-            messagesFile = new File(messagesFilename);
-            if (!messagesFile.exists()) {
-                System.out.println("\nError: \"" + messagesFilename
-                        + "\" file not found.");
-                System.exit(1);
-            } else if (!messagesFile.isFile()) {
-                System.out.println("\nError: \"" + messagesFilename
-                        + "\" is not a file.");
-                System.exit(1);
-            }
-        } else if (writeMessages) {
-            System.out.println("\nError: Expected the name of a messages"
-                    + " file with a .json extension.");
-            usage();
-            System.exit(1);
-        }
-        TestRunner tr = new TestRunner();
-        if (tr.runTestSuite()) {
-            System.exit(0);
-        } else {
-            System.exit(1);
-        }
-    }
-
-    private static void usage() {
-        System.out.println("\nUsage:");
-        System.out.println("\n    java nu.validator.client.TestRunner [--errors-only] [--write-messages]");
-        System.out.println("          [--verbose] [MESSAGES.json]");
-        System.out.println("\n...where the MESSAGES.json file contains name/value pairs in which the name is");
-        System.out.println("a pathname of a document to check and the value is the first error message or");
-        System.out.println("warning message the validator is expected to report when checking that document.");
-        System.out.println("Use the --write-messages option to create the file.");
-    }
-}
diff --git a/src/nu/validator/datatype/FunctionBody.java b/src/nu/validator/datatype/FunctionBody.java
index 5e8bd69..35642ef 100644
--- a/src/nu/validator/datatype/FunctionBody.java
+++ b/src/nu/validator/datatype/FunctionBody.java
@@ -27,9 +27,9 @@ import java.io.IOException;
 import java.io.Reader;
 import java.io.StringReader;
 
-import org.mozilla.javascript.Context;
-import org.mozilla.javascript.ContextFactory;
-import org.mozilla.javascript.RhinoException;
+//import org.mozilla.javascript.Context;
+//import org.mozilla.javascript.ContextFactory;
+//import org.mozilla.javascript.RhinoException;
 import org.relaxng.datatype.DatatypeException;
 
 public class FunctionBody extends AbstractDatatype {
@@ -44,25 +44,25 @@ public class FunctionBody extends AbstractDatatype {
     }
 
     public void checkValid(CharSequence literal) throws DatatypeException {
-        try {
-            Reader reader = new BufferedReader((new StringReader(
-                    "function(event){" + literal.toString() + "}")));
-            reader.mark(1);
-            try {
-                Context context = ContextFactory.getGlobal().enterContext();
-                context.setOptimizationLevel(0);
-                context.setLanguageVersion(Context.VERSION_1_6);
-                // -1 for lineno arg prevents Rhino from appending
-                // "(unnamed script#1)" to all error messages
-                context.compileReader(reader, null, -1, null);
-            } finally {
-                Context.exit();
-            }
-        } catch (IOException e) {
-            throw newDatatypeException(e.getMessage());
-        } catch (RhinoException e) {
-            throw newDatatypeException(e.getMessage());
-        }
+//        try {
+//            Reader reader = new BufferedReader((new StringReader(
+//                    "function(event){" + literal.toString() + "}")));
+//            reader.mark(1);
+//            try {
+//                Context context = ContextFactory.getGlobal().enterContext();
+//                context.setOptimizationLevel(0);
+//                context.setLanguageVersion(Context.VERSION_1_6);
+//                // -1 for lineno arg prevents Rhino from appending
+//                // "(unnamed script#1)" to all error messages
+//                context.compileReader(reader, null, -1, null);
+//            } finally {
+//                Context.exit();
+//            }
+//        } catch (IOException e) {
+//            throw newDatatypeException(e.getMessage());
+//        } catch (RhinoException e) {
+//            throw newDatatypeException(e.getMessage());
+//        }
     }
 
     @Override public String getName() {
diff --git a/src/nu/validator/datatype/IriRef.java b/src/nu/validator/datatype/IriRef.java
index e9d6fff..2b42415 100644
--- a/src/nu/validator/datatype/IriRef.java
+++ b/src/nu/validator/datatype/IriRef.java
@@ -29,18 +29,18 @@ import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
 
-import org.mozilla.javascript.Context;
-import org.mozilla.javascript.ContextFactory;
-import org.mozilla.javascript.RhinoException;
+//import org.mozilla.javascript.Context;
+//import org.mozilla.javascript.ContextFactory;
+//import org.mozilla.javascript.RhinoException;
 import org.relaxng.datatype.DatatypeException;
 import nu.validator.io.DataUri;
 import nu.validator.io.DataUriException;
 import nu.validator.io.Utf8PercentDecodingReader;
 
-import io.mola.galimatias.URL;
-import io.mola.galimatias.URLParsingSettings;
-import io.mola.galimatias.GalimatiasParseException;
-import io.mola.galimatias.StrictErrorHandler;
+//import io.mola.galimatias.URL;
+//import io.mola.galimatias.URLParsingSettings;
+//import io.mola.galimatias.GalimatiasParseException;
+//import io.mola.galimatias.StrictErrorHandler;
 
 public class IriRef extends AbstractDatatype {
 
@@ -93,112 +93,112 @@ public class IriRef extends AbstractDatatype {
     }
 
     public void checkValid(CharSequence literal) throws DatatypeException {
-        String messagePrologue = "";
-        int length = literal.length();
-        if (reportValue()) {
-            if (length < ELIDE_LIMIT) {
-                messagePrologue = "\u201c" + literal + "\u201d: ";
-            } else {
-                StringBuilder sb = new StringBuilder(ELIDE_LIMIT + 1);
-                sb.append(literal, 0, ELIDE_LIMIT / 2);
-                sb.append('\u2026');
-                sb.append(literal, length - ELIDE_LIMIT / 2, length);
-                messagePrologue = "\u201c" + sb.toString() + "\u201d: ";
-            }
-        }
-        if ("".equals(trimHtmlSpaces(literal.toString()))) {
-            throw newDatatypeException("Must be non-empty.");
-        }
-        URL url = null;
-        URLParsingSettings settings = URLParsingSettings.create().withErrorHandler(
-                StrictErrorHandler.getInstance());
-        boolean data = false;
-        try {
-            CharSequencePair pair = splitScheme(literal);
-            if (pair == null) {
-                // no scheme or scheme is private
-                if (isAbsolute()) {
-                    throw newDatatypeException("The string \u201c" + literal
-                            + "\u201d is not an absolute URL.");
-                } else {
-                    // in this case, doc's actual base URL isn't relevant,
-                    // so just use http://example.org/foo/bar as base
-                    url = URL.parse(settings,
-                            URL.parse("http://example.org/foo/bar"),
-                            literal.toString());
-                }
-            } else {
-                CharSequence scheme = pair.getHead();
-                CharSequence tail = pair.getTail();
-                if (isWellKnown(scheme)) {
-                    url = URL.parse(settings, literal.toString());
-                } else if ("javascript".contentEquals(scheme)) {
-                    // StringBuilder sb = new StringBuilder(2 +
-                    // literal.length());
-                    // sb.append("x-").append(literal);
-                    // iri = fac.construct(sb.toString());
-                    url = null; // Don't bother user with generic IRI syntax
-                    Reader reader = new BufferedReader(
-                            new Utf8PercentDecodingReader(new StringReader(
-                                    "function(event){" + tail.toString() + "}")));
-                    // XXX CharSequenceReader
-                    reader.mark(1);
-                    int c = reader.read();
-                    if (c != 0xFEFF) {
-                        reader.reset();
-                    }
-                    try {
-                        Context context = ContextFactory.getGlobal().enterContext();
-                        context.setOptimizationLevel(0);
-                        context.setLanguageVersion(Context.VERSION_1_6);
-                        // -1 for lineno arg prevents Rhino from appending
-                        // "(unnamed script#1)" to all error messages
-                        context.compileReader(reader, null, -1, null);
-                    } finally {
-                        Context.exit();
-                    }
-                } else if ("data".contentEquals(scheme)) {
-                    data = true;
-                    url = URL.parse(settings, literal.toString());
-                } else if (isHttpAlias(scheme)) {
-                    StringBuilder sb = new StringBuilder(5 + tail.length());
-                    sb.append("http:").append(tail);
-                    url = URL.parse(settings, sb.toString());
-                } else {
-                    StringBuilder sb = new StringBuilder(2 + literal.length());
-                    sb.append("x-").append(literal);
-                    url = URL.parse(settings, sb.toString());
-                }
-            }
-        } catch (GalimatiasParseException e) {
-            throw newDatatypeException(messagePrologue + e.getMessage() + ".");
-        } catch (IOException e) {
-            throw newDatatypeException(messagePrologue + e.getMessage());
-        } catch (RhinoException e) {
-            throw newDatatypeException(messagePrologue + e.getMessage());
-        }
-        if (url != null) {
-            if (data) {
-                try {
-                    DataUri dataUri = new DataUri(url);
-                    InputStream is = dataUri.getInputStream();
-                    while (is.read() >= 0) {
-                        // spin
-                    }
-                } catch (DataUriException e) {
-                    throw newDatatypeException(e.getIndex(), e.getHead(),
-                            e.getLiteral(), e.getTail());
-                } catch (IOException e) {
-                    String msg = e.getMessage();
-                    if (WARN
-                            && "Fragment is not allowed for data: URIs according to RFC 2397.".equals(msg)) {
-                        throw newDatatypeException(messagePrologue + msg, WARN);
-                    } else {
-                        throw newDatatypeException(messagePrologue + msg);
-                    }
-                }
-            }
-        }
+//        String messagePrologue = "";
+//        int length = literal.length();
+//        if (reportValue()) {
+//            if (length < ELIDE_LIMIT) {
+//                messagePrologue = "\u201c" + literal + "\u201d: ";
+//            } else {
+//                StringBuilder sb = new StringBuilder(ELIDE_LIMIT + 1);
+//                sb.append(literal, 0, ELIDE_LIMIT / 2);
+//                sb.append('\u2026');
+//                sb.append(literal, length - ELIDE_LIMIT / 2, length);
+//                messagePrologue = "\u201c" + sb.toString() + "\u201d: ";
+//            }
+//        }
+//        if ("".equals(trimHtmlSpaces(literal.toString()))) {
+//            throw newDatatypeException("Must be non-empty.");
+//        }
+//        URL url = null;
+//        URLParsingSettings settings = URLParsingSettings.create().withErrorHandler(
+//                StrictErrorHandler.getInstance());
+//        boolean data = false;
+//        try {
+//            CharSequencePair pair = splitScheme(literal);
+//            if (pair == null) {
+//                // no scheme or scheme is private
+//                if (isAbsolute()) {
+//                    throw newDatatypeException("The string \u201c" + literal
+//                            + "\u201d is not an absolute URL.");
+//                } else {
+//                    // in this case, doc's actual base URL isn't relevant,
+//                    // so just use http://example.org/foo/bar as base
+//                    url = URL.parse(settings,
+//                            URL.parse("http://example.org/foo/bar"),
+//                            literal.toString());
+//                }
+//            } else {
+//                CharSequence scheme = pair.getHead();
+//                CharSequence tail = pair.getTail();
+//                if (isWellKnown(scheme)) {
+//                    url = URL.parse(settings, literal.toString());
+//                } else if ("javascript".contentEquals(scheme)) {
+//                    // StringBuilder sb = new StringBuilder(2 +
+//                    // literal.length());
+//                    // sb.append("x-").append(literal);
+//                    // iri = fac.construct(sb.toString());
+//                    url = null; // Don't bother user with generic IRI syntax
+//                    Reader reader = new BufferedReader(
+//                            new Utf8PercentDecodingReader(new StringReader(
+//                                    "function(event){" + tail.toString() + "}")));
+//                    // XXX CharSequenceReader
+//                    reader.mark(1);
+//                    int c = reader.read();
+//                    if (c != 0xFEFF) {
+//                        reader.reset();
+//                    }
+//                    try {
+//                        Context context = ContextFactory.getGlobal().enterContext();
+//                        context.setOptimizationLevel(0);
+//                        context.setLanguageVersion(Context.VERSION_1_6);
+//                        // -1 for lineno arg prevents Rhino from appending
+//                        // "(unnamed script#1)" to all error messages
+//                        context.compileReader(reader, null, -1, null);
+//                    } finally {
+//                        Context.exit();
+//                    }
+//                } else if ("data".contentEquals(scheme)) {
+//                    data = true;
+//                    url = URL.parse(settings, literal.toString());
+//                } else if (isHttpAlias(scheme)) {
+//                    StringBuilder sb = new StringBuilder(5 + tail.length());
+//                    sb.append("http:").append(tail);
+//                    url = URL.parse(settings, sb.toString());
+//                } else {
+//                    StringBuilder sb = new StringBuilder(2 + literal.length());
+//                    sb.append("x-").append(literal);
+//                    url = URL.parse(settings, sb.toString());
+//                }
+//            }
+//        } catch (GalimatiasParseException e) {
+//            throw newDatatypeException(messagePrologue + e.getMessage() + ".");
+//        } catch (IOException e) {
+//            throw newDatatypeException(messagePrologue + e.getMessage());
+//        } catch (RhinoException e) {
+//            throw newDatatypeException(messagePrologue + e.getMessage());
+//        }
+//        if (url != null) {
+//            if (data) {
+//                try {
+//                    DataUri dataUri = new DataUri(url);
+//                    InputStream is = dataUri.getInputStream();
+//                    while (is.read() >= 0) {
+//                        // spin
+//                    }
+//                } catch (DataUriException e) {
+//                    throw newDatatypeException(e.getIndex(), e.getHead(),
+//                            e.getLiteral(), e.getTail());
+//                } catch (IOException e) {
+//                    String msg = e.getMessage();
+//                    if (WARN
+//                            && "Fragment is not allowed for data: URIs according to RFC 2397.".equals(msg)) {
+//                        throw newDatatypeException(messagePrologue + msg, WARN);
+//                    } else {
+//                        throw newDatatypeException(messagePrologue + msg);
+//                    }
+//                }
+//            }
+//        }
     }
 
     private final boolean isHttpAlias(CharSequence scheme) {
diff --git a/src/nu/validator/datatype/Language.java b/src/nu/validator/datatype/Language.java
index b61913d..24fb4a4 100644
--- a/src/nu/validator/datatype/Language.java
+++ b/src/nu/validator/datatype/Language.java
@@ -227,7 +227,7 @@ public final class Language extends AbstractDatatype {
             checkPrivateUse(i, subtags);
             return;
         }
-        if (subtag.length() == 4 & isLowerCaseAlpha(subtag)) {
+        if (subtag.length() == 4 && isLowerCaseAlpha(subtag)) {
             if (!isScript(subtag)) {
                 throw newDatatypeException("Bad script subtag.");
             }
diff --git a/src/nu/validator/datatype/Pattern.java b/src/nu/validator/datatype/Pattern.java
index aa10750..67766c7 100644
--- a/src/nu/validator/datatype/Pattern.java
+++ b/src/nu/validator/datatype/Pattern.java
@@ -22,10 +22,10 @@
 
 package nu.validator.datatype;
 
-import org.mozilla.javascript.Context;
-import org.mozilla.javascript.ContextFactory;
-import org.mozilla.javascript.EcmaError;
-import org.mozilla.javascript.regexp.RegExpImpl;
+//import org.mozilla.javascript.Context;
+//import org.mozilla.javascript.ContextFactory;
+//import org.mozilla.javascript.EcmaError;
+//import org.mozilla.javascript.regexp.RegExpImpl;
 import org.relaxng.datatype.DatatypeException;
 
 /**
@@ -58,18 +58,18 @@ public final class Pattern extends AbstractDatatype {
     public void checkValid(CharSequence literal)
             throws DatatypeException {
         // TODO find out what kind of thread concurrency guarantees are made
-        ContextFactory cf = new ContextFactory();
-        Context cx = cf.enterContext();
-        cx.setOptimizationLevel(0);
-        RegExpImpl rei = new RegExpImpl();
-        String anchoredRegex = "^(?:" + literal + ")$";
-        try {
-            rei.compileRegExp(cx, anchoredRegex, "");
-        } catch (EcmaError ee) {
-            throw newDatatypeException(ee.getErrorMessage());
-        } finally {
-            Context.exit();
-        }
+//        ContextFactory cf = new ContextFactory();
+//        Context cx = cf.enterContext();
+//        cx.setOptimizationLevel(0);
+//        RegExpImpl rei = new RegExpImpl();
+//        String anchoredRegex = "^(?:" + literal + ")$";
+//        try {
+//            rei.compileRegExp(cx, anchoredRegex, "");
+//        } catch (EcmaError ee) {
+//            throw newDatatypeException(ee.getErrorMessage());
+//        } finally {
+//            Context.exit();
+//        }
     }
 
     @Override
diff --git a/src/nu/validator/datatype/SourceSizeList.java b/src/nu/validator/datatype/SourceSizeList.java
index 1c61f81..e3bd20a 100644
--- a/src/nu/validator/datatype/SourceSizeList.java
+++ b/src/nu/validator/datatype/SourceSizeList.java
@@ -26,7 +26,6 @@ import java.text.ParseException;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
-import nu.validator.datatype.tools.CssParser;
 import org.relaxng.datatype.DatatypeException;
 
 public class SourceSizeList extends AbstractDatatype {
@@ -41,8 +40,6 @@ public class SourceSizeList extends AbstractDatatype {
 
     private static final StringBuilder VALID_UNITS = new StringBuilder();
 
-    private static CssParser cssParser = new CssParser();
-
     static {
         /* font-relative lengths */
         LENGTH_UNITS.add("em");
@@ -119,18 +116,6 @@ public class SourceSizeList extends AbstractDatatype {
             errEmpty(isFirst, isLast, extract);
             return;
         }
-        try {
-            cssParser.tokenize(unparsedSize.toString());
-            if ('(' == unparsedSize.codePointAt(0)) {
-                cssParser.parseARule("@media " + unparsedSize.toString()
-                        + " {}");
-            } else {
-                cssParser.parseARule(".foo { width: " + unparsedSize.toString()
-                        + " }");
-            }
-        } catch (ParseException e) {
-            errCssParseError(e.getMessage(), unparsedSize, extract);
-        }
         if (')' == unparsedSize.charAt(unparsedSize.length() - 1)) {
             checkCalc(unparsedSize, extract, isLast);
             return;
diff --git a/src/nu/validator/datatype/tools/CssParser.java b/src/nu/validator/datatype/tools/CssParser.java
deleted file mode 100644
index 9daa67f..0000000
--- a/src/nu/validator/datatype/tools/CssParser.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2015 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.datatype.tools;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.text.ParseException;
-
-import org.mozilla.javascript.Context;
-import org.mozilla.javascript.ContextFactory;
-import org.mozilla.javascript.Function;
-import org.mozilla.javascript.JavaScriptException;
-import org.mozilla.javascript.ScriptableObject;
-
-public class CssParser {
-
-    private static ScriptableObject scope;
-
-    private static Function tokenizer;
-
-    private static Function ruleParser;
-
-    static {
-        try {
-            BufferedReader br = new BufferedReader(
-                    new InputStreamReader(
-                            CssParser.class.getClassLoader().getResourceAsStream(
-                                    "nu/validator/localentities/files/parse-css-js")));
-            br.mark(1);
-            Context context = ContextFactory.getGlobal().enterContext();
-            context.setOptimizationLevel(1);
-            context.setLanguageVersion(Context.VERSION_1_6);
-            scope = context.initStandardObjects();
-            context.evaluateReader(scope, br, null, -1, null);
-            tokenizer = (Function) scope.get("tokenize", scope);
-            ruleParser = (Function) scope.get("parseARule", scope);
-        } catch (IOException e) {
-        }
-    }
-
-    public String[] tokenize(CharSequence cs) throws ParseException {
-        try {
-            Context context = ContextFactory.getGlobal().enterContext();
-            context.setOptimizationLevel(0);
-            context.setLanguageVersion(Context.VERSION_1_6);
-            return (String[]) Context.jsToJava(
-                    tokenizer.call(context, scope, scope, new Object[] { cs }),
-                    String[].class);
-        } catch (JavaScriptException e) {
-            throw new ParseException(e.details(), -1);
-        }
-    }
-
-    public String parseARule(CharSequence cs) throws ParseException {
-        try {
-            Context context = ContextFactory.getGlobal().enterContext();
-            context.setOptimizationLevel(0);
-            context.setLanguageVersion(Context.VERSION_1_6);
-            return (String) Context.jsToJava(ruleParser.call(context, scope, scope,
-                    new Object[] { tokenizer.call(context, scope, scope,
-                            new Object[] { cs }) }), String.class);
-        } catch (JavaScriptException e) {
-            throw new ParseException(e.details(), -1);
-        }
-    }
-
-}
diff --git a/src/nu/validator/io/DataUri.java b/src/nu/validator/io/DataUri.java
index 74811a9..9677758 100644
--- a/src/nu/validator/io/DataUri.java
+++ b/src/nu/validator/io/DataUri.java
@@ -22,13 +22,14 @@
 
 package nu.validator.io;
 
+import com.hp.hpl.jena.iri.IRIFactory;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringReader;
 import java.net.MalformedURLException;
 
-import io.mola.galimatias.URL;
-import io.mola.galimatias.GalimatiasParseException;
+//import io.mola.galimatias.URL;
+//import io.mola.galimatias.GalimatiasParseException;
 
 public class DataUri {
 
@@ -54,17 +55,17 @@ public class DataUri {
      * @throws MalformedURLException
      * @throws IOException
      */
-    protected void init(URL url) throws IOException, MalformedURLException {
-        if (!url.scheme().equals("data")) {
+    protected void init(com.hp.hpl.jena.iri.IRI url) throws IOException, MalformedURLException {
+        if (!url.getScheme().equals("data")) {
             throw new IllegalArgumentException("The input did not start with data:.");
         }
 
-        if (url.fragment() != null) {
+        if (url.getRawFragment() != null) {
             throw new MalformedURLException(
                     "Fragment is not allowed for data: URIs according to RFC 2397.");
         }
 
-        InputStream is = new PercentDecodingReaderInputStream(new StringReader(url.schemeData()));
+        InputStream is = new PercentDecodingReaderInputStream(new StringReader(url.getRawPath()));
         StringBuilder sb = new StringBuilder();
         State state = State.AT_START;
         int i = 0; // string counter
@@ -254,11 +255,15 @@ public class DataUri {
     }
 
     public DataUri(String url) throws IOException {
-        try {
-            init(URL.parse(url));
-        } catch (GalimatiasParseException e) {
-            throw new MalformedURLException(e.getMessage());
-        }
+        
+        IRIFactory fac = new IRIFactory();
+        fac.shouldViolation(true, false);
+        fac.securityViolation(true, false);
+        fac.dnsViolation(true, false);
+        fac.mintingViolation(false, false);
+        fac.useSpecificationIRI(true);
+        init(fac.construct(url));
+        
     }
 
     /**
@@ -266,7 +271,7 @@ public class DataUri {
      * @throws MalformedURLException
      * @throws IOException
      */
-    public DataUri(URL url) throws IOException, MalformedURLException {
+    public DataUri(com.hp.hpl.jena.iri.IRI url) throws IOException, MalformedURLException {
         init(url);
     }
 
diff --git a/src/nu/validator/localentities/LocalCacheEntityResolver.java b/src/nu/validator/localentities/LocalCacheEntityResolver.java
index 8b6ac69..18de3b8 100644
--- a/src/nu/validator/localentities/LocalCacheEntityResolver.java
+++ b/src/nu/validator/localentities/LocalCacheEntityResolver.java
@@ -4,6 +4,7 @@ import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.net.URL;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -41,7 +42,7 @@ public class LocalCacheEntityResolver implements EntityResolver {
     }
 
     public static InputStream getPresetsAsStream() {
-        return LOADER.getResourceAsStream("nu/validator/localentities/files/presets");
+        return LOADER.getResourceAsStream("nu/validator/localentities/presets");
     }
 
     public static InputStream getHtml5SpecAsStream() {
@@ -53,18 +54,27 @@ public class LocalCacheEntityResolver implements EntityResolver {
     private boolean allowRnc = false;
 
     /**
+     * The map must be safe for concurrent reads.
+     * 
+     * @param pathMap
      * @param delegate
      */
     public LocalCacheEntityResolver(EntityResolver delegate) {
         this.delegate = delegate;
     }
 
+    public static URL getResource(String systemId) {
+        String path = PATH_MAP.get(systemId);
+        return path != null ? LOADER.getResource(path) : null;
+    }
+
     /**
      * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
      *      java.lang.String)
      */
     public InputSource resolveEntity(String publicId, String systemId)
             throws SAXException, IOException {
+        long a = System.currentTimeMillis();
         String path = PATH_MAP.get(systemId);
         if (path != null) {
             InputStream stream = LOADER.getResourceAsStream(path);
@@ -89,6 +99,7 @@ public class LocalCacheEntityResolver implements EntityResolver {
                 return is;
             }
         }
+        System.out.println("resolve :" + publicId +" " + systemId); 
         return delegate.resolveEntity(publicId, systemId);
     }
 
@@ -106,4 +117,4 @@ public class LocalCacheEntityResolver implements EntityResolver {
     public void setAllowRnc(boolean allowRnc) {
         this.allowRnc = allowRnc;
     }
-}
+}
\ No newline at end of file
diff --git a/src/nu/validator/localentities/presets b/src/nu/validator/localentities/presets
new file mode 100644
index 0000000..7966803
--- /dev/null
+++ b/src/nu/validator/localentities/presets
@@ -0,0 +1,10 @@
+-1	-	HTML5 + SVG 1.1 + MathML 3.0	http://s.validator.nu/html5.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/
+-1	-	HTML5 + SVG 1.1 + MathML 3.0 + ITS 2.0	http://s.validator.nu/html5-its.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/
+3	-	HTML5 + SVG 1.1 + MathML 3.0 + RDFa Lite 1.1	http://s.validator.nu/html5-rdfalite.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/
+2	-	HTML 4.01 Strict + IRI / XHTML 1.0 Strict + IRI	http://s.validator.nu/xhtml10/xhtml-strict.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all-html4/
+1	-	HTML 4.01 Transitional + IRI / XHTML 1.0 Transitional + IRI	http://s.validator.nu/xhtml10/xhtml-transitional.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all-html4/
+-1	-	HTML 4.01 Frameset + IRI / XHTML 1.0 Frameset + IRI	http://s.validator.nu/xhtml10/xhtml-frameset.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all-html4/
+-1	-	XHTML5 + SVG 1.1 + MathML 3.0	http://s.validator.nu/xhtml5.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/
+7	http://www.w3.org/1999/xhtml	XHTML5 + SVG 1.1 + MathML 3.0 + RDFa Lite 1.1	http://s.validator.nu/xhtml5-rdfalite.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/
+-1	-	XHTML 1.0 Strict + IRI + Ruby + SVG 1.1 + MathML 3.0	http://s.validator.nu/xhtml1-ruby-rdf-svg-mathml.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all-html4/
+-1	http://www.w3.org/2000/svg	SVG 1.1 + IRI + XHTML5 + MathML 3.0	http://s.validator.nu/svg-xhtml5-rdf-mathml.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/
\ No newline at end of file
diff --git a/src/nu/validator/messages/BufferingRootNamespaceSniffer.java b/src/nu/validator/messages/BufferingRootNamespaceSniffer.java
new file mode 100644
index 0000000..13e5b07
--- /dev/null
+++ b/src/nu/validator/messages/BufferingRootNamespaceSniffer.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2006 Henri Sivonen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a 
+ * copy of this software and associated documentation files (the "Software"), 
+ * to deal in the Software without restriction, including without limitation 
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
+ * and/or sell copies of the Software, and to permit persons to whom the 
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in 
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package nu.validator.messages;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+public class BufferingRootNamespaceSniffer implements ContentHandler {
+
+    private ContentHandler ch = null;
+
+    private Locator locator = null;
+    
+    private List<String[]> namespaces = new LinkedList<String[]>();
+    
+    private ValidationTransaction vst;
+    
+    public BufferingRootNamespaceSniffer(ValidationTransaction vst) {
+        super();
+        this.vst = vst;
+    }
+
+    public void setContentHandler(ContentHandler contentHandler) throws SAXException {
+        this.ch = contentHandler;
+        if (locator != null) {
+            ch.setDocumentLocator(locator);
+        }
+        ch.startDocument();
+        for (Iterator<String[]> iter = namespaces.iterator(); iter.hasNext();) {
+            String[] element = iter.next();
+            ch.startPrefixMapping(element[0], element[1]);
+        }
+    }
+    
+    /**
+     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+     */
+    public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
+        if (ch != null) {
+            ch.characters(arg0, arg1, arg2);
+        }
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endDocument()
+     */
+    public void endDocument() throws SAXException {
+        if (ch != null) {
+            ch.endDocument();
+        }
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endElement(java.lang.String,
+     *      java.lang.String, java.lang.String)
+     */
+    public void endElement(String arg0, String arg1, String arg2)
+            throws SAXException {
+        if (ch != null) {
+            ch.endElement(arg0, arg1, arg2);
+        }
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
+     */
+    public void endPrefixMapping(String arg0) throws SAXException {
+        if (ch != null) {
+            ch.endPrefixMapping(arg0);
+        }
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+     */
+    public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
+            throws SAXException {
+        if (ch != null) {
+            ch.ignorableWhitespace(arg0, arg1, arg2);
+        }
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String,
+     *      java.lang.String)
+     */
+    public void processingInstruction(String arg0, String arg1)
+            throws SAXException {
+        if (ch != null) {
+            ch.processingInstruction(arg0, arg1);
+        }
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
+     */
+    public void setDocumentLocator(Locator arg0) {
+        locator = arg0;
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
+     */
+    public void skippedEntity(String arg0) throws SAXException {
+        if (ch != null) {
+            ch.skippedEntity(arg0);
+        }
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startDocument()
+     */
+    public void startDocument() throws SAXException {
+
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startElement(java.lang.String,
+     *      java.lang.String, java.lang.String, org.xml.sax.Attributes)
+     */
+    public void startElement(String arg0, String arg1, String arg2,
+            Attributes arg3) throws SAXException {
+        if (ch != null) {
+            ch.startElement(arg0, arg1, arg2, arg3);
+        } else {
+            vst.rootNamespace(arg0, locator);
+            ch.startElement(arg0, arg1, arg2, arg3);
+        }
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String,
+     *      java.lang.String)
+     */
+    public void startPrefixMapping(String arg0, String arg1)
+            throws SAXException {
+        if (ch != null) {
+            ch.startPrefixMapping(arg0, arg1);
+        } else {
+            String[] arr = new String[2];
+            arr[0] = arg0;
+            arr[1] = arg1;
+            namespaces.add(arr);
+        }
+    }
+
+}
diff --git a/src/nu/validator/messages/RootNamespaceSniffer.java b/src/nu/validator/messages/RootNamespaceSniffer.java
new file mode 100644
index 0000000..1981475
--- /dev/null
+++ b/src/nu/validator/messages/RootNamespaceSniffer.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2006 Henri Sivonen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a 
+ * copy of this software and associated documentation files (the "Software"), 
+ * to deal in the Software without restriction, including without limitation 
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
+ * and/or sell copies of the Software, and to permit persons to whom the 
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in 
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package nu.validator.messages;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+public class RootNamespaceSniffer implements ContentHandler {
+
+    private ValidationTransaction vst;
+    private ContentHandler ch;
+    private Locator locator;
+
+    public RootNamespaceSniffer(ValidationTransaction vst, ContentHandler ch) {
+        super();
+        this.vst = vst;
+        this.ch = ch;
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+     */
+    public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
+        ch.characters(arg0, arg1, arg2);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endDocument()
+     */
+    public void endDocument() throws SAXException {
+        ch.endDocument();
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+     */
+    public void endElement(String arg0, String arg1, String arg2) throws SAXException {
+        ch.endElement(arg0, arg1, arg2);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
+     */
+    public void endPrefixMapping(String arg0) throws SAXException {
+        ch.endPrefixMapping(arg0);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+     */
+    public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
+        ch.ignorableWhitespace(arg0, arg1, arg2);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String)
+     */
+    public void processingInstruction(String arg0, String arg1) throws SAXException {
+        ch.processingInstruction(arg0, arg1);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
+     */
+    public void setDocumentLocator(Locator arg0) {
+        this.locator = arg0;
+        ch.setDocumentLocator(arg0);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
+     */
+    public void skippedEntity(String arg0) throws SAXException {
+        ch.skippedEntity(arg0);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startDocument()
+     */
+    public void startDocument() throws SAXException {
+        ch.startDocument();
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+     */
+    public void startElement(String arg0, String arg1, String arg2, Attributes arg3) throws SAXException {
+        vst.rootNamespace(arg0, locator);
+        ch.startElement(arg0, arg1, arg2, arg3);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
+     */
+    public void startPrefixMapping(String arg0, String arg1) throws SAXException {
+        ch.startPrefixMapping(arg0, arg1);
+    }
+
+}
diff --git a/src/nu/validator/messages/ValidationTransaction.java b/src/nu/validator/messages/ValidationTransaction.java
new file mode 100644
index 0000000..9b60092
--- /dev/null
+++ b/src/nu/validator/messages/ValidationTransaction.java
@@ -0,0 +1,466 @@
+package nu.validator.messages;
+
+import com.thaiopensource.relaxng.impl.CombineValidator;
+import com.thaiopensource.util.PropertyMap;
+import com.thaiopensource.validate.IncorrectSchemaException;
+import com.thaiopensource.validate.Schema;
+import com.thaiopensource.validate.SchemaReader;
+import com.thaiopensource.validate.SchemaResolver;
+import com.thaiopensource.validate.Validator;
+import com.thaiopensource.validate.auto.AutoSchemaReader;
+import com.thaiopensource.validate.prop.wrap.WrapProperty;
+import com.thaiopensource.validate.rng.CompactSchemaReader;
+import java.io.IOException;
+import java.lang.ref.SoftReference;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import nu.validator.checker.XmlPiChecker;
+import nu.validator.checker.jing.CheckerSchema;
+import nu.validator.htmlparser.common.DocumentMode;
+import nu.validator.htmlparser.common.DocumentModeHandler;
+import nu.validator.htmlparser.sax.HtmlParser;
+import nu.validator.localentities.LocalCacheEntityResolver;
+import nu.validator.spec.Spec;
+import nu.validator.xml.TypedInputSource;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * This class code was mainly extracted from the original class {@link VerifierServletTransaction}.
+ *
+ * @author hsivonen, mfukala@netbeans.org
+ */
+public class ValidationTransaction implements DocumentModeHandler, SchemaResolver {
+    private static final Logger LOGGER = Logger.getLogger(ValidationTransaction.class.getCanonicalName());
+
+    // XXX SVG!!!
+    private static final String[] KNOWN_CONTENT_TYPES = {
+        "application/atom+xml", "application/docbook+xml",
+        "application/xhtml+xml", "application/xv+xml", "image/svg+xml"};
+    private static final String[] NAMESPACES_FOR_KNOWN_CONTENT_TYPES = {
+        "http://www.w3.org/2005/Atom", "http://docbook.org/ns/docbook",
+        "http://www.w3.org/1999/xhtml", "http://www.w3.org/1999/xhtml",
+        "http://www.w3.org/2000/svg"};
+    protected static final String[] ALL_CHECKERS = {
+        "http://c.validator.nu/table/", "http://c.validator.nu/nfc/",
+        "http://c.validator.nu/text-content/",
+        "http://c.validator.nu/unchecked/",
+        "http://c.validator.nu/usemap/", "http://c.validator.nu/obsolete/",
+        "http://c.validator.nu/xml-pi/"};
+    private static final String[] ALL_CHECKERS_HTML4 = {
+        "http://c.validator.nu/table/", "http://c.validator.nu/nfc/",
+        "http://c.validator.nu/unchecked/", "http://c.validator.nu/usemap/"};
+    
+    protected BufferingRootNamespaceSniffer bufferingRootNamespaceSniffer = null;
+    protected boolean rootNamespaceSeen = false;
+    protected String contentType = null;
+    
+    protected static int[] presetDoctypes;
+    protected static String[] presetLabels;
+    protected static String[] presetUrls;
+    protected static String[] presetNamespaces;
+    
+    protected MessageEmitterAdapter errorHandler;
+    protected static String[] preloadedSchemaUrls;
+    protected static Schema[] preloadedSchemas;
+    
+    private Map<String, Validator> loadedValidatorUrls = new HashMap<String, Validator>();
+    
+    protected Validator validator = null;
+    protected LocalCacheEntityResolver entityResolver;
+    
+    private static final Pattern SPACE = Pattern.compile("\\s+");
+    protected static final int HTML5_SCHEMA = 3;
+    protected static final int XHTML1STRICT_SCHEMA = 2;
+    protected static final int XHTML1FRAMESET_SCHEMA = 4;
+    protected static final int XHTML1TRANSITIONAL_SCHEMA = 1;
+    protected static final int XHTML5_SCHEMA = 7;
+    
+    public HtmlParser htmlParser = null;
+    protected PropertyMap jingPropertyMap;
+    protected static Spec html5spec;
+    
+    protected XMLReader reader;
+    protected LexicalHandler lexicalHandler;
+    
+    public void rootNamespace(String namespace, Locator locator) throws SAXException {
+        if (validator == null) {
+            int index = -1;
+            for (int i = 0; i < presetNamespaces.length; i++) {
+                if (namespace.equals(presetNamespaces[i])) {
+                    index = i;
+                    break;
+                }
+            }
+            if (index == -1) {
+                String message = "Cannot find preset schema for namespace: \u201C"
+                        + namespace + "\u201D.";
+                SAXException se = new SAXException(message);
+                errorHandler.schemaError(se);
+                throw se;
+            }
+            String label = presetLabels[index];
+            String urls = presetUrls[index];
+            errorHandler.info("Using the preset for " + label
+                    + " based on the root namespace " + namespace);
+            try {
+                validator = validatorByUrls(urls);
+            } catch (IOException ioe) {
+                // At this point the schema comes from memory.
+                throw new RuntimeException(ioe);
+            } catch (IncorrectSchemaException e) {
+                // At this point the schema comes from memory.
+                throw new RuntimeException(e);
+            }
+            if (bufferingRootNamespaceSniffer == null) {
+                throw new RuntimeException(
+                        "Bug! bufferingRootNamespaceSniffer was null.");
+            }
+            bufferingRootNamespaceSniffer.setContentHandler(validator.getContentHandler());
+        }
+
+        if (!rootNamespaceSeen) {
+            rootNamespaceSeen = true;
+            if (contentType != null) {
+                int i;
+                if ((i = Arrays.binarySearch(KNOWN_CONTENT_TYPES, contentType)) > -1) {
+                    if (!NAMESPACES_FOR_KNOWN_CONTENT_TYPES[i].equals(namespace)) {
+                        String message = "".equals(namespace) ? "\u201C"
+                                + contentType
+                                + "\u201D is not an appropriate Content-Type for a document whose root element is not in a namespace."
+                                : "\u201C"
+                                + contentType
+                                + "\u201D is not an appropriate Content-Type for a document whose root namespace is \u201C"
+                                + namespace + "\u201D.";
+                        SAXParseException spe = new SAXParseException(message,
+                                locator);
+                        errorHandler.warning(spe);
+                    }
+                }
+            }
+        }
+    }
+    
+    @Override
+    public void documentMode(DocumentMode mode, String publicIdentifier,
+            String systemIdentifier, boolean html4SpecificAdditionalErrorChecks)
+            throws SAXException {
+        if (validator == null) {
+            try {
+                if ("-//W3C//DTD XHTML 1.0 Transitional//EN".equals(publicIdentifier)) {
+                    errorHandler.info("XHTML 1.0 Transitional doctype seen. Appendix C is not supported. Proceeding anyway for your convenience. The parser is still an HTML parser, so namespace processing is not performed and \u201Cxml:*\u201D attributes are not supported. Using the schema for "
+                            + getPresetLabel(XHTML1TRANSITIONAL_SCHEMA)
+                            + "."
+                            + (html4SpecificAdditionalErrorChecks ? " HTML4-specific tokenization errors are enabled."
+                            : ""));
+                    validator = validatorByDoctype(XHTML1TRANSITIONAL_SCHEMA);
+                } else if ("-//W3C//DTD XHTML 1.0 Strict//EN".equals(publicIdentifier)) {
+                    errorHandler.info("XHTML 1.0 Strict doctype seen. Appendix C is not supported. Proceeding anyway for your convenience. The parser is still an HTML parser, so namespace processing is not performed and \u201Cxml:*\u201D attributes are not supported. Using the schema for "
+                            + getPresetLabel(XHTML1STRICT_SCHEMA)
+                            + "."
+                            + (html4SpecificAdditionalErrorChecks ? " HTML4-specific tokenization errors are enabled."
+                            : ""));
+                    validator = validatorByDoctype(XHTML1STRICT_SCHEMA);
+                } else if ("-//W3C//DTD HTML 4.01 Transitional//EN".equals(publicIdentifier)) {
+                    errorHandler.info("HTML 4.01 Transitional doctype seen. Using the schema for "
+                            + getPresetLabel(XHTML1TRANSITIONAL_SCHEMA)
+                            + "."
+                            + (html4SpecificAdditionalErrorChecks ? ""
+                            : " HTML4-specific tokenization errors are not enabled."));
+                    validator = validatorByDoctype(XHTML1TRANSITIONAL_SCHEMA);
+                } else if ("-//W3C//DTD HTML 4.01//EN".equals(publicIdentifier)) {
+                    errorHandler.info("HTML 4.01 Strict doctype seen. Using the schema for "
+                            + getPresetLabel(XHTML1STRICT_SCHEMA)
+                            + "."
+                            + (html4SpecificAdditionalErrorChecks ? ""
+                            : " HTML4-specific tokenization errors are not enabled."));
+                    validator = validatorByDoctype(XHTML1STRICT_SCHEMA);
+                } else if ("-//W3C//DTD HTML 4.0 Transitional//EN".equals(publicIdentifier)) {
+                    errorHandler.info("Legacy HTML 4.0 Transitional doctype seen.  Please consider using HTML 4.01 Transitional instead. Proceeding anyway for your convenience with the schema for "
+                            + getPresetLabel(XHTML1TRANSITIONAL_SCHEMA)
+                            + "."
+                            + (html4SpecificAdditionalErrorChecks ? ""
+                            : " HTML4-specific tokenization errors are not enabled."));
+                    validator = validatorByDoctype(XHTML1TRANSITIONAL_SCHEMA);
+                } else if ("-//W3C//DTD HTML 4.0//EN".equals(publicIdentifier)) {
+                    errorHandler.info("Legacy HTML 4.0 Strict doctype seen. Please consider using HTML 4.01 instead. Proceeding anyway for your convenience with the schema for "
+                            + getPresetLabel(XHTML1STRICT_SCHEMA)
+                            + "."
+                            + (html4SpecificAdditionalErrorChecks ? ""
+                            : " HTML4-specific tokenization errors are not enabled."));
+                    validator = validatorByDoctype(XHTML1STRICT_SCHEMA);
+                } else {
+                    errorHandler.info("Using the schema for "
+                            + getPresetLabel(HTML5_SCHEMA)
+                            + "."
+                            + (html4SpecificAdditionalErrorChecks ? " HTML4-specific tokenization errors are enabled."
+                            : ""));
+                    validator = validatorByDoctype(HTML5_SCHEMA);
+                }
+            } catch (IOException ioe) {
+                // At this point the schema comes from memory.
+                throw new RuntimeException(ioe);
+            } catch (IncorrectSchemaException e) {
+                // At this point the schema comes from memory.
+                throw new RuntimeException(e);
+            }
+            ContentHandler ch = validator.getContentHandler();
+            ch.setDocumentLocator(htmlParser.getDocumentLocator());
+            ch.startDocument();
+            reader.setContentHandler(ch);
+        } else {
+            if (html4SpecificAdditionalErrorChecks) {
+                errorHandler.info("HTML4-specific tokenization errors are enabled.");
+            }
+        }
+    }
+
+    public Schema resolveSchema(String url, PropertyMap options)
+            throws SAXException, IOException, IncorrectSchemaException {
+        int i = Arrays.binarySearch(preloadedSchemaUrls, url);
+        if (i > -1) {
+            Schema rv = preloadedSchemas[i];
+            if (options.contains(WrapProperty.ATTRIBUTE_OWNER)) {
+                if(rv instanceof ValidationTransaction.ProxySchema && ((ValidationTransaction.ProxySchema)rv).getWrappedSchema() instanceof CheckerSchema) {
+                    errorHandler.error(new SAXParseException(
+                            "A non-schema checker cannot be used as an attribute schema.",
+                            null, url, -1, -1));
+                    throw new IncorrectSchemaException();
+                } else {
+                    // ugly fall through
+                }
+            } else {
+                return rv;
+            }
+        }
+
+        //this code line should not normally be encountered since the necessary
+        //schemas have been preloaded
+        LOGGER.log(Level.INFO, "Going to create a non preloaded Schema for {0}", url); //NOI18N
+        
+        TypedInputSource schemaInput = (TypedInputSource) entityResolver.resolveEntity(
+                null, url);
+        SchemaReader sr = null;
+        if ("application/relax-ng-compact-syntax".equals(schemaInput.getType())) {
+            sr = CompactSchemaReader.getInstance();
+        } else {
+            sr = new AutoSchemaReader();
+        }
+        Schema sch = sr.createSchema(schemaInput, options);
+        return sch;
+    }
+    
+    /**
+     * @param validator
+     * @return
+     * @throws SAXException
+     * @throws IOException
+     * @throws IncorrectSchemaException
+     */
+    protected Validator validatorByUrls(String schemaList) throws SAXException,
+            IOException, IncorrectSchemaException {
+        Validator v = null;
+        String[] schemas = SPACE.split(schemaList);
+        for (int i = schemas.length - 1; i > -1; i--) {
+            String url = schemas[i];
+            if ("http://c.validator.nu/all/".equals(url)
+                    || "http://hsivonen.iki.fi/checkers/all/".equals(url)) {
+                for (int j = 0; j < ALL_CHECKERS.length; j++) {
+                    v = combineValidatorByUrl(v, ALL_CHECKERS[j]);
+                }
+            } else if ("http://c.validator.nu/all-html4/".equals(url)
+                    || "http://hsivonen.iki.fi/checkers/all-html4/".equals(url)) {
+                for (int j = 0; j < ALL_CHECKERS_HTML4.length; j++) {
+                    v = combineValidatorByUrl(v, ALL_CHECKERS_HTML4[j]);
+                }
+            } else {
+                v = combineValidatorByUrl(v, url);
+            }
+        }
+        return v;
+    }
+    
+    /**
+     * @param val
+     * @param url
+     * @return
+     * @throws SAXException
+     * @throws IOException
+     * @throws IncorrectSchemaException
+     */
+    private Validator combineValidatorByUrl(Validator val, String url)
+            throws SAXException, IOException, IncorrectSchemaException {
+        if (!"".equals(url)) {
+            Validator v = validatorByUrl(url);
+            if (val == null) {
+                val = v;
+            } else {
+                val = new CombineValidator(v, val);
+            }
+        }
+        return val;
+    }
+    
+    /**
+     * @param url
+     * @return
+     * @throws SAXException
+     * @throws IOException
+     * @throws IncorrectSchemaException
+     */
+    private Validator validatorByUrl(String url) throws SAXException,
+            IOException, IncorrectSchemaException {
+        Validator v = loadedValidatorUrls.get(url);
+        if (v != null) {
+            return v;
+        }
+
+
+        if ("http://s.validator.nu/html5/html5full-aria.rnc".equals(url)
+                || "http://s.validator.nu/xhtml5-aria-rdf-svg-mathml.rnc".equals(url)
+                || "http://s.validator.nu/html5/html5full.rnc".equals(url)
+                || "http://s.validator.nu/html5/xhtml5full-xhtml.rnc".equals(url)
+                || "http://s.validator.nu/html5-aria-svg-mathml.rnc".equals(url)) {
+            errorHandler.setSpec(html5spec);
+        }
+        Schema sch = resolveSchema(url, jingPropertyMap);
+        Validator validatorInstance = sch.createValidator(jingPropertyMap);
+        if (validatorInstance.getContentHandler() instanceof XmlPiChecker) {
+            lexicalHandler = (LexicalHandler) validatorInstance.getContentHandler();
+        }
+
+        loadedValidatorUrls.put(url, v);
+        return validatorInstance;
+    }
+    
+    private String getPresetLabel(int schemaId) {
+        for (int i = 0; i < presetDoctypes.length; i++) {
+            if (presetDoctypes[i] == schemaId) {
+                return presetLabels[i];
+            }
+        }
+        return "unknown";
+    }
+    
+    protected Validator validatorByDoctype(int schemaId) throws SAXException,
+            IOException, IncorrectSchemaException {
+        if (schemaId == 0) {
+            return null;
+        }
+        for (int i = 0; i < presetDoctypes.length; i++) {
+            if (presetDoctypes[i] == schemaId) {
+                return validatorByUrls(presetUrls[i]);
+            }
+        }
+        throw new RuntimeException("Doctype mappings not initialized properly.");
+    }
+    
+    
+    /**
+     * @param url
+     * @return
+     * @throws SAXException
+     * @throws IOException
+     * @throws IncorrectSchemaException
+     */
+    private static Schema schemaByUrl(String url, EntityResolver resolver,
+            PropertyMap pMap) throws SAXException, IOException,
+            IncorrectSchemaException {
+        LOGGER.fine(String.format("Will load schema: %s", url));
+        long a = System.currentTimeMillis();
+        TypedInputSource schemaInput;
+        try {
+            schemaInput = (TypedInputSource) resolver.resolveEntity(
+                    null, url);
+        } catch (ClassCastException e) {
+            LOGGER.log(Level.SEVERE, url, e);
+            throw e;
+        }
+
+        SchemaReader sr = null;
+        if ("application/relax-ng-compact-syntax".equals(schemaInput.getType())) {
+            sr = CompactSchemaReader.getInstance();
+            LOGGER.log(Level.FINE, "Used CompactSchemaReader");
+        } else {
+            sr = new AutoSchemaReader();
+            LOGGER.log(Level.FINE, "Used AutoSchemaReader");
+        }
+        long c = System.currentTimeMillis();
+
+        Schema sch = sr.createSchema(schemaInput, pMap);
+        LOGGER.log(Level.FINE, String.format("Schema created in %s ms.", (System.currentTimeMillis() - c)));
+        return sch;
+    }
+    
+    protected static Schema proxySchemaByUrl(String uri, EntityResolver resolver, PropertyMap pMap) {
+        return new ProxySchema(uri, resolver, pMap);
+    }
+    
+    /**
+     * A Schema instance delegate, the delegated instance if softly reachable so it should 
+     * not be GCed so often. If the delegate is GCed a new instance is recreated.
+     */
+    private static class ProxySchema implements Schema {
+    
+        private String uri;
+        private EntityResolver resolver;
+        private PropertyMap pMap;
+        
+        private SoftReference<Schema> delegateWeakRef;
+        
+        private ProxySchema(String uri, EntityResolver resolver, PropertyMap pMap) {
+            this.uri = uri;
+            this.resolver = resolver;
+            this.pMap = pMap;
+        }
+
+        //exposing just because of some instanceof test used in the code
+        private Schema getWrappedSchema() throws SAXException, IOException, IncorrectSchemaException {
+            return getSchemaDelegate();
+        }
+        
+        public Validator createValidator(PropertyMap pm) {
+            try {
+                return getSchemaDelegate().createValidator(pm);
+            } catch (Exception ex) { //SAXException, IOException, IncorrectSchemaException
+                LOGGER.log(Level.INFO, "Cannot create schema delegate", ex); //NOI18N
+            }
+            return null;
+        }
+
+        public PropertyMap getProperties() {
+            try {
+                return getSchemaDelegate().getProperties();
+            } catch (Exception ex) { //SAXException, IOException, IncorrectSchemaException
+                LOGGER.log(Level.INFO, "Cannot create schema delegate", ex); //NOI18N
+            }
+            return null;
+        }
+        
+        private synchronized Schema getSchemaDelegate() throws SAXException, IOException, IncorrectSchemaException {
+            Schema delegate = delegateWeakRef != null ? delegateWeakRef.get() : null;
+            if(delegate == null) {
+                long a = System.currentTimeMillis();
+                delegate = schemaByUrl(uri, resolver, pMap);
+                long b = System.currentTimeMillis();
+                delegateWeakRef = new SoftReference<Schema>(delegate);
+                LOGGER.log(Level.FINE, "Created new Schema instance for {0} in {1}ms.", new Object[]{uri, (b-a)});
+            } else {
+                LOGGER.log(Level.FINE, "Using cached Schema instance for {0}", uri);
+            }
+            return delegate;
+        }
+        
+        
+    }
+    
+}
diff --git a/src/nu/validator/servlet/BufferingRootNamespaceSniffer.java b/src/nu/validator/servlet/BufferingRootNamespaceSniffer.java
deleted file mode 100644
index f339667..0000000
--- a/src/nu/validator/servlet/BufferingRootNamespaceSniffer.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (c) 2006 Henri Sivonen
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-
-public class BufferingRootNamespaceSniffer implements ContentHandler {
-
-    private ContentHandler ch = null;
-
-    private Locator locator = null;
-    
-    private List<String[]> namespaces = new LinkedList<String[]>();
-    
-    private VerifierServletTransaction vst;
-    
-    public BufferingRootNamespaceSniffer(VerifierServletTransaction vst) {
-        super();
-        this.vst = vst;
-    }
-
-    public void setContentHandler(ContentHandler contentHandler) throws SAXException {
-        this.ch = contentHandler;
-        if (locator != null) {
-            ch.setDocumentLocator(locator);
-        }
-        ch.startDocument();
-        for (Iterator<String[]> iter = namespaces.iterator(); iter.hasNext();) {
-            String[] element = iter.next();
-            ch.startPrefixMapping(element[0], element[1]);
-        }
-    }
-    
-    /**
-     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
-     */
-    public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
-        if (ch != null) {
-            ch.characters(arg0, arg1, arg2);
-        }
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#endDocument()
-     */
-    public void endDocument() throws SAXException {
-        if (ch != null) {
-            ch.endDocument();
-        }
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#endElement(java.lang.String,
-     *      java.lang.String, java.lang.String)
-     */
-    public void endElement(String arg0, String arg1, String arg2)
-            throws SAXException {
-        if (ch != null) {
-            ch.endElement(arg0, arg1, arg2);
-        }
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
-     */
-    public void endPrefixMapping(String arg0) throws SAXException {
-        if (ch != null) {
-            ch.endPrefixMapping(arg0);
-        }
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
-     */
-    public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
-            throws SAXException {
-        if (ch != null) {
-            ch.ignorableWhitespace(arg0, arg1, arg2);
-        }
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String,
-     *      java.lang.String)
-     */
-    public void processingInstruction(String arg0, String arg1)
-            throws SAXException {
-        if (ch != null) {
-            ch.processingInstruction(arg0, arg1);
-        }
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
-     */
-    public void setDocumentLocator(Locator arg0) {
-        locator = arg0;
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
-     */
-    public void skippedEntity(String arg0) throws SAXException {
-        if (ch != null) {
-            ch.skippedEntity(arg0);
-        }
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#startDocument()
-     */
-    public void startDocument() throws SAXException {
-
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#startElement(java.lang.String,
-     *      java.lang.String, java.lang.String, org.xml.sax.Attributes)
-     */
-    public void startElement(String arg0, String arg1, String arg2,
-            Attributes arg3) throws SAXException {
-        if (ch != null) {
-            ch.startElement(arg0, arg1, arg2, arg3);
-        } else {
-            vst.rootNamespace(arg0, locator);
-            ch.startElement(arg0, arg1, arg2, arg3);
-        }
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String,
-     *      java.lang.String)
-     */
-    public void startPrefixMapping(String arg0, String arg1)
-            throws SAXException {
-        if (ch != null) {
-            ch.startPrefixMapping(arg0, arg1);
-        } else {
-            String[] arr = new String[2];
-            arr[0] = arg0;
-            arr[1] = arg1;
-            namespaces.add(arr);
-        }
-    }
-
-}
diff --git a/src/nu/validator/servlet/CharsetEmitter.java b/src/nu/validator/servlet/CharsetEmitter.java
deleted file mode 100644
index 6b795b8..0000000
--- a/src/nu/validator/servlet/CharsetEmitter.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/* This code was generated by nu.validator.tools.SaxCompiler. Please regenerate instead of editing. */
-package nu.validator.servlet;
-public final class CharsetEmitter {
-private CharsetEmitter() {}
-public static void emit(org.xml.sax.ContentHandler contentHandler, nu.validator.servlet.VerifierServletTransaction t) throws org.xml.sax.SAXException {
-org.xml.sax.helpers.AttributesImpl __attrs__ = new org.xml.sax.helpers.AttributesImpl();
-contentHandler.startPrefixMapping("", "http://www.w3.org/1999/xhtml");
-__attrs__.clear();
-__attrs__.addAttribute("", "title", "title", "CDATA", "Override for transfer protocol character encoding declaration.");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "tr", "tr", __attrs__);
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "th", "th", __attrs__);
-__attrs__.clear();
-__attrs__.addAttribute("", "for", "for", "CDATA", "charset");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "label", "label", __attrs__);
-contentHandler.characters(__chars__, 0, 8);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "label", "label");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "th", "th");
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "td", "td", __attrs__);
-__attrs__.clear();
-__attrs__.addAttribute("", "id", "id", "CDATA", "charset");
-__attrs__.addAttribute("", "name", "name", "CDATA", "charset");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "select", "select", __attrs__);
-__attrs__.clear();
-__attrs__.addAttribute("", "value", "value", "CDATA", "");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "option", "option", __attrs__);
-contentHandler.characters(__chars__, 8, 25);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "option", "option");
-t.emitCharsetOptions(); 
-		
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "select", "select");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "td", "td");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "tr", "tr");
-contentHandler.endPrefixMapping("");
-}
-private static final char[] __chars__ = { 'E', 'n', 'c', 'o', 'd', 'i', 'n', 'g', 'A', 's', ' ', 's', 'e', 't', ' ', 'b', 'y', ' ', 't', 'h', 'e', ' ', 's', 'e', 'r', 'v', 'e', 'r', '/', 'p', 'a', 'g', 'e' };
-}
diff --git a/src/nu/validator/servlet/CssDetector.java b/src/nu/validator/servlet/CssDetector.java
deleted file mode 100644
index e9308d7..0000000
--- a/src/nu/validator/servlet/CssDetector.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (c) 2008 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import nu.validator.checker.AttributeUtil;
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.DTDHandler;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-
-import com.thaiopensource.validate.Validator;
-
-public class CssDetector implements Validator, ContentHandler {
-
-    public static boolean lowerCaseLiteralEqualsIgnoreAsciiCase(String lowerCaseLiteral,
-            String string) {
-        if (string == null) {
-            return false;
-        }
-        if (lowerCaseLiteral.length() != string.length()) {
-            return false;
-        }
-        for (int i = 0; i < lowerCaseLiteral.length(); i++) {
-            char c0 = lowerCaseLiteral.charAt(i);
-            char c1 = string.charAt(i);
-            if (c1 >= 'A' && c1 <= 'Z') {
-                c1 += 0x20;
-            }
-            if (c0 != c1) {
-                return false;
-            }
-        }
-        return true;
-    }
-    
-    private static final Pattern TEXT_CSS = Pattern.compile("^[tT][eE][xX][tT]/[cC][sS][sS]\\s*(?:;.*)?$");
-    
-    private boolean sawCss = false;
-    
-    public ContentHandler getContentHandler() {
-        return this;
-    }
-
-    public DTDHandler getDTDHandler() {
-        return null;
-    }
-
-    public void reset() {
-        sawCss = false;
-    }
-
-    public void characters(char[] ch, int start, int length)
-            throws SAXException {
-        
-    }
-
-    public void endDocument() throws SAXException {
-        
-    }
-
-    public void endElement(String uri, String localName, String name)
-            throws SAXException {
-        
-    }
-
-    public void endPrefixMapping(String prefix) throws SAXException {
-        // TODO Auto-generated method stub
-        
-    }
-
-    public void ignorableWhitespace(char[] ch, int start, int length)
-            throws SAXException {
-        
-    }
-
-    public void processingInstruction(String target, String data)
-            throws SAXException {
-        
-    }
-
-    public void setDocumentLocator(Locator locator) {
-        
-    }
-
-    public void skippedEntity(String name) throws SAXException {
-        
-    }
-
-    public void startDocument() throws SAXException {
-        reset();
-    }
-
-    public void startElement(String uri, String localName, String name,
-            Attributes atts) throws SAXException {
-        if ("http://www.w3.org/1999/xhtml" == uri) {
-            if ("style" == localName) {
-                checkType(atts);
-                return;
-            } else if ("link" == localName) {
-                String rel = atts.getValue("", "rel");
-                if (rel != null) {
-                    String[] tokens = AttributeUtil.split(rel);
-                    for (int i = 0; i < tokens.length; i++) {
-                        String token = tokens[i];
-                        if (lowerCaseLiteralEqualsIgnoreAsciiCase("stylesheet", token)) {
-                            checkType(atts);
-                            return;
-                        }
-                    }
-                }
-            } else {
-                if (atts.getIndex("", "style") > -1) {
-                    sawCss = true;
-                }
-            }
-        }
-    }
-
-    private void checkType(Attributes atts) {
-        String type = atts.getValue("", "type");
-        if (type == null) {
-            sawCss = true;
-        } else {
-            Matcher m = TEXT_CSS.matcher(type);
-            if (m.matches()) {
-                sawCss = true;                
-            }
-        }
-    }
-
-    public void startPrefixMapping(String prefix, String uri)
-            throws SAXException {
-        
-    }
-
-}
diff --git a/src/nu/validator/servlet/DelegatingServletInputStream.java b/src/nu/validator/servlet/DelegatingServletInputStream.java
deleted file mode 100644
index db669fa..0000000
--- a/src/nu/validator/servlet/DelegatingServletInputStream.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (c) 2007-2015 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.servlet.ReadListener;
-import javax.servlet.ServletInputStream;
-
-public final class DelegatingServletInputStream extends ServletInputStream {
-
-    private final InputStream delegate;
-
-    public DelegatingServletInputStream(InputStream delegate) {
-        this.delegate = delegate;
-    }
-
-    /**
-     * @return
-     * @throws IOException
-     * @see java.io.InputStream#available()
-     */
-    public int available() throws IOException {
-        return delegate.available();
-    }
-
-    /**
-     * @throws IOException
-     * @see java.io.InputStream#close()
-     */
-    public void close() throws IOException {
-        delegate.close();
-    }
-
-    /**
-     * @param obj
-     * @return
-     * @see java.lang.Object#equals(java.lang.Object)
-     */
-    public boolean equals(Object obj) {
-        return delegate.equals(obj);
-    }
-
-    /**
-     * @return
-     * @see java.lang.Object#hashCode()
-     */
-    public int hashCode() {
-        return delegate.hashCode();
-    }
-
-    /**
-     * @param readlimit
-     * @see java.io.InputStream#mark(int)
-     */
-    public void mark(int readlimit) {
-        delegate.mark(readlimit);
-    }
-
-    /**
-     * @return
-     * @see java.io.InputStream#markSupported()
-     */
-    public boolean markSupported() {
-        return delegate.markSupported();
-    }
-
-    /**
-     * @return
-     * @throws IOException
-     * @see java.io.InputStream#read()
-     */
-    public int read() throws IOException {
-        return delegate.read();
-    }
-
-    /**
-     * @param b
-     * @param off
-     * @param len
-     * @return
-     * @throws IOException
-     * @see java.io.InputStream#read(byte[], int, int)
-     */
-    public int read(byte[] b, int off, int len) throws IOException {
-        return delegate.read(b, off, len);
-    }
-
-    /**
-     * @param b
-     * @return
-     * @throws IOException
-     * @see java.io.InputStream#read(byte[])
-     */
-    public int read(byte[] b) throws IOException {
-        return delegate.read(b);
-    }
-
-    /**
-     * @throws IOException
-     * @see java.io.InputStream#reset()
-     */
-    public void reset() throws IOException {
-        delegate.reset();
-    }
-
-    /**
-     * @param n
-     * @return
-     * @throws IOException
-     * @see java.io.InputStream#skip(long)
-     */
-    public long skip(long n) throws IOException {
-        return delegate.skip(n);
-    }
-
-    /**
-     * @return
-     * @see java.lang.Object#toString()
-     */
-    public String toString() {
-        return delegate.toString();
-    }
-
-    /**
-     * @return
-     */
-    @Override public boolean isFinished() {
-        return false;
-    }
-
-    /**
-     * @return
-     */
-    @Override public boolean isReady() {
-        return false;
-    }
-
-    /**
-     * @param arg0
-     */
-    @Override public void setReadListener(ReadListener arg0) {
-    }
-
-}
diff --git a/src/nu/validator/servlet/Html5ConformanceCheckerTransaction.java b/src/nu/validator/servlet/Html5ConformanceCheckerTransaction.java
deleted file mode 100644
index f9837f8..0000000
--- a/src/nu/validator/servlet/Html5ConformanceCheckerTransaction.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (c) 2005, 2006 Henri Sivonen
- * Copyright (c) 2007-2010 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import nu.validator.htmlparser.common.DoctypeExpectation;
-
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXNotRecognizedException;
-import org.xml.sax.SAXNotSupportedException;
-import org.xml.sax.SAXParseException;
-
-import com.thaiopensource.validate.IncorrectSchemaException;
-
-
-public class Html5ConformanceCheckerTransaction extends
-        VerifierServletTransaction {
-
-    /**
-     * @see nu.validator.servlet.VerifierServletTransaction#isSimple()
-     */
-    @Override
-    protected boolean isSimple() {
-        return true;
-    }
-    
-    private final static String GENERIC_FACET = (VerifierServlet.GENERIC_HOST.isEmpty() ? "" : ("//" + VerifierServlet.GENERIC_HOST)) + VerifierServlet.GENERIC_PATH;
-
-    private static final char[] GENERIC_UI = "More options".toCharArray();    
-    
-    private static final char[] SERVICE_TITLE = (System.getProperty(
-            "nu.validator.servlet.service-name", "Validator.nu") + " (X)HTML5 Validator ").toCharArray();
-
-    private static final char[] TECHNOLOGY_PREVIEW = "(Living Validator)".toCharArray();
-
-    private static final char[] RESULTS_TITLE = "(X)HTML5 validation results".toCharArray();
-
-    private static final char[] FOR = " for ".toCharArray();
-    
-    private static final String SUCCESS_HTML = "The document is valid HTML5 + ARIA + SVG 1.1 + MathML 2.0 (subject to the utter previewness of this service).";
-
-    private static final String SUCCESS_XHTML = "The document is valid XHTML5 + ARIA + SVG 1.1 + MathML 2.0 (subject to the utter previewness of this service).";
-
-    private static final String FAILURE_HTML = "There were errors. (Tried in the text/html mode.)";
-
-    private static final String FAILURE_XHTML = "There were errors. (Tried in the XHTML mode.)";
-
-    private boolean usingHtml = false;
-    
-    public Html5ConformanceCheckerTransaction(HttpServletRequest request,
-            HttpServletResponse response) {
-        super(request, response);
-    }
-
-    /**
-     * @see nu.validator.servlet.VerifierServletTransaction#successMessage()
-     */
-    protected String successMessage() throws SAXException {
-        if (usingHtml) {
-            return SUCCESS_HTML;
-        } else {
-            return SUCCESS_XHTML;
-        }
-    }
-
-    /**
-     * @see nu.validator.servlet.VerifierServletTransaction#loadDocAndSetupParser()
-     */
-    protected void loadDocAndSetupParser() throws SAXException, IOException, IncorrectSchemaException, SAXNotRecognizedException, SAXNotSupportedException {
-        setAllowGenericXml(false);
-        setAcceptAllKnownXmlTypes(false);
-        setAllowHtml(true);
-        setAllowXhtml(true);
-        loadDocumentInput();
-        String type = documentInput.getType();
-        if ("text/html".equals(type) || "text/html-sandboxed".equals(type)) {
-            validator = validatorByDoctype(HTML5_SCHEMA);
-            usingHtml = true;
-            newHtmlParser();
-            htmlParser.setDoctypeExpectation(DoctypeExpectation.HTML);
-            htmlParser.setDocumentModeHandler(this);
-            htmlParser.setContentHandler(validator.getContentHandler());
-            reader = htmlParser;
-        } else {
-            validator = validatorByDoctype(XHTML5_SCHEMA);
-            setupXmlParser();
-            if (!("application/xhtml+xml".equals(type) || "application/xml".equals(type))) {
-                String message = "The preferred Content-Type for XHTML5 is application/xhtml+xml. The Content-Type was " + type + ".";
-                SAXParseException spe = new SAXParseException(message, null, documentInput.getSystemId(), -1, -1);
-                errorHandler.warning(spe);
-            }
-        }
-
-    }
-
-    /**
-     * @see nu.validator.servlet.VerifierServletTransaction#setup()
-     */
-    protected void setup() throws ServletException {
-        // No-op
-    }
-
-    /**
-     * @see nu.validator.servlet.VerifierServletTransaction#emitTitle()
-     */
-    void emitTitle(boolean markupAllowed) throws SAXException {
-        if (willValidate()) {
-            emitter.characters(RESULTS_TITLE);
-            if (document != null && document.length() > 0) {
-                emitter.characters(FOR);                
-                emitter.characters(scrub(shortenDataUri(document)));                
-            }
-        } else {
-            emitter.characters(SERVICE_TITLE);
-            if (markupAllowed && System.getProperty("nu.validator.servlet.service-name", "Validator.nu").equals("Validator.nu")) {
-                emitter.startElement("span");
-                emitter.characters(TECHNOLOGY_PREVIEW);
-                emitter.endElement("span");
-            }
-        }
-    }
-
-    /**
-     * @see nu.validator.servlet.VerifierServletTransaction#tryToSetupValidator()
-     */
-    protected void tryToSetupValidator() throws SAXException, IOException, IncorrectSchemaException {
-        // No-op
-    }
-
-    /**
-     * @see nu.validator.servlet.VerifierServletTransaction#failureMessage()
-     */
-    protected String failureMessage() throws SAXException {
-        if (usingHtml) {
-            return FAILURE_HTML;
-        } else {
-            return FAILURE_XHTML;
-        }
-    }
-
-    /**
-     * @see nu.validator.servlet.VerifierServletTransaction#emitFormContent()
-     */
-    protected void emitFormContent() throws SAXException {
-        Html5FormEmitter.emit(contentHandler, this);
-    }
-
-    void maybeEmitNsfilterField() throws SAXException {
-        if (request.getParameter("nsfilter") != null) {
-            NsFilterEmitter.emit(contentHandler, this);
-        }
-    }
-
-    void maybeEmitCharsetField() throws SAXException {
-        if (request.getParameter("charset") != null) {
-            CharsetEmitter.emit(contentHandler, this);
-        }
-    }
-    
-    void emitOtherFacetLink() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("href", GENERIC_FACET);
-        emitter.startElement("a", attrs);
-        emitter.characters(GENERIC_UI);
-        emitter.endElement("a");   
-    }
-
-}
diff --git a/src/nu/validator/servlet/Html5FormEmitter.java b/src/nu/validator/servlet/Html5FormEmitter.java
deleted file mode 100644
index a2901ba..0000000
--- a/src/nu/validator/servlet/Html5FormEmitter.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/* This code was generated by nu.validator.tools.SaxCompiler. Please regenerate instead of editing. */
-package nu.validator.servlet;
-public final class Html5FormEmitter {
-private Html5FormEmitter() {}
-public static void emit(org.xml.sax.ContentHandler contentHandler, nu.validator.servlet.VerifierServletTransaction t) throws org.xml.sax.SAXException {
-org.xml.sax.helpers.AttributesImpl __attrs__ = new org.xml.sax.helpers.AttributesImpl();
-contentHandler.startPrefixMapping("", "http://www.w3.org/1999/xhtml");
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "fieldset", "fieldset", __attrs__);
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "legend", "legend", __attrs__);
-contentHandler.characters(__chars__, 0, 15);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "legend", "legend");
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "table", "table", __attrs__);
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "tbody", "tbody", __attrs__);
-__attrs__.clear();
-__attrs__.addAttribute("", "title", "title", "CDATA", "The document to validate.");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "tr", "tr", __attrs__);
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "th", "th", __attrs__);
-__attrs__.clear();
-__attrs__.addAttribute("", "for", "for", "CDATA", "doc");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "label", "label", __attrs__);
-contentHandler.characters(__chars__, 15, 8);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "label", "label");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "th", "th");
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "td", "td", __attrs__);
-t.emitDocField(); 
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "td", "td");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "tr", "tr");
-t.maybeEmitCharsetField(); t.maybeEmitNsfilterField(); 
-__attrs__.clear();
-__attrs__.addAttribute("", "title", "title", "CDATA", "Display a report about the textual alternatives for images.");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "tr", "tr", __attrs__);
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "th", "th", __attrs__);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "th", "th");
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "td", "td", __attrs__);
-__attrs__.clear();
-__attrs__.addAttribute("", "for", "for", "CDATA", "showimagereport");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "label", "label", __attrs__);
-t.emitShowImageReportField(); 
-						
-contentHandler.characters(__chars__, 23, 18);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "label", "label");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "td", "td");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "tr", "tr");
-__attrs__.clear();
-__attrs__.addAttribute("", "title", "title", "CDATA", "Display the markup source of the input document.");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "tr", "tr", __attrs__);
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "th", "th", __attrs__);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "th", "th");
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "td", "td", __attrs__);
-__attrs__.clear();
-__attrs__.addAttribute("", "for", "for", "CDATA", "showsource");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "label", "label", __attrs__);
-t.emitShowSourceField(); 
-						
-contentHandler.characters(__chars__, 41, 12);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "label", "label");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "td", "td");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "tr", "tr");
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "tr", "tr", __attrs__);
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "th", "th", __attrs__);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "th", "th");
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "td", "td", __attrs__);
-__attrs__.clear();
-__attrs__.addAttribute("", "value", "value", "CDATA", "Validate");
-__attrs__.addAttribute("", "type", "type", "CDATA", "submit");
-__attrs__.addAttribute("", "id", "id", "CDATA", "submit");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "input", "input", __attrs__);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "input", "input");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "td", "td");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "tr", "tr");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "tbody", "tbody");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "table", "table");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "fieldset", "fieldset");
-contentHandler.endPrefixMapping("");
-}
-private static final char[] __chars__ = { 'V', 'a', 'l', 'i', 'd', 'a', 't', 'o', 'r', ' ', 'I', 'n', 'p', 'u', 't', 'D', 'o', 'c', 'u', 'm', 'e', 'n', 't', ' ', 'S', 'h', 'o', 'w', ' ', 'I', 'm', 'a', 'g', 'e', ' ', 'R', 'e', 'p', 'o', 'r', 't', ' ', 'S', 'h', 'o', 'w', ' ', 'S', 'o', 'u', 'r', 'c', 'e' };
-}
diff --git a/src/nu/validator/servlet/InboundGzipFilter.java b/src/nu/validator/servlet/InboundGzipFilter.java
deleted file mode 100644
index e5c13a9..0000000
--- a/src/nu/validator/servlet/InboundGzipFilter.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (c) 2007-2008 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.zip.GZIPInputStream;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpServletResponse;
-
-public final class InboundGzipFilter implements Filter {
-
-    public void destroy() {
-    }
-
-    public void doFilter(ServletRequest req, ServletResponse res,
-            FilterChain chain) throws IOException, ServletException {
-        HttpServletRequest request = (HttpServletRequest) req;
-        HttpServletResponse response = (HttpServletResponse) res;
-        response.setHeader("Accept-Encoding", "gzip");
-        String ce = request.getHeader("Content-Encoding");
-        if (ce != null && "gzip".equalsIgnoreCase(ce.trim())) {
-            chain.doFilter(new RequestWrapper(request), res);
-        } else {
-            chain.doFilter(req, res);
-        }
-    }
-
-    public void init(FilterConfig config) throws ServletException {
-    }
-
-    private final class RequestWrapper extends HttpServletRequestWrapper {
-
-        private ServletInputStream stream = null;
-
-        public RequestWrapper(HttpServletRequest req) throws IOException {
-            super(req);
-        }
-
-        /**
-         * @see javax.servlet.http.HttpServletRequestWrapper#getDateHeader(java.lang.String)
-         */
-        @Override
-        public long getDateHeader(String name) {
-            if ("Content-Length".equalsIgnoreCase(name)) {
-                return -1;
-            } else if ("Content-MD5".equalsIgnoreCase(name)) {
-                return -1;
-            } else if ("Content-Encoding".equalsIgnoreCase(name)) {
-                return -1;
-            } else {
-                return super.getDateHeader(name);
-            }
-        }
-
-        /**
-         * @see javax.servlet.http.HttpServletRequestWrapper#getHeader(java.lang.String)
-         */
-        @Override
-        public String getHeader(String name) {
-            if ("Content-Length".equalsIgnoreCase(name)) {
-                return null;
-            } else if ("Content-MD5".equalsIgnoreCase(name)) {
-                return null;
-            } else if ("Content-Encoding".equalsIgnoreCase(name)) {
-                return null;
-            } else {
-                return super.getHeader(name);
-            }
-        }
-
-        /**
-         * @see javax.servlet.http.HttpServletRequestWrapper#getHeaderNames()
-         */
-        @Override
-        public Enumeration getHeaderNames() {
-            Enumeration e = super.getHeaderNames();
-            List<String> list = new ArrayList<String>();
-            while (e.hasMoreElements()) {
-                String name = (String) e.nextElement();
-                if ("Content-Length".equalsIgnoreCase(name)) {
-                    continue;
-                } else if ("Content-MD5".equalsIgnoreCase(name)) {
-                    continue;
-                } else if ("Content-Encoding".equalsIgnoreCase(name)) {
-                    continue;
-                } else {
-                    list.add(name);
-                }
-            }
-            return Collections.enumeration(list);
-        }
-
-        /**
-         * @see javax.servlet.http.HttpServletRequestWrapper#getHeaders(java.lang.String)
-         */
-        @SuppressWarnings("unchecked")
-        @Override
-        public Enumeration getHeaders(String name) {
-            if ("Content-Length".equalsIgnoreCase(name)) {
-                return Collections.enumeration(Collections.EMPTY_SET);
-            } else if ("Content-MD5".equalsIgnoreCase(name)) {
-                return Collections.enumeration(Collections.EMPTY_SET);
-            } else if ("Content-Encoding".equalsIgnoreCase(name)) {
-                return Collections.enumeration(Collections.EMPTY_SET);
-            } else {
-                return super.getHeaders(name);
-            }
-        }
-
-        /**
-         * @see javax.servlet.http.HttpServletRequestWrapper#getIntHeader(java.lang.String)
-         */
-        @Override
-        public int getIntHeader(String name) {
-            if ("Content-Length".equalsIgnoreCase(name)) {
-                return -1;
-            } else if ("Content-MD5".equalsIgnoreCase(name)) {
-                return -1;
-            } else if ("Content-Encoding".equalsIgnoreCase(name)) {
-                return -1;
-            } else {
-                return super.getIntHeader(name);
-            }
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getContentLength()
-         */
-        @Override
-        public int getContentLength() {
-            return -1;
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getInputStream()
-         */
-        @Override
-        public ServletInputStream getInputStream() throws IOException {
-            if (stream == null) {
-                stream = new DelegatingServletInputStream(new GZIPInputStream(super.getInputStream()));
-            }
-            return stream;
-        }
-
-    }
-
-}
diff --git a/src/nu/validator/servlet/InboundSizeLimitFilter.java b/src/nu/validator/servlet/InboundSizeLimitFilter.java
deleted file mode 100644
index dc8b6ee..0000000
--- a/src/nu/validator/servlet/InboundSizeLimitFilter.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2007-2008 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.IOException;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-
-import nu.validator.io.BoundedInputStream;
-import nu.validator.io.StreamBoundException;
-
-public final class InboundSizeLimitFilter implements Filter {
-
-    private long sizeLimit;
-    
-    /**
-     * @param sizeLimit
-     */
-    public InboundSizeLimitFilter(final long sizeLimit) {
-        this.sizeLimit = sizeLimit;
-    }
-    
-    public InboundSizeLimitFilter() {
-        this(Long.MAX_VALUE);
-    }
-    
-    public void destroy() {
-    }
-
-    public void doFilter(ServletRequest req, ServletResponse res,
-            FilterChain chain) throws IOException, ServletException {
-        HttpServletRequest request = (HttpServletRequest) req;
-        chain.doFilter(new RequestWrapper(request), res);
-    }
-
-    public void init(FilterConfig config) throws ServletException {
-        // XXX add configurability
-    }
-
-    private final class RequestWrapper extends HttpServletRequestWrapper {
-
-        private ServletInputStream stream = null;
-
-        public RequestWrapper(HttpServletRequest req) throws IOException {
-            super(req);
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getInputStream()
-         */
-        @Override
-        public ServletInputStream getInputStream() throws IOException {
-            if (stream == null) {
-                if (super.getContentLength() > sizeLimit) {
-                    throw new StreamBoundException("Resource size exceeds limit.");
-                }
-                stream = new DelegatingServletInputStream(new BoundedInputStream(super.getInputStream(), sizeLimit, super.getHeader("Content-Location")));
-            }
-            return stream;
-        }
-    }
-
-}
diff --git a/src/nu/validator/servlet/ListErrorHandler.java b/src/nu/validator/servlet/ListErrorHandler.java
deleted file mode 100644
index 90bb994..0000000
--- a/src/nu/validator/servlet/ListErrorHandler.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2007 Henri Sivonen
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.util.LinkedList;
-
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-public class ListErrorHandler implements ErrorHandler {
-
-    private boolean fatal = false;
-    
-    private LinkedList<String> errors = new LinkedList<String>();
-    
-    public void error(SAXParseException spe) throws SAXException {
-        errors.add(Integer.toString(spe.getColumnNumber()) + ": " + spe.getMessage());
-    }
-
-    public void fatalError(SAXParseException arg0) throws SAXException {
-        fatal = true;
-    }
-
-    public void warning(SAXParseException arg0) throws SAXException {
-    }
-
-    /**
-     * Returns the errors.
-     * 
-     * @return the errors
-     */
-    public LinkedList<String> getErrors() {
-        return errors;
-    }
-
-    /**
-     * Returns the fatal.
-     * 
-     * @return the fatal
-     */
-    public boolean isFatal() {
-        return fatal;
-    }
-
-}
diff --git a/src/nu/validator/servlet/Main.java b/src/nu/validator/servlet/Main.java
deleted file mode 100644
index 631e57b..0000000
--- a/src/nu/validator/servlet/Main.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2005 Henri Sivonen
- * Copyright (c) 2007-2015 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ConnectException;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.EnumSet;
-
-import javax.servlet.DispatcherType;
-
-import org.apache.log4j.PropertyConfigurator;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.servlets.GzipFilter;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-
-/**
- * @version $Id$
- * @author hsivonen
- */
-public class Main {
-
-    private static final long SIZE_LIMIT = Integer.parseInt(System.getProperty(
-            "nu.validator.servlet.max-file-size", "2097152"));
-
-    public static void main(String[] args) throws Exception {
-        if (!"1".equals(System.getProperty("nu.validator.servlet.read-local-log4j-properties"))) {
-            PropertyConfigurator.configure(Main.class.getClassLoader().getResource(
-                    "nu/validator/localentities/files/log4j.properties"));
-        } else {
-            PropertyConfigurator.configure(System.getProperty(
-                    "nu.validator.servlet.log4j-properties", "log4j.properties"));
-        }
-
-        ServletContextHandler contextHandler = new ServletContextHandler();
-        contextHandler.setContextPath("/");
-        contextHandler.addFilter(new FilterHolder(new GzipFilter()), "/*",
-                EnumSet.of(DispatcherType.REQUEST));
-        contextHandler.addFilter(new FilterHolder(new InboundSizeLimitFilter(
-                SIZE_LIMIT)), "/*", EnumSet.of(DispatcherType.REQUEST));
-        contextHandler.addFilter(new FilterHolder(new InboundGzipFilter()),
-                "/*", EnumSet.of(DispatcherType.REQUEST));
-        contextHandler.addFilter(
-                new FilterHolder(new MultipartFormDataFilter()), "/*",
-                EnumSet.of(DispatcherType.REQUEST));
-        contextHandler.addServlet(new ServletHolder(new VerifierServlet()),
-                "/*");
-
-        Server server = new Server(new QueuedThreadPool(100));
-        server.setHandler(contextHandler);
-
-        ServerConnector serverConnector = new ServerConnector(server,
-                new HttpConnectionFactory(new HttpConfiguration()));
-        int port = args.length > 0 ? Integer.parseInt(args[0]) : 8888;
-        serverConnector.setPort(port);
-        server.setConnectors(new Connector[] { serverConnector });
-
-        int stopPort = -1;
-        if (args.length > 1) {
-            stopPort = Integer.parseInt(args[1]);
-        }
-        if (stopPort != -1) {
-            try {
-                Socket clientSocket = new Socket(
-                        InetAddress.getByName("127.0.0.1"), stopPort);
-                InputStream in = clientSocket.getInputStream();
-                in.read();
-                in.close();
-                clientSocket.close();
-            } catch (ConnectException e) {
-
-            }
-
-            server.start();
-
-            ServerSocket serverSocket = new ServerSocket(stopPort, 0,
-                    InetAddress.getByName("127.0.0.1"));
-            Socket s = serverSocket.accept();
-
-            server.stop();
-
-            OutputStream out = s.getOutputStream();
-            out.close();
-            s.close();
-            serverSocket.close();
-        } else {
-            server.start();
-        }
-    }
-}
diff --git a/src/nu/validator/servlet/MultipartFormDataFilter.java b/src/nu/validator/servlet/MultipartFormDataFilter.java
deleted file mode 100644
index 4c976ab..0000000
--- a/src/nu/validator/servlet/MultipartFormDataFilter.java
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Copyright (c) 2007-2015 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CodingErrorAction;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.fileupload.FileItemIterator;
-import org.apache.commons.fileupload.FileItemStream;
-import org.apache.commons.fileupload.FileUploadException;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
-
-public final class MultipartFormDataFilter implements Filter {
-
-    private static Pattern EXTENSION = Pattern.compile("^.*\\.(.+)$");
-
-    private final static Map<String, String> EXTENSION_TO_TYPE = new HashMap<String, String>();
-
-    static {
-        EXTENSION_TO_TYPE.put("html", "text/html");
-        EXTENSION_TO_TYPE.put("htm", "text/html");
-        EXTENSION_TO_TYPE.put("xhtml", "application/xhtml+xml");
-        EXTENSION_TO_TYPE.put("xht", "application/xhtml+xml");
-        EXTENSION_TO_TYPE.put("atom", "application/atom+xml");
-        EXTENSION_TO_TYPE.put("rng", "application/xml");
-        EXTENSION_TO_TYPE.put("xsl", "application/xml");
-        EXTENSION_TO_TYPE.put("xml", "application/xml");
-        EXTENSION_TO_TYPE.put("dbk", "application/xml");
-        EXTENSION_TO_TYPE.put("csl", "application/xml");
-    }
-
-    private static String utf8ByteStreamToString(InputStream stream)
-            throws IOException {
-        CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
-        dec.onMalformedInput(CodingErrorAction.REPORT);
-        dec.onUnmappableCharacter(CodingErrorAction.REPORT);
-        Reader reader = new InputStreamReader(stream, dec);
-        StringBuilder builder = new StringBuilder();
-        int c;
-        int i = 0;
-        while ((c = reader.read()) != -1) {
-            if (i > 2048) {
-                throw new IOException("Form field value too large.");
-            }
-            builder.append((char) c);
-            i++;
-        }
-        return builder.toString();
-    }
-
-    private static void putParam(Map<String, String[]> params, String key,
-            String value) {
-        String[] oldVal = params.get(key);
-        if (oldVal == null) {
-            String[] arr = new String[1];
-            arr[0] = value;
-            params.put(key, arr);
-        } else {
-            for (int i = 0; i < oldVal.length; i++) {
-                String string = oldVal[i];
-                if (string.equals(value)) {
-                    return;
-                }
-            }
-            String[] arr = new String[oldVal.length + 1];
-            System.arraycopy(oldVal, 0, arr, 0, oldVal.length);
-            arr[oldVal.length] = value;
-            params.put(key, arr);
-        }
-    }
-
-    public void destroy() {
-    }
-
-    public void doFilter(ServletRequest req, ServletResponse res,
-            FilterChain chain) throws IOException, ServletException {
-        HttpServletRequest request = (HttpServletRequest) req;
-        HttpServletResponse response = (HttpServletResponse) res;
-        if (ServletFileUpload.isMultipartContent(request)) {
-            try {
-                boolean utf8 = false;
-                String contentType = null;
-                Map<String, String[]> params = new HashMap<String, String[]>();
-                InputStream fileStream = null;
-                ServletFileUpload upload = new ServletFileUpload();
-                FileItemIterator iter = upload.getItemIterator(request);
-                while (iter.hasNext()) {
-                    FileItemStream fileItemStream = iter.next();
-                    if (fileItemStream.isFormField()) {
-                        String fieldName = fileItemStream.getFieldName();
-                        if ("content".equals(fieldName)) {
-                            utf8 = true;
-                            String[] parser = params.get("parser");
-                            if (parser != null && parser[0].startsWith("xml")) {
-                                contentType = "application/xml";
-                            } else {
-                                contentType = "text/html";
-                            }
-                            request.setAttribute("nu.validator.servlet.MultipartFormDataFilter.type", "textarea");
-                            fileStream = fileItemStream.openStream();
-                            break;
-                        } else {
-                            putParam(
-                                    params,
-                                    fieldName,
-                                    utf8ByteStreamToString(fileItemStream.openStream()));
-                        }
-                    } else {
-                        String fileName = fileItemStream.getName();
-                        if (fileName != null) {
-                            putParam(params, fileItemStream.getFieldName(),
-                                    fileName);
-                            request.setAttribute(
-                                    "nu.validator.servlet.MultipartFormDataFilter.filename",
-                                    fileName);
-                            Matcher m = EXTENSION.matcher(fileName);
-                            if (m.matches()) {
-                                contentType = EXTENSION_TO_TYPE.get(m.group(1));
-                            }
-                        }
-                        if (contentType == null) {
-                            contentType = fileItemStream.getContentType();
-                        }
-                        request.setAttribute("nu.validator.servlet.MultipartFormDataFilter.type", "file");
-                        fileStream = fileItemStream.openStream();
-                        break;
-                    }
-                }
-                if (fileStream == null) {
-                    fileStream = new ByteArrayInputStream(new byte[0]);
-                }
-                if (contentType == null) {
-                    contentType = "application/octet-stream";
-                }
-                chain.doFilter(new RequestWrapper(request, params, contentType,
-                        utf8, fileStream), response);
-            } catch (FileUploadException e) {
-                response.sendError(415, e.getMessage());
-            } catch (CharacterCodingException e) {
-                response.sendError(415, e.getMessage());
-            } catch (IOException e) {
-                response.sendError(HttpServletResponse.SC_BAD_REQUEST,
-                        e.getMessage());
-            }
-        } else {
-            chain.doFilter(req, res);
-        }
-    }
-
-    public void init(FilterConfig arg0) throws ServletException {
-    }
-
-    private final class RequestWrapper extends HttpServletRequestWrapper {
-
-        private final Map<String, String[]> params;
-
-        private final String contentType;
-
-        private final boolean utf8;
-
-        private final ServletInputStream stream;
-
-        public RequestWrapper(HttpServletRequest req,
-                Map<String, String[]> params, String contentType, boolean utf8,
-                InputStream stream) {
-            super(req);
-            this.params = Collections.unmodifiableMap(params);
-            this.contentType = contentType;
-            this.utf8 = utf8;
-            this.stream = new DelegatingServletInputStream(stream);
-        }
-
-        /**
-         * @see javax.servlet.http.HttpServletRequestWrapper#getDateHeader(java.lang.String)
-         */
-        @Override
-        public long getDateHeader(String name) {
-            if ("Content-Length".equalsIgnoreCase(name)) {
-                return -1;
-            } else if ("Content-MD5".equalsIgnoreCase(name)) {
-                return -1;
-            } else if ("Content-Encoding".equalsIgnoreCase(name)) {
-                return -1;
-            } else if ("Content-Type".equalsIgnoreCase(name)) {
-                return -1;
-            } else {
-                return super.getDateHeader(name);
-            }
-        }
-
-        /**
-         * @see javax.servlet.http.HttpServletRequestWrapper#getHeader(java.lang.String)
-         */
-        @Override
-        public String getHeader(String name) {
-            if ("Content-Length".equalsIgnoreCase(name)) {
-                return null;
-            } else if ("Content-MD5".equalsIgnoreCase(name)) {
-                return null;
-            } else if ("Content-Encoding".equalsIgnoreCase(name)) {
-                return null;
-            } else if ("Content-Type".equalsIgnoreCase(name)) {
-                return getContentType();
-            } else {
-                return super.getHeader(name);
-            }
-        }
-
-        /**
-         * @see javax.servlet.http.HttpServletRequestWrapper#getHeaderNames()
-         */
-        @Override
-        public Enumeration getHeaderNames() {
-            Enumeration e = super.getHeaderNames();
-            List<String> list = new ArrayList<String>();
-            while (e.hasMoreElements()) {
-                String name = (String) e.nextElement();
-                if ("Content-Length".equalsIgnoreCase(name)) {
-                    continue;
-                } else if ("Content-MD5".equalsIgnoreCase(name)) {
-                    continue;
-                } else if ("Content-Encoding".equalsIgnoreCase(name)) {
-                    continue;
-                } else if ("Content-Type".equalsIgnoreCase(name)) {
-                    list.add(getContentType());
-                } else {
-                    list.add(name);
-                }
-            }
-            return Collections.enumeration(list);
-        }
-
-        /**
-         * @see javax.servlet.http.HttpServletRequestWrapper#getHeaders(java.lang.String)
-         */
-        @SuppressWarnings("unchecked")
-        @Override
-        public Enumeration getHeaders(String name) {
-            if ("Content-Length".equalsIgnoreCase(name)) {
-                return Collections.enumeration(Collections.EMPTY_SET);
-            } else if ("Content-MD5".equalsIgnoreCase(name)) {
-                return Collections.enumeration(Collections.EMPTY_SET);
-            } else if ("Content-Encoding".equalsIgnoreCase(name)) {
-                return Collections.enumeration(Collections.EMPTY_SET);
-            } else if ("Content-Type".equalsIgnoreCase(name)) {
-                return Collections.enumeration(Collections.singleton(getContentType()));
-            } else {
-                return super.getHeaders(name);
-            }
-        }
-
-        /**
-         * @see javax.servlet.http.HttpServletRequestWrapper#getIntHeader(java.lang.String)
-         */
-        @Override
-        public int getIntHeader(String name) {
-            if ("Content-Length".equalsIgnoreCase(name)) {
-                return -1;
-            } else if ("Content-MD5".equalsIgnoreCase(name)) {
-                return -1;
-            } else if ("Content-Encoding".equalsIgnoreCase(name)) {
-                return -1;
-            } else if ("Content-Type".equalsIgnoreCase(name)) {
-                return -1;
-            } else {
-                return super.getIntHeader(name);
-            }
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getCharacterEncoding()
-         */
-        @Override
-        public String getCharacterEncoding() {
-            return utf8 ? "utf-8" : null;
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getContentLength()
-         */
-        @Override
-        public int getContentLength() {
-            return -1;
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getContentType()
-         */
-        @Override
-        public String getContentType() {
-            return utf8 ? contentType + "; charset=utf-8" : contentType;
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getInputStream()
-         */
-        @Override
-        public ServletInputStream getInputStream() throws IOException {
-            return stream;
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getParameter(java.lang.String)
-         */
-        @Override
-        public String getParameter(String key) {
-            String[] arr = params.get(key);
-            if (arr == null) {
-                return null;
-            } else {
-                return arr[0];
-            }
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getParameterMap()
-         */
-        @Override
-        public Map getParameterMap() {
-            return params;
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getParameterNames()
-         */
-        @Override
-        public Enumeration getParameterNames() {
-            return Collections.enumeration(params.keySet());
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getParameterValues(java.lang.String)
-         */
-        @Override
-        public String[] getParameterValues(String key) {
-            return params.get(key);
-        }
-
-        /**
-         * @see javax.servlet.ServletRequestWrapper#getReader()
-         */
-        @Override
-        public BufferedReader getReader() throws IOException {
-            CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
-            dec.onMalformedInput(CodingErrorAction.REPORT);
-            dec.onUnmappableCharacter(CodingErrorAction.REPORT);
-            Reader reader = new InputStreamReader(stream, dec);
-            return new BufferedReader(reader);
-        }
-
-    }
-
-}
diff --git a/src/nu/validator/servlet/NsFilterEmitter.java b/src/nu/validator/servlet/NsFilterEmitter.java
deleted file mode 100644
index d9e23be..0000000
--- a/src/nu/validator/servlet/NsFilterEmitter.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/* This code was generated by nu.validator.tools.SaxCompiler. Please regenerate instead of editing. */
-package nu.validator.servlet;
-public final class NsFilterEmitter {
-private NsFilterEmitter() {}
-public static void emit(org.xml.sax.ContentHandler contentHandler, nu.validator.servlet.VerifierServletTransaction t) throws org.xml.sax.SAXException {
-org.xml.sax.helpers.AttributesImpl __attrs__ = new org.xml.sax.helpers.AttributesImpl();
-contentHandler.startPrefixMapping("", "http://www.w3.org/1999/xhtml");
-__attrs__.clear();
-__attrs__.addAttribute("", "title", "title", "CDATA", "Space-separated list of namespace URIs.");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "tr", "tr", __attrs__);
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "th", "th", __attrs__);
-__attrs__.clear();
-__attrs__.addAttribute("", "for", "for", "CDATA", "nsfilter");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "label", "label", __attrs__);
-__attrs__.clear();
-__attrs__.addAttribute("", "title", "title", "CDATA", "XML namespace");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "abbr", "abbr", __attrs__);
-contentHandler.characters(__chars__, 0, 5);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "abbr", "abbr");
-contentHandler.characters(__chars__, 5, 1);
-contentHandler.characters(__chars__, 6, 6);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "label", "label");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "th", "th");
-__attrs__.clear();
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "td", "td", __attrs__);
-t.emitNsfilterField(); 
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "td", "td");
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "tr", "tr");
-contentHandler.endPrefixMapping("");
-}
-private static final char[] __chars__ = { 'X', 'M', 'L', 'N', 'S', '\u00a0', 'F', 'i', 'l', 't', 'e', 'r' };
-}
diff --git a/src/nu/validator/servlet/OutlineBuildingXMLReaderWrapper.java b/src/nu/validator/servlet/OutlineBuildingXMLReaderWrapper.java
deleted file mode 100644
index a22b654..0000000
--- a/src/nu/validator/servlet/OutlineBuildingXMLReaderWrapper.java
+++ /dev/null
@@ -1,847 +0,0 @@
-/*
- * Copyright (c) 2012 Vadim Zaslawski, Ontos AG
- * Copyright (c) 2012-2014 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * This code contains comments that are verbatim quotations from the
- * document "HTML: The Living Standard", which has the following copyright
- * and permission notice:
- *
- *   Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
- *   Opera Software ASA. You are granted a license to use, reproduce and
- *   create derivative works of this document.
- */
-
-package nu.validator.servlet;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Deque;
-import java.util.LinkedList;
-import java.util.regex.Pattern;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.DTDHandler;
-import org.xml.sax.EntityResolver;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.InputSource;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXNotRecognizedException;
-import org.xml.sax.SAXNotSupportedException;
-import org.xml.sax.XMLReader;
-
-public final class OutlineBuildingXMLReaderWrapper implements XMLReader,
-        ContentHandler {
-
-    private final XMLReader wrappedReader;
-
-    private final HttpServletRequest request;
-
-    private ContentHandler contentHandler;
-
-    public OutlineBuildingXMLReaderWrapper(XMLReader wrappedReader,
-            HttpServletRequest request) {
-        this.request = request;
-        this.wrappedReader = wrappedReader;
-        this.contentHandler = wrappedReader.getContentHandler();
-        wrappedReader.setContentHandler(this);
-    }
-
-    private static final int MAX_EXCERPT = 500;
-
-    private static final String[] SECTIONING_CONTENT_ELEMENTS = { "article",
-            "aside", "nav", "section" };
-
-    private static final String[] SECTIONING_ROOT_ELEMENTS = { "blockquote",
-            "body", "details", "fieldset", "figure", "td" };
-
-    private static final String[] HEADING_CONTENT_ELEMENTS = { "h1", "h2",
-            "h3", "h4", "h5", "h6", "hgroup", };
-
-    private Deque<Section> outline;
-
-    public Deque<Section> getOutline() {
-        return this.outline;
-    }
-
-    protected void setOutline(Deque<Section> outline) {
-        this.outline = outline;
-    }
-
-    // an outlinee, a heading content element, or an element with a hidden
-    // attribute;
-    // during a walk over the nodes of a DOM tree, nodes are identified with
-    // their depth and local name
-    private class Element {
-        // the depth of element in the DOM
-        final private int depth;
-
-        // the local name of element
-        final private String name;
-
-        // whether the element is hidden
-        final private boolean hidden;
-
-        // the outline for a sectioning content element or a sectioning root
-        // element consists of a list of one or more potentially nested sections
-        final private Deque<Section> outline = new LinkedList<Section>();
-
-        public Element(int depth, String name, boolean hidden) {
-            this.depth = depth;
-            this.name = name;
-            this.hidden = hidden;
-        }
-
-        public boolean isHidden() {
-            return hidden;
-        }
-
-        public boolean equals(int depth, String name) {
-            return this.depth == depth && this.name.equals(name);
-        }
-
-        /**
-         * @return the outline
-         */
-        public Deque<Section> getOutline() {
-            return outline;
-        }
-
-        /**
-         * @return 1-6 for a heading content element MAX_VALUE for an implied
-         *         heading -1 for no section
-         */
-        public int getLastSectionHeadingRank() {
-            Section section = outline.peekLast();
-            return section != null ? section.getHeadingRank() : -1;
-        }
-    }
-
-    // a section is a container that corresponds to some nodes in the original
-    // DOM tree
-    public class Section {
-        // the section that contains this section
-        private Section parent;
-
-        // an outlinee or a heading content element
-        final String elementName;
-
-        // each section can have one heading associated with it
-        final private StringBuilder headingTextBuilder = new StringBuilder();
-
-        // we generate an "implied heading" for some sections that lack headings
-        private boolean hasImpliedHeading;
-
-        // Generating a special kind of implied heading specifically for
-        // the "empty heading" case (e.g., empty <h1></h1> descendant) as
-        // opposed to the "no heading" case (no h1-h6 descendants at all)
-        // isn't required by the spec, but it's nonetheless useful.
-        private boolean hasEmptyHeading;
-
-        // MAX_VALUE for an implied heading, 1-6 for a heading content element
-        private int headingRank = Integer.MAX_VALUE;
-
-        // each section can contain any number of further nested sections
-        final public Deque<Section> sections = new LinkedList<Section>();
-
-        public Section(String elementName) {
-            this.elementName = elementName;
-        }
-
-        /**
-         * @return the parent section
-         */
-        public Section getParent() {
-            return parent;
-        }
-
-        /**
-         * @return the lement name
-         */
-        public String getElementName() {
-            return elementName;
-        }
-
-        /**
-         * @param parent
-         *            the parent section to set
-         */
-        public void setParent(Section parent) {
-            this.parent = parent;
-        }
-
-        /**
-         * @return the heading text builder
-         */
-        public StringBuilder getHeadingTextBuilder() {
-            return headingTextBuilder;
-        }
-
-        /**
-         * @return the heading rank
-         */
-        public int getHeadingRank() {
-            return headingRank;
-        }
-
-        /**
-         * @return the sections
-         */
-        public Deque<Section> getSections() {
-            return sections;
-        }
-
-        public void setHeadingRank(int headingRank) {
-            this.headingRank = headingRank;
-        }
-
-        public boolean hasHeading() {
-            return headingRank < 7 || hasImpliedHeading;
-        }
-
-        public void createImpliedHeading() {
-            hasImpliedHeading = true;
-        }
-
-        public void createEmptyHeading() {
-            hasEmptyHeading = true;
-        }
-
-        public boolean hasEmptyHeading() {
-            return hasEmptyHeading;
-        }
-    }
-
-    // tracks the depth of walk through the DOM
-    private int currentWalkDepth;
-
-    // holds the element whose outline is being created;
-    // a sectioning content element or a sectioning root element
-    private Element currentOutlinee;
-
-    // A stack (not defined in the spec) to hold all open elements. We just
-    // use this stack for the purpose of checking whether there are any
-    // open elements at all with a "hidden" attribute -- including elements
-    // that may be descendants of heading-content elements (which per the
-    // spec never end up on the outline stack).
-    private Deque<Element> elementStack = new LinkedList<Element>();
-
-    private boolean inHiddenSubtree() {
-        for (Element element : elementStack) {
-            if (element.isHidden()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // A stack, defined in the spec, to which we only add open
-    // heading-content elements and elements with a "hidden" attribute that
-    // are ancestors to heading-content elements.
-    private Deque<Element> outlineStack = new LinkedList<Element>();
-
-    // The top of the outline stack defined in the spec is always either a
-    // heading content element or an element with a hidden attribute.
-    private boolean inHeadingContentOrHiddenElement;
-
-    private boolean needHeading;
-
-    private boolean skipHeading;
-
-    // holds a pointer to a section, so that elements in the DOM can all be
-    // associated with a section
-    private Section currentSection;
-
-    private boolean isWalkOver;
-
-    private static final Pattern excerptPattern = Pattern.compile("\\W*\\S*$");
-
-    private static final Pattern whitespacePattern = Pattern.compile("\\s+");
-
-    /*
-     * Returns the string excerpt.
-     */
-    private String excerpt(String str, int maxLength) {
-        return str.length() > maxLength ? excerptPattern.matcher(
-                str.substring(0, maxLength)).replaceFirst("&hellip;") : str;
-    }
-
-    /**
-     * @see org.xml.sax.helpers.XMLFilterImpl#characters(char[], int, int)
-     */
-    public void characters(char[] ch, int start, int length)
-            throws SAXException {
-        if (contentHandler == null) {
-            return;
-        }
-        if (isWalkOver) {
-            contentHandler.characters(ch, start, length);
-            return;
-        }
-
-        if (inHeadingContentOrHiddenElement && !inHiddenSubtree()) {
-            currentSection.getHeadingTextBuilder().append(ch, start, length);
-        }
-        contentHandler.characters(ch, start, length);
-    }
-
-    /**
-     * @see org.xml.sax.helpers.XMLFilterImpl#endElement(java.lang.String,
-     *      java.lang.String, java.lang.String)
-     */
-    public void endElement(String uri, String localName, String qName)
-            throws SAXException {
-        if (contentHandler == null) {
-            return;
-        }
-        elementStack.pop();
-        if ("hgroup".equals(localName)) {
-            needHeading = false;
-            skipHeading = false;
-        }
-        if (isWalkOver) {
-            contentHandler.endElement(uri, localName, qName);
-            return;
-        }
-
-        int depth = currentWalkDepth--;
-
-        if (inHeadingContentOrHiddenElement) {
-            // When exiting an element, if that element is the element at the
-            // top of the stack
-            // Note: The element being exited is a heading content element or an
-            // element with a hidden attribute.
-            Element topElement = outlineStack.peek();
-            assert topElement != null;
-            if (topElement.equals(depth, localName)) {
-                // Pop that element from the stack.
-                outlineStack.pop();
-                inHeadingContentOrHiddenElement = false;
-
-                if (currentSection != null) {
-                    StringBuilder headingTextBuilder = currentSection.getHeadingTextBuilder();
-                    String heading = excerpt(
-                            whitespacePattern.matcher(headingTextBuilder).replaceAll(
-                                    " ").trim(), MAX_EXCERPT);
-                    headingTextBuilder.setLength(0);
-                    if (heading.length() > 0) {
-                        headingTextBuilder.append(heading);
-                    } else {
-                        currentSection.createEmptyHeading();
-                    }
-                }
-            }
-
-            // If the top of the stack is a heading content element or an
-            // element with a hidden attribute
-            // Do nothing.
-            contentHandler.endElement(uri, localName, qName);
-            return;
-        }
-
-        if (Arrays.binarySearch(SECTIONING_CONTENT_ELEMENTS, localName) > -1) {
-            // When exiting a sectioning content element, if the stack is not
-            // empty
-            if (!outlineStack.isEmpty()) {
-                // If the current section has no heading,
-                if (currentSection != null && !currentSection.hasHeading()) {
-                    // create an implied heading and let that be the heading for
-                    // the current section.
-                    currentSection.createImpliedHeading();
-                }
-                Element exitedSectioningContentElement = currentOutlinee;
-                assert exitedSectioningContentElement != null;
-
-                // Pop the top element from the stack, and let the current
-                // outlinee be that element.
-                currentOutlinee = outlineStack.pop();
-
-                // Let current section be the last section in the outline of the
-                // current outlinee element.
-                currentSection = currentOutlinee.getOutline().peekLast();
-                assert currentSection != null;
-
-                // Append the outline of the sectioning content element being
-                // exited to the current section.
-                // (This does not change which section is the last section in
-                // the outline.)
-                for (Section section : exitedSectioningContentElement.outline) {
-                    section.setParent(currentSection);
-                    currentSection.sections.add(section);
-                }
-            }
-        } else if (Arrays.binarySearch(SECTIONING_ROOT_ELEMENTS, localName) > -1) {
-            // When exiting a sectioning root element, if the stack is not empty
-            if (!outlineStack.isEmpty()) {
-                // Run these steps:
-
-                // If the current section has no heading,
-                if (currentSection != null && !currentSection.hasHeading()) {
-                    // create an implied heading and let that be the heading for
-                    // the current section.
-                    currentSection.createImpliedHeading();
-                }
-
-                // Pop the top element from the stack, and let the current
-                // outlinee be that element.
-                currentOutlinee = outlineStack.pop();
-
-                // Let current section be the last section in the outline of the
-                // current outlinee element.
-                currentSection = currentOutlinee.getOutline().peekLast();
-
-                // Finding the deepest child:
-                // If current section has no child sections, stop these steps.
-                while (!currentSection.sections.isEmpty())
-                    // Let current section be the last child section of the
-                    // current current section.
-                    currentSection = currentSection.sections.peekLast();
-                // Go back to the substep labeled finding the deepest child.
-            }
-        } else {
-            // neither a sectioning content element nor a sectioning root
-            // element
-            contentHandler.endElement(uri, localName, qName);
-            return;
-        }
-
-        // When exiting a sectioning content element or a sectioning root
-        // element
-        // Note: The current outlinee is the element being exited, and
-        // it is the sectioning content element or a sectioning root element at
-        // the root of the subtree for which an outline is being generated.
-
-        // If the current section has no heading,
-        if (currentSection != null && !currentSection.hasHeading()) {
-            // create an implied heading and let that be the heading for the
-            // current section.
-            currentSection.createImpliedHeading();
-        }
-
-        // Skip to the next step in the overall set of steps.
-        // (The walk is over.)
-        // / isWalkOver = true;
-
-        contentHandler.endElement(uri, localName, qName);
-    }
-
-    /**
-     * @see org.xml.sax.helpers.XMLFilterImpl#startDocument()
-     */
-    public void startDocument() throws SAXException {
-        if (contentHandler == null) {
-            return;
-        }
-        contentHandler.startDocument();
-    }
-
-    /**
-     * @see org.xml.sax.helpers.XMLFilterImpl#startElement(java.lang.String,
-     *      java.lang.String, java.lang.String, org.xml.sax.Attributes)
-     */
-    public void startElement(String uri, String localName, String qName,
-            Attributes atts) throws SAXException {
-        if (contentHandler == null) {
-            return;
-        }
-        if (isWalkOver) {
-            contentHandler.startElement(uri, localName, qName, atts);
-            return;
-        }
-
-        ++currentWalkDepth;
-
-        boolean hidden = atts.getIndex("", "hidden") >= 0;
-        elementStack.push(new Element(currentWalkDepth, localName, hidden));
-
-        // If the top of the stack is a heading content element or an element
-        // with a hidden attribute
-        if (inHeadingContentOrHiddenElement) {
-            if (!inHiddenSubtree() && "img".equals(localName)
-                    && atts.getIndex("", "alt") >= 0) {
-                currentSection.getHeadingTextBuilder().append(
-                        atts.getValue("", "alt"));
-            }
-            // Do nothing.
-            contentHandler.startElement(uri, localName, qName, atts);
-            return;
-        }
-
-        // When entering an element with a hidden attribute
-        if (hidden) {
-            // Push the element being entered onto the stack. (This causes the
-            // algorithm to skip that element and any descendants of the
-            // element.)
-            outlineStack.push(new Element(currentWalkDepth, localName, hidden));
-            inHeadingContentOrHiddenElement = true;
-            contentHandler.startElement(uri, localName, qName, atts);
-            return;
-        }
-
-        // When entering a sectioning content element or a sectioning root
-        // element
-        if (Arrays.binarySearch(SECTIONING_CONTENT_ELEMENTS, localName) > -1
-                || Arrays.binarySearch(SECTIONING_ROOT_ELEMENTS, localName) > -1) {
-            if (currentOutlinee != null) {
-                // If current outlinee is not null, and the current section has
-                // no heading,
-                // create an implied heading and let that be the heading for the
-                // current section.
-                if (currentSection != null && !currentSection.hasHeading()) {
-                    currentSection.createImpliedHeading();
-                }
-                // If current outlinee is not null, push current outlinee onto
-                // the stack.
-                outlineStack.push(currentOutlinee);
-            }
-
-            // Let current outlinee be the element that is being entered.
-            currentOutlinee = new Element(currentWalkDepth, localName, hidden);
-
-            // Let current section be a newly created section for the current
-            // outlinee element.
-            // Associate current outlinee with current section.
-            currentSection = new Section(localName);
-
-            // Let there be a new outline for the new current outlinee,
-            // initialized with just the new current section as the only section
-            // in the outline.
-            currentOutlinee.getOutline().add(currentSection);
-            contentHandler.startElement(uri, localName, qName, atts);
-            return;
-        }
-
-        // When entering a heading content element
-        // Note: Recall that h1 has the highest rank, and h6 has the lowest
-        // rank.
-        if (Arrays.binarySearch(HEADING_CONTENT_ELEMENTS, localName) > -1
-                && currentOutlinee != null) {
-            if ("hgroup".equals(localName)) {
-                needHeading = true;
-                skipHeading = false;
-                contentHandler.startElement(uri, localName, qName, atts);
-                return;
-            } else if (skipHeading) {
-                contentHandler.startElement(uri, localName, qName, atts);
-                return;
-            } else if (needHeading) {
-                skipHeading = true;
-                needHeading = false;
-            }
-            int rank = localName.charAt(1) - '0';
-
-            // If the current section has no heading,
-            // let the element being entered be the heading for the current
-            // section.
-            if (currentSection != null && !currentSection.hasHeading()) {
-                currentSection.setHeadingRank(rank);
-            }
-            // Otherwise, if the element being entered has a rank equal to
-            // or higher than the heading of the last section of the
-            // outline of the current outlinee, or if the heading of the
-            // last section of the outline of the current outlinee is an
-            // implied heading,
-            else if (rank <= currentOutlinee.getLastSectionHeadingRank()) {
-                // then create a new section and append it to the outline
-                // of the current outlinee element, so that this new
-                // section is the new last section of that outline.
-                // Let current section be that new section.
-                currentSection = new Section(localName);
-                currentOutlinee.getOutline().add(currentSection);
-
-                // Let the element being entered be the new heading for the
-                // current section.
-                currentSection.setHeadingRank(rank);
-            }
-            // Otherwise, run these substeps:
-            else {
-                // Let candidate section be current section.
-                Section candidateSection = currentSection;
-
-                // Heading loop:
-                while (candidateSection != null) {
-                    // If the element being entered has a rank lower than the
-                    // rank of the heading of the candidate section,
-                    if (rank > candidateSection.getHeadingRank()) {
-                        // then create a new section, and append it to candidate
-                        // section.
-                        // (This does not change which section is the last
-                        // section in the outline.)
-                        // Let current section be this new section.
-                        currentSection = new Section(localName);
-                        currentSection.setParent(candidateSection);
-                        candidateSection.getSections().add(currentSection);
-
-                        // Let the element being entered be the new heading for
-                        // the current section.
-                        currentSection.setHeadingRank(rank);
-
-                        // Abort these substeps.
-                        break;
-                    }
-
-                    // Let new candidate section be the section that contains
-                    // candidate section in the outline of current outlinee.
-                    // Let candidate section be new candidate section.
-                    candidateSection = candidateSection.getParent();
-
-                    // Return to the step labeled heading loop.
-                }
-            }
-
-            // Push the element being entered onto the stack.
-            // (This causes the algorithm to skip any descendants of the
-            // element.)
-            outlineStack.push(new Element(currentWalkDepth, localName, hidden));
-            inHeadingContentOrHiddenElement = true;
-        }
-        contentHandler.startElement(uri, localName, qName, atts);
-    }
-
-    /**
-     * @see org.xml.sax.helpers.XMLFilterImpl#setDocumentLocator(org.xml.sax.Locator)
-     */
-    public void setDocumentLocator(Locator locator) {
-        if (contentHandler == null) {
-            return;
-        }
-        contentHandler.setDocumentLocator(locator);
-    }
-
-    public ContentHandler getContentHandler() {
-        return contentHandler;
-    }
-
-    /**
-     * @throws SAXException
-     * @see org.xml.sax.ContentHandler#endDocument()
-     */
-    public void endDocument() throws SAXException {
-        if (contentHandler == null) {
-            return;
-        }
-        if (currentOutlinee != null) {
-            request.setAttribute(
-                    "http://validator.nu/properties/document-outline",
-                    (Deque<Section>) currentOutlinee.outline);
-            setOutline(currentOutlinee.outline);
-        }
-        contentHandler.endDocument();
-    }
-
-    /**
-     * @param prefix
-     * @throws SAXException
-     * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
-     */
-    public void endPrefixMapping(String prefix) throws SAXException {
-        if (contentHandler == null) {
-            return;
-        }
-        contentHandler.endPrefixMapping(prefix);
-    }
-
-    /**
-     * @param ch
-     * @param start
-     * @param length
-     * @throws SAXException
-     * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
-     */
-    public void ignorableWhitespace(char[] ch, int start, int length)
-            throws SAXException {
-        if (contentHandler == null) {
-            return;
-        }
-        contentHandler.ignorableWhitespace(ch, start, length);
-    }
-
-    /**
-     * @param target
-     * @param data
-     * @throws SAXException
-     * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String,
-     *      java.lang.String)
-     */
-    public void processingInstruction(String target, String data)
-            throws SAXException {
-        if (contentHandler == null) {
-            return;
-        }
-        contentHandler.processingInstruction(target, data);
-    }
-
-    /**
-     * @param name
-     * @throws SAXException
-     * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
-     */
-    public void skippedEntity(String name) throws SAXException {
-        if (contentHandler == null) {
-            return;
-        }
-        contentHandler.skippedEntity(name);
-    }
-
-    /**
-     * @param prefix
-     * @param uri
-     * @throws SAXException
-     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String,
-     *      java.lang.String)
-     */
-    public void startPrefixMapping(String prefix, String uri)
-            throws SAXException {
-        if (contentHandler == null) {
-            return;
-        }
-        contentHandler.startPrefixMapping(prefix, uri);
-    }
-
-    /**
-     * @return
-     * @see org.xml.sax.XMLReader#getDTDHandler()
-     */
-    public DTDHandler getDTDHandler() {
-        return wrappedReader.getDTDHandler();
-    }
-
-    /**
-     * @return
-     * @see org.xml.sax.XMLReader#getEntityResolver()
-     */
-    public EntityResolver getEntityResolver() {
-        return wrappedReader.getEntityResolver();
-    }
-
-    /**
-     * @return
-     * @see org.xml.sax.XMLReader#getErrorHandler()
-     */
-    public ErrorHandler getErrorHandler() {
-        return wrappedReader.getErrorHandler();
-    }
-
-    /**
-     * @param name
-     * @return
-     * @throws SAXNotRecognizedException
-     * @throws SAXNotSupportedException
-     * @see org.xml.sax.XMLReader#getFeature(java.lang.String)
-     */
-    public boolean getFeature(String name) throws SAXNotRecognizedException,
-            SAXNotSupportedException {
-        return wrappedReader.getFeature(name);
-    }
-
-    /**
-     * @param name
-     * @return
-     * @throws SAXNotRecognizedException
-     * @throws SAXNotSupportedException
-     * @see org.xml.sax.XMLReader#getProperty(java.lang.String)
-     */
-    public Object getProperty(String name) throws SAXNotRecognizedException,
-            SAXNotSupportedException {
-        return wrappedReader.getProperty(name);
-    }
-
-    /**
-     * @param input
-     * @throws IOException
-     * @throws SAXException
-     * @see org.xml.sax.XMLReader#parse(org.xml.sax.InputSource)
-     */
-    public void parse(InputSource input) throws IOException, SAXException {
-        wrappedReader.parse(input);
-    }
-
-    /**
-     * @param systemId
-     * @throws IOException
-     * @throws SAXException
-     * @see org.xml.sax.XMLReader#parse(java.lang.String)
-     */
-    public void parse(String systemId) throws IOException, SAXException {
-        wrappedReader.parse(systemId);
-    }
-
-    /**
-     * @param handler
-     * @see org.xml.sax.XMLReader#setContentHandler(org.xml.sax.ContentHandler)
-     */
-    public void setContentHandler(ContentHandler handler) {
-        contentHandler = handler;
-    }
-
-    /**
-     * @param handler
-     * @see org.xml.sax.XMLReader#setDTDHandler(org.xml.sax.DTDHandler)
-     */
-    public void setDTDHandler(DTDHandler handler) {
-        wrappedReader.setDTDHandler(handler);
-    }
-
-    /**
-     * @param resolver
-     * @see org.xml.sax.XMLReader#setEntityResolver(org.xml.sax.EntityResolver)
-     */
-    public void setEntityResolver(EntityResolver resolver) {
-        wrappedReader.setEntityResolver(resolver);
-    }
-
-    /**
-     * @param handler
-     * @see org.xml.sax.XMLReader#setErrorHandler(org.xml.sax.ErrorHandler)
-     */
-    public void setErrorHandler(ErrorHandler handler) {
-        wrappedReader.setErrorHandler(handler);
-    }
-
-    /**
-     * @param name
-     * @param value
-     * @throws SAXNotRecognizedException
-     * @throws SAXNotSupportedException
-     * @see org.xml.sax.XMLReader#setFeature(java.lang.String, boolean)
-     */
-    public void setFeature(String name, boolean value)
-            throws SAXNotRecognizedException, SAXNotSupportedException {
-        wrappedReader.setFeature(name, value);
-    }
-
-    /**
-     * @param name
-     * @param value
-     * @throws SAXNotRecognizedException
-     * @throws SAXNotSupportedException
-     * @see org.xml.sax.XMLReader#setProperty(java.lang.String,
-     *      java.lang.Object)
-     */
-    public void setProperty(String name, Object value)
-            throws SAXNotRecognizedException, SAXNotSupportedException {
-        wrappedReader.setProperty(name, value);
-    }
-
-}
diff --git a/src/nu/validator/servlet/ParseTreePrinter.java b/src/nu/validator/servlet/ParseTreePrinter.java
deleted file mode 100644
index 4203c1c..0000000
--- a/src/nu/validator/servlet/ParseTreePrinter.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (c) 2007-2015 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.StringReader;
-import java.io.Writer;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import nu.validator.gnu.xml.aelfred2.SAXDriver;
-import nu.validator.htmlparser.common.Heuristics;
-import nu.validator.htmlparser.common.XmlViolationPolicy;
-import nu.validator.io.BoundedInputStream;
-import nu.validator.io.StreamBoundException;
-import nu.validator.xml.ContentTypeParser;
-import nu.validator.xml.NullEntityResolver;
-import nu.validator.xml.PrudentHttpEntityResolver;
-import nu.validator.xml.TypedInputSource;
-
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-
-import io.mola.galimatias.URL;
-import io.mola.galimatias.GalimatiasParseException;
-
-public class ParseTreePrinter {
-    
-    private static final String FORM_HTML = "<!DOCTYPE html><title>Parse Tree Dump</title><form><p><input type='url' name='doc' id='doc' pattern='(?:https?://.+)?'> <input name='submit' value='Print Tree' type='submit' id='submit'></form><hr><form><p><select id=parser name=parser><option value=xml>XML; don\u2019t load external entities</option><option value=html5 selected>HTML5</option></select><p><textarea name=content rows=20 cols=72></textarea> <input name='submit' value='Print Tree' type='submit' id='submit'></form>";
-    
-    private static final long SIZE_LIMIT = Integer.parseInt(System.getProperty(
-            "nu.validator.servlet.max-file-size", "2097152"));
-    
-    private final HttpServletRequest request;
-
-    private final HttpServletResponse response;
-
-    /**
-     * @param request
-     * @param response
-     */
-    public ParseTreePrinter(final HttpServletRequest request,
-            final HttpServletResponse response) {
-        this.request = request;
-        this.response = response;
-    }
-
-    private String scrubUrl(String urlStr) {
-        if (urlStr == null) {
-            return null;
-        }
-        try {
-            return URL.parse(urlStr).toString();
-        } catch (GalimatiasParseException e) {
-            return null;
-        }
-    }
-
-    public void service() throws IOException {
-        request.setCharacterEncoding("utf-8");
-        String content = null;
-        String document = scrubUrl(request.getParameter("doc"));
-        document = ("".equals(document)) ? null : document;
-        Writer writer = new OutputStreamWriter(response.getOutputStream(), "UTF-8");
-        if (document == null && methodIsGet() && (content = request.getParameter("content")) == null) {
-            response.setContentType("text/html; charset=utf-8");
-            writer.write(FORM_HTML);
-            writer.flush();
-            writer.close();
-            return;
-        } else {
-            response.setContentType("text/plain; charset=utf-8");
-            try {
-            PrudentHttpEntityResolver entityResolver = new PrudentHttpEntityResolver(
-                    2048 * 1024, false, null);
-            entityResolver.setAllowGenericXml(false);
-            entityResolver.setAcceptAllKnownXmlTypes(false);
-            entityResolver.setAllowHtml(true);
-            entityResolver.setAllowXhtml(true);
-            TypedInputSource documentInput;
-            if (methodIsGet()) {
-                if (content == null) {
-                    documentInput = (TypedInputSource) entityResolver.resolveEntity(
-                            null, document);
-                } else {
-                    documentInput = new TypedInputSource(new StringReader(content));
-                    if ("xml".equals(request.getParameter("parser"))) {
-                        documentInput.setType("application/xhtml+xml");
-                    } else {
-                        documentInput.setType("text/html");
-                    }
-                }
-            } else { // POST
-                String postContentType = request.getContentType();
-                if (postContentType == null) {
-                    response.sendError(HttpServletResponse.SC_BAD_REQUEST,
-                            "Content-Type missing");
-                    return;
-                } else if (postContentType.trim().toLowerCase().startsWith(
-                        "application/x-www-form-urlencoded")) {
-                    response.sendError(
-                            HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
-                            "application/x-www-form-urlencoded not supported. Please use multipart/form-data.");
-                    return;
-                }
-                long len = request.getContentLength();
-                if (len > SIZE_LIMIT) {
-                    throw new StreamBoundException("Resource size exceeds limit.");
-                }
-                ContentTypeParser contentTypeParser = new ContentTypeParser(null, false);
-                contentTypeParser.setAllowGenericXml(false);
-                contentTypeParser.setAcceptAllKnownXmlTypes(false);
-                contentTypeParser.setAllowHtml(true);
-                contentTypeParser.setAllowXhtml(true);
-                documentInput = contentTypeParser.buildTypedInputSource(document,
-                        null, postContentType);
-                documentInput.setByteStream(len < 0 ? new BoundedInputStream(
-                        request.getInputStream(), SIZE_LIMIT, document)
-                        : request.getInputStream());
-                documentInput.setSystemId(request.getHeader("Content-Location"));
-            }
-            String type = documentInput.getType();
-            XMLReader parser;
-            if ("text/html".equals(type) || "text/html-sandboxed".equals(type)) {
-                writer.write("HTML parser\n\n#document\n");
-                parser = new nu.validator.htmlparser.sax.HtmlParser();
-                parser.setProperty("http://validator.nu/properties/heuristics", Heuristics.ALL);
-                parser.setProperty("http://validator.nu/properties/xml-policy", XmlViolationPolicy.ALLOW);
-            } else if ("application/xhtml+xml".equals(type)) {
-                writer.write("XML parser\n\n#document\n");
-                parser = new SAXDriver();
-                parser.setFeature(
-                        "http://xml.org/sax/features/external-general-entities",
-                        false);
-                parser.setFeature(
-                        "http://xml.org/sax/features/external-parameter-entities",
-                        false);
-                parser.setEntityResolver(new NullEntityResolver());
-            } else {
-                writer.write("Unsupported content type.\n");
-                writer.flush();
-                writer.close();
-                return;
-            }
-            TreeDumpContentHandler treeDumpContentHandler = new TreeDumpContentHandler(writer, false);
-            ListErrorHandler listErrorHandler = new ListErrorHandler();
-            parser.setContentHandler(treeDumpContentHandler);
-            parser.setProperty("http://xml.org/sax/properties/lexical-handler", treeDumpContentHandler);
-            parser.setErrorHandler(listErrorHandler);
-            parser.parse(documentInput);
-            writer.write("#errors\n");
-            for (String err : listErrorHandler.getErrors()) {
-                writer.write(err);
-                writer.write('\n');
-            }
-            } catch (SAXException e) {
-                writer.write("SAXException:\n");
-                writer.write(e.getMessage());
-                writer.write("\n");
-            } catch (IOException e) {
-                writer.write("IOException:\n");
-                writer.write(e.getMessage());
-                writer.write("\n");
-            } finally {
-                writer.flush();
-                writer.close();
-            }
-        }
-    }
-
-    private boolean methodIsGet() {
-        return "GET".equals(request.getMethod())
-                || "HEAD".equals(request.getMethod());
-    }
-
-}
diff --git a/src/nu/validator/servlet/RootNamespaceSniffer.java b/src/nu/validator/servlet/RootNamespaceSniffer.java
deleted file mode 100644
index df4bad2..0000000
--- a/src/nu/validator/servlet/RootNamespaceSniffer.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2006 Henri Sivonen
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-
-public class RootNamespaceSniffer implements ContentHandler {
-
-    private VerifierServletTransaction vst;
-    private ContentHandler ch;
-    private Locator locator;
-
-    public RootNamespaceSniffer(VerifierServletTransaction vst, ContentHandler ch) {
-        super();
-        this.vst = vst;
-        this.ch = ch;
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
-     */
-    public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
-        ch.characters(arg0, arg1, arg2);
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#endDocument()
-     */
-    public void endDocument() throws SAXException {
-        ch.endDocument();
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
-     */
-    public void endElement(String arg0, String arg1, String arg2) throws SAXException {
-        ch.endElement(arg0, arg1, arg2);
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
-     */
-    public void endPrefixMapping(String arg0) throws SAXException {
-        ch.endPrefixMapping(arg0);
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
-     */
-    public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
-        ch.ignorableWhitespace(arg0, arg1, arg2);
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String)
-     */
-    public void processingInstruction(String arg0, String arg1) throws SAXException {
-        ch.processingInstruction(arg0, arg1);
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
-     */
-    public void setDocumentLocator(Locator arg0) {
-        this.locator = arg0;
-        ch.setDocumentLocator(arg0);
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
-     */
-    public void skippedEntity(String arg0) throws SAXException {
-        ch.skippedEntity(arg0);
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#startDocument()
-     */
-    public void startDocument() throws SAXException {
-        ch.startDocument();
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
-     */
-    public void startElement(String arg0, String arg1, String arg2, Attributes arg3) throws SAXException {
-        vst.rootNamespace(arg0, locator);
-        ch.startElement(arg0, arg1, arg2, arg3);
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
-     */
-    public void startPrefixMapping(String arg0, String arg1) throws SAXException {
-        ch.startPrefixMapping(arg0, arg1);
-    }
-
-}
diff --git a/src/nu/validator/servlet/Statistics.java b/src/nu/validator/servlet/Statistics.java
deleted file mode 100644
index 37ada19..0000000
--- a/src/nu/validator/servlet/Statistics.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (c) 2012 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.IOException;
-import java.text.DecimalFormat;
-
-import javax.servlet.http.HttpServletResponse;
-
-import nu.validator.htmlparser.sax.HtmlSerializer;
-import nu.validator.xml.EmptyAttributes;
-
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-public class Statistics {
-
-    public static final Statistics STATISTICS;
-
-    private static final char[] VALIDATOR_STATISTICS = "Validator statistics".toCharArray();
-
-    private static final char[] COUNTER_NAME = "Counter".toCharArray();
-
-    private static final char[] COUNTER_VALUE = "Value".toCharArray();
-
-    private static final char[] COUNTER_PROPORTION = "Proportion".toCharArray();
-
-    private static final char[] TOTAL_VALIDATIONS = "Total number of validations".toCharArray();
-
-    private static final char[] UPTIME_DAYS = "Uptime in days".toCharArray();
-
-    private static final char[] VALIDATIONS_PER_SECOND = "Validations per second".toCharArray();
-
-    public enum Field {
-        // Sigh. Eclipse's formatting of the following code is sad.
-        CUSTOM_ENC("Manually set character encoding"), AUTO_SCHEMA(
-                "Automatically chosen schema"), PRESET_SCHEMA("Preset schema"), BUILT_IN_NON_PRESET(
-                "Custom schema combined from built-ins"), HTML5_SCHEMA(
-                "(X)HTML5 schema"), HTML5_RDFA_LITE_SCHEMA(
-                "(X)HTML5+RDFa Lite schema"), HTML4_STRICT_SCHEMA(
-                "Legacy Strict schema"), HTML4_TRANSITIONAL_SCHEMA(
-                "Legacy Transitional schema"), HTML4_FRAMESET_SCHEMA(
-                "Legacy Frameset schema"), XHTML1_COMPOUND_SCHEMA(
-                "Legacy XHTML+SVG+MathML schema"), SVG_SCHEMA("SVG schema"), EXTERNAL_SCHEMA_NON_SCHEMATRON(
-                "non-Schematron custom schema"), EXTERNAL_SCHEMA_SCHEMATRON(
-                "Schematron custom schema"), LOGIC_ERROR(
-                "Logic errors in schema stats"), PARSER_XML_EXTERNAL(
-                "Parser set to XML with external entities"), PARSER_HTML4(
-                "Parser set to explicit HTML4 mode"), XMLNS_FILTER(
-                "XMLNS filter set"), LAX_TYPE(
-                "Being lax about HTTP content type"), IMAGE_REPORT(
-                "Image report"), SHOW_SOURCE("Show source"), SHOW_OUTLINE(
-                "Show outline"), INPUT_GET("GET-based input"), INPUT_POST(
-                "POST-based input"), INPUT_TEXT_FIELD("\u2514 Text-field input"), INPUT_FILE_UPLOAD(
-                "\u2514 File-upload input"), INPUT_ENTITY_BODY(
-                "\u2514 Entity-body input"), OUTPUT_HTML("HTML output"), OUTPUT_XHTML(
-                "XHTML output"), OUTPUT_XML("XML output"), OUTPUT_JSON(
-                "JSON output"), OUTPUT_GNU("GNU output"), OUTPUT_TEXT(
-                "Text output"), INPUT_HTML("HTML input"), INPUT_XML("XML input");
-
-        Field(String description) {
-            this.description = description;
-        }
-
-        private final String description;
-
-        /**
-         * @see java.lang.Enum#toString()
-         */
-        @Override public String toString() {
-            return description;
-        }
-    }
-
-    static {
-        if ("1".equals(System.getProperty("nu.validator.servlet.statistics"))) {
-            STATISTICS = new Statistics();
-        } else {
-            STATISTICS = null;
-        }
-    }
-
-    private final long startTime = System.currentTimeMillis();
-
-    private long total = 0;
-
-    private final long[] counters;
-
-    private Statistics() {
-        counters = new long[Field.values().length];
-    }
-
-    public void incrementTotal() {
-        total++;
-    }
-
-    public void incrementField(Field field) {
-        counters[field.ordinal()]++;
-    }
-
-    public void writeToResponse(HttpServletResponse response)
-            throws IOException {
-        try {
-            long totalCopy;
-            long[] countersCopy = new long[counters.length];
-            synchronized (this) {
-                totalCopy = total;
-                System.arraycopy(counters, 0, countersCopy, 0, counters.length);
-            }
-            double totalDouble = (double) totalCopy;
-            double uptimeMillis = (double) (System.currentTimeMillis() - startTime);
-            response.setContentType("text/html; charset=utf-8");
-            ContentHandler ch = new HtmlSerializer(response.getOutputStream());
-            try {
-                ch.startDocument();
-                startElement(ch, "html");
-                startElement(ch, "head");
-                startElement(ch, "title");
-                characters(ch, VALIDATOR_STATISTICS);
-                endElement(ch, "title");
-                endElement(ch, "head");
-                startElement(ch, "body");
-                startElement(ch, "h1");
-                characters(ch, VALIDATOR_STATISTICS);
-                endElement(ch, "h1");
-
-                startElement(ch, "dl");
-                startElement(ch, "dt");
-                characters(ch, TOTAL_VALIDATIONS);
-                endElement(ch, "dt");
-                startElement(ch, "dd");
-                characters(ch, totalCopy);
-                endElement(ch, "dd");
-
-                startElement(ch, "dt");
-                characters(ch, UPTIME_DAYS);
-                endElement(ch, "dt");
-                startElement(ch, "dd");
-                characters(ch, uptimeMillis / (1000 * 60 * 60 * 24));
-                endElement(ch, "dd");
-
-                startElement(ch, "dt");
-                characters(ch, VALIDATIONS_PER_SECOND);
-                endElement(ch, "dt");
-                startElement(ch, "dd");
-                characters(ch, totalDouble / (uptimeMillis / 1000.0));
-                endElement(ch, "dd");
-
-                endElement(ch, "dl");
-
-                startElement(ch, "table");
-                startElement(ch, "thead");
-                startElement(ch, "tr");
-                startElement(ch, "th");
-                characters(ch, COUNTER_NAME);
-                endElement(ch, "th");
-                startElement(ch, "th");
-                characters(ch, COUNTER_VALUE);
-                endElement(ch, "th");
-                startElement(ch, "th");
-                characters(ch, COUNTER_PROPORTION);
-                endElement(ch, "th");
-                endElement(ch, "tr");
-                endElement(ch, "thead");
-                startElement(ch, "tbody");
-                for (int i = 0; i < countersCopy.length; i++) {
-                    long count = countersCopy[i];
-                    startElement(ch, "tr");
-                    startElement(ch, "td");
-
-                    characters(ch, Field.values()[i].toString());
-
-                    endElement(ch, "td");
-                    startElement(ch, "td");
-
-                    characters(ch, count);
-
-                    endElement(ch, "td");
-                    startElement(ch, "td");
-
-                    characters(ch, ((double) count) / totalDouble);
-
-                    endElement(ch, "td");
-                    endElement(ch, "tr");
-                }
-                endElement(ch, "tbody");
-                endElement(ch, "table");
-                endElement(ch, "body");
-                endElement(ch, "html");
-            } finally {
-                ch.endDocument();
-            }
-        } catch (SAXException e) {
-            throw new IOException(e);
-        }
-    }
-
-    private void characters(ContentHandler ch, double d) throws SAXException {
-        // Let's just create a new DecimalFormat each time to avoid the 
-        // complexity of recycling an instance correctly without threading
-        // hazards.
-        DecimalFormat df = new DecimalFormat("#,###,##0.000000");
-        characters(ch, df.format(d));
-    }
-
-    private void characters(ContentHandler ch, long l) throws SAXException {
-        characters(ch, Long.toString(l));
-    }
-
-    private void characters(ContentHandler ch, String str) throws SAXException {
-        characters(ch, str.toCharArray());
-    }
-
-    private void characters(ContentHandler ch, char[] cs) throws SAXException {
-        ch.characters(cs, 0, cs.length);
-    }
-
-    private void endElement(ContentHandler ch, String name) throws SAXException {
-        ch.endElement("http://www.w3.org/1999/xhtml", name, name);
-    }
-
-    private void startElement(ContentHandler ch, String name)
-            throws SAXException {
-        ch.startElement("http://www.w3.org/1999/xhtml", name, name,
-                EmptyAttributes.EMPTY_ATTRIBUTES);
-    }
-
-}
diff --git a/src/nu/validator/servlet/StatsEmitter.java b/src/nu/validator/servlet/StatsEmitter.java
deleted file mode 100644
index 9ed17a1..0000000
--- a/src/nu/validator/servlet/StatsEmitter.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/* This code was generated by nu.validator.tools.SaxCompiler. Please regenerate instead of editing. */
-package nu.validator.servlet;
-public final class StatsEmitter {
-private StatsEmitter() {}
-public static void emit(org.xml.sax.ContentHandler contentHandler, nu.validator.servlet.VerifierServletTransaction t) throws org.xml.sax.SAXException {
-org.xml.sax.helpers.AttributesImpl __attrs__ = new org.xml.sax.helpers.AttributesImpl();
-contentHandler.startPrefixMapping("", "http://www.w3.org/1999/xhtml");
-__attrs__.clear();
-__attrs__.addAttribute("", "class", "class", "CDATA", "stats");
-contentHandler.startElement("http://www.w3.org/1999/xhtml", "p", "p", __attrs__);
-contentHandler.characters(__chars__, 0, 21);
-t.emitTotalDuration(); 
-contentHandler.characters(__chars__, 21, 14);
-contentHandler.endElement("http://www.w3.org/1999/xhtml", "p", "p");
-contentHandler.endPrefixMapping("");
-}
-private static final char[] __chars__ = { 'T', 'o', 't', 'a', 'l', ' ', 'e', 'x', 'e', 'c', 'u', 't', 'i', 'o', 'n', ' ', 't', 'i', 'm', 'e', ' ', ' ', 'm', 'i', 'l', 'l', 'i', 's', 'e', 'c', 'o', 'n', 'd', 's', '.' };
-}
diff --git a/src/nu/validator/servlet/TreeDumpContentHandler.java b/src/nu/validator/servlet/TreeDumpContentHandler.java
deleted file mode 100644
index 5728c8d..0000000
--- a/src/nu/validator/servlet/TreeDumpContentHandler.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (c) 2007 Henri Sivonen
- * Copyright (c) 2008 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-import org.xml.sax.ext.LexicalHandler;
-
-public class TreeDumpContentHandler implements ContentHandler, LexicalHandler {
-
-    private final Writer writer;
-
-    private int level = 0;
-
-    private boolean inCharacters = false;
-
-    private boolean close;
-
-    /**
-     * @param writer
-     */
-    public TreeDumpContentHandler(final Writer writer, boolean close) {
-        this.writer = writer;
-        this.close = close;
-    }
-
-    public TreeDumpContentHandler(final Writer writer) {
-        this(writer, true);
-    }
-
-    private void printLead() throws IOException {
-        if (inCharacters) {
-            writer.write("\"\n");
-            inCharacters = false;
-        }
-        writer.write("| ");
-        for (int i = 0; i < level; i++) {
-            writer.write("  ");
-        }
-    }
-
-    /**
-     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
-     */
-    public void characters(char[] ch, int start, int length)
-            throws SAXException {
-        try {
-            if (!inCharacters) {
-                printLead();
-                writer.write('"');
-                inCharacters = true;
-            }
-            writer.write(ch, start, length);
-        } catch (IOException e) {
-            throw new SAXException(e);
-        }
-    }
-
-    public void endElement(String uri, String localName, String qName)
-            throws SAXException {
-        try {
-            if (inCharacters) {
-                writer.write("\"\n");
-                inCharacters = false;
-            }
-            level--;
-        } catch (IOException e) {
-            throw new SAXException(e);
-        }
-    }
-
-    public void startElement(String uri, String localName, String qName,
-            Attributes atts) throws SAXException {
-        try {
-            printLead();
-            writer.write('<');
-            if ("http://www.w3.org/1998/Math/MathML" == uri) {
-                writer.write("math ");                
-            } else if ("http://www.w3.org/2000/svg" == uri) {
-                writer.write("svg ");                                
-            } else if ("http://www.w3.org/1999/xhtml" != uri) {
-                writer.write("otherns ");                                
-            }
-            writer.write(localName);
-            writer.write(">\n");
-            level++;
-
-            TreeMap<String, String> map = new TreeMap<String, String>();
-            for (int i = 0; i < atts.getLength(); i++) {
-                String ns = atts.getURI(i);
-                String name;
-                if ("http://www.w3.org/1999/xlink" == ns) {
-                    name = "xlink " + atts.getLocalName(i);
-                } else if ("http://www.w3.org/XML/1998/namespace" == ns) {
-                    name = "xml " + atts.getLocalName(i);                    
-                } else if ("http://www.w3.org/2000/xmlns/" == ns) {
-                    name = "xmlns " + atts.getLocalName(i);                    
-                } else if ("" != uri) {
-                    name = atts.getLocalName(i);                    
-                } else {
-                    name = "otherns " + atts.getLocalName(i);                    
-                }
-                map.put(name, atts.getValue(i));
-            }
-            for (Map.Entry<String, String> entry : map.entrySet()) {
-                printLead();
-                writer.write(entry.getKey());
-                writer.write("=\"");
-                writer.write(entry.getValue());
-                writer.write("\"\n");
-            }
-        } catch (IOException e) {
-            throw new SAXException(e);
-        }
-    }
-
-    public void comment(char[] ch, int offset, int len) throws SAXException {
-        try {
-            printLead();
-            writer.write("<!-- ");
-            writer.write(ch, offset, len);
-            writer.write(" -->\n");
-        } catch (IOException e) {
-            throw new SAXException(e);
-        }
-    }
-
-    public void startDTD(String name, String publicIdentifier,
-            String systemIdentifier) throws SAXException {
-        try {
-            printLead();
-            writer.write("<!DOCTYPE ");
-            writer.write(name);
-            if (publicIdentifier.length() > 0 || systemIdentifier.length() > 0) {
-                writer.write(' ');
-                writer.write('\"');
-                writer.write(publicIdentifier);
-                writer.write('\"');
-                writer.write(' ');
-                writer.write('\"');
-                writer.write(systemIdentifier);
-                writer.write('\"');
-            }
-            writer.write(">\n");
-        } catch (IOException e) {
-            throw new SAXException(e);
-        }
-    }
-
-    public void endDocument() throws SAXException {
-        try {
-            if (inCharacters) {
-                writer.write("\"\n");
-                inCharacters = false;
-            }
-            if (close) {
-                writer.flush();
-                writer.close();
-            }
-        } catch (IOException e) {
-            throw new SAXException(e);
-        }
-    }
-
-    public void startPrefixMapping(String prefix, String uri)
-            throws SAXException {
-    }
-
-    public void startEntity(String arg0) throws SAXException {
-    }
-
-    public void endCDATA() throws SAXException {
-    }
-
-    public void endDTD() throws SAXException {
-    }
-
-    public void endEntity(String arg0) throws SAXException {
-    }
-
-    public void startCDATA() throws SAXException {
-    }
-
-    public void endPrefixMapping(String prefix) throws SAXException {
-    }
-
-    public void ignorableWhitespace(char[] ch, int start, int length)
-            throws SAXException {
-    }
-
-    public void processingInstruction(String target, String data)
-            throws SAXException {
-    }
-
-    public void setDocumentLocator(Locator locator) {
-    }
-
-    public void skippedEntity(String name) throws SAXException {
-    }
-
-    public void startDocument() throws SAXException {
-    }
-
-}
diff --git a/src/nu/validator/servlet/VerifierServlet.java b/src/nu/validator/servlet/VerifierServlet.java
deleted file mode 100644
index 987fed9..0000000
--- a/src/nu/validator/servlet/VerifierServlet.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright (c) 2005 Henri Sivonen
- * Copyright (c) 2007-2014 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.UnsupportedEncodingException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import nu.validator.messages.MessageEmitterAdapter;
-import nu.validator.xml.PrudentHttpEntityResolver;
-
-import org.apache.log4j.Logger;
-
-
-/**
- * @version $Id$
- * @author hsivonen
- */
-public class VerifierServlet extends HttpServlet {
-    /**
-     * 
-     */
-    private static final long serialVersionUID = 7811043632732680935L;
-
-    private static final Logger log4j = Logger.getLogger(VerifierServlet.class);
-
-    static final String GENERIC_HOST = System.getProperty("nu.validator.servlet.host.generic", "");
-
-    static final String HTML5_HOST = System.getProperty("nu.validator.servlet.host.html5", "");
-
-    static final String PARSETREE_HOST = System.getProperty("nu.validator.servlet.host.parsetree", "");
-
-    static final String GENERIC_PATH = System.getProperty("nu.validator.servlet.path.generic", "/");
-
-    static final String HTML5_PATH = System.getProperty("nu.validator.servlet.path.html5", "/html5/");
-
-    static final String PARSETREE_PATH = System.getProperty("nu.validator.servlet.path.parsetree", "/parsetree/");
-
-    private static final byte[] GENERIC_ROBOTS_TXT;
-
-    private static final byte[] HTML5_ROBOTS_TXT;
-
-    private static final byte[] PARSETREE_ROBOTS_TXT;
-
-    private static final byte[] STYLE_CSS;
-
-    private static final byte[] SCRIPT_JS;
-
-    private static final byte[] ICON_PNG;
-
-    private static final byte[] ABOUT_HTML;
-
-    static {
-        try {
-            GENERIC_ROBOTS_TXT = buildRobotsTxt(GENERIC_HOST, GENERIC_PATH, HTML5_HOST, HTML5_PATH, PARSETREE_HOST, PARSETREE_PATH);
-            HTML5_ROBOTS_TXT = buildRobotsTxt(HTML5_HOST, HTML5_PATH, GENERIC_HOST, GENERIC_PATH, PARSETREE_HOST, PARSETREE_PATH);
-            PARSETREE_ROBOTS_TXT = buildRobotsTxt(PARSETREE_HOST, PARSETREE_PATH, HTML5_HOST, HTML5_PATH, GENERIC_HOST, GENERIC_PATH);
-        } catch (UnsupportedEncodingException e) {
-            throw new RuntimeException(e);
-        }
-        try {
-            STYLE_CSS = readFromClassLoaderIntoByteArray("nu/validator/localentities/files/style.css");
-            SCRIPT_JS = readFromClassLoaderIntoByteArray("nu/validator/localentities/files/script.js");
-            ICON_PNG = readFromClassLoaderIntoByteArray("nu/validator/localentities/files/icon.png");
-            ABOUT_HTML = readFromClassLoaderIntoByteArray("nu/validator/localentities/files/about.html");
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        PrudentHttpEntityResolver.setParams(
-            Integer.parseInt(System.getProperty("nu.validator.servlet.connection-timeout","5000")),
-            Integer.parseInt(System.getProperty("nu.validator.servlet.socket-timeout","5000")),
-            100);
-        // force some class loading
-        new VerifierServletTransaction(null, null);
-        new MessageEmitterAdapter(null, false, null, 0, false, null);
-    }
-
-    /**
-     * @return
-     * @throws UnsupportedEncodingException
-     */
-    private static byte[] buildRobotsTxt(String primaryHost, String primaryPath, String secondaryHost, String secondaryPath, String tertiaryHost, String tertiaryPath) throws UnsupportedEncodingException {
-        StringBuilder builder = new StringBuilder();
-        builder.append("User-agent: *\nDisallow: ");
-        builder.append(primaryPath);
-        builder.append("?\n");
-        if (primaryHost.equals(secondaryHost)) {
-            builder.append("Disallow: ");
-            builder.append(secondaryPath);
-            builder.append("?\n");
-        }
-        if (primaryHost.equals(tertiaryHost)) {
-            builder.append("Disallow: ");
-            builder.append(tertiaryPath);
-            builder.append("?\n");
-        }
-        return builder.toString().getBytes("UTF-8");
-    }
-
-    private static byte[] readFromClassLoaderIntoByteArray(String name)
-            throws IOException {
-        InputStream ios = VerifierServlet.class.getClassLoader().getResourceAsStream(
-                name);
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        try {
-            for (int b = ios.read(); b != -1; b = ios.read()) {
-                baos.write(b);
-            }
-            ios.close();
-            baos.close();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        return baos.toByteArray();
-    }
-
-    private void writeResponse(byte[] buffer, String type,
-            HttpServletResponse response) throws IOException {
-        try {
-            response.setContentType(type);
-            response.setContentLength(buffer.length);
-            response.setDateHeader("Expires",
-                    System.currentTimeMillis() + 43200000); // 12 hours
-            OutputStream out = response.getOutputStream();
-            out.write(buffer);
-            out.flush();
-            out.close();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-     */
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-        if ("/robots.txt".equals(request.getPathInfo())) {
-            String serverName = request.getServerName();
-            byte[] robotsTxt = null;
-            if (hostMatch(GENERIC_HOST, serverName)) {
-                robotsTxt = GENERIC_ROBOTS_TXT;
-            } else if (hostMatch(HTML5_HOST, serverName)) {
-                robotsTxt = HTML5_ROBOTS_TXT;
-            } else if (hostMatch(PARSETREE_HOST, serverName)) {
-                robotsTxt = PARSETREE_ROBOTS_TXT;
-            } else {
-                response.sendError(HttpServletResponse.SC_NOT_FOUND);
-                return;
-            }
-            writeResponse(robotsTxt, "text/plain; charset=utf-8", response);
-            return;
-        } else if ("/style.css".equals(request.getPathInfo())) {
-            writeResponse(STYLE_CSS, "text/css; charset=utf-8", response);
-            return;
-        } else if ("/script.js".equals(request.getPathInfo())) {
-            writeResponse(SCRIPT_JS, "text/javascript; charset=utf-8", response);
-            return;
-        } else if ("/icon.png".equals(request.getPathInfo())) {
-            writeResponse(ICON_PNG, "image/png", response);
-            return;
-        } else if ("/about.html".equals(request.getPathInfo())) {
-            writeResponse(ABOUT_HTML, "text/html; charset=utf-8", response);
-            return;
-        } else if (Statistics.STATISTICS != null && "/stats.html".equals(request.getPathInfo())) {
-            Statistics.STATISTICS.writeToResponse(response);
-            return;
-        }
-        doPost(request, response);
-    }
-
-    private boolean hostMatch(String reference, String host) {
-        if ("".equals(reference)) {
-            return true;
-        } else {
-            // XXX case-sensitivity
-            return reference.equalsIgnoreCase(host);
-        }
-    }
-
-    /**
-     * @see javax.servlet.http.HttpServlet#doOptions(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-     */
-    @Override
-    protected void doOptions(HttpServletRequest request,
-            HttpServletResponse response) throws ServletException, IOException {
-        String pathInfo = request.getPathInfo();
-        if ("*".equals(pathInfo)) { // useless RFC 2616 complication
-            return;
-        } else if ("/robots.txt".equals(pathInfo)) {
-            String serverName = request.getServerName();
-            if (hostMatch(GENERIC_HOST, serverName)
-                    || hostMatch(HTML5_HOST, serverName)
-                    || hostMatch(PARSETREE_HOST, serverName)) {
-                sendGetOnlyOptions(request, response);
-                return;
-            } else {
-                response.sendError(HttpServletResponse.SC_NOT_FOUND);
-                return;
-            }
-        }
-        doPost(request, response);
-    }
-
-    /**
-     * @see javax.servlet.http.HttpServlet#doTrace(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-     */
-    @Override
-    protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-        response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
-    }
-
-    /**
-     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
-     *      javax.servlet.http.HttpServletResponse)
-     */
-    protected void doPost(HttpServletRequest request,
-            HttpServletResponse response) throws ServletException, IOException {
-        String pathInfo = request.getPathInfo();
-        if (pathInfo == null) {
-            pathInfo = "/"; // Fix for Jigsaw
-        }
-        String serverName = request.getServerName();
-        if ("/robots.txt".equals(pathInfo)) {
-            // if we get here, we've got a POST
-            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
-            return;
-        }
-        log4j.debug("pathInfo: " + pathInfo);
-        log4j.debug("serverName: " + serverName);
-        boolean isOptions = "OPTIONS".equals(request.getMethod());
-
-        if ("validator.nu".equals(serverName) && "/html5/".equals(pathInfo)) {
-                response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
-                String queryString = request.getQueryString();
-                response.setHeader("Location", "http://html5.validator.nu/" + (queryString == null ? "" : "?" + queryString));
-        } else if (hostMatch(GENERIC_HOST, serverName) && GENERIC_PATH.equals(pathInfo)) {
-            response.setHeader("Access-Control-Allow-Origin", "*");
-            response.setHeader("Access-Control-Allow-Headers", "content-type");
-            if (isOptions) {
-                sendOptions(request, response);
-            } else {
-                new VerifierServletTransaction(request, response).service();
-            }
-        } else if (hostMatch(HTML5_HOST, serverName) && HTML5_PATH.equals(pathInfo)) {
-            response.setHeader("Access-Control-Allow-Origin", "*");
-            response.setHeader("Access-Control-Allow-Headers", "content-type");
-            if (isOptions) {
-                sendOptions(request, response);
-            } else {
-                new Html5ConformanceCheckerTransaction(request, response).service();
-            }
-        } else if (hostMatch(PARSETREE_HOST, serverName) && PARSETREE_PATH.equals(pathInfo)) {
-            if (isOptions) {
-                sendGetOnlyOptions(request, response);
-            } else {
-                new ParseTreePrinter(request, response).service();
-            }
-        } else {
-            response.sendError(HttpServletResponse.SC_NOT_FOUND);
-        }
-    }
-
-    private void sendGetOnlyOptions(HttpServletRequest request, HttpServletResponse response) {
-        response.setHeader("Allow", "GET, HEAD, OPTIONS");
-        response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, OPTIONS");
-        response.setContentType("application/octet-stream");
-        response.setContentLength(0);
-    }
-
-    private void sendOptions(HttpServletRequest request, HttpServletResponse response) {
-        response.setHeader("Access-Control-Max-Age", "43200"); // 12 hours
-        response.setHeader("Allow", "GET, HEAD, POST, OPTIONS");
-        response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, OPTIONS");
-        response.setContentType("application/octet-stream");
-        response.setContentLength(0);
-    }
-}
diff --git a/src/nu/validator/servlet/VerifierServletTransaction.java b/src/nu/validator/servlet/VerifierServletTransaction.java
deleted file mode 100644
index 5239a06..0000000
--- a/src/nu/validator/servlet/VerifierServletTransaction.java
+++ /dev/null
@@ -1,2051 +0,0 @@
-/*
- * Copyright (c) 2005, 2006 Henri Sivonen
- * Copyright (c) 2007-2015 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.util.Arrays;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import nu.validator.checker.XmlPiChecker;
-import nu.validator.checker.jing.CheckerSchema;
-import nu.validator.gnu.xml.aelfred2.SAXDriver;
-import nu.validator.htmlparser.common.DoctypeExpectation;
-import nu.validator.htmlparser.common.DocumentMode;
-import nu.validator.htmlparser.common.DocumentModeHandler;
-import nu.validator.htmlparser.common.Heuristics;
-import nu.validator.htmlparser.common.XmlViolationPolicy;
-import nu.validator.htmlparser.sax.HtmlParser;
-import nu.validator.htmlparser.sax.HtmlSerializer;
-import nu.validator.htmlparser.sax.XmlSerializer;
-import nu.validator.io.BoundedInputStream;
-import nu.validator.io.DataUri;
-import nu.validator.io.StreamBoundException;
-import nu.validator.localentities.LocalCacheEntityResolver;
-import nu.validator.messages.GnuMessageEmitter;
-import nu.validator.messages.JsonMessageEmitter;
-import nu.validator.messages.MessageEmitterAdapter;
-import nu.validator.messages.TextMessageEmitter;
-import nu.validator.messages.TooManyErrorsException;
-import nu.validator.messages.XhtmlMessageEmitter;
-import nu.validator.messages.XmlMessageEmitter;
-import nu.validator.servlet.imagereview.ImageCollector;
-import nu.validator.servlet.OutlineBuildingXMLReaderWrapper.Section;
-import nu.validator.source.SourceCode;
-import nu.validator.spec.Spec;
-import nu.validator.spec.html5.Html5SpecBuilder;
-import nu.validator.xml.AttributesImpl;
-import nu.validator.xml.AttributesPermutingXMLReaderWrapper;
-import nu.validator.xml.BaseUriTracker;
-import nu.validator.xml.CharacterUtil;
-import nu.validator.xml.CombineContentHandler;
-import nu.validator.xml.ContentTypeParser;
-import nu.validator.xml.DataUriEntityResolver;
-import nu.validator.xml.IdFilter;
-import nu.validator.xml.NamespaceDroppingXMLReaderWrapper;
-import nu.validator.xml.NullEntityResolver;
-import nu.validator.xml.PrudentHttpEntityResolver;
-import nu.validator.xml.SystemErrErrorHandler;
-import nu.validator.xml.TypedInputSource;
-import nu.validator.xml.WiretapXMLReaderWrapper;
-import nu.validator.xml.XhtmlSaxEmitter;
-import nu.validator.xml.dataattributes.DataAttributeDroppingSchemaWrapper;
-import nu.validator.xml.langattributes.XmlLangAttributeDroppingSchemaWrapper;
-import nu.validator.xml.roleattributes.RoleAttributeFilteringSchemaWrapper;
-
-import org.xml.sax.ContentHandler;
-import org.xml.sax.EntityResolver;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXNotRecognizedException;
-import org.xml.sax.SAXNotSupportedException;
-import org.xml.sax.SAXParseException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.ext.LexicalHandler;
-
-import com.thaiopensource.relaxng.impl.CombineValidator;
-import com.thaiopensource.util.PropertyMap;
-import com.thaiopensource.util.PropertyMapBuilder;
-import com.thaiopensource.validate.IncorrectSchemaException;
-import com.thaiopensource.validate.Schema;
-import com.thaiopensource.validate.SchemaReader;
-import com.thaiopensource.validate.SchemaResolver;
-import com.thaiopensource.validate.ValidateProperty;
-import com.thaiopensource.validate.Validator;
-import com.thaiopensource.validate.auto.AutoSchemaReader;
-import com.thaiopensource.validate.prop.rng.RngProperty;
-import com.thaiopensource.validate.prop.wrap.WrapProperty;
-import com.thaiopensource.validate.rng.CompactSchemaReader;
-
-import org.apache.log4j.Logger;
-import com.ibm.icu.text.Normalizer;
-
-/**
- * @version $Id: VerifierServletTransaction.java,v 1.10 2005/07/24 07:32:48
- *          hsivonen Exp $
- * @author hsivonen
- */
-class VerifierServletTransaction implements DocumentModeHandler, SchemaResolver {
-
-    private enum OutputFormat {
-        HTML, XHTML, TEXT, XML, JSON, RELAXED, SOAP, UNICORN, GNU
-    }
-
-    private static final Logger log4j = Logger.getLogger(VerifierServletTransaction.class);
-
-    private static final Pattern SPACE = Pattern.compile("\\s+");
-
-    private static final Pattern JS_IDENTIFIER = Pattern.compile("[\\p{Lu}\\p{Ll}\\p{Lt}\\p{Lm}\\p{Lo}\\p{Nl}_\\$][\\p{Lu}\\p{Ll}\\p{Lt}\\p{Lm}\\p{Lo}\\p{Nl}_\\$\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}]*");
-
-    private static final String[] JS_RESERVED_WORDS = { "abstract", "boolean",
-            "break", "byte", "case", "catch", "char", "class", "const",
-            "continue", "debugger", "default", "delete", "do", "double",
-            "else", "enum", "export", "extends", "final", "finally", "float",
-            "for", "function", "goto", "if", "implements", "import", "in",
-            "instanceof", "int", "interface", "long", "native", "new",
-            "package", "private", "protected", "public", "return", "short",
-            "static", "super", "switch", "synchronized", "this", "throw",
-            "throws", "transient", "try", "typeof", "var", "void", "volatile",
-            "while", "with" };
-
-    private static final String[] CHARSETS = { "UTF-8", "UTF-16",
-            "Windows-1250", "Windows-1251", "Windows-1252", "Windows-1253",
-            "Windows-1254", "Windows-1255", "Windows-1256", "Windows-1257",
-            "Windows-1258", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3",
-            "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7",
-            "ISO-8859-8", "ISO-8859-9", "ISO-8859-13", "ISO-8859-15", "KOI8-R",
-            "TIS-620", "GBK", "GB18030", "Big5", "Big5-HKSCS", "Shift_JIS",
-            "ISO-2022-JP", "EUC-JP", "ISO-2022-KR", "EUC-KR" };
-
-    private static final char[][] CHARSET_DESCRIPTIONS = {
-            "UTF-8 (Global)".toCharArray(), "UTF-16 (Global)".toCharArray(),
-            "Windows-1250 (Central European)".toCharArray(),
-            "Windows-1251 (Cyrillic)".toCharArray(),
-            "Windows-1252 (Western)".toCharArray(),
-            "Windows-1253 (Greek)".toCharArray(),
-            "Windows-1254 (Turkish)".toCharArray(),
-            "Windows-1255 (Hebrew)".toCharArray(),
-            "Windows-1256 (Arabic)".toCharArray(),
-            "Windows-1257 (Baltic)".toCharArray(),
-            "Windows-1258 (Vietnamese)".toCharArray(),
-            "ISO-8859-1 (Western)".toCharArray(),
-            "ISO-8859-2 (Central European)".toCharArray(),
-            "ISO-8859-3 (South European)".toCharArray(),
-            "ISO-8859-4 (Baltic)".toCharArray(),
-            "ISO-8859-5 (Cyrillic)".toCharArray(),
-            "ISO-8859-6 (Arabic)".toCharArray(),
-            "ISO-8859-7 (Greek)".toCharArray(),
-            "ISO-8859-8 (Hebrew)".toCharArray(),
-            "ISO-8859-9 (Turkish)".toCharArray(),
-            "ISO-8859-13 (Baltic)".toCharArray(),
-            "ISO-8859-15 (Western)".toCharArray(),
-            "KOI8-R (Russian)".toCharArray(), "TIS-620 (Thai)".toCharArray(),
-            "GBK (Chinese, simplified)".toCharArray(),
-            "GB18030 (Chinese, simplified)".toCharArray(),
-            "Big5 (Chinese, traditional)".toCharArray(),
-            "Big5-HKSCS (Chinese, traditional)".toCharArray(),
-            "Shift_JIS (Japanese)".toCharArray(),
-            "ISO-2022-JP (Japanese)".toCharArray(),
-            "EUC-JP (Japanese)".toCharArray(),
-            "ISO-2022-KR (Korean)".toCharArray(),
-            "EUC-KR (Korean)".toCharArray() };
-
-    protected static final int HTML5_SCHEMA = 3;
-
-    protected static final int XHTML1STRICT_SCHEMA = 2;
-
-    protected static final int XHTML1TRANSITIONAL_SCHEMA = 1;
-
-    protected static final int XHTML5_SCHEMA = 7;
-
-    private static final char[] SERVICE_TITLE;
-
-    private static final char[] LIVING_VERSION = "Living Validator".toCharArray();
-
-    private static final char[] VERSION;
-
-    private static final char[] RESULTS_TITLE;
-
-    private static final char[] FOR = " for ".toCharArray();
-
-    private static final char[] ABOUT_THIS_SERVICE = "About this Service".toCharArray();
-
-    private static final char[] SIMPLE_UI = "Simplified Interface".toCharArray();
-
-    private static final String USER_AGENT;
-
-    private static Spec html5spec;
-
-    private static int[] presetDoctypes;
-
-    private static String[] presetLabels;
-
-    private static String[] presetUrls;
-
-    private static String[] presetNamespaces;
-
-    // XXX SVG!!!
-
-    private static final String[] KNOWN_CONTENT_TYPES = {
-            "application/atom+xml", "application/docbook+xml",
-            "application/xhtml+xml", "application/xv+xml", "image/svg+xml" };
-
-    private static final String[] NAMESPACES_FOR_KNOWN_CONTENT_TYPES = {
-            "http://www.w3.org/2005/Atom", "http://docbook.org/ns/docbook",
-            "http://www.w3.org/1999/xhtml", "http://www.w3.org/1999/xhtml",
-            "http://www.w3.org/2000/svg" };
-
-    private static final String[] ALL_CHECKERS = {
-            "http://c.validator.nu/table/", "http://c.validator.nu/nfc/",
-            "http://c.validator.nu/text-content/",
-            "http://c.validator.nu/unchecked/",
-            "http://c.validator.nu/usemap/", "http://c.validator.nu/obsolete/",
-            "http://c.validator.nu/xml-pi/", "http://c.validator.nu/unsupported/",
-            "http://c.validator.nu/microdata/" };
-
-    private static final String[] ALL_CHECKERS_HTML4 = {
-            "http://c.validator.nu/table/", "http://c.validator.nu/nfc/",
-            "http://c.validator.nu/unchecked/", "http://c.validator.nu/usemap/" };
-
-    private long start = System.currentTimeMillis();
-
-    protected final HttpServletRequest request;
-
-    private final HttpServletResponse response;
-
-    protected String document = null;
-
-    private ParserMode parser = ParserMode.AUTO;
-
-    private String profile = "";
-
-    private boolean laxType = false;
-
-    protected ContentHandler contentHandler;
-
-    protected XhtmlSaxEmitter emitter;
-
-    protected MessageEmitterAdapter errorHandler;
-
-    protected final AttributesImpl attrs = new AttributesImpl();
-
-    private OutputStream out;
-
-    private PropertyMap jingPropertyMap;
-
-    protected LocalCacheEntityResolver entityResolver;
-
-    private static long lastModified;
-
-    private static String[] preloadedSchemaUrls;
-
-    private static Schema[] preloadedSchemas;
-
-    private final static String ABOUT_PAGE = System.getProperty(
-            "nu.validator.servlet.about-page", "https://about.validator.nu/");
-
-    private final static String HTML5_FACET = (VerifierServlet.HTML5_HOST.isEmpty() ? "" : ("//" + VerifierServlet.HTML5_HOST)) + VerifierServlet.HTML5_PATH;
-
-    private final static String STYLE_SHEET = System.getProperty(
-            "nu.validator.servlet.style-sheet",
-            "style.css");
-
-    private final static String ICON = System.getProperty(
-            "nu.validator.servlet.icon",
-            "icon.png");
-
-    private final static String SCRIPT = System.getProperty(
-            "nu.validator.servlet.script",
-            "script.js");
-
-    private final static String[] LEGACY_HOSTS = System.getProperty(
-            "nu.validator.servlet.host.legacy", "").split("\\s+");
-
-    private static final long SIZE_LIMIT = Integer.parseInt(System.getProperty(
-            "nu.validator.servlet.max-file-size", "2097152"));
-
-    private String schemaUrls = null;
-
-    protected Validator validator = null;
-
-    private BufferingRootNamespaceSniffer bufferingRootNamespaceSniffer = null;
-
-    private String contentType = null;
-
-    protected HtmlParser htmlParser = null;
-
-    protected SAXDriver xmlParser = null;
-
-    protected XMLReader reader;
-
-    protected TypedInputSource documentInput;
-
-    protected PrudentHttpEntityResolver httpRes;
-
-    protected DataUriEntityResolver dataRes;
-
-    protected ContentTypeParser contentTypeParser;
-
-    private Set<String> loadedValidatorUrls = new HashSet<String>();
-
-    private boolean checkNormalization = false;
-
-    private boolean rootNamespaceSeen = false;
-
-    private OutputFormat outputFormat;
-
-    private String postContentType;
-
-    private boolean methodIsGet;
-
-    private SourceCode sourceCode = new SourceCode();
-
-    private Deque<Section> outline;
-
-    private boolean showSource;
-
-    private boolean showOutline;
-
-    private String userAgent;
-
-    private BaseUriTracker baseUriTracker = null;
-
-    private String charsetOverride = null;
-
-    private Set<String> filteredNamespaces = new LinkedHashSet<String>(); // linked
-
-    private LexicalHandler lexicalHandler;
-
-    // for
-    // UI
-    // stability
-
-    protected ImageCollector imageCollector;
-
-    private boolean externalSchema = false;
-
-    private boolean externalSchematron = false;
-
-    private String schemaListForStats = null;
-
-    static {
-        try {
-            log4j.debug("Starting static initializer.");
-
-            lastModified = 0;
-            BufferedReader r = new BufferedReader(new InputStreamReader(LocalCacheEntityResolver.getPresetsAsStream(), "UTF-8"));
-            String line;
-            List<String> doctypes = new LinkedList<String>();
-            List<String> namespaces = new LinkedList<String>();
-            List<String> labels = new LinkedList<String>();
-            List<String> urls = new LinkedList<String>();
-            Properties props = new Properties();
-
-            log4j.debug("Reading miscellaneous properties.");
-
-            props.load(VerifierServlet.class.getClassLoader().getResourceAsStream(
-                    "nu/validator/localentities/files/misc.properties"));
-            SERVICE_TITLE = (System.getProperty(
-                    "nu.validator.servlet.service-name",
-                    props.getProperty("nu.validator.servlet.service-name",
-                            "Validator.nu")) + " ").toCharArray();
-            RESULTS_TITLE = (System.getProperty(
-                    "nu.validator.servlet.results-title", props.getProperty(
-                            "nu.validator.servlet.results-title",
-                            "Validation results"))).toCharArray();
-            VERSION = (System.getProperty("nu.validator.servlet.version",
-                    props.getProperty("nu.validator.servlet.version",
-                            "Living Validator"))).toCharArray();
-            USER_AGENT= (System.getProperty("nu.validator.servlet.user-agent",
-                    props.getProperty("nu.validator.servlet.user-agent",
-                            "Validator.nu/LV")));
-
-            log4j.debug("Starting to loop over config file lines.");
-
-            while ((line = r.readLine()) != null) {
-                if ("".equals(line.trim())) {
-                    break;
-                }
-                String s[] = line.split("\t");
-                doctypes.add(s[0]);
-                namespaces.add(s[1]);
-                labels.add(s[2]);
-                urls.add(s[3]);
-            }
-
-            log4j.debug("Finished reading config.");
-
-            String[] presetDoctypesAsStrings = doctypes.toArray(new String[0]);
-            presetNamespaces = namespaces.toArray(new String[0]);
-            presetLabels = labels.toArray(new String[0]);
-            presetUrls = urls.toArray(new String[0]);
-
-            log4j.debug("Converted config to arrays.");
-
-            for (int i = 0; i < presetNamespaces.length; i++) {
-                String str = presetNamespaces[i];
-                if ("-".equals(str)) {
-                    presetNamespaces[i] = null;
-                } else {
-                    presetNamespaces[i] = presetNamespaces[i].intern();
-                }
-            }
-
-            log4j.debug("Prepared namespace array.");
-
-            presetDoctypes = new int[presetDoctypesAsStrings.length];
-            for (int i = 0; i < presetDoctypesAsStrings.length; i++) {
-                presetDoctypes[i] = Integer.parseInt(presetDoctypesAsStrings[i]);
-            }
-
-            log4j.debug("Parsed doctype numbers into ints.");
-
-            String prefix = System.getProperty("nu.validator.servlet.cachepathprefix");
-
-            log4j.debug("The cache path prefix is: " + prefix);
-
-            ErrorHandler eh = new SystemErrErrorHandler();
-            LocalCacheEntityResolver er = new LocalCacheEntityResolver(new NullEntityResolver());
-            er.setAllowRnc(true);
-            PropertyMapBuilder pmb = new PropertyMapBuilder();
-            pmb.put(ValidateProperty.ERROR_HANDLER, eh);
-            pmb.put(ValidateProperty.ENTITY_RESOLVER, er);
-            pmb.put(ValidateProperty.XML_READER_CREATOR,
-                    new VerifierServletXMLReaderCreator(eh, er));
-            RngProperty.CHECK_ID_IDREF.add(pmb);
-            PropertyMap pMap = pmb.toPropertyMap();
-
-            log4j.debug("Parsing set up. Starting to read schemas.");
-
-            SortedMap<String, Schema> schemaMap = new TreeMap<String, Schema>();
-
-            schemaMap.put("http://c.validator.nu/table/",
-                    CheckerSchema.TABLE_CHECKER);
-            schemaMap.put("http://hsivonen.iki.fi/checkers/table/",
-                    CheckerSchema.TABLE_CHECKER);
-            schemaMap.put("http://c.validator.nu/nfc/",
-                    CheckerSchema.NORMALIZATION_CHECKER);
-            schemaMap.put("http://hsivonen.iki.fi/checkers/nfc/",
-                    CheckerSchema.NORMALIZATION_CHECKER);
-            schemaMap.put("http://c.validator.nu/debug/",
-                    CheckerSchema.DEBUG_CHECKER);
-            schemaMap.put("http://hsivonen.iki.fi/checkers/debug/",
-                    CheckerSchema.DEBUG_CHECKER);
-            schemaMap.put("http://c.validator.nu/text-content/",
-                    CheckerSchema.TEXT_CONTENT_CHECKER);
-            schemaMap.put("http://hsivonen.iki.fi/checkers/text-content/",
-                    CheckerSchema.TEXT_CONTENT_CHECKER);
-            schemaMap.put("http://c.validator.nu/usemap/",
-                    CheckerSchema.USEMAP_CHECKER);
-            schemaMap.put("http://n.validator.nu/checkers/usemap/",
-                    CheckerSchema.USEMAP_CHECKER);
-            schemaMap.put("http://c.validator.nu/unchecked/",
-                    CheckerSchema.UNCHECKED_SUBTREE_WARNER);
-            schemaMap.put("http://s.validator.nu/html5/assertions.sch",
-                    CheckerSchema.ASSERTION_SCH);
-            schemaMap.put("http://s.validator.nu/html4/assertions.sch",
-                    CheckerSchema.HTML4ASSERTION_SCH);
-            schemaMap.put("http://c.validator.nu/obsolete/",
-                    CheckerSchema.CONFORMING_BUT_OBSOLETE_WARNER);
-            schemaMap.put("http://c.validator.nu/xml-pi/",
-                    CheckerSchema.XML_PI_CHECKER);
-            schemaMap.put("http://c.validator.nu/unsupported/",
-                    CheckerSchema.UNSUPPORTED_CHECKER);
-            schemaMap.put("http://c.validator.nu/microdata/",
-                    CheckerSchema.MICRODATA_CHECKER);
-            schemaMap.put("http://c.validator.nu/rdfalite/",
-                    CheckerSchema.RDFALITE_CHECKER);
-
-            for (int i = 0; i < presetUrls.length; i++) {
-                String[] urls1 = SPACE.split(presetUrls[i]);
-                for (int j = 0; j < urls1.length; j++) {
-                    String url = urls1[j];
-                    if (schemaMap.get(url) == null && !isCheckerUrl(url)) {
-                        Schema sch = schemaByUrl(url, er, pMap);
-                        schemaMap.put(url, sch);
-                    }
-                }
-            }
-
-            log4j.debug("Schemas read.");
-
-            preloadedSchemaUrls = new String[schemaMap.size()];
-            preloadedSchemas = new Schema[schemaMap.size()];
-            int i = 0;
-            for (Map.Entry<String, Schema> entry : schemaMap.entrySet()) {
-                preloadedSchemaUrls[i] = entry.getKey().intern();
-                Schema s = entry.getValue();
-                String u = entry.getKey();
-                if (isDataAttributeDroppingSchema(u)) {
-                    s = new DataAttributeDroppingSchemaWrapper(
-                            s);
-                }
-                if (isXmlLangAllowingSchema(u)) {
-                    s = new XmlLangAttributeDroppingSchemaWrapper(s);
-                }
-                if (isRoleAttributeFilteringSchema(u)) {
-                    s = new RoleAttributeFilteringSchemaWrapper(s);
-                }
-                preloadedSchemas[i] = s;
-                i++;
-            }
-
-            log4j.debug("Reading spec.");
-
-            html5spec = Html5SpecBuilder.parseSpec(LocalCacheEntityResolver.getHtml5SpecAsStream());
-
-            log4j.debug("Spec read.");
-
-            log4j.debug("Initialization complete.");
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    protected static String scrub(CharSequence s) {
-        return Normalizer.normalize(
-                CharacterUtil.prudentlyScrubCharacterData(s), Normalizer.NFC);
-    }
-
-    private static boolean isDataAttributeDroppingSchema(String key) {
-        return ("http://s.validator.nu/xhtml5.rnc".equals(key)
-                || "http://s.validator.nu/html5.rnc".equals(key)
-                || "http://s.validator.nu/html5-all.rnc".equals(key)
-                || "http://s.validator.nu/xhtml5-all.rnc".equals(key)
-                || "http://s.validator.nu/html5-its.rnc".equals(key)
-                || "http://s.validator.nu/xhtml5-rdfalite.rnc".equals(key)
-                || "http://s.validator.nu/html5-rdfalite.rnc".equals(key));
-    }
-
-    private static boolean isXmlLangAllowingSchema(String key) {
-        return ("http://s.validator.nu/xhtml5.rnc".equals(key)
-                || "http://s.validator.nu/html5.rnc".equals(key)
-                || "http://s.validator.nu/html5-all.rnc".equals(key)
-                || "http://s.validator.nu/xhtml5-all.rnc".equals(key)
-                || "http://s.validator.nu/html5-its.rnc".equals(key)
-                || "http://s.validator.nu/xhtml5-rdfalite.rnc".equals(key)
-                || "http://s.validator.nu/html5-rdfalite.rnc".equals(key));
-    }
-
-    private static boolean isRoleAttributeFilteringSchema(String key) {
-        return ("http://s.validator.nu/xhtml5.rnc".equals(key)
-                || "http://s.validator.nu/html5.rnc".equals(key)
-                || "http://s.validator.nu/html5-all.rnc".equals(key)
-                || "http://s.validator.nu/xhtml5-all.rnc".equals(key)
-                || "http://s.validator.nu/html5-its.rnc".equals(key)
-                || "http://s.validator.nu/xhtml5-rdfalite.rnc".equals(key)
-                || "http://s.validator.nu/html5-rdfalite.rnc".equals(key));
-    }
-
-    private static boolean isCheckerUrl(String url) {
-        if ("http://c.validator.nu/all/".equals(url)
-                || "http://hsivonen.iki.fi/checkers/all/".equals(url)) {
-            return true;
-        } else if ("http://c.validator.nu/all-html4/".equals(url)
-                || "http://hsivonen.iki.fi/checkers/all-html4/".equals(url)) {
-            return true;
-        } else if ("http://c.validator.nu/base/".equals(url)) {
-            return true;
-        } else if ("http://c.validator.nu/rdfalite/".equals(url)) {
-            return true;
-        }
-        for (int i = 0; i < ALL_CHECKERS.length; i++) {
-            if (ALL_CHECKERS[i].equals(url)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * @param request
-     * @param response
-     */
-    VerifierServletTransaction(HttpServletRequest request,
-            HttpServletResponse response) {
-        this.request = request;
-        this.response = response;
-    }
-
-    protected boolean willValidate() {
-        if (methodIsGet) {
-            return document != null;
-        } else { // POST
-            return true;
-        }
-    }
-
-    void service() throws ServletException, IOException {
-        this.methodIsGet = "GET".equals(request.getMethod())
-                || "HEAD".equals(request.getMethod());
-
-        this.out = response.getOutputStream();
-
-        System.setProperty("nu.validator.servlet.request.legacy", "false");
-
-        if (Arrays.asList(LEGACY_HOSTS).contains(request.getRemoteHost())) {
-            System.setProperty("nu.validator.servlet.request.legacy", "true");
-        }
-
-        try {
-            request.setCharacterEncoding("utf-8");
-        } catch (NoSuchMethodError e) {
-            log4j.debug("Vintage Servlet API doesn't support setCharacterEncoding().", e);
-        }
-
-        if (!methodIsGet) {
-            postContentType = request.getContentType();
-            if (postContentType == null) {
-                response.sendError(HttpServletResponse.SC_BAD_REQUEST,
-                        "Content-Type missing");
-                return;
-            } else if (postContentType.trim().toLowerCase().startsWith(
-                    "application/x-www-form-urlencoded")) {
-                response.sendError(
-                        HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
-                        "application/x-www-form-urlencoded not supported. Please use multipart/form-data.");
-                return;
-            }
-        }
-
-        String outFormat = request.getParameter("out");
-        if (outFormat == null) {
-            outputFormat = OutputFormat.HTML;
-        } else {
-            if ("html".equals(outFormat)) {
-                outputFormat = OutputFormat.HTML;
-            } else if ("xhtml".equals(outFormat)) {
-                outputFormat = OutputFormat.XHTML;
-            } else if ("text".equals(outFormat)) {
-                outputFormat = OutputFormat.TEXT;
-            } else if ("gnu".equals(outFormat)) {
-                outputFormat = OutputFormat.GNU;
-            } else if ("xml".equals(outFormat)) {
-                outputFormat = OutputFormat.XML;
-            } else if ("json".equals(outFormat)) {
-                outputFormat = OutputFormat.JSON;
-            } else {
-                response.sendError(HttpServletResponse.SC_BAD_REQUEST,
-                        "Unsupported output format");
-                return;
-            }
-        }
-
-        if (!methodIsGet) {
-            document = request.getHeader("Content-Location");
-        }
-        if (document == null) {
-            document = request.getParameter("doc");
-        }
-        if (document == null) {
-            document = request.getParameter("file");
-        }
-
-        document = ("".equals(document)) ? null : document;
-
-        String callback = null;
-        if (outputFormat == OutputFormat.JSON) {
-            callback = request.getParameter("callback");
-            if (callback != null) {
-                Matcher m = JS_IDENTIFIER.matcher(callback);
-                if (m.matches()) {
-                    if (Arrays.binarySearch(JS_RESERVED_WORDS, callback) >= 0) {
-                        response.sendError(HttpServletResponse.SC_BAD_REQUEST,
-                                "Callback is a reserved word.");
-                        return;
-                    }
-                } else {
-                    response.sendError(HttpServletResponse.SC_BAD_REQUEST,
-                            "Callback is not a valid ECMA 262 IdentifierName.");
-                    return;
-                }
-            }
-        }
-
-        if (willValidate()) {
-            response.setDateHeader("Expires", 0);
-            response.setHeader("Cache-Control", "no-cache");
-        } else if (outputFormat == OutputFormat.HTML
-                || outputFormat == OutputFormat.XHTML) {
-            response.setDateHeader("Last-Modified", lastModified);
-        } else {
-            response.sendError(HttpServletResponse.SC_BAD_REQUEST,
-                    "No input document");
-            return;
-        }
-
-        setup();
-
-        if (request.getParameter("useragent") != null) {
-            userAgent = scrub(request.getParameter("useragent"));
-        } else {
-            userAgent = USER_AGENT;
-        }
-        showSource = (request.getParameter("showsource") != null);
-        showOutline = (request.getParameter("showoutline") != null);
-        if (request.getParameter("showimagereport") != null) {
-            imageCollector = new ImageCollector(sourceCode);
-        }
-
-        String charset = request.getParameter("charset");
-        if (charset != null) {
-            charset = scrub(charset.trim());
-            if (!"".equals(charset)) {
-                charsetOverride = charset;
-            }
-        }
-
-        String nsfilter = request.getParameter("nsfilter");
-        if (nsfilter != null) {
-            String[] nsfilterArr = SPACE.split(nsfilter);
-            for (int i = 0; i < nsfilterArr.length; i++) {
-                String ns = nsfilterArr[i];
-                if (ns.length() > 0) {
-                    filteredNamespaces.add(ns);
-                }
-            }
-        }
-
-        boolean errorsOnly = ("error".equals(request.getParameter("level")));
-
-        boolean asciiQuotes = (request.getParameter("asciiquotes") != null);
-
-        int lineOffset = 0;
-        String lineOffsetStr = request.getParameter("lineoffset");
-        if (lineOffsetStr != null) {
-            try {
-                lineOffset = Integer.parseInt(lineOffsetStr);
-            } catch (NumberFormatException e) {
-
-            }
-        }
-
-        try {
-            if (outputFormat == OutputFormat.HTML
-                    || outputFormat == OutputFormat.XHTML) {
-                if (outputFormat == OutputFormat.HTML) {
-                    response.setContentType("text/html; charset=utf-8");
-                    contentHandler = new HtmlSerializer(out);
-                } else {
-                    response.setContentType("application/xhtml+xml");
-                    contentHandler = 
-                            new XmlSerializer(out);
-                }
-                emitter = new XhtmlSaxEmitter(contentHandler);
-                errorHandler = new MessageEmitterAdapter(sourceCode,
-                        showSource, imageCollector, lineOffset, false,
-                        new XhtmlMessageEmitter(contentHandler));
-                PageEmitter.emit(contentHandler, this);
-            } else {
-                if (outputFormat == OutputFormat.TEXT) {
-                    response.setContentType("text/plain; charset=utf-8");
-                    errorHandler = new MessageEmitterAdapter(sourceCode,
-                            showSource, null, lineOffset, false,
-                            new TextMessageEmitter(out, asciiQuotes));
-                } else if (outputFormat == OutputFormat.GNU) {
-                    response.setContentType("text/plain; charset=utf-8");
-                    errorHandler = new MessageEmitterAdapter(sourceCode,
-                            showSource, null, lineOffset, false,
-                            new GnuMessageEmitter(out, asciiQuotes));
-                } else if (outputFormat == OutputFormat.XML) {
-                    response.setContentType("application/xml");
-                    errorHandler = new MessageEmitterAdapter(sourceCode,
-                            showSource, null, lineOffset, false,
-                            new XmlMessageEmitter(new XmlSerializer(out)));
-                } else if (outputFormat == OutputFormat.JSON) {
-                    if (callback == null) {
-                        response.setContentType("application/json; charset=utf-8");
-                    } else {
-                        response.setContentType("application/javascript; charset=utf-8");
-                    }
-                    errorHandler = new MessageEmitterAdapter(sourceCode,
-                            showSource, null, lineOffset, false,
-                            new JsonMessageEmitter(
-                                    new nu.validator.json.Serializer(out),
-                                    callback));
-                } else {
-                    throw new RuntimeException("Unreachable.");
-                }
-                errorHandler.setErrorsOnly(errorsOnly);
-                validate();
-            }
-        } catch (SAXException e) {
-            throw new ServletException(e);
-        }
-    }
-
-    /**
-     * @throws ServletException
-     */
-    protected void setup() throws ServletException {
-        String preset = request.getParameter("preset");
-
-        if (preset != null && !"".equals(preset)) {
-            schemaUrls = preset;
-        } else {
-            schemaUrls = request.getParameter("schema");
-        }
-        if (schemaUrls == null) {
-            schemaUrls = "";
-        }
-
-        String parserStr = request.getParameter("parser");
-
-        if ("html".equals(parserStr)) {
-            parser = ParserMode.HTML_AUTO;
-        } else if ("xmldtd".equals(parserStr)) {
-            parser = ParserMode.XML_EXTERNAL_ENTITIES_NO_VALIDATION;
-        } else if ("xml".equals(parserStr)) {
-            parser = ParserMode.XML_NO_EXTERNAL_ENTITIES;
-        } else if ("html5".equals(parserStr)) {
-            parser = ParserMode.HTML;
-        } else if ("html4".equals(parserStr)) {
-            parser = ParserMode.HTML401_STRICT;
-        } else if ("html4tr".equals(parserStr)) {
-            parser = ParserMode.HTML401_TRANSITIONAL;
-        } // else auto
-
-        laxType = (request.getParameter("laxtype") != null);
-    }
-
-    private boolean useHtml5Schema() {
-        if ("".equals(schemaUrls)) {
-            return false;
-        }
-        return (schemaUrls.contains("http://s.validator.nu/html5.rnc")
-                || schemaUrls.contains("http://s.validator.nu/html5-all.rnc")
-                || schemaUrls.contains("http://s.validator.nu/html5-its.rnc")
-                || schemaUrls.contains("http://s.validator.nu/html5-rdfalite.rnc"));
-    }
-
-    private boolean isHtmlUnsafePreset() {
-        if ("".equals(schemaUrls)) {
-            return false;
-        }
-        boolean preset = false;
-        for (int i = 0; i < presetUrls.length; i++) {
-            if (presetUrls[i].equals(schemaUrls)) {
-                preset = true;
-                break;
-            }
-        }
-        if (!preset) {
-            return false;
-        }
-        return !(schemaUrls.startsWith("http://s.validator.nu/xhtml10/xhtml-basic.rnc")
-                || schemaUrls.startsWith("http://s.validator.nu/xhtml10/xhtml-strict.rnc")
-                || schemaUrls.startsWith("http://s.validator.nu/xhtml10/xhtml-transitional.rnc")
-                || schemaUrls.startsWith("http://s.validator.nu/xhtml10/xhtml-frameset.rnc")
-                || schemaUrls.startsWith("http://s.validator.nu/html5.rnc")
-                || schemaUrls.startsWith("http://s.validator.nu/html5-all.rnc")
-                || schemaUrls.startsWith("http://s.validator.nu/html5-its.rnc")
-                || schemaUrls.startsWith("http://s.validator.nu/html5-rdfalite.rnc"));
-
-    }
-
-    /**
-     * @throws SAXException
-     */
-    @SuppressWarnings("deprecation") void validate() throws SAXException {
-        if (!willValidate()) {
-            return;
-        }
-
-        boolean isHtmlOrXhtml = (outputFormat == OutputFormat.HTML || outputFormat == OutputFormat.XHTML);
-        if (isHtmlOrXhtml) {
-            try {
-                out.flush();
-            } catch (IOException e1) {
-                throw new SAXException(e1);
-            }
-        }
-        httpRes = new PrudentHttpEntityResolver(SIZE_LIMIT, laxType,
-                errorHandler);
-        httpRes.setUserAgent(userAgent);
-        dataRes = new DataUriEntityResolver(httpRes, laxType, errorHandler);
-        contentTypeParser = new ContentTypeParser(errorHandler, laxType);
-        entityResolver = new LocalCacheEntityResolver(dataRes);
-        setAllowRnc(true);
-        try {
-            this.errorHandler.start(document);
-            PropertyMapBuilder pmb = new PropertyMapBuilder();
-            pmb.put(ValidateProperty.ERROR_HANDLER, errorHandler);
-            pmb.put(ValidateProperty.ENTITY_RESOLVER, entityResolver);
-            pmb.put(ValidateProperty.XML_READER_CREATOR,
-                    new VerifierServletXMLReaderCreator(errorHandler,
-                            entityResolver));
-            pmb.put(ValidateProperty.SCHEMA_RESOLVER, this);
-            RngProperty.CHECK_ID_IDREF.add(pmb);
-            jingPropertyMap = pmb.toPropertyMap();
-
-            tryToSetupValidator();
-
-            setAllowRnc(false);
-
-            loadDocAndSetupParser();
-            setErrorProfile();
-
-            reader.setErrorHandler(errorHandler);
-            contentType = documentInput.getType();
-            sourceCode.initialize(documentInput);
-            if (validator == null) {
-                checkNormalization = true;
-            }
-            if (checkNormalization) {
-                reader.setFeature(
-                        "http://xml.org/sax/features/unicode-normalization-checking",
-                        true);
-            }
-            WiretapXMLReaderWrapper wiretap = new WiretapXMLReaderWrapper(
-                    reader);
-            ContentHandler recorder = sourceCode.getLocationRecorder();
-            if (baseUriTracker == null) {
-                wiretap.setWiretapContentHander(recorder);
-            } else {
-                wiretap.setWiretapContentHander(new CombineContentHandler(
-                        recorder, baseUriTracker));
-            }
-            wiretap.setWiretapLexicalHandler((LexicalHandler) recorder);
-            reader = wiretap;
-            if (htmlParser != null) {
-                htmlParser.addCharacterHandler(sourceCode);
-                htmlParser.setMappingLangToXmlLang(true);
-                htmlParser.setErrorHandler(errorHandler.getExactErrorHandler());
-                htmlParser.setTreeBuilderErrorHandlerOverride(errorHandler);
-                errorHandler.setHtml(true);
-            } else if (xmlParser != null) {
-                // this must be after wiretap!
-                if (!filteredNamespaces.isEmpty()) {
-                    reader = new NamespaceDroppingXMLReaderWrapper(reader,
-                            filteredNamespaces);
-                }
-                xmlParser.setErrorHandler(errorHandler.getExactErrorHandler());
-                xmlParser.lockErrorHandler();
-            } else {
-                throw new RuntimeException("Bug. Unreachable.");
-            }
-            reader = new AttributesPermutingXMLReaderWrapper(reader); // make
-            // RNG
-            // validation
-            // better
-            if (charsetOverride != null) {
-                String charset = documentInput.getEncoding();
-                if (charset == null) {
-                    errorHandler.warning(new SAXParseException(
-                            "Overriding document character encoding from none to \u201C"
-                                    + charsetOverride + "\u201D.", null));
-                } else {
-                    errorHandler.warning(new SAXParseException(
-                            "Overriding document character encoding from \u201C"
-                                    + charset + "\u201D to \u201C"
-                                    + charsetOverride + "\u201D.", null));
-                }
-                documentInput.setEncoding(charsetOverride);
-            }
-            if (showOutline) {
-                reader = new OutlineBuildingXMLReaderWrapper(reader, request);
-                reader.parse(documentInput);
-                outline = (Deque<Section>) request.getAttribute("http://validator.nu/properties/document-outline");
-            } else {
-                reader.parse(documentInput);
-            }
-        } catch (TooManyErrorsException e) {
-            log4j.debug("TooManyErrorsException", e);
-            errorHandler.fatalError(e);
-        } catch (SAXException e) {
-            log4j.debug("SAXException", e);
-        } catch (IOException e) {
-            isHtmlOrXhtml = false;
-            log4j.info("IOException", e);
-            errorHandler.ioError(e);
-        } catch (IncorrectSchemaException e) {
-            log4j.debug("IncorrectSchemaException", e);
-            errorHandler.schemaError(e);
-        } catch (RuntimeException e) {
-            isHtmlOrXhtml = false;
-            log4j.error("RuntimeException, doc: " + document + " schema: "
-                    + schemaUrls + " lax: " + laxType, e);
-            errorHandler.internalError(
-                    e,
-                    "Oops. That was not supposed to happen. A bug manifested itself in the application internals. Unable to continue. Sorry. The admin was notified.");
-        } catch (Error e) {
-            isHtmlOrXhtml = false;
-            log4j.error("Error, doc: " + document + " schema: " + schemaUrls
-                    + " lax: " + laxType, e);
-            errorHandler.internalError(
-                    e,
-                    "Oops. That was not supposed to happen. A bug manifested itself in the application internals. Unable to continue. Sorry. The admin was notified.");
-        } finally {
-            errorHandler.end(successMessage(), failureMessage());
-            gatherStatistics();
-        }
-        if (isHtmlOrXhtml) {
-            XhtmlOutlineEmitter outlineEmitter = new XhtmlOutlineEmitter(
-                    contentHandler, outline);
-            outlineEmitter.emit();
-            StatsEmitter.emit(contentHandler, this);
-        }
-    }
-
-    private void gatherStatistics() {
-        Statistics stats = Statistics.STATISTICS;
-        if (stats == null) {
-            return;
-        }
-        synchronized (stats) {
-            stats.incrementTotal();
-            if (charsetOverride != null) {
-                stats.incrementField(Statistics.Field.CUSTOM_ENC);
-            }
-            switch (parser) {
-                case HTML401_STRICT:
-                case HTML401_TRANSITIONAL:
-                    stats.incrementField(Statistics.Field.PARSER_HTML4);
-                    break;
-                case XML_EXTERNAL_ENTITIES_NO_VALIDATION:
-                    stats.incrementField(Statistics.Field.PARSER_XML_EXTERNAL);
-                    break;
-            }
-            if (!filteredNamespaces.isEmpty()) {
-                stats.incrementField(Statistics.Field.XMLNS_FILTER);
-            }
-            if (laxType) {
-                stats.incrementField(Statistics.Field.LAX_TYPE);
-            }
-            if (imageCollector != null) {
-                stats.incrementField(Statistics.Field.IMAGE_REPORT);
-            }
-            if (showSource) {
-                stats.incrementField(Statistics.Field.SHOW_SOURCE);
-            }
-            if (showOutline) {
-                stats.incrementField(Statistics.Field.SHOW_OUTLINE);
-            }
-            if (methodIsGet) {
-                stats.incrementField(Statistics.Field.INPUT_GET);
-            } else { // POST
-                stats.incrementField(Statistics.Field.INPUT_POST);
-                Object inputType = request.getAttribute("nu.validator.servlet.MultipartFormDataFilter.type");
-                if ("textarea".equals(inputType)) {
-                    stats.incrementField(Statistics.Field.INPUT_TEXT_FIELD);
-                } else if ("file".equals(inputType)) {
-                    stats.incrementField(Statistics.Field.INPUT_FILE_UPLOAD);
-                } else {
-                    stats.incrementField(Statistics.Field.INPUT_ENTITY_BODY);
-                }
-            }
-            if (htmlParser != null) {
-                stats.incrementField(Statistics.Field.INPUT_HTML);
-            }
-            if (xmlParser != null) {
-                stats.incrementField(Statistics.Field.INPUT_XML);
-            }
-            switch (outputFormat) {
-                case GNU:
-                    stats.incrementField(Statistics.Field.OUTPUT_GNU);
-                    break;
-                case HTML:
-                    stats.incrementField(Statistics.Field.OUTPUT_HTML);
-                    break;
-                case JSON:
-                    stats.incrementField(Statistics.Field.OUTPUT_JSON);
-                    break;
-                case TEXT:
-                    stats.incrementField(Statistics.Field.OUTPUT_TEXT);
-                    break;
-                case XHTML:
-                    stats.incrementField(Statistics.Field.OUTPUT_XHTML);
-                    break;
-                case XML:
-                    stats.incrementField(Statistics.Field.OUTPUT_XML);
-                    break;
-            }
-            if (schemaListForStats == null) {
-                stats.incrementField(Statistics.Field.LOGIC_ERROR);
-            } else {
-                boolean preset = false;
-                for (int i = 0; i < presetUrls.length; i++) {
-                    if (presetUrls[i].equals(schemaListForStats)) {
-                        preset = true;
-                        if (externalSchema || externalSchematron) {
-                            stats.incrementField(Statistics.Field.LOGIC_ERROR);
-                        } else {
-                            stats.incrementField(Statistics.Field.PRESET_SCHEMA);
-                            /*
-                             * XXX WARNING WARNING: These mappings correspond to
-                             * values in the presets.txt file in the validator
-                             * source repo. They might be bogus if a custom
-                             * presets file is used instead.
-                             */
-                            switch (i) {
-                                case 0:
-                                case 5:
-                                    stats.incrementField(Statistics.Field.HTML5_SCHEMA);
-                                    break;
-                                case 1:
-                                case 6:
-                                    stats.incrementField(Statistics.Field.HTML5_RDFA_LITE_SCHEMA);
-                                    break;
-                                case 2:
-                                    stats.incrementField(Statistics.Field.HTML4_STRICT_SCHEMA);
-                                    break;
-                                case 3:
-                                    stats.incrementField(Statistics.Field.HTML4_TRANSITIONAL_SCHEMA);
-                                    break;
-                                case 4:
-                                    stats.incrementField(Statistics.Field.HTML4_FRAMESET_SCHEMA);
-                                    break;
-                                case 7:
-                                    stats.incrementField(Statistics.Field.XHTML1_COMPOUND_SCHEMA);
-                                    break;
-                                case 8:
-                                    stats.incrementField(Statistics.Field.SVG_SCHEMA);
-                                    break;
-                                default:
-                                    stats.incrementField(Statistics.Field.LOGIC_ERROR);
-                                    break;
-                            }
-                        }
-                        break;
-                    }
-                }
-                if (!preset && !externalSchema) {
-                    stats.incrementField(Statistics.Field.BUILT_IN_NON_PRESET);
-                }
-            }
-            if ("".equals(schemaUrls)) {
-                stats.incrementField(Statistics.Field.AUTO_SCHEMA);
-                if (externalSchema) {
-                    stats.incrementField(Statistics.Field.LOGIC_ERROR);
-                }
-            } else if (externalSchema) {
-                if (externalSchematron) {
-                    stats.incrementField(Statistics.Field.EXTERNAL_SCHEMA_SCHEMATRON);
-                } else {
-                    stats.incrementField(Statistics.Field.EXTERNAL_SCHEMA_NON_SCHEMATRON);
-                }
-            } else if (externalSchematron) {
-                stats.incrementField(Statistics.Field.LOGIC_ERROR);
-            }
-        }
-    }
-
-    /**
-     * @return
-     * @throws SAXException
-     */
-    protected String successMessage() throws SAXException {
-        return "The document validates according to the specified schema(s) and to additional constraints checked by the validator.";
-    }
-
-    protected String failureMessage() throws SAXException {
-        return "There were errors.";
-    }
-
-    /**
-     * @throws SAXException
-     * @throws IOException
-     * @throws IncorrectSchemaException
-     */
-    protected void tryToSetupValidator() throws SAXException, IOException,
-            IncorrectSchemaException {
-        validator = validatorByUrls(schemaUrls);
-    }
-
-    protected void setErrorProfile() {
-        profile = request.getParameter("profile");
-
-        HashMap<String, String> profileMap = new HashMap<String, String>();
-
-        if ("pedagogical".equals(profile)) {
-            profileMap.put("xhtml1", "warn");
-        } else if ("polyglot".equals(profile)) {
-            profileMap.put("xhtml1", "warn");
-            profileMap.put("xhtml2", "warn");
-        } else {
-            return; // presumed to be permissive
-        }
-
-        htmlParser.setErrorProfile(profileMap);
-    }
-
-    /**
-     * @throws SAXException
-     * @throws IOException
-     * @throws IncorrectSchemaException
-     * @throws SAXNotRecognizedException
-     * @throws SAXNotSupportedException
-     */
-    protected void loadDocAndSetupParser() throws SAXException, IOException,
-            IncorrectSchemaException, SAXNotRecognizedException,
-            SAXNotSupportedException {
-        switch (parser) {
-            case HTML_AUTO:
-            case HTML:
-            case HTML401_STRICT:
-            case HTML401_TRANSITIONAL:
-                if (isHtmlUnsafePreset()) {
-                    String message = "The chosen preset schema is not appropriate for HTML.";
-                    SAXException se = new SAXException(message);
-                    errorHandler.schemaError(se);
-                    throw se;
-                }
-                setAllowGenericXml(false);
-                setAllowHtml(true);
-                setAcceptAllKnownXmlTypes(false);
-                setAllowXhtml(false);
-                loadDocumentInput();
-                newHtmlParser();
-                DoctypeExpectation doctypeExpectation;
-                int schemaId;
-                switch (parser) {
-                    case HTML:
-                        doctypeExpectation = DoctypeExpectation.HTML;
-                        schemaId = HTML5_SCHEMA;
-                        break;
-                    case HTML401_STRICT:
-                        doctypeExpectation = DoctypeExpectation.HTML401_STRICT;
-                        schemaId = XHTML1STRICT_SCHEMA;
-                        break;
-                    case HTML401_TRANSITIONAL:
-                        doctypeExpectation = DoctypeExpectation.HTML401_TRANSITIONAL;
-                        schemaId = XHTML1TRANSITIONAL_SCHEMA;
-                        break;
-                    default:
-                        doctypeExpectation = DoctypeExpectation.AUTO;
-                        schemaId = 0;
-                        break;
-                }
-                htmlParser.setDoctypeExpectation(doctypeExpectation);
-                htmlParser.setDocumentModeHandler(this);
-                reader = htmlParser;
-                if (validator == null) {
-                    validator = validatorByDoctype(schemaId);
-                }
-                if (validator != null) {
-                    reader.setContentHandler(validator.getContentHandler());
-                }
-                break;
-            case XML_NO_EXTERNAL_ENTITIES:
-            case XML_EXTERNAL_ENTITIES_NO_VALIDATION:
-                setAllowGenericXml(true);
-                setAllowHtml(false);
-                setAcceptAllKnownXmlTypes(true);
-                setAllowXhtml(true);
-                loadDocumentInput();
-                setupXmlParser();
-                break;
-            default:
-                setAllowGenericXml(true);
-                setAllowHtml(true);
-                setAcceptAllKnownXmlTypes(true);
-                setAllowXhtml(true);
-                loadDocumentInput();
-                String type = documentInput.getType();
-                if ("text/html".equals(type) || "text/html-sandboxed".equals(type)) {
-                    if (isHtmlUnsafePreset()) {
-                        String message = "The Content-Type was \u201C" + type + "\u201D, but the chosen preset schema is not appropriate for HTML.";
-                        SAXException se = new SAXException(message);
-                        errorHandler.schemaError(se);
-                        throw se;
-                    }
-                    errorHandler.info("The Content-Type was \u201C" + type + "\u201D. Using the HTML parser.");
-                    newHtmlParser();
-                    if (useHtml5Schema()) {
-                        htmlParser.setDoctypeExpectation(DoctypeExpectation.HTML);
-                    } else {
-                        htmlParser.setDoctypeExpectation(DoctypeExpectation.AUTO);
-                    }
-                    htmlParser.setDocumentModeHandler(this);
-                    reader = htmlParser;
-                    if (validator != null) {
-                        reader.setContentHandler(validator.getContentHandler());
-                    }
-                } else {
-                    errorHandler.info("The Content-Type was \u201C"
-                            + type
-                            + "\u201D. Using the XML parser (not resolving external entities).");
-                    setupXmlParser();
-                }
-                break;
-        }
-    }
-
-    /**
-     * 
-     */
-    protected void newHtmlParser() {
-        htmlParser = new HtmlParser();
-        htmlParser.setCommentPolicy(XmlViolationPolicy.ALLOW);
-        htmlParser.setContentNonXmlCharPolicy(XmlViolationPolicy.ALLOW);
-        htmlParser.setContentSpacePolicy(XmlViolationPolicy.ALTER_INFOSET);
-        htmlParser.setNamePolicy(XmlViolationPolicy.ALLOW);
-        htmlParser.setStreamabilityViolationPolicy(XmlViolationPolicy.FATAL);
-        htmlParser.setXmlnsPolicy(XmlViolationPolicy.ALTER_INFOSET);
-        htmlParser.setMappingLangToXmlLang(true);
-        htmlParser.setHtml4ModeCompatibleWithXhtml1Schemata(true);
-        htmlParser.setHeuristics(Heuristics.ALL);
-    }
-
-    protected Validator validatorByDoctype(int schemaId) throws SAXException,
-            IOException, IncorrectSchemaException {
-        if (schemaId == 0) {
-            return null;
-        }
-        for (int i = 0; i < presetDoctypes.length; i++) {
-            if (presetDoctypes[i] == schemaId) {
-                return validatorByUrls(presetUrls[i]);
-            }
-        }
-        throw new RuntimeException("Doctype mappings not initialized properly.");
-    }
-
-    /**
-     * @throws SAXNotRecognizedException
-     * @throws SAXNotSupportedException
-     */
-    protected void setupXmlParser() throws SAXNotRecognizedException,
-            SAXNotSupportedException {
-        xmlParser = new SAXDriver();
-        xmlParser.setCharacterHandler(sourceCode);
-        if (lexicalHandler != null) {
-          xmlParser.setProperty("http://xml.org/sax/properties/lexical-handler",
-              (LexicalHandler) lexicalHandler);
-        }
-        reader = new IdFilter(xmlParser);
-        reader.setFeature("http://xml.org/sax/features/string-interning", true);
-        reader.setFeature(
-                "http://xml.org/sax/features/external-general-entities",
-                parser == ParserMode.XML_EXTERNAL_ENTITIES_NO_VALIDATION);
-        reader.setFeature(
-                "http://xml.org/sax/features/external-parameter-entities",
-                parser == ParserMode.XML_EXTERNAL_ENTITIES_NO_VALIDATION);
-        if (parser == ParserMode.XML_EXTERNAL_ENTITIES_NO_VALIDATION) {
-            reader.setEntityResolver(entityResolver);
-        } else {
-            reader.setEntityResolver(new NullEntityResolver());
-        }
-        if (validator == null) {
-            bufferingRootNamespaceSniffer = new BufferingRootNamespaceSniffer(
-                    this);
-            reader.setContentHandler(bufferingRootNamespaceSniffer);
-        } else {
-            reader.setContentHandler(new RootNamespaceSniffer(this,
-                    validator.getContentHandler()));
-            reader.setDTDHandler(validator.getDTDHandler());
-        }
-    }
-
-    /**
-     * @param validator
-     * @return
-     * @throws SAXException
-     * @throws IOException
-     * @throws IncorrectSchemaException
-     */
-    private Validator validatorByUrls(String schemaList) throws SAXException,
-            IOException, IncorrectSchemaException {
-        System.setProperty("nu.validator.schema.rdfa-full", "0");
-        schemaListForStats  = schemaList;
-        Validator v = null;
-        String[] schemas = SPACE.split(schemaList);
-        for (int i = schemas.length - 1; i > -1; i--) {
-            String url = schemas[i];
-            if ("http://s.validator.nu/html5-all.rnc".equals(url)) {
-                System.setProperty("nu.validator.schema.rdfa-full", "1");
-            }
-            if ("http://c.validator.nu/all/".equals(url)
-                    || "http://hsivonen.iki.fi/checkers/all/".equals(url)) {
-                for (int j = 0; j < ALL_CHECKERS.length; j++) {
-                    v = combineValidatorByUrl(v, ALL_CHECKERS[j]);
-                }
-            } else if ("http://c.validator.nu/all-html4/".equals(url)
-                    || "http://hsivonen.iki.fi/checkers/all-html4/".equals(url)) {
-                for (int j = 0; j < ALL_CHECKERS_HTML4.length; j++) {
-                    v = combineValidatorByUrl(v, ALL_CHECKERS_HTML4[j]);
-                }
-            } else {
-                v = combineValidatorByUrl(v, url);
-            }
-        }
-        if (imageCollector != null && v != null) {
-            v = new CombineValidator(imageCollector, v);
-        }
-        return v;
-    }
-
-    /**
-     * @param val
-     * @param url
-     * @return
-     * @throws SAXException
-     * @throws IOException
-     * @throws IncorrectSchemaException
-     */
-    private Validator combineValidatorByUrl(Validator val, String url)
-            throws SAXException, IOException, IncorrectSchemaException {
-        if (!"".equals(url)) {
-            Validator v = validatorByUrl(url);
-            if (val == null) {
-                val = v;
-            } else {
-                val = new CombineValidator(v, val);
-            }
-        }
-        return val;
-    }
-
-    /**
-     * @param url
-     * @return
-     * @throws SAXException
-     * @throws IOException
-     * @throws IncorrectSchemaException
-     */
-    private Validator validatorByUrl(String url) throws SAXException,
-            IOException, IncorrectSchemaException {
-        if (loadedValidatorUrls.contains(url)) {
-            return null;
-        }
-        loadedValidatorUrls.add(url);
-        if ("http://s.validator.nu/xhtml5.rnc".equals(url)
-                || "http://s.validator.nu/html5.rnc".equals(url)
-                || "http://s.validator.nu/html5-all.rnc".equals(url)
-                || "http://s.validator.nu/xhtml5-all.rnc".equals(url)
-                || "http://s.validator.nu/html5-its.rnc".equals(url)
-                || "http://s.validator.nu/xhtml5-rdfalite.rnc".equals(url)
-                || "http://s.validator.nu/html5-rdfalite.rnc".equals(url)) {
-            errorHandler.setSpec(html5spec);
-        }
-        Schema sch = resolveSchema(url, jingPropertyMap);
-        Validator validator = sch.createValidator(jingPropertyMap);
-        if (validator.getContentHandler() instanceof XmlPiChecker) {
-          lexicalHandler = (LexicalHandler) validator.getContentHandler();
-        }
-        return validator;
-    }
-
-    public Schema resolveSchema(String url, PropertyMap options)
-            throws SAXException, IOException, IncorrectSchemaException {
-        int i = Arrays.binarySearch(preloadedSchemaUrls, url);
-        if (i > -1) {
-            Schema rv = preloadedSchemas[i];
-            if (options.contains(WrapProperty.ATTRIBUTE_OWNER)) {
-                if (rv instanceof CheckerSchema) {
-                    errorHandler.error(new SAXParseException(
-                            "A non-schema checker cannot be used as an attribute schema.",
-                            null, url, -1, -1));
-                    throw new IncorrectSchemaException();
-                } else {
-                    // ugly fall through
-                }
-            } else {
-                return rv;
-            }
-        }
-
-        externalSchema  = true;
-
-        TypedInputSource schemaInput = (TypedInputSource) entityResolver.resolveEntity(
-                null, url);
-        SchemaReader sr = null;
-        if ("application/relax-ng-compact-syntax".equals(schemaInput.getType())) {
-            sr = CompactSchemaReader.getInstance();
-        } else {
-            sr = new AutoSchemaReader();
-        }
-        Schema sch = sr.createSchema(schemaInput, options);
-
-        if (Statistics.STATISTICS != null && "com.thaiopensource.validate.schematron.SchemaImpl".equals(sch.getClass().getName())) {
-            externalSchematron  = true;
-        }
-
-        return sch;
-    }
-
-    /**
-     * @param url
-     * @return
-     * @throws SAXException
-     * @throws IOException
-     * @throws IncorrectSchemaException
-     */
-    private static Schema schemaByUrl(String url, EntityResolver resolver,
-            PropertyMap pMap) throws SAXException, IOException,
-            IncorrectSchemaException {
-        log4j.debug("Will load schema: " + url);
-        TypedInputSource schemaInput;
-        try {
-        schemaInput = (TypedInputSource) resolver.resolveEntity(
-                null, url);
-        } catch (ClassCastException e) {
-            log4j.fatal(url, e);
-            throw e;
-        }
-        SchemaReader sr = null;
-        if ("application/relax-ng-compact-syntax".equals(schemaInput.getType())) {
-            sr = CompactSchemaReader.getInstance();
-        } else {
-            sr = new AutoSchemaReader();
-        }
-        Schema sch = sr.createSchema(schemaInput, pMap);
-        return sch;
-    }
-
-    /**
-     * @throws SAXException
-     */
-    void emitTitle(boolean markupAllowed) throws SAXException {
-        if (willValidate()) {
-            emitter.characters(RESULTS_TITLE);
-            emitter.characters(FOR);
-            if (document != null && document.length() > 0) {
-                emitter.characters(scrub(shortenDataUri(document)));
-            } else if (request.getAttribute("nu.validator.servlet.MultipartFormDataFilter.filename") != null) {
-                emitter.characters("uploaded file "
-                        + scrub(request.getAttribute(
-                                "nu.validator.servlet.MultipartFormDataFilter.filename").toString()));
-            } else {
-                emitter.characters("contents of text-input area");
-            }
-        } else {
-            emitter.characters(SERVICE_TITLE);
-            if (markupAllowed
-                    && System.getProperty("nu.validator.servlet.service-name",
-                            "").equals("Validator.nu")) {
-                emitter.startElement("span");
-                emitter.characters(LIVING_VERSION);
-                emitter.endElement("span");
-            }
-        }
-    }
-
-    protected String shortenDataUri(String uri) {
-        if (DataUri.startsWithData(uri)) {
-            return "data:\u2026";
-        } else {
-            return uri;
-        }
-    }
-
-    void emitForm() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("method", "get");
-//        attrs.addAttribute("action", request.getRequestURL().toString());
-        if (isSimple()) {
-            attrs.addAttribute("class", "simple");
-        }
-        // attrs.addAttribute("onsubmit", "formSubmission()");
-        emitter.startElement("form", attrs);
-        emitFormContent();
-        emitter.endElement("form");
-    }
-
-    protected boolean isSimple() {
-        return false;
-    }
-
-    /**
-     * @throws SAXException
-     */
-    protected void emitFormContent() throws SAXException {
-        FormEmitter.emit(contentHandler, this);
-    }
-
-    void emitSchemaField() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("name", "schema");
-        attrs.addAttribute("id", "schema");
-        // attrs.addAttribute("onchange", "schemaChanged();");
-        attrs.addAttribute(
-                "pattern",
-                "(?:(?:(?:https?://\\S+)|(?:data:\\S+))(?:\\s+(?:(?:https?://\\S+)|(?:data:\\S+)))*)?");
-        attrs.addAttribute("title",
-                "Space-separated list of schema IRIs. (Leave blank to let the service guess.)");
-        if (schemaUrls != null) {
-            attrs.addAttribute("value", scrub(schemaUrls));
-        }
-        emitter.startElement("input", attrs);
-        emitter.endElement("input");
-    }
-
-    void emitDocField() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("type", "url");
-        attrs.addAttribute("name", "doc");
-        attrs.addAttribute("id", "doc");
-        attrs.addAttribute("pattern", "(?:(?:https?://.+)|(?:data:.+))?");
-        attrs.addAttribute("title",
-                "Absolute IRI (http, https or data only) of the document to be checked.");
-        if (document != null) {
-            attrs.addAttribute("value", scrub(document));
-        }
-        Object att = request.getAttribute("nu.validator.servlet.MultipartFormDataFilter.type");
-        if (att != null) {
-            attrs.addAttribute("class", att.toString());
-        }
-        emitter.startElement("input", attrs);
-        emitter.endElement("input");
-    }
-
-    /**
-     * @throws SAXException
-     * 
-     */
-    void emitSchemaDuration() throws SAXException {
-    }
-
-    /**
-     * @throws SAXException
-     * 
-     */
-    void emitDocDuration() throws SAXException {
-    }
-
-    /**
-     * @throws SAXException
-     * 
-     */
-    void emitTotalDuration() throws SAXException {
-        emitter.characters("" + (System.currentTimeMillis() - start));
-    }
-
-    /**
-     * @throws SAXException
-     * 
-     */
-    void emitPresetOptions() throws SAXException {
-        for (int i = 0; i < presetUrls.length; i++) {
-            emitter.option(presetLabels[i], presetUrls[i], false);
-        }
-    }
-
-    /**
-     * @throws SAXException
-     * 
-     */
-    void emitParserOptions() throws SAXException {
-        emitter.option("Automatically from Content-Type", "",
-                (parser == ParserMode.AUTO));
-        emitter.option("XML; don\u2019t load external entities", "xml",
-                (parser == ParserMode.XML_NO_EXTERNAL_ENTITIES));
-        emitter.option("XML; load external entities", "xmldtd",
-                (parser == ParserMode.XML_EXTERNAL_ENTITIES_NO_VALIDATION));
-        emitter.option("HTML; flavor from doctype", "html",
-                (parser == ParserMode.HTML_AUTO));
-        emitter.option("HTML5", "html5", (parser == ParserMode.HTML));
-        emitter.option("HTML 4.01 Strict", "html4",
-                (parser == ParserMode.HTML401_STRICT));
-        emitter.option("HTML 4.01 Transitional", "html4tr",
-                (parser == ParserMode.HTML401_TRANSITIONAL));
-    }
-
-    /**
-     * @throws SAXException
-     * 
-     */
-    void emitProfileOptions() throws SAXException {
-        profile = request.getParameter("profile");
-
-        emitter.option("Permissive: only what the spec requires",
-                "", ("".equals(profile)));
-        emitter.option("Pedagogical: suitable for teaching purposes",
-                "pedagogical", ("pedagogical".equals(profile)));
-        emitter.option("Polyglot: works both as HTML and as XML",
-                "polyglot", ("polyglot".equals(profile)));
-    }
-
-    /**
-     * @throws SAXException
-     * 
-     */
-    void emitLaxTypeField() throws SAXException {
-        emitter.checkbox("laxtype", "yes", laxType);
-    }
-
-    /**
-     * @throws SAXException
-     * 
-     */
-    void emitShowSourceField() throws SAXException {
-        emitter.checkbox("showsource", "yes", showSource);
-    }
-
-    /**
-     * @throws SAXException
-     *
-     */
-    void emitShowOutlineField() throws SAXException {
-        emitter.checkbox("showoutline", "yes", showOutline);
-    }
-
-    /**
-     * @throws SAXException
-     * 
-     */
-    void emitShowImageReportField() throws SAXException {
-        emitter.checkbox("showimagereport", "yes", imageCollector != null);
-    }
-
-    void rootNamespace(String namespace, Locator locator) throws SAXException {
-        if (validator == null) {
-            int index = -1;
-            for (int i = 0; i < presetNamespaces.length; i++) {
-                if (namespace.equals(presetNamespaces[i])) {
-                    index = i;
-                    break;
-                }
-            }
-            if (index == -1) {
-                String message = "Cannot find preset schema for namespace: \u201C"
-                        + namespace + "\u201D.";
-                SAXException se = new SAXException(message);
-                errorHandler.schemaError(se);
-                throw se;
-            }
-            String label = presetLabels[index];
-            String urls = presetUrls[index];
-            errorHandler.info("Using the preset for " + label
-                    + " based on the root namespace.");
-            try {
-                validator = validatorByUrls(urls);
-            } catch (IOException ioe) {
-                // At this point the schema comes from memory.
-                throw new RuntimeException(ioe);
-            } catch (IncorrectSchemaException e) {
-                // At this point the schema comes from memory.
-                throw new RuntimeException(e);
-            }
-            if (bufferingRootNamespaceSniffer == null) {
-                throw new RuntimeException(
-                        "Bug! bufferingRootNamespaceSniffer was null.");
-            }
-            bufferingRootNamespaceSniffer.setContentHandler(validator.getContentHandler());
-        }
-
-        if (!rootNamespaceSeen) {
-            rootNamespaceSeen = true;
-            if (contentType != null) {
-                int i;
-                if ((i = Arrays.binarySearch(KNOWN_CONTENT_TYPES, contentType)) > -1) {
-                    if (!NAMESPACES_FOR_KNOWN_CONTENT_TYPES[i].equals(namespace)) {
-                        String message = "".equals(namespace) ? "\u201C"
-                                + contentType
-                                + "\u201D is not an appropriate Content-Type for a document whose root element is not in a namespace."
-                                : "\u201C"
-                                        + contentType
-                                        + "\u201D is not an appropriate Content-Type for a document whose root namespace is \u201C"
-                                        + namespace + "\u201D.";
-                        SAXParseException spe = new SAXParseException(message,
-                                locator);
-                        errorHandler.warning(spe);
-                    }
-                }
-            }
-        }
-    }
-
-    public void documentMode(DocumentMode mode, String publicIdentifier,
-            String systemIdentifier, boolean html4SpecificAdditionalErrorChecks)
-            throws SAXException {
-        if (validator == null) {
-            try {
-                if ("yes".equals(request.getParameter("sniffdoctype"))) {
-                    if ("-//W3C//DTD XHTML 1.0 Transitional//EN".equals(publicIdentifier)) {
-                        errorHandler.info("XHTML 1.0 Transitional doctype seen. Appendix C is not supported. Proceeding anyway for your convenience. The parser is still an HTML parser, so namespace processing is not performed and \u201Cxml:*\u201D attributes are not supported. Using the schema for "
-                                + getPresetLabel(XHTML1TRANSITIONAL_SCHEMA)
-                                + "."
-                                + (html4SpecificAdditionalErrorChecks ? " HTML4-specific tokenization errors are enabled."
-                                        : ""));
-                        validator = validatorByDoctype(XHTML1TRANSITIONAL_SCHEMA);
-                    } else if ("-//W3C//DTD XHTML 1.0 Strict//EN".equals(publicIdentifier)) {
-                        errorHandler.info("XHTML 1.0 Strict doctype seen. Appendix C is not supported. Proceeding anyway for your convenience. The parser is still an HTML parser, so namespace processing is not performed and \u201Cxml:*\u201D attributes are not supported. Using the schema for "
-                                + getPresetLabel(XHTML1STRICT_SCHEMA)
-                                + "."
-                                + (html4SpecificAdditionalErrorChecks ? " HTML4-specific tokenization errors are enabled."
-                                        : ""));
-                        validator = validatorByDoctype(XHTML1STRICT_SCHEMA);
-                    } else if ("-//W3C//DTD HTML 4.01 Transitional//EN".equals(publicIdentifier)) {
-                        errorHandler.info("HTML 4.01 Transitional doctype seen. Using the schema for "
-                                + getPresetLabel(XHTML1TRANSITIONAL_SCHEMA)
-                                + "."
-                                + (html4SpecificAdditionalErrorChecks ? ""
-                                        : " HTML4-specific tokenization errors are not enabled."));
-                        validator = validatorByDoctype(XHTML1TRANSITIONAL_SCHEMA);
-                    } else if ("-//W3C//DTD HTML 4.01//EN".equals(publicIdentifier)) {
-                        errorHandler.info("HTML 4.01 Strict doctype seen. Using the schema for "
-                                + getPresetLabel(XHTML1STRICT_SCHEMA)
-                                + "."
-                                + (html4SpecificAdditionalErrorChecks ? ""
-                                        : " HTML4-specific tokenization errors are not enabled."));
-                        validator = validatorByDoctype(XHTML1STRICT_SCHEMA);
-                    } else if ("-//W3C//DTD HTML 4.0 Transitional//EN".equals(publicIdentifier)) {
-                        errorHandler.info("Legacy HTML 4.0 Transitional doctype seen.  Please consider using HTML 4.01 Transitional instead. Proceeding anyway for your convenience with the schema for "
-                                + getPresetLabel(XHTML1TRANSITIONAL_SCHEMA)
-                                + "."
-                                + (html4SpecificAdditionalErrorChecks ? ""
-                                        : " HTML4-specific tokenization errors are not enabled."));
-                        validator = validatorByDoctype(XHTML1TRANSITIONAL_SCHEMA);
-                    } else if ("-//W3C//DTD HTML 4.0//EN".equals(publicIdentifier)) {
-                        errorHandler.info("Legacy HTML 4.0 Strict doctype seen. Please consider using HTML 4.01 instead. Proceeding anyway for your convenience with the schema for "
-                                + getPresetLabel(XHTML1STRICT_SCHEMA)
-                                + "."
-                                + (html4SpecificAdditionalErrorChecks ? ""
-                                        : " HTML4-specific tokenization errors are not enabled."));
-                        validator = validatorByDoctype(XHTML1STRICT_SCHEMA);
-                    }
-                } else {
-                    errorHandler.info("Using the schema for "
-                            + getPresetLabel(HTML5_SCHEMA)
-                            + "."
-                            + (html4SpecificAdditionalErrorChecks ? " HTML4-specific tokenization errors are enabled."
-                                    : ""));
-                    validator = validatorByDoctype(HTML5_SCHEMA);
-                }
-            } catch (IOException ioe) {
-                // At this point the schema comes from memory.
-                throw new RuntimeException(ioe);
-            } catch (IncorrectSchemaException e) {
-                // At this point the schema comes from memory.
-                throw new RuntimeException(e);
-            }
-            ContentHandler ch = validator.getContentHandler();
-            ch.setDocumentLocator(htmlParser.getDocumentLocator());
-            ch.startDocument();
-            reader.setContentHandler(ch);
-        } else {
-            if (html4SpecificAdditionalErrorChecks) {
-                errorHandler.info("HTML4-specific tokenization errors are enabled.");
-            }
-        }
-    }
-
-    private String getPresetLabel(int schemaId) {
-        for (int i = 0; i < presetDoctypes.length; i++) {
-            if (presetDoctypes[i] == schemaId) {
-                return presetLabels[i];
-            }
-        }
-        return "unknown";
-    }
-
-    /**
-     * @param acceptAllKnownXmlTypes
-     * @see nu.validator.xml.ContentTypeParser#setAcceptAllKnownXmlTypes(boolean)
-     */
-    protected void setAcceptAllKnownXmlTypes(boolean acceptAllKnownXmlTypes) {
-        contentTypeParser.setAcceptAllKnownXmlTypes(acceptAllKnownXmlTypes);
-        dataRes.setAcceptAllKnownXmlTypes(acceptAllKnownXmlTypes);
-        httpRes.setAcceptAllKnownXmlTypes(acceptAllKnownXmlTypes);
-    }
-
-    /**
-     * @param allowGenericXml
-     * @see nu.validator.xml.ContentTypeParser#setAllowGenericXml(boolean)
-     */
-    protected void setAllowGenericXml(boolean allowGenericXml) {
-        contentTypeParser.setAllowGenericXml(allowGenericXml);
-        httpRes.setAllowGenericXml(allowGenericXml);
-        dataRes.setAllowGenericXml(allowGenericXml);
-    }
-
-    /**
-     * @param allowHtml
-     * @see nu.validator.xml.ContentTypeParser#setAllowHtml(boolean)
-     */
-    protected void setAllowHtml(boolean allowHtml) {
-        contentTypeParser.setAllowHtml(allowHtml);
-        httpRes.setAllowHtml(allowHtml);
-        dataRes.setAllowHtml(allowHtml);
-    }
-
-    /**
-     * @param allowRnc
-     * @see nu.validator.xml.ContentTypeParser#setAllowRnc(boolean)
-     */
-    protected void setAllowRnc(boolean allowRnc) {
-        contentTypeParser.setAllowRnc(allowRnc);
-        httpRes.setAllowRnc(allowRnc);
-        dataRes.setAllowRnc(allowRnc);
-        entityResolver.setAllowRnc(allowRnc);
-    }
-
-    /**
-     * @param allowXhtml
-     * @see nu.validator.xml.ContentTypeParser#setAllowXhtml(boolean)
-     */
-    protected void setAllowXhtml(boolean allowXhtml) {
-        contentTypeParser.setAllowXhtml(allowXhtml);
-        httpRes.setAllowXhtml(allowXhtml);
-        dataRes.setAllowXhtml(allowXhtml);
-    }
-
-    /**
-     * @throws SAXException
-     * @throws IOException
-     */
-    protected void loadDocumentInput() throws SAXException, IOException {
-        if (methodIsGet) {
-            documentInput = (TypedInputSource) entityResolver.resolveEntity(
-                    null, document);
-            errorHandler.setLoggingOk(true);
-        } else { // POST
-            long len = request.getContentLength();
-            if (len > SIZE_LIMIT) {
-                throw new StreamBoundException("Resource size exceeds limit.");
-            }
-            documentInput = contentTypeParser.buildTypedInputSource(document,
-                    null, postContentType);
-            documentInput.setByteStream(len < 0 ? new BoundedInputStream(
-                    request.getInputStream(), SIZE_LIMIT, document)
-                    : request.getInputStream());
-            documentInput.setSystemId(request.getHeader("Content-Location"));
-        }
-        if (imageCollector != null) {
-            baseUriTracker = new BaseUriTracker(documentInput.getSystemId(),
-                    documentInput.getLanguage());
-            imageCollector.initializeContext(baseUriTracker);
-        }
-    }
-
-    void emitStyle() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("href", STYLE_SHEET);
-        attrs.addAttribute("rel", "stylesheet");
-        emitter.startElement("link", attrs);
-        emitter.endElement("link");
-    }
-
-    void emitIcon() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("href", ICON);
-        attrs.addAttribute("rel", "icon");
-        emitter.startElement("link", attrs);
-        emitter.endElement("link");
-    }
-
-    void emitScript() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("src", SCRIPT);
-        emitter.startElement("script", attrs);
-        emitter.endElement("script");
-    }
-
-    void emitAbout() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("href", ABOUT_PAGE);
-        emitter.startElement("a", attrs);
-        emitter.characters(ABOUT_THIS_SERVICE);
-        emitter.endElement("a");
-    }
-
-    void emitVersion() throws SAXException {
-        emitter.characters(VERSION);
-    }
-
-    void emitUserAgentInput() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("name", "useragent");
-        attrs.addAttribute("list", "useragents");
-        attrs.addAttribute("value", userAgent);
-        emitter.startElement("input", attrs);
-        emitter.endElement("input");
-    }
-
-    void emitOtherFacetLink() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("href", HTML5_FACET);
-        emitter.startElement("a", attrs);
-        emitter.characters(SIMPLE_UI);
-        emitter.endElement("a");
-    }
-
-    void emitNsfilterField() throws SAXException {
-        attrs.clear();
-        attrs.addAttribute("name", "nsfilter");
-        attrs.addAttribute("id", "nsfilter");
-        attrs.addAttribute("pattern", "(?:.+:.+(?:\\s+.+:.+)*)?");
-        attrs.addAttribute("title",
-                "Space-separated namespace URIs for vocabularies to be filtered out.");
-        if (!filteredNamespaces.isEmpty()) {
-            StringBuilder sb = new StringBuilder();
-            boolean first = true;
-            for (String ns : filteredNamespaces) {
-                if (!first) {
-                    sb.append(' ');
-                }
-                sb.append(ns);
-                first = false;
-            }
-            attrs.addAttribute("value", scrub(sb));
-        }
-        emitter.startElement("input", attrs);
-        emitter.endElement("input");
-    }
-
-    void maybeEmitNsfilterField() throws SAXException {
-        NsFilterEmitter.emit(contentHandler, this);
-    }
-
-    void emitCharsetOptions() throws SAXException {
-        boolean found = false;
-        for (int i = 0; i < CHARSETS.length; i++) {
-            String charset = CHARSETS[i];
-            boolean selected = charset.equalsIgnoreCase(charsetOverride); // XXX
-            // use
-            // ASCII-caseinsensitivity
-            emitter.option(CHARSET_DESCRIPTIONS[i], charset, selected);
-            if (selected) {
-                found = true;
-            }
-        }
-        if (!found && charsetOverride != null) {
-            emitter.option(charsetOverride, charsetOverride, true);
-        }
-    }
-
-    void maybeEmitCharsetField() throws SAXException {
-        CharsetEmitter.emit(contentHandler, this);
-    }
-
-}
diff --git a/src/nu/validator/servlet/VerifierServletXMLReaderCreator.java b/src/nu/validator/servlet/VerifierServletXMLReaderCreator.java
deleted file mode 100644
index 9fb99e1..0000000
--- a/src/nu/validator/servlet/VerifierServletXMLReaderCreator.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2005, 2006 Henri Sivonen
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import nu.validator.gnu.xml.aelfred2.SAXDriver;
-
-import org.xml.sax.EntityResolver;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-
-import com.thaiopensource.xml.sax.XMLReaderCreator;
-
-
-/**
- * @version $Id$
- * @author hsivonen
- */
-public class VerifierServletXMLReaderCreator implements XMLReaderCreator {
-
-    private ErrorHandler errorHandler;
-
-    private EntityResolver entityResolver;
-
-    /**
-     * @param errorHandler
-     * @param entityResolver
-     */
-    public VerifierServletXMLReaderCreator(ErrorHandler errorHandler,
-            EntityResolver entityResolver) {
-        this.errorHandler = errorHandler;
-        this.entityResolver = entityResolver;
-    }
-
-    /**
-     * @see com.thaiopensource.xml.sax.XMLReaderCreator#createXMLReader()
-     */
-    public XMLReader createXMLReader() throws SAXException {
-        XMLReader r = new SAXDriver();
-        r.setFeature("http://xml.org/sax/features/external-general-entities",
-                true);
-        r.setFeature("http://xml.org/sax/features/external-parameter-entities",
-                true);
-        r.setEntityResolver(this.entityResolver);
-        r.setErrorHandler(this.errorHandler);
-        return r;
-    }
-
-}
diff --git a/src/nu/validator/servlet/XhtmlOutlineEmitter.java b/src/nu/validator/servlet/XhtmlOutlineEmitter.java
deleted file mode 100644
index e34feef..0000000
--- a/src/nu/validator/servlet/XhtmlOutlineEmitter.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2012 Vadim Zaslawski, Ontos AG
- * Copyright (c) 2012 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.servlet;
-
-import java.io.IOException;
-import java.util.Deque;
-
-import nu.validator.servlet.OutlineBuildingXMLReaderWrapper.Section;
-import nu.validator.xml.AttributesImpl;
-import nu.validator.xml.XhtmlSaxEmitter;
-
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-public class XhtmlOutlineEmitter {
-
-    private static final char[] OUTLINE = "Outline".toCharArray();
-
-    private final Deque<Section> outline;
-
-    private final XhtmlSaxEmitter emitter;
-
-    private final AttributesImpl attrs = new AttributesImpl();
-
-    public XhtmlOutlineEmitter(final ContentHandler contentHandler,
-            final Deque<Section> outline) {
-        this.emitter = new XhtmlSaxEmitter(contentHandler);
-        this.outline = outline;
-    }
-
-    public void emit() throws SAXException {
-        if (outline != null) {
-            attrs.clear();
-            attrs.addAttribute("id", "outline");
-            emitter.startElement("section", attrs);
-            emitter.startElement("h2");
-            emitter.characters(OUTLINE);
-            emitter.endElement("h2");
-            try {
-                emitOutline(outline, 0);
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
-            emitter.endElement("section");
-        }
-    }
-
-    protected void emitOutline(Deque<Section> outline, int currentDepth)
-            throws IOException, SAXException {
-        emitter.startElement("ol");
-        for (Section section : outline) {
-            emitter.startElement("li");
-            StringBuilder headingText = section.getHeadingTextBuilder();
-            if (headingText.length() > 0) {
-                emitter.startElementWithClass("span", "heading");
-                emitter.characters(headingText.toString().toCharArray());
-                emitter.endElement("span");
-            } else if (section.hasEmptyHeading()) {
-                emitter.characters(("[" + section.getElementName() + " element with empty heading]").toCharArray());
-            } else if ("h1".equals(section.getElementName())
-                    || "h2".equals(section.getElementName())
-                    || "h3".equals(section.getElementName())
-                    || "h4".equals(section.getElementName())
-                    || "h5".equals(section.getElementName())
-                    || "h6".equals(section.getElementName())) {
-                emitter.characters(("[section implied by empty "
-                        + section.getElementName() + " element]").toCharArray());
-            } else {
-                emitter.characters(("[" + section.getElementName() + " element with no heading]").toCharArray());
-            }
-            Deque<Section> sections = section.sections;
-            if (!sections.isEmpty()) {
-                emitOutline(sections, currentDepth + 1);
-            }
-            emitter.endElement("li");
-        }
-        emitter.endElement("ol");
-    }
-
-}
diff --git a/src/nu/validator/xml/BaseUriTracker.java b/src/nu/validator/xml/BaseUriTracker.java
index 8a4c91b..afea01c 100644
--- a/src/nu/validator/xml/BaseUriTracker.java
+++ b/src/nu/validator/xml/BaseUriTracker.java
@@ -32,17 +32,17 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.Locator;
 import org.xml.sax.SAXException;
 
-import io.mola.galimatias.URL;
-import io.mola.galimatias.GalimatiasParseException;
+import com.hp.hpl.jena.iri.IRI;
+import com.hp.hpl.jena.iri.IRIFactory;
 
 public class BaseUriTracker implements ContentHandler, UriLangContext {
-
+    
     private enum Direction {
         LTR, RTL, INHERIT
     }
 
     private class Node {
-        public URL currentAbsolute; // not null
+        public URI currentAbsolute; // not null
 
         public String originalRelative; // null if no xml:base
 
@@ -56,7 +56,7 @@ public class BaseUriTracker implements ContentHandler, UriLangContext {
          * @param currentAbsolute
          * @param originalRelative
          */
-        public Node(URL currentAbsolute, String originalRelative, String lang,
+        public Node(URI currentAbsolute, String originalRelative, String lang,
                 boolean langSpecified, boolean rtl) {
             this.currentAbsolute = currentAbsolute;
             this.originalRelative = originalRelative;
@@ -66,6 +66,8 @@ public class BaseUriTracker implements ContentHandler, UriLangContext {
         }
     }
 
+    private final IRIFactory iriFactory;
+    
     private LinkedList<Node> stack = new LinkedList<Node>();
 
     private boolean baseSeen = false;
@@ -110,13 +112,26 @@ public class BaseUriTracker implements ContentHandler, UriLangContext {
 
     public BaseUriTracker(String systemId, String contentLanguage) {
 
-        URL url = null;
+        this.iriFactory = new IRIFactory();
+        this.iriFactory.shouldViolation(false, false);
+        this.iriFactory.securityViolation(false, false);
+        this.iriFactory.dnsViolation(false, false);
+        this.iriFactory.mintingViolation(false, false);
+        this.iriFactory.useSpecificationIRI(false);
+        this.iriFactory.useSchemeSpecificRules("http", false);
+        this.iriFactory.useSchemeSpecificRules("https", false);
+        this.iriFactory.useSchemeSpecificRules("ftp", false);
+        this.iriFactory.useSchemeSpecificRules("data", false);
+
+        URI uri = null;
         try {
-            url = URL.parse(systemId);
-        } catch (GalimatiasParseException e) {
-            url = null;
+            IRI iri = iriFactory.construct(systemId);
+            uri = new URI(iri.toASCIIString());
+            if (!uri.isAbsolute()) {
+                uri = null;
+            }
         } catch (Exception e) {
-            url = null;
+            uri = null;
         }
 
         String lang = "";
@@ -131,8 +146,8 @@ public class BaseUriTracker implements ContentHandler, UriLangContext {
             } catch (DatatypeException e) {
             }
         }
-        stack.add(new Node(url, null, lang, langSpecified, false));
-        stack.add(new Node(url, null, lang, false, false)); // base/content-language placeholder
+        stack.add(new Node(uri, null, lang, langSpecified, false));
+        stack.add(new Node(uri, null, lang, false, false)); // base/content-language placeholder
     }
 
     private Node peek() {
@@ -158,7 +173,7 @@ public class BaseUriTracker implements ContentHandler, UriLangContext {
         }
 
         Node curr = peek();
-        URL base = curr.currentAbsolute;
+        URI base = curr.currentAbsolute;
         if (!langSpecified) {
             lang = curr.lang;
         }
@@ -178,19 +193,19 @@ public class BaseUriTracker implements ContentHandler, UriLangContext {
         if (relative == null) {
             stack.addLast(new Node(base, null, lang, langSpecified, rtl));
         } else {
-            URL newBase;
+            URI newBase;
             String ascii = null;
             try {
+                IRI relIri = iriFactory.construct(relative);
+                ascii = relIri.toASCIIString();
                 if (base != null) {
-                    try {
-                        newBase = base.resolve(relative);
-                    } catch (GalimatiasParseException e) {
+                    newBase = base.resolve(ascii);
+                    if (!newBase.isAbsolute()) {
                         newBase = base;
                     }
                 } else {
-                    try {
-                        newBase = URL.parse((new URI(ascii)).toString());
-                    } catch (GalimatiasParseException e) {
+                    newBase = new URI(ascii);
+                    if (!newBase.isAbsolute()) {
                         newBase = null;
                     }
                 }
@@ -283,22 +298,23 @@ public class BaseUriTracker implements ContentHandler, UriLangContext {
      */
     public String toAbsoluteUriWithCurrentBase(String uri) {
         try {
-            URL relUrl = URL.parse(uri);
+            IRI relIri = iriFactory.construct(uri);
             String ascii;
-            ascii = relUrl.toString();
+            ascii = relIri.toASCIIString();
 
-            URL base = stack.getLast().currentAbsolute;
-            URL rv;
-            try {
-                if (base == null) {
-                    rv = URL.parse(ascii);
-                } else {
-                    rv = base.resolve(ascii);
-                }
-            } catch (GalimatiasParseException e) {
+            URI base = stack.getLast().currentAbsolute;
+            URI rv;
+            if (base == null) {
+                rv = new URI(ascii);
+            } else {
+                rv = base.resolve(ascii);
+
+            }
+            if (rv.isAbsolute()) {
+                return rv.toASCIIString();
+            } else {
                 return null;
             }
-            return rv.toString();
         } catch (Exception e) {
             return null;
         }
diff --git a/src/nu/validator/xml/DataUriEntityResolver.java b/src/nu/validator/xml/DataUriEntityResolver.java
index eaacb82..2eac45a 100644
--- a/src/nu/validator/xml/DataUriEntityResolver.java
+++ b/src/nu/validator/xml/DataUriEntityResolver.java
@@ -31,8 +31,11 @@ import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
 
-import io.mola.galimatias.URL;
-import io.mola.galimatias.GalimatiasParseException;
+//import io.mola.galimatias.URL;
+//import io.mola.galimatias.GalimatiasParseException;
+import com.hp.hpl.jena.iri.IRI;
+import com.hp.hpl.jena.iri.IRIException;
+import com.hp.hpl.jena.iri.IRIFactory;
 
 public class DataUriEntityResolver implements EntityResolver {
 
@@ -51,6 +54,8 @@ public class DataUriEntityResolver implements EntityResolver {
     private boolean acceptAllKnownXmlTypes = false;
 
     private boolean allowGenericXml = true;
+    
+    private final IRIFactory iriFactory;
 
     private final ContentTypeParser contentTypeParser;
     
@@ -61,6 +66,9 @@ public class DataUriEntityResolver implements EntityResolver {
             ErrorHandler errorHandler) {
         this.laxContentType = laxContentType;
         this.errorHandler = errorHandler;
+        this.iriFactory = new IRIFactory();
+        this.iriFactory.useSpecificationXMLSystemID(true);
+        this.iriFactory.useSchemeSpecificRules("data", true);
         this.contentTypeParser = new ContentTypeParser(errorHandler,
                 laxContentType, this.allowRnc, this.allowHtml, this.allowXhtml,
                 this.acceptAllKnownXmlTypes, this.allowGenericXml);
@@ -74,10 +82,10 @@ public class DataUriEntityResolver implements EntityResolver {
     public InputSource resolveEntity(String publicId, String systemId)
             throws SAXException, IOException {
         if (DataUri.startsWithData(systemId)) {
-            URL url;
+            IRI iri;
             try {
-                url = URL.parse(systemId);
-            } catch (GalimatiasParseException e) {
+                iri = iriFactory.construct(systemId);
+            } catch (IRIException e) {
                 IOException ioe = (IOException) new IOException(e.getMessage()).initCause(e);
                 SAXParseException spe = new SAXParseException(e.getMessage(),
                         publicId, systemId, -1, -1, ioe);
@@ -86,7 +94,7 @@ public class DataUriEntityResolver implements EntityResolver {
                 }
                 throw spe;
             }
-            systemId = url.toString();
+            systemId = iri.toASCIIString();
             DataUri du = new DataUri(systemId);
             TypedInputSource is = contentTypeParser.buildTypedInputSource(systemId, publicId,
                     du.getContentType());
diff --git a/src/nu/validator/xml/PrudentHttpEntityResolver.java b/src/nu/validator/xml/PrudentHttpEntityResolver.java
deleted file mode 100644
index 1a172f1..0000000
--- a/src/nu/validator/xml/PrudentHttpEntityResolver.java
+++ /dev/null
@@ -1,548 +0,0 @@
-/*
- * Copyright (c) 2005 Henri Sivonen
- * Copyright (c) 2007-2015 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.xml;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.KeyManagementException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.zip.GZIPInputStream;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLContext;
-
-import nu.validator.io.BoundedInputStream;
-import nu.validator.io.ObservableInputStream;
-import nu.validator.io.StreamBoundException;
-import nu.validator.io.StreamObserver;
-import nu.validator.io.SystemIdIOException;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.config.CookieSpecs;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.config.Registry;
-import org.apache.http.config.RegistryBuilder;
-import org.apache.http.conn.socket.ConnectionSocketFactory;
-import org.apache.http.conn.socket.PlainConnectionSocketFactory;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.conn.ssl.SSLContextBuilder;
-import org.apache.http.conn.ssl.TrustStrategy;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.impl.client.LaxRedirectStrategy;
-import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
-import org.apache.log4j.Logger;
-
-import org.xml.sax.EntityResolver;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-import io.mola.galimatias.URL;
-import io.mola.galimatias.GalimatiasParseException;
-
-/**
- * @version $Id: PrudentHttpEntityResolver.java,v 1.1 2005/01/08 08:11:26
- *          hsivonen Exp $
- * @author hsivonen
- */
-@SuppressWarnings("deprecation") public class PrudentHttpEntityResolver
-        implements EntityResolver {
-
-    private static final Logger log4j = Logger.getLogger(PrudentHttpEntityResolver.class);
-
-    private static HttpClient client;
-
-    private static int maxRequests;
-
-    private long sizeLimit;
-
-    private final ErrorHandler errorHandler;
-
-    private int requestsLeft;
-
-    private boolean allowRnc = false;
-
-    private boolean allowHtml = false;
-
-    private boolean allowXhtml = false;
-
-    private boolean acceptAllKnownXmlTypes = false;
-
-    private boolean allowGenericXml = true;
-
-    private final ContentTypeParser contentTypeParser;
-
-    private String userAgent;
-
-    /**
-     * Sets the timeouts of the HTTP client.
-     *
-     * @param connectionTimeout
-     *            timeout until connection established in milliseconds. Zero
-     *            means no timeout.
-     * @param socketTimeout
-     *            timeout for waiting for data in milliseconds. Zero means no
-     *            timeout.
-     * @param maxRequests
-     *            maximum number of connections to a particuar host
-     */
-    public static void setParams(int connectionTimeout, int socketTimeout,
-            int maxRequests) {
-        PrudentHttpEntityResolver.maxRequests = maxRequests;
-        PoolingHttpClientConnectionManager phcConnMgr;
-        Registry<ConnectionSocketFactory> registry = //
-        RegistryBuilder.<ConnectionSocketFactory> create() //
-        .register("http", PlainConnectionSocketFactory.getSocketFactory()) //
-        .register("https", SSLConnectionSocketFactory.getSocketFactory()) //
-        .build();
-        HttpClientBuilder builder = HttpClients.custom();
-        builder.setRedirectStrategy(new LaxRedirectStrategy());
-        builder.setMaxConnPerRoute(maxRequests);
-        builder.setMaxConnTotal(200);
-        if ("true".equals(System.getProperty(
-                "nu.validator.xml.promiscuous-ssl", "false"))) { //
-            try {
-                SSLContext promiscuousSSLContext = new SSLContextBuilder() //
-                .loadTrustMaterial(null, new TrustStrategy() {
-                    public boolean isTrusted(X509Certificate[] arg0, String arg1)
-                            throws CertificateException {
-                        return true;
-                    }
-                }).build();
-                builder.setSslcontext(promiscuousSSLContext);
-                HostnameVerifier verifier = //
-                SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
-                SSLConnectionSocketFactory promiscuousSSLConnSocketFactory = //
-                new SSLConnectionSocketFactory(promiscuousSSLContext, verifier);
-                registry = RegistryBuilder.<ConnectionSocketFactory> create() //
-                .register("https", promiscuousSSLConnSocketFactory) //
-                .register("http",
-                        PlainConnectionSocketFactory.getSocketFactory()) //
-                .build();
-            } catch (KeyManagementException e) {
-                e.printStackTrace();
-            } catch (NumberFormatException e) {
-                e.printStackTrace();
-            } catch (NoSuchAlgorithmException e) {
-                e.printStackTrace();
-            } catch (KeyStoreException e) {
-                e.printStackTrace();
-            }
-        }
-        phcConnMgr = new PoolingHttpClientConnectionManager(registry);
-        phcConnMgr.setDefaultMaxPerRoute(maxRequests);
-        phcConnMgr.setMaxTotal(200);
-        builder.setConnectionManager(phcConnMgr);
-        RequestConfig.Builder config = RequestConfig.custom();
-        config.setCircularRedirectsAllowed(true);
-        config.setMaxRedirects(20); // Gecko default
-        config.setConnectTimeout(connectionTimeout);
-        config.setCookieSpec(CookieSpecs.BEST_MATCH);
-        config.setSocketTimeout(socketTimeout);
-        client = builder.setDefaultRequestConfig(config.build()).build();
-    }
-
-    public void setUserAgent(String ua) {
-        userAgent = ua;
-    }
-
-    /**
-     * @param sizeLimit
-     * @param laxContentType
-     * @param errorHandler
-     */
-    public PrudentHttpEntityResolver(long sizeLimit, boolean laxContentType,
-            ErrorHandler errorHandler) {
-        this.sizeLimit = sizeLimit;
-        this.requestsLeft = maxRequests;
-        this.errorHandler = errorHandler;
-        this.contentTypeParser = new ContentTypeParser(errorHandler,
-                laxContentType, this.allowRnc, this.allowHtml, this.allowXhtml,
-                this.acceptAllKnownXmlTypes, this.allowGenericXml);
-    }
-
-    /**
-     * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
-     *      java.lang.String)
-     */
-    public InputSource resolveEntity(String publicId, String systemId)
-            throws SAXException, IOException {
-        if (requestsLeft > -1) {
-            if (requestsLeft == 0) {
-                throw new IOException(
-                        "Number of permitted HTTP requests exceeded.");
-            } else {
-                requestsLeft--;
-            }
-        }
-        HttpGet m = null;
-        try {
-            URL url;
-            try {
-                url = URL.parse(systemId);
-            } catch (GalimatiasParseException e) {
-                IOException ioe = (IOException) new IOException(e.getMessage()).initCause(e);
-                SAXParseException spe = new SAXParseException(e.getMessage(),
-                        publicId, systemId, -1, -1, ioe);
-                if (errorHandler != null) {
-                    errorHandler.fatalError(spe);
-                }
-                throw spe;
-            }
-            String scheme = url.scheme();
-            if (!("http".equals(scheme) || "https".equals(scheme))) {
-                String msg = "Unsupported URI scheme: \u201C" + scheme
-                        + "\u201D.";
-                SAXParseException spe = new SAXParseException(msg, publicId,
-                        systemId, -1, -1, new IOException(msg));
-                if (errorHandler != null) {
-                    errorHandler.fatalError(spe);
-                }
-                throw spe;
-            }
-            systemId = url.toString();
-            try {
-                m = new HttpGet(systemId);
-            } catch (IllegalArgumentException e) {
-                SAXParseException spe = new SAXParseException(
-                        e.getMessage(),
-                        publicId,
-                        systemId,
-                        -1,
-                        -1,
-                        (IOException) new IOException(e.getMessage()).initCause(e));
-                if (errorHandler != null) {
-                    errorHandler.fatalError(spe);
-                }
-                throw spe;
-            }
-            m.setHeader("User-Agent", userAgent);
-            m.setHeader("Accept", buildAccept());
-            m.setHeader("Accept-Encoding", "gzip");
-            log4j.info(systemId);
-            HttpResponse response = client.execute(m);
-            int statusCode = response.getStatusLine().getStatusCode();
-            if (statusCode != 200) {
-                String msg = "HTTP resource not retrievable. The HTTP status from the remote server was: "
-                        + statusCode + ".";
-                SAXParseException spe = new SAXParseException(msg, publicId,
-                        m.getURI().toString(), -1, -1, new IOException(msg));
-                if (errorHandler != null) {
-                    errorHandler.fatalError(spe);
-                }
-                throw spe;
-            }
-            HttpEntity entity = response.getEntity();
-            long len = entity.getContentLength();
-            if (sizeLimit > -1 && len > sizeLimit) {
-                SAXParseException spe = new SAXParseException(
-                        "Resource size exceeds limit.",
-                        publicId,
-                        m.getURI().toString(),
-                        -1,
-                        -1,
-                        new StreamBoundException("Resource size exceeds limit."));
-                if (errorHandler != null) {
-                    errorHandler.fatalError(spe);
-                }
-                throw spe;
-            }
-            TypedInputSource is;
-            org.apache.http.Header ct = response.getFirstHeader("Content-Type");
-            String contentType = null;
-            final String baseUri = m.getURI().toString();
-            if (ct != null) {
-                contentType = ct.getValue();
-            }
-            is = contentTypeParser.buildTypedInputSource(baseUri, publicId,
-                    contentType);
-
-            Header cl = response.getFirstHeader("Content-Language");
-            if (cl != null) {
-                is.setLanguage(cl.getValue().trim());
-            }
-
-            Header xuac = response.getFirstHeader("X-UA-Compatible");
-            if (xuac != null) {
-                String val = xuac.getValue().trim();
-                if (!"ie=edge".equalsIgnoreCase(val)) {
-                    SAXParseException spe = new SAXParseException(
-                            "X-UA-Compatible HTTP header must have the value \u201CIE=edge\u201D,"
-                                    + " was \u201C" + val + "\u201D.",
-                            publicId, systemId, -1, -1);
-                    errorHandler.error(spe);
-                }
-            }
-
-            final HttpGet meth = m;
-            InputStream stream = entity.getContent();
-            if (sizeLimit > -1) {
-                stream = new BoundedInputStream(stream, sizeLimit, baseUri);
-            }
-            Header ce = response.getFirstHeader("Content-Encoding");
-            if (ce != null) {
-                String val = ce.getValue().trim();
-                if ("gzip".equalsIgnoreCase(val)
-                        || "x-gzip".equalsIgnoreCase(val)) {
-                    stream = new GZIPInputStream(stream);
-                    if (sizeLimit > -1) {
-                        stream = new BoundedInputStream(stream, sizeLimit,
-                                baseUri);
-                    }
-                }
-            }
-            is.setByteStream(new ObservableInputStream(stream,
-                    new StreamObserver() {
-                        private final Logger log4j = Logger.getLogger("nu.validator.xml.PrudentEntityResolver.StreamObserver");
-
-                        private boolean released = false;
-
-                        public void closeCalled() {
-                            log4j.debug("closeCalled");
-                            if (!released) {
-                                log4j.debug("closeCalled, not yet released");
-                                released = true;
-                                try {
-                                    meth.releaseConnection();
-                                } catch (Exception e) {
-                                    log4j.debug(
-                                            "closeCalled, releaseConnection", e);
-                                }
-                            }
-                        }
-
-                        public void exceptionOccurred(Exception ex)
-                                throws IOException {
-                            if (!released) {
-                                released = true;
-                                try {
-                                    meth.abort();
-                                } catch (Exception e) {
-                                    log4j.debug("exceptionOccurred, abort", e);
-                                } finally {
-                                    try {
-                                        meth.releaseConnection();
-                                    } catch (Exception e) {
-                                        log4j.debug(
-                                                "exceptionOccurred, releaseConnection",
-                                                e);
-                                    }
-                                }
-                            }
-                            if (ex instanceof SystemIdIOException) {
-                                SystemIdIOException siie = (SystemIdIOException) ex;
-                                throw siie;
-                            } else if (ex instanceof IOException) {
-                                IOException ioe = (IOException) ex;
-                                throw new SystemIdIOException(baseUri,
-                                        ioe.getMessage(), ioe);
-                            } else if (ex instanceof RuntimeException) {
-                                RuntimeException re = (RuntimeException) ex;
-                                throw re;
-                            } else {
-                                throw new RuntimeException(
-                                        "API contract violation. Wrong exception type.",
-                                        ex);
-                            }
-                        }
-
-                        public void finalizerCalled() {
-                            if (!released) {
-                                released = true;
-                                try {
-                                    meth.abort();
-                                } catch (Exception e) {
-                                    log4j.debug("finalizerCalled, abort", e);
-                                } finally {
-                                    try {
-                                        meth.releaseConnection();
-                                    } catch (Exception e) {
-                                        log4j.debug(
-                                                "finalizerCalled, releaseConnection",
-                                                e);
-                                    }
-                                }
-                            }
-                        }
-
-                    }));
-            return is;
-        } catch (IOException e) {
-            if (m != null) {
-                try {
-                    m.abort();
-                } catch (Exception ex) {
-                    log4j.debug("abort", ex);
-                } finally {
-                    try {
-                        m.releaseConnection();
-                    } catch (Exception ex) {
-                        log4j.debug("releaseConnection", ex);
-                    }
-                }
-            }
-            throw e;
-        } catch (SAXException e) {
-            if (m != null) {
-                try {
-                    m.abort();
-                } catch (Exception ex) {
-                    log4j.debug("abort", ex);
-                } finally {
-                    try {
-                        m.releaseConnection();
-                    } catch (Exception ex) {
-                        log4j.debug("releaseConnection", ex);
-                    }
-                }
-            }
-            throw e;
-        } catch (RuntimeException e) {
-            if (m != null) {
-                try {
-                    m.abort();
-                } catch (Exception ex) {
-                    log4j.debug("abort", ex);
-                } finally {
-                    try {
-                        m.releaseConnection();
-                    } catch (Exception ex) {
-                        log4j.debug("releaseConnection", ex);
-                    }
-                }
-            }
-            throw e;
-        }
-    }
-
-    /**
-     * @return Returns the allowRnc.
-     */
-    public boolean isAllowRnc() {
-        return allowRnc;
-    }
-
-    /**
-     * @param allowRnc
-     *            The allowRnc to set.
-     */
-    public void setAllowRnc(boolean allowRnc) {
-        this.allowRnc = allowRnc;
-        this.contentTypeParser.setAllowRnc(allowRnc);
-    }
-
-    /**
-     * @param allowHtml
-     */
-    public void setAllowHtml(boolean allowHtml) {
-        this.allowHtml = allowHtml;
-        this.contentTypeParser.setAllowHtml(allowHtml);
-    }
-
-    /**
-     * Returns the acceptAllKnownXmlTypes.
-     *
-     * @return the acceptAllKnownXmlTypes
-     */
-    public boolean isAcceptAllKnownXmlTypes() {
-        return acceptAllKnownXmlTypes;
-    }
-
-    /**
-     * Sets the acceptAllKnownXmlTypes.
-     *
-     * @param acceptAllKnownXmlTypes
-     *            the acceptAllKnownXmlTypes to set
-     */
-    public void setAcceptAllKnownXmlTypes(boolean acceptAllKnownXmlTypes) {
-        this.acceptAllKnownXmlTypes = acceptAllKnownXmlTypes;
-        this.contentTypeParser.setAcceptAllKnownXmlTypes(acceptAllKnownXmlTypes);
-    }
-
-    /**
-     * Returns the allowGenericXml.
-     *
-     * @return the allowGenericXml
-     */
-    public boolean isAllowGenericXml() {
-        return allowGenericXml;
-    }
-
-    /**
-     * Sets the allowGenericXml.
-     *
-     * @param allowGenericXml
-     *            the allowGenericXml to set
-     */
-    public void setAllowGenericXml(boolean allowGenericXml) {
-        this.allowGenericXml = allowGenericXml;
-        this.contentTypeParser.setAllowGenericXml(allowGenericXml);
-    }
-
-    /**
-     * Returns the allowXhtml.
-     *
-     * @return the allowXhtml
-     */
-    public boolean isAllowXhtml() {
-        return allowXhtml;
-    }
-
-    /**
-     * Sets the allowXhtml.
-     *
-     * @param allowXhtml
-     *            the allowXhtml to set
-     */
-    public void setAllowXhtml(boolean allowXhtml) {
-        this.allowXhtml = allowXhtml;
-        this.contentTypeParser.setAllowXhtml(allowXhtml);
-    }
-
-    private String buildAccept() {
-        return "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
-    }
-
-    /**
-     * Returns the allowHtml.
-     *
-     * @return the allowHtml
-     */
-    public boolean isAllowHtml() {
-        return allowHtml;
-    }
-
-    public boolean isOnlyHtmlAllowed() {
-        return !isAllowGenericXml() && !isAllowRnc() && !isAllowXhtml();
-    }
-}
