summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--luni/src/test/java/javax/xml/parsers/DocumentBuilderTest.java33
-rw-r--r--support/src/test/java/tests/support/Support_Xml.java54
-rw-r--r--xml/src/main/java/org/kxml2/io/KXmlSerializer.java43
-rw-r--r--xml/src/main/java/org/xmlpull/v1/XmlSerializer.java23
-rw-r--r--xml/src/test/java/org/kxml2/io/KXmlSerializerTest.java105
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>a&#65;b</p>")));
@@ -58,14 +37,6 @@ public class DocumentBuilderTest extends junit.framework.TestCase {
assertEquals("a\"b", firstChildTextOf(domOf("<p>a&quot;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 &amp; 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);
}
}