14a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson/*
24a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson * Copyright (C) 2010 The Android Open Source Project
34a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson *
44a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
54a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson * you may not use this file except in compliance with the License.
64a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson * You may obtain a copy of the License at
74a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson *
84a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
94a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson *
104a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson * Unless required by applicable law or agreed to in writing, software
114a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
124a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson * See the License for the specific language governing permissions and
144a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson * limitations under the License.
154a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson */
164a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
171c92541d38ed4d0b6e958f478cd7b129f140cd44Jesse Wilsonpackage libcore.xml;
184a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
196789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilsonimport java.io.StringReader;
206789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilsonimport java.util.Arrays;
216789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilsonimport java.util.List;
226789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilsonimport javax.xml.parsers.SAXParser;
236789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilsonimport javax.xml.parsers.SAXParserFactory;
246789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilsonimport junit.framework.AssertionFailedError;
254a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilsonimport junit.framework.TestCase;
264a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilsonimport org.xml.sax.Attributes;
274a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilsonimport org.xml.sax.ContentHandler;
284a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilsonimport org.xml.sax.InputSource;
296789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilsonimport org.xml.sax.SAXParseException;
304a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilsonimport org.xml.sax.XMLReader;
314a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilsonimport org.xml.sax.helpers.DefaultHandler;
324a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
334a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson/**
344a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson * Initiate and observe a SAX parse session.
354a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson */
366789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilsonpublic final class SaxTest extends TestCase {
374a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
384a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    public void testNoPrefixesNoNamespaces() throws Exception {
394a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        parse(false, false, "<foo bar=\"baz\"/>", new DefaultHandler() {
404a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            @Override public void startElement(String uri, String localName,
414a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                    String qName, Attributes attributes) {
424a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", uri);
434a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", localName);
444a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("foo", qName);
454a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals(1, attributes.getLength());
464a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", attributes.getURI(0));
474a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertOneOf("bar", "", attributes.getLocalName(0));
484a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("bar", attributes.getQName(0));
494a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            }
504a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        });
514a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
524a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        parse(false, false, "<a:foo a:bar=\"baz\"/>", new DefaultHandler() {
534a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            @Override public void startElement(String uri, String localName,
544a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                    String qName, Attributes attributes) {
554a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", uri);
564a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", localName);
574a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("a:foo", qName);
584a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals(1, attributes.getLength());
594a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", attributes.getURI(0));
604a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertOneOf("a:bar", "", attributes.getLocalName(0));
614a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("a:bar", attributes.getQName(0));
624a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            }
634a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        });
644a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    }
654a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
664a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    public void testNoPrefixesYesNamespaces() throws Exception {
674a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        parse(false, true, "<foo bar=\"baz\"/>", new DefaultHandler() {
684a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            @Override public void startElement(String uri, String localName,
694a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                    String qName, Attributes attributes) {
704a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", uri);
714a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("foo", localName);
724a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("foo", qName);
734a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals(1, attributes.getLength());
744a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", attributes.getURI(0));
754a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("bar", attributes.getLocalName(0));
764a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("bar", attributes.getQName(0));
774a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            }
784a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        });
794a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
804a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        parse(false, true, "<a:foo a:bar=\"baz\" xmlns:a=\"http://quux\"/>", new DefaultHandler() {
814a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            @Override public void startElement(String uri, String localName,
824a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                    String qName, Attributes attributes) {
834a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("http://quux", uri);
844a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("foo", localName);
854a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("a:foo", qName);
864a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals(1, attributes.getLength());
874a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("http://quux", attributes.getURI(0));
884a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("bar", attributes.getLocalName(0));
894a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("a:bar", attributes.getQName(0));
904a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            }
914a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        });
924a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    }
934a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
944a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    /**
954a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson     * Android's Expat-based SAX parser fails this test because Expat doesn't
964a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson     * supply us with our much desired {@code xmlns="http://..."} attributes.
974a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson     */
984a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    public void testYesPrefixesYesNamespaces() throws Exception {
994a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        parse(true, true, "<foo bar=\"baz\"/>", new DefaultHandler() {
1004a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            @Override public void startElement(String uri, String localName,
1014a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                    String qName, Attributes attributes) {
1024a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", uri);
1034a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("foo", localName);
1044a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("foo", qName);
1054a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals(1, attributes.getLength());
1064a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", attributes.getURI(0));
1074a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("bar", attributes.getLocalName(0));
1084a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("bar", attributes.getQName(0));
1094a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            }
1104a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        });
1114a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
1124a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        parse(true, true, "<a:foo a:bar=\"baz\" xmlns:a=\"http://quux\"/>", new DefaultHandler() {
1134a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            @Override public void startElement(String uri, String localName,
1144a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                    String qName, Attributes attributes) {
1154a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("http://quux", uri);
1164a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("foo", localName);
1174a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("a:foo", qName);
1184a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals(2, attributes.getLength());
1194a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("http://quux", attributes.getURI(0));
1204a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("bar", attributes.getLocalName(0));
1214a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("a:bar", attributes.getQName(0));
1224a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", attributes.getURI(1));
1234a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", attributes.getLocalName(1));
1244a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("xmlns:a", attributes.getQName(1));
1254a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            }
1264a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        });
1274a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    }
1284a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
1294a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    public void testYesPrefixesNoNamespaces() throws Exception {
1304a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        parse(true, false, "<foo bar=\"baz\"/>", new DefaultHandler() {
1314a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            @Override public void startElement(String uri, String localName,
1324a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                    String qName, Attributes attributes) {
1334a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", uri);
1344a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", localName);
1354a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("foo", qName);
1364a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals(1, attributes.getLength());
1374a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", attributes.getURI(0));
1384a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertOneOf("bar", "", attributes.getLocalName(0));
1394a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("bar", attributes.getQName(0));
1404a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            }
1414a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        });
1424a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
1434a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        parse(true, false, "<a:foo a:bar=\"baz\"/>", new DefaultHandler() {
1444a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            @Override public void startElement(String uri, String localName,
1454a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                    String qName, Attributes attributes) {
1464a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", uri);
1474a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", localName);
1484a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("a:foo", qName);
1494a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals(1, attributes.getLength());
1504a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("", attributes.getURI(0));
1514a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertOneOf("a:bar", "", attributes.getLocalName(0));
1524a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                assertEquals("a:bar", attributes.getQName(0));
1534a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            }
1544a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        });
1554a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    }
1564a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
1576789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    /**
1586789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     * Test that the external-general-entities feature can be disabled.
1596789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=9493
1606789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     */
1616789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    public void testDisableExternalGeneralEntities() throws Exception {
1626789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        String xml = "<!DOCTYPE foo ["
1636789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                + "  <!ENTITY bar SYSTEM \"/no-such-document.xml\">"
1646789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                + "]>"
1656789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                + "<foo>&bar;</foo>";
1666789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        testDisableExternalEntities("http://xml.org/sax/features/external-general-entities", xml);
1676789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    }
1686789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson
1696789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    /**
1706789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     * Test that the external-parameter-entities feature can be disabled.
1716789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=9493
1726789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     */
1736789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    public void testDisableExternalParameterEntities() throws Exception {
1746789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        String xml = "<!DOCTYPE foo ["
1756789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                + "  <!ENTITY % bar SYSTEM \"/no-such-document.xml\">"
1766789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                + "  %bar;"
1776789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                + "]>"
1786789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                + "<foo/>";
1796789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        testDisableExternalEntities("http://xml.org/sax/features/external-parameter-entities", xml);
1806789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    }
1816789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson
1826789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    /**
1836789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     * Disables the named feature and then parses the supplied XML. The content
1846789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     * is expected to be equivalent to "<foo/>".
1856789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     */
1866789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    private void testDisableExternalEntities(String feature, String xml) throws Exception {
1876789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
1886789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        XMLReader reader = parser.getXMLReader();
1896789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        reader.setFeature(feature, false);
1906789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        assertFalse(reader.getFeature(feature));
1916789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        reader.setContentHandler(new ThrowingHandler() {
1926789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            @Override public void startElement(
1936789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                    String uri, String localName, String qName, Attributes attributes) {
1946789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                assertEquals("foo", qName);
1956789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            }
1966789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            @Override public void endElement(String uri, String localName, String qName) {
1976789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                assertEquals("foo", qName);
1986789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            }
1996789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        });
2006789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        reader.parse(new InputSource(new StringReader(xml)));
2016789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    }
2026789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson
2034a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    private void parse(boolean prefixes, boolean namespaces, String xml,
2044a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson            ContentHandler handler) throws Exception {
2054a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
2064a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        XMLReader reader = parser.getXMLReader();
2074a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        reader.setFeature("http://xml.org/sax/features/namespace-prefixes", prefixes);
2084a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        reader.setFeature("http://xml.org/sax/features/namespaces", namespaces);
2094a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        reader.setContentHandler(handler);
2104a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        reader.parse(new InputSource(new StringReader(xml)));
2114a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    }
2124a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson
2134a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    /**
2144a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson     * @param expected an optional value that may or may have not been supplied
2154a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson     * @param sentinel a marker value that means the expected value was omitted
2164a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson     */
2174a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    private void assertOneOf(String expected, String sentinel, String actual) {
2184a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        List<String> optionsList = Arrays.asList(sentinel, expected);
2194a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson        assertTrue("Expected one of " + optionsList + " but was " + actual,
2204a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson                optionsList.contains(actual));
2214a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson    }
2226789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson
2236789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    /**
2246789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     * This SAX handler throws on everything but startDocument, endDocument,
2256789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     * and setDocumentLocator(). Override the methods that are expected to be
2266789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     * called.
2276789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson     */
2286789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    static class ThrowingHandler extends DefaultHandler {
2296789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public InputSource resolveEntity(String publicId, String systemId) {
2306789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2316789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2326789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void notationDecl(String name, String publicId, String systemId) {
2336789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2346789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2356789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void unparsedEntityDecl(
2366789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                String name, String publicId, String systemId, String notationName) {
2376789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2386789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2396789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void startPrefixMapping(String prefix, String uri) {
2406789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2416789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2426789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void endPrefixMapping(String prefix) {
2436789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2446789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2456789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void startElement(
2466789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson                String uri, String localName, String qName, Attributes attributes) {
2476789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2486789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2496789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void endElement(String uri, String localName, String qName) {
2506789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2516789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2526789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void characters(char[] ch, int start, int length) {
2536789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2546789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2556789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void ignorableWhitespace(char[] ch, int start, int length) {
2566789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2576789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2586789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void processingInstruction(String target, String data) {
2596789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2606789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2616789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void skippedEntity(String name) {
2626789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2636789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2646789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void warning(SAXParseException e) {
2656789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2666789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2676789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void error(SAXParseException e) {
2686789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2696789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2706789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        @Override public void fatalError(SAXParseException e) {
2716789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson            throw new AssertionFailedError();
2726789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson        }
2736789e65622548dd7d2842ec77c19004afc9d4426Jesse Wilson    }
2744a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9dJesse Wilson}
275