From 4a5c3fdcab5bffc6b8258c8029b02d97b0dd3f9d Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Fri, 19 Mar 2010 12:51:43 -0700 Subject: Fixing namespace+prefix mode in Expat and removing optional fields from callbacks. The first part is related to bug 6632: http://code.google.com/p/android/issues/detail?id=6632 I added these optional fields back when I was originally updating the XML parser for Froyo. I've decided to remove them to simplify migrating between Android and the RI. It should also save some object allocations. Note that the RI v5 and the RI v6 behave differently for optional values on attributes; this motivated me to add the otherwise unfortunate assertOneOf() method to the testcase. (We behave more like RI v6, which is to supply the values upon request) Change-Id: Icfa5d29976a86bf194b3ed7c0d9e2275c3bff9dd --- .../java/org/apache/harmony/xml/ExpatReader.java | 2 +- .../native/org_apache_harmony_xml_ExpatParser.cpp | 4 +- xml/src/test/java/tests/xml/AllTests.java | 1 + xml/src/test/java/tests/xml/SaxTest.java | 175 +++++++++++++++++++++ 4 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 xml/src/test/java/tests/xml/SaxTest.java (limited to 'xml') diff --git a/xml/src/main/java/org/apache/harmony/xml/ExpatReader.java b/xml/src/main/java/org/apache/harmony/xml/ExpatReader.java index dbe3a3a..d187456 100644 --- a/xml/src/main/java/org/apache/harmony/xml/ExpatReader.java +++ b/xml/src/main/java/org/apache/harmony/xml/ExpatReader.java @@ -244,7 +244,7 @@ public class ExpatReader implements XMLReader { } public void parse(InputSource input) throws IOException, SAXException { - if (processNamespacePrefixes == processNamespaces) { + if (processNamespacePrefixes && processNamespaces) { /* * Expat has XML_SetReturnNSTriplet, but that still doesn't * include xmlns attributes like this feature requires. We may diff --git a/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp index 4721800..b893309 100644 --- a/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp +++ b/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp @@ -628,8 +628,8 @@ static void startElement(void* data, const char* elementName, jobject javaParser = parsingContext->object; ExpatElementName e(env, parsingContext, elementName); - jstring uri = e.uri(); - jstring localName = e.localName(); + jstring uri = parsingContext->processNamespaces ? e.uri() : emptyString; + jstring localName = parsingContext->processNamespaces ? e.localName() : emptyString; jstring qName = e.qName(); stringStackPush(parsingContext, qName); diff --git a/xml/src/test/java/tests/xml/AllTests.java b/xml/src/test/java/tests/xml/AllTests.java index 96b96c5..e9f833f 100644 --- a/xml/src/test/java/tests/xml/AllTests.java +++ b/xml/src/test/java/tests/xml/AllTests.java @@ -30,6 +30,7 @@ public class AllTests { suite.addTestSuite(SimpleBuilderTest.class); suite.addTestSuite(NodeTest.class); suite.addTestSuite(NormalizeTest.class); + suite.addTestSuite(SaxTest.class); //suite.addTest(tests.org.w3c.dom.AllTests.suite()); suite.addTest(tests.api.javax.xml.parsers.AllTests.suite()); diff --git a/xml/src/test/java/tests/xml/SaxTest.java b/xml/src/test/java/tests/xml/SaxTest.java new file mode 100644 index 0000000..2c75a73 --- /dev/null +++ b/xml/src/test/java/tests/xml/SaxTest.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.xml; + +import junit.framework.TestCase; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.StringReader; +import java.util.Arrays; +import java.util.List; + +/** + * Initiate and observe a SAX parse session. + */ +public class SaxTest extends TestCase { + + public void testNoPrefixesNoNamespaces() throws Exception { + parse(false, false, "", new DefaultHandler() { + @Override public void startElement(String uri, String localName, + String qName, Attributes attributes) { + assertEquals("", uri); + assertEquals("", localName); + assertEquals("foo", qName); + assertEquals(1, attributes.getLength()); + assertEquals("", attributes.getURI(0)); + assertOneOf("bar", "", attributes.getLocalName(0)); + assertEquals("bar", attributes.getQName(0)); + } + }); + + parse(false, false, "", new DefaultHandler() { + @Override public void startElement(String uri, String localName, + String qName, Attributes attributes) { + assertEquals("", uri); + assertEquals("", localName); + assertEquals("a:foo", qName); + assertEquals(1, attributes.getLength()); + assertEquals("", attributes.getURI(0)); + assertOneOf("a:bar", "", attributes.getLocalName(0)); + assertEquals("a:bar", attributes.getQName(0)); + } + }); + } + + public void testNoPrefixesYesNamespaces() throws Exception { + parse(false, true, "", new DefaultHandler() { + @Override public void startElement(String uri, String localName, + String qName, Attributes attributes) { + assertEquals("", uri); + assertEquals("foo", localName); + assertEquals("foo", qName); + assertEquals(1, attributes.getLength()); + assertEquals("", attributes.getURI(0)); + assertEquals("bar", attributes.getLocalName(0)); + assertEquals("bar", attributes.getQName(0)); + } + }); + + parse(false, true, "", new DefaultHandler() { + @Override public void startElement(String uri, String localName, + String qName, Attributes attributes) { + assertEquals("http://quux", uri); + assertEquals("foo", localName); + assertEquals("a:foo", qName); + assertEquals(1, attributes.getLength()); + assertEquals("http://quux", attributes.getURI(0)); + assertEquals("bar", attributes.getLocalName(0)); + assertEquals("a:bar", attributes.getQName(0)); + } + }); + } + + /** + * Android's Expat-based SAX parser fails this test because Expat doesn't + * supply us with our much desired {@code xmlns="http://..."} attributes. + */ + public void testYesPrefixesYesNamespaces() throws Exception { + parse(true, true, "", new DefaultHandler() { + @Override public void startElement(String uri, String localName, + String qName, Attributes attributes) { + assertEquals("", uri); + assertEquals("foo", localName); + assertEquals("foo", qName); + assertEquals(1, attributes.getLength()); + assertEquals("", attributes.getURI(0)); + assertEquals("bar", attributes.getLocalName(0)); + assertEquals("bar", attributes.getQName(0)); + } + }); + + parse(true, true, "", new DefaultHandler() { + @Override public void startElement(String uri, String localName, + String qName, Attributes attributes) { + assertEquals("http://quux", uri); + assertEquals("foo", localName); + assertEquals("a:foo", qName); + assertEquals(2, attributes.getLength()); + assertEquals("http://quux", attributes.getURI(0)); + assertEquals("bar", attributes.getLocalName(0)); + assertEquals("a:bar", attributes.getQName(0)); + assertEquals("", attributes.getURI(1)); + assertEquals("", attributes.getLocalName(1)); + assertEquals("xmlns:a", attributes.getQName(1)); + } + }); + } + + public void testYesPrefixesNoNamespaces() throws Exception { + parse(true, false, "", new DefaultHandler() { + @Override public void startElement(String uri, String localName, + String qName, Attributes attributes) { + assertEquals("", uri); + assertEquals("", localName); + assertEquals("foo", qName); + assertEquals(1, attributes.getLength()); + assertEquals("", attributes.getURI(0)); + assertOneOf("bar", "", attributes.getLocalName(0)); + assertEquals("bar", attributes.getQName(0)); + } + }); + + parse(true, false, "", new DefaultHandler() { + @Override public void startElement(String uri, String localName, + String qName, Attributes attributes) { + assertEquals("", uri); + assertEquals("", localName); + assertEquals("a:foo", qName); + assertEquals(1, attributes.getLength()); + assertEquals("", attributes.getURI(0)); + assertOneOf("a:bar", "", attributes.getLocalName(0)); + assertEquals("a:bar", attributes.getQName(0)); + } + }); + } + + private void parse(boolean prefixes, boolean namespaces, String xml, + ContentHandler handler) throws Exception { + SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); + XMLReader reader = parser.getXMLReader(); + reader.setFeature("http://xml.org/sax/features/namespace-prefixes", prefixes); + reader.setFeature("http://xml.org/sax/features/namespaces", namespaces); + reader.setContentHandler(handler); + reader.parse(new InputSource(new StringReader(xml))); + } + + /** + * @param expected an optional value that may or may have not been supplied + * @param sentinel a marker value that means the expected value was omitted + */ + private void assertOneOf(String expected, String sentinel, String actual) { + List optionsList = Arrays.asList(sentinel, expected); + assertTrue("Expected one of " + optionsList + " but was " + actual, + optionsList.contains(actual)); + } +} -- cgit v1.1