diff options
5 files changed, 191 insertions, 67 deletions
diff --git a/luni/src/test/java/javax/xml/parsers/DocumentBuilderTest.java b/luni/src/test/java/javax/xml/parsers/DocumentBuilderTest.java index 6d3c463..b1aea21 100644 --- a/luni/src/test/java/javax/xml/parsers/DocumentBuilderTest.java +++ b/luni/src/test/java/javax/xml/parsers/DocumentBuilderTest.java @@ -16,33 +16,12 @@ package javax.xml.parsers; -import java.io.ByteArrayInputStream; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import junit.framework.Test; import junit.framework.TestSuite; +import static tests.support.Support_Xml.*; + public class DocumentBuilderTest extends junit.framework.TestCase { - private static Document domOf(String xml) throws Exception { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setCoalescing(true); - dbf.setExpandEntityReferences(true); - - ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); - DocumentBuilder builder = dbf.newDocumentBuilder(); - - return builder.parse(stream); - } - - private static String firstChildTextOf(Document doc) throws Exception { - NodeList children = doc.getFirstChild().getChildNodes(); - assertEquals(1, children.getLength()); - return children.item(0).getNodeValue(); - } - // http://code.google.com/p/android/issues/detail?id=2607 public void test_characterReferences() throws Exception { assertEquals("aAb", firstChildTextOf(domOf("<p>aAb</p>"))); @@ -58,14 +37,6 @@ public class DocumentBuilderTest extends junit.framework.TestCase { assertEquals("a\"b", firstChildTextOf(domOf("<p>a"b</p>"))); } - private static Element firstElementOf(Document doc) throws Exception { - return (Element) doc.getFirstChild(); - } - - private static String attrOf(Element e) throws Exception { - return e.getAttribute("attr"); - } - // http://code.google.com/p/android/issues/detail?id=2487 public void test_cdata_attributes() throws Exception { assertEquals("hello & world", attrOf(firstElementOf(domOf("<?xml version=\"1.0\"?><root attr=\"hello & world\" />")))); diff --git a/support/src/test/java/tests/support/Support_Xml.java b/support/src/test/java/tests/support/Support_Xml.java new file mode 100644 index 0000000..03ed4a1 --- /dev/null +++ b/support/src/test/java/tests/support/Support_Xml.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 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.support; + +import java.io.ByteArrayInputStream; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import static junit.framework.Assert.assertEquals; + +public class Support_Xml { + public static Document domOf(String xml) throws Exception { + // DocumentBuilderTest assumes we're using DocumentBuilder to do this parsing! + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setCoalescing(true); + dbf.setExpandEntityReferences(true); + + ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); + DocumentBuilder builder = dbf.newDocumentBuilder(); + + return builder.parse(stream); + } + + public static String firstChildTextOf(Document doc) throws Exception { + NodeList children = doc.getFirstChild().getChildNodes(); + assertEquals(1, children.getLength()); + return children.item(0).getNodeValue(); + } + + public static Element firstElementOf(Document doc) throws Exception { + return (Element) doc.getFirstChild(); + } + + public static String attrOf(Element e) throws Exception { + return e.getAttribute("attr"); + } +} diff --git a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java index d63ed04..1f13357 100644 --- a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java +++ b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java @@ -124,18 +124,33 @@ public class KXmlSerializer implements XmlSerializer { break; } default : - //if(c < ' ') - // throw new IllegalArgumentException("Illegal control code:"+((int) c)); - - if (c >= ' ' && c !='@' && (c < 127 || unicode)) + // BEGIN android-changed: refuse to output invalid characters + // See http://www.w3.org/TR/REC-xml/#charsets for definition. + // No other Java XML writer we know of does this, but no Java + // XML reader we know of is able to parse the bad output we'd + // otherwise generate. + // Note: tab, newline, and carriage return have already been + // handled above. + boolean valid = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd); + if (!valid) { + reportInvalidCharacter(c); + } + if (unicode || c < 127) { writer.write(c); - else + } else { writer.write("&#" + ((int) c) + ";"); - + } + // END android-changed } } } + // BEGIN android-added + private static void reportInvalidCharacter(char ch) { + throw new IllegalArgumentException("Illegal character (" + Integer.toHexString((int) ch) + ")"); + } + // END android-added + /* private final void writeIndent() throws IOException { writer.write("\r\n"); @@ -540,9 +555,23 @@ public class KXmlSerializer implements XmlSerializer { public void cdsect(String data) throws IOException { check(false); + // BEGIN android-changed: ]]> is not allowed within a CDATA, + // so break and start a new one when necessary. + data = data.replace("]]>", "]]]]><![CDATA[>"); + char[] chars = data.toCharArray(); + // We also aren't allowed any invalid characters. + for (char ch : chars) { + boolean valid = (ch >= 0x20 && ch <= 0xd7ff) || + (ch == '\t' || ch == '\n' || ch == '\r') || + (ch >= 0xe000 && ch <= 0xfffd); + if (!valid) { + reportInvalidCharacter(ch); + } + } writer.write("<![CDATA["); - writer.write(data); + writer.write(chars, 0, chars.length); writer.write("]]>"); + // END android-changed } public void comment(String comment) throws IOException { diff --git a/xml/src/main/java/org/xmlpull/v1/XmlSerializer.java b/xml/src/main/java/org/xmlpull/v1/XmlSerializer.java index 8e85e2f..09503ba 100644 --- a/xml/src/main/java/org/xmlpull/v1/XmlSerializer.java +++ b/xml/src/main/java/org/xmlpull/v1/XmlSerializer.java @@ -5,8 +5,8 @@ import java.io.OutputStream; import java.io.Writer; /** - * Define an interface to serialziation of XML Infoset. - * This interface abstracts away if serialized XML is XML 1.0 comaptible text or + * Define an interface to serialization of XML Infoset. + * This interface abstracts away if serialized XML is XML 1.0 compatible text or * other formats of XML 1.0 serializations (such as binary XML for example with WBXML). * * <p><b>PLEASE NOTE:</b> This interface will be part of XmlPull 1.2 API. @@ -27,7 +27,7 @@ import java.io.Writer; * <p><b>NOTE:</b> writing CDSECT, ENTITY_REF, IGNORABLE_WHITESPACE, * PROCESSING_INSTRUCTION, COMMENT, and DOCDECL in some implementations * may not be supported (for example when serializing to WBXML). - * In such case IllegalStateException will be thrown and it is recommened + * In such case IllegalStateException will be thrown and it is recommended * to use an optional feature to signal that implementation is not * supporting this kind of output. */ @@ -40,7 +40,7 @@ public interface XmlSerializer { * <a href="http://www.xmlpull.org/v1/doc/features.html"> * http://www.xmlpull.org/v1/doc/features.html</a>. * - * If feature is not recocgnized or can not be set + * If feature is not recognized or can not be set * then IllegalStateException MUST be thrown. * * @exception IllegalStateException If the feature is not supported or can not be set @@ -63,12 +63,12 @@ public interface XmlSerializer { /** * Set the value of a property. - * (the property name is recommened to be URI for uniqueness). + * (the property name is recommended to be URI for uniqueness). * Some well known optional properties are defined in * <a href="http://www.xmlpull.org/v1/doc/properties.html"> * http://www.xmlpull.org/v1/doc/properties.html</a>. * - * If property is not recocgnized or can not be set + * If property is not recognized or can not be set * then IllegalStateException MUST be thrown. * * @exception IllegalStateException if the property is not supported or can not be set @@ -144,7 +144,7 @@ public interface XmlSerializer { * If there is no prefix bound to this namespace return null * but if generatePrefix is false then return generated prefix. * - * <p><b>NOTE:</b> if the prefix is empty string "" and defualt namespace is bound + * <p><b>NOTE:</b> if the prefix is empty string "" and default namespace is bound * to this prefix then empty string ("") is returned. * * <p><b>NOTE:</b> prefixes "xml" and "xmlns" are already bound @@ -176,7 +176,7 @@ public interface XmlSerializer { /** * Returns the namespace URI of the current element as set by startTag(). * - * <p><b>NOTE:</b> that measn in particaulr that: <ul> + * <p><b>NOTE:</b> that means in particular that: <ul> * <li>if there was startTag("", ...) then getNamespace() returns "" * <li>if there was startTag(null, ...) then getNamespace() returns null * </ul> @@ -201,7 +201,7 @@ public interface XmlSerializer { * The explicit prefixes for namespaces can be established by calling setPrefix() * immediately before this method. * If namespace is null no namespace prefix is printed but just name. - * If namespace is empty string then serialzier will make sure that + * If namespace is empty string then serializer will make sure that * default empty namespace is declared (in XML 1.0 xmlns='') * or throw IllegalStateException if default namespace is already bound * to non-empty string. @@ -224,7 +224,7 @@ public interface XmlSerializer { * <p><b>Background:</b> in kXML endTag had no arguments, and non matching tags were * very difficult to find... * If namespace is null no namespace prefix is printed but just name. - * If namespace is empty string then serialzier will make sure that + * If namespace is empty string then serializer will make sure that * default empty namespace is declared (in XML 1.0 xmlns=''). */ XmlSerializer endTag (String namespace, String name) @@ -315,7 +315,7 @@ public interface XmlSerializer { * before flush() is called on underlying output stream. * * <p><b>NOTE:</b> if there is need to close start tag - * (so no more attribute() calls are allowed) but without flushinging output + * (so no more attribute() calls are allowed) but without flushing output * call method text() with empty string (text("")). * */ @@ -323,4 +323,3 @@ public interface XmlSerializer { throws IOException; } - diff --git a/xml/src/test/java/org/kxml2/io/KXmlSerializerTest.java b/xml/src/test/java/org/kxml2/io/KXmlSerializerTest.java index 2d5ddf7..01c7393 100644 --- a/xml/src/test/java/org/kxml2/io/KXmlSerializerTest.java +++ b/xml/src/test/java/org/kxml2/io/KXmlSerializerTest.java @@ -20,29 +20,100 @@ import junit.framework.TestCase; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.StringWriter; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xmlpull.v1.XmlSerializer; -public class KXmlSerializerTest extends TestCase { - - /** the namespace */ - final String ns = null; +import static tests.support.Support_Xml.*; - public void testEmittingNullCharacterThrows() throws IOException { +public class KXmlSerializerTest extends TestCase { + private static final String NAMESPACE = null; + + private static boolean isValidXmlCodePoint(int c) { + // http://www.w3.org/TR/REC-xml/#charsets + return (c >= 0x20 && c <= 0xd7ff) || (c == 0x9) || (c == 0xa) || (c == 0xd) || + (c >= 0xe000 && c <= 0xfffd) || (c >= 0x10000 && c <= 0x10ffff); + } + + private static XmlSerializer newSerializer() throws IOException { ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - KXmlSerializer serializer = new KXmlSerializer(); + XmlSerializer serializer = new KXmlSerializer(); serializer.setOutput(bytesOut, "UTF-8"); serializer.startDocument("UTF-8", null); - - serializer.startTag(ns, "foo"); - try { - serializer.text("bar\0baz"); - fail(); - } catch (IllegalArgumentException expected) { + return serializer; + } + + public void testInvalidCharactersInText() throws IOException { + XmlSerializer serializer = newSerializer(); + serializer.startTag(NAMESPACE, "root"); + for (int ch = 0; ch <= 0xffff; ++ch) { + final String s = Character.toString((char) ch); + if (isValidXmlCodePoint(ch)) { + serializer.text("a" + s + "b"); + } else { + try { + serializer.text("a" + s + "b"); + fail(s); + } catch (IllegalArgumentException expected) { + } + } } - - serializer.startTag(ns, "bar"); - try { - serializer.attribute(ns, "baz", "qu\0ux"); - } catch (IllegalArgumentException expected) { + serializer.endTag(NAMESPACE, "root"); + } + + public void testInvalidCharactersInAttributeValues() throws IOException { + XmlSerializer serializer = newSerializer(); + serializer.startTag(NAMESPACE, "root"); + for (int ch = 0; ch <= 0xffff; ++ch) { + final String s = Character.toString((char) ch); + if (isValidXmlCodePoint(ch)) { + serializer.attribute(NAMESPACE, "a", "a" + s + "b"); + } else { + try { + serializer.attribute(NAMESPACE, "a", "a" + s + "b"); + fail(s); + } catch (IllegalArgumentException expected) { + } + } + } + serializer.endTag(NAMESPACE, "root"); + } + + public void testInvalidCharactersInCdataSections() throws IOException { + XmlSerializer serializer = newSerializer(); + serializer.startTag(NAMESPACE, "root"); + for (int ch = 0; ch <= 0xffff; ++ch) { + final String s = Character.toString((char) ch); + if (isValidXmlCodePoint(ch)) { + serializer.cdsect("a" + s + "b"); + } else { + try { + serializer.cdsect("a" + s + "b"); + fail(s); + } catch (IllegalArgumentException expected) { + } + } + } + serializer.endTag(NAMESPACE, "root"); + } + + public void testCdataWithTerminatorInside() throws Exception { + StringWriter writer = new StringWriter(); + XmlSerializer serializer = new KXmlSerializer(); + serializer.setOutput(writer); + serializer.startDocument("UTF-8", null); + serializer.startTag(NAMESPACE, "p"); + serializer.cdsect("a]]>b"); + serializer.endTag(NAMESPACE, "p"); + serializer.endDocument(); + // Adjacent CDATA sections aren't merged, so let's stick them together ourselves... + Document doc = domOf(writer.toString()); + NodeList children = doc.getFirstChild().getChildNodes(); + String text = ""; + for (int i = 0; i < children.getLength(); ++i) { + text += children.item(i).getNodeValue(); } + assertEquals("a]]>b", text); } } |