Merge commit '995a3842' into manualmerge

Conflicts:
	expectations/knownfailures.txt

Change-Id: Iee137d2c0c5e8bfa6994258f5fab8e07caeb86e1
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index a683e74..6b577d4 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -24,6 +24,14 @@
   bug: 3325799
 },
 {
+  description: "KxmlPullParser doesn't enforce top-level document element",
+  names: [
+    "libcore.xml.KxmlPullParserDtdTest#testDoctypeInDocumentElement",
+    "libcore.xml.KxmlPullParserDtdTest#testDoctypeAfterDocumentElement"
+  ],
+  bug: 3452274
+},
+{
   description: "URLConnection fails on URLs containing {}",
   name: "libcore.java.net.URLConnectionTest#testMalformedUrl",
   bug: 1158780,
diff --git a/luni/src/test/java/libcore/xml/PullParserDtdTest.java b/luni/src/test/java/libcore/xml/PullParserDtdTest.java
index 834bf00..0e4eac0 100644
--- a/luni/src/test/java/libcore/xml/PullParserDtdTest.java
+++ b/luni/src/test/java/libcore/xml/PullParserDtdTest.java
@@ -16,7 +16,6 @@
 
 package libcore.xml;
 
-import java.io.IOException;
 import java.io.StringReader;
 import java.util.Arrays;
 import junit.framework.TestCase;
@@ -44,20 +43,16 @@
     }
 
     public void testUsingParameterEntitiesInDtds() throws Exception {
-        String xml = "<!DOCTYPE foo ["
+        assertParseFailure("<!DOCTYPE foo ["
             + "  <!ENTITY % a \"android\">"
             + "  <!ENTITY b \"%a;\">"
-            + "]><foo></foo>";
-        XmlPullParser parser = newPullParser(xml);
-        assertParseFailure(parser);
+            + "]><foo></foo>");
     }
 
     public void testUsingParameterInDocuments() throws Exception {
-        String xml = "<!DOCTYPE foo ["
+        assertParseFailure("<!DOCTYPE foo ["
             + "  <!ENTITY % a \"android\">"
-            + "]><foo>&a;</foo>";
-        XmlPullParser parser = newPullParser(xml);
-        assertParseFailure(parser);
+            + "]><foo>&a;</foo>");
     }
 
     public void testGeneralAndParameterEntityWithTheSameName() throws Exception {
@@ -223,11 +218,9 @@
 
     public void testExternalIdIsCaseSensitive() throws Exception {
         // The spec requires 'SYSTEM' in upper case
-        String xml = "<!DOCTYPE foo ["
+        assertParseFailure("<!DOCTYPE foo ["
                 + "  <!ENTITY a system \"http://localhost:1/no-such-file.xml\">"
-                + "]><foo/>";
-        XmlPullParser parser = newPullParser(xml);
-        assertParseFailure(parser);
+                + "]><foo/>");
     }
 
     /**
@@ -381,15 +374,13 @@
     }
 
     public void testAttributeEntitiesExpandedEagerly() throws Exception {
-        String xml = "<!DOCTYPE foo [\n"
+        assertParseFailure("<!DOCTYPE foo [\n"
                 + "  <!ELEMENT foo ANY>\n"
                 + "  <!ATTLIST foo\n"
                 + "    bar CDATA \"abc &amp; def &g; jk\">"
                 + "  <!ENTITY g \"ghi\">"
                 + "]>"
-                + "<foo></foo>";
-        XmlPullParser parser = newPullParser(xml);
-        assertParseFailure(parser);
+                + "<foo></foo>");
     }
 
     public void testRequiredAttributesOmitted() throws Exception {
@@ -513,7 +504,17 @@
         assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
     }
 
-    private void assertParseFailure(XmlPullParser parser) throws IOException {
+    public void testDoctypeInDocumentElement() throws Exception {
+        assertParseFailure("<foo><!DOCTYPE foo></foo>");
+    }
+
+    public void testDoctypeAfterDocumentElement() throws Exception {
+        assertParseFailure("<foo/><!DOCTYPE foo>");
+    }
+
+    private void assertParseFailure(String xml) throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(xml));
         try {
             while (parser.next() != XmlPullParser.END_DOCUMENT) {
             }
diff --git a/luni/src/test/java/libcore/xml/PullParserTest.java b/luni/src/test/java/libcore/xml/PullParserTest.java
index aaa76a6..06a40fd 100644
--- a/luni/src/test/java/libcore/xml/PullParserTest.java
+++ b/luni/src/test/java/libcore/xml/PullParserTest.java
@@ -74,7 +74,7 @@
         testNamespace(parser);
     }
 
-    private void testNamespace(XmlPullParser parser) throws XmlPullParserException, IOException {
+    private void testNamespace(XmlPullParser parser) throws Exception {
         assertEquals(XmlPullParser.START_TAG, parser.next());
         assertEquals("http://foo", parser.getNamespace());
         assertEquals("a", parser.getName());
@@ -99,17 +99,11 @@
     }
 
     public void testNumericEntitiesLargerThanChar() throws Exception {
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader(
-                "<foo>&#2147483647; &#-2147483648;</foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo>&#2147483647; &#-2147483648;</foo>");
     }
 
     public void testNumericEntitiesLargerThanInt() throws Exception {
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader(
-                "<foo>&#2147483648;</foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo>&#2147483648;</foo>");
     }
 
     public void testCharacterReferenceOfHexUtf16Surrogates() throws Exception {
@@ -125,7 +119,7 @@
         parser.setInput(new StringReader(xml));
         assertEquals(XmlPullParser.START_TAG, parser.next());
         assertEquals(XmlPullParser.TEXT, parser.next());
-        assertEquals(new String(new int[] { 65536, ' ', 66433, ' ', 1114096 }, 0, 5),
+        assertEquals(new String(new int[]{65536, ' ', 66433, ' ', 1114096}, 0, 5),
                 parser.getText());
         assertEquals(XmlPullParser.END_TAG, parser.next());
     }
@@ -135,14 +129,12 @@
         parser.setInput(new StringReader("<foo>&#x10FFFF;</foo>"));
         assertEquals(XmlPullParser.START_TAG, parser.next());
         assertEquals(XmlPullParser.TEXT, parser.next());
-        assertEquals(new String(new int[] { 0x10FFFF }, 0, 1), parser.getText());
+        assertEquals(new String(new int[]{0x10FFFF}, 0, 1), parser.getText());
         assertEquals(XmlPullParser.END_TAG, parser.next());
     }
 
     public void testOmittedNumericEntities() throws Exception {
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader("<foo>&#;</foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo>&#;</foo>");
     }
 
     /**
@@ -163,7 +155,7 @@
         testLineEndings("\n<foo\na='b\nc'\n>d\ne</foo\n>\n");
     }
 
-    private void testLineEndings(String xml) throws XmlPullParserException, IOException {
+    private void testLineEndings(String xml) throws Exception {
         XmlPullParser parser = newPullParser();
         parser.setInput(new StringReader(xml));
         assertEquals(XmlPullParser.START_TAG, parser.next());
@@ -189,14 +181,7 @@
     }
 
     public void testXmlDeclarationExtraAttributes() throws Exception {
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader(
-                "<?xml version='1.0' encoding='UTF-8' standalone='no' a='b'?><foo/>"));
-        try {
-            parser.nextToken();
-            fail();
-        } catch (XmlPullParserException expected) {
-        }
+        assertParseFailure("<?xml version='1.0' encoding='UTF-8' standalone='no' a='b'?><foo/>");
     }
 
     public void testCustomEntitiesUsingNext() throws Exception {
@@ -238,9 +223,7 @@
     }
 
     public void testMissingEntities() throws Exception {
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader("<foo>&aaa;</foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo>&aaa;</foo>");
     }
 
     public void testMissingEntitiesWithRelaxed() throws Exception {
@@ -266,8 +249,7 @@
         testMissingEntitiesUsingNextToken(parser);
     }
 
-    private void testMissingEntitiesUsingNextToken(XmlPullParser parser)
-            throws XmlPullParserException, IOException {
+    private void testMissingEntitiesUsingNextToken(XmlPullParser parser) throws Exception {
         parser.setInput(new StringReader("<foo>&aaa;</foo>"));
         assertEquals(XmlPullParser.START_TAG, parser.nextToken());
         assertEquals(XmlPullParser.ENTITY_REF, parser.nextToken());
@@ -285,9 +267,7 @@
     }
 
     public void testMissingEntitiesInAttributesUsingNext() throws Exception {
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader("<foo b='&aaa;'></foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo b='&aaa;'></foo>");
     }
 
     public void testMissingEntitiesInAttributesUsingNextWithRelaxed() throws Exception {
@@ -315,7 +295,7 @@
     }
 
     private void testMissingEntitiesInAttributesUsingNextToken(XmlPullParser parser)
-            throws IOException, XmlPullParserException {
+            throws Exception {
         assertEquals(XmlPullParser.START_TAG, parser.nextToken());
         assertEquals(1, parser.getAttributeCount());
         assertEquals("b", parser.getAttributeName(0));
@@ -339,15 +319,11 @@
     }
 
     public void testLessThanInText() throws Exception{
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader("<foo><</foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo><</foo>");
     }
 
     public void testLessThanInAttribute() throws Exception{
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader("<foo a='<'></foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo a='<'></foo>");
     }
 
     public void testQuotesInAttribute() throws Exception{
@@ -374,33 +350,23 @@
     }
 
     public void testCdataDelimiterInText() throws Exception{
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader("<foo>]]></foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo>]]></foo>");
     }
 
     public void testUnexpectedEof() throws Exception {
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader("<foo><![C"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo><![C");
     }
 
     public void testUnexpectedSequence() throws Exception {
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader("<foo><![Cdata[bar]]></foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo><![Cdata[bar]]></foo>");
     }
 
     public void testThreeDashCommentDelimiter() throws Exception {
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader("<foo><!--a---></foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo><!--a---></foo>");
     }
 
     public void testTwoDashesInComment() throws Exception {
-        XmlPullParser parser = newPullParser();
-        parser.setInput(new StringReader("<foo><!-- -- --></foo>"));
-        assertParseFailure(parser);
+        assertParseFailure("<foo><!-- -- --></foo>");
     }
 
     public void testEmptyComment() throws Exception {
@@ -594,8 +560,7 @@
         validate(parser);
     }
 
-    static void validate(XmlPullParser parser)
-            throws XmlPullParserException, IOException {
+    static void validate(XmlPullParser parser) throws Exception {
         assertEquals(XmlPullParser.START_DOCUMENT, parser.getEventType());
         assertEquals(0, parser.getDepth());
         assertEquals(XmlPullParser.START_TAG, parser.next());
@@ -718,7 +683,21 @@
         assertEquals("ns:default", parser.getNamespaceUri(0));
     }
 
-    private void assertParseFailure(XmlPullParser parser) throws IOException {
+    public void testTextBeforeDocumentElement() throws Exception {
+        assertParseFailure("not xml<foo/>");
+    }
+
+    public void testTextAfterDocumentElement() throws Exception {
+        assertParseFailure("<foo/>not xml");
+    }
+
+    public void testTextNoDocumentElement() throws Exception {
+        assertParseFailure("not xml");
+    }
+
+    private void assertParseFailure(String xml) throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(xml));
         try {
             while (parser.next() != XmlPullParser.END_DOCUMENT) {
             }
diff --git a/xml/src/main/java/org/kxml2/io/KXmlParser.java b/xml/src/main/java/org/kxml2/io/KXmlParser.java
index f9d6461..4b4f328 100644
--- a/xml/src/main/java/org/kxml2/io/KXmlParser.java
+++ b/xml/src/main/java/org/kxml2/io/KXmlParser.java
@@ -422,6 +422,10 @@
                 throw new XmlPullParserException("Unexpected token", this, null);
             }
 
+            if (depth == 0 && (type == ENTITY_REF || type == TEXT || type == CDSECT)) {
+                throw new XmlPullParserException("Unexpected token", this, null);
+            }
+
             if (justOneToken) {
                 return type;
             }
@@ -2027,7 +2031,6 @@
 
     public void require(int type, String namespace, String name)
             throws XmlPullParserException, IOException {
-
         if (type != this.type
                 || (namespace != null && !namespace.equals(getNamespace()))
                 || (name != null && !name.equals(getName()))) {