1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package libcore.xml; 18 19import java.io.StringReader; 20import java.util.Arrays; 21import junit.framework.TestCase; 22import org.xmlpull.v1.XmlPullParser; 23import org.xmlpull.v1.XmlPullParserException; 24 25/** 26 * Test doctype handling in pull parsers. 27 */ 28public abstract class PullParserDtdTest extends TestCase { 29 30 private static final int READ_BUFFER_SIZE = 8192; 31 32 /** 33 * Android's Expat pull parser permits parameter entities to be declared, 34 * but it doesn't permit such entities to be used. 35 */ 36 public void testDeclaringParameterEntities() throws Exception { 37 String xml = "<!DOCTYPE foo [" 38 + " <!ENTITY % a \"android\">" 39 + "]><foo></foo>"; 40 XmlPullParser parser = newPullParser(xml); 41 while (parser.next() != XmlPullParser.END_DOCUMENT) { 42 } 43 } 44 45 public void testUsingParameterEntitiesInDtds() throws Exception { 46 assertParseFailure("<!DOCTYPE foo [" 47 + " <!ENTITY % a \"android\">" 48 + " <!ENTITY b \"%a;\">" 49 + "]><foo></foo>"); 50 } 51 52 public void testUsingParameterInDocuments() throws Exception { 53 assertParseFailure("<!DOCTYPE foo [" 54 + " <!ENTITY % a \"android\">" 55 + "]><foo>&a;</foo>"); 56 } 57 58 public void testGeneralAndParameterEntityWithTheSameName() throws Exception { 59 String xml = "<!DOCTYPE foo [" 60 + " <!ENTITY a \"aaa\">" 61 + " <!ENTITY % a \"bbb\">" 62 + "]><foo>&a;</foo>"; 63 XmlPullParser parser = newPullParser(xml); 64 assertEquals(XmlPullParser.START_TAG, parser.next()); 65 assertEquals(XmlPullParser.TEXT, parser.next()); 66 assertEquals("aaa", parser.getText()); 67 assertEquals(XmlPullParser.END_TAG, parser.next()); 68 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 69 } 70 71 public void testInternalEntities() throws Exception { 72 String xml = "<!DOCTYPE foo [" 73 + " <!ENTITY a \"android\">" 74 + "]><foo>&a;</foo>"; 75 XmlPullParser parser = newPullParser(xml); 76 assertEquals(XmlPullParser.START_TAG, parser.next()); 77 assertEquals(XmlPullParser.TEXT, parser.next()); 78 assertEquals("android", parser.getText()); 79 assertEquals(XmlPullParser.END_TAG, parser.next()); 80 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 81 } 82 83 public void testExternalDtdIsSilentlyIgnored() throws Exception { 84 String xml = "<!DOCTYPE foo SYSTEM \"http://127.0.0.1:1/no-such-file.dtd\"><foo></foo>"; 85 XmlPullParser parser = newPullParser(xml); 86 assertEquals(XmlPullParser.START_TAG, parser.next()); 87 assertEquals("foo", parser.getName()); 88 assertEquals(XmlPullParser.END_TAG, parser.next()); 89 assertEquals("foo", parser.getName()); 90 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 91 } 92 93 public void testExternalAndInternalDtd() throws Exception { 94 String xml = "<!DOCTYPE foo SYSTEM \"http://127.0.0.1:1/no-such-file.dtd\" [" 95 + " <!ENTITY a \"android\">" 96 + "]><foo>&a;</foo>"; 97 XmlPullParser parser = newPullParser(xml); 98 assertEquals(XmlPullParser.START_TAG, parser.next()); 99 assertEquals(XmlPullParser.TEXT, parser.next()); 100 assertEquals("android", parser.getText()); 101 assertEquals(XmlPullParser.END_TAG, parser.next()); 102 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 103 } 104 105 public void testInternalEntitiesAreParsed() throws Exception { 106 String xml = "<!DOCTYPE foo [" 107 + " <!ENTITY a \"&#65;\">" // & expands to '&', A expands to 'A' 108 + "]><foo>&a;</foo>"; 109 XmlPullParser parser = newPullParser(xml); 110 assertEquals(XmlPullParser.START_TAG, parser.next()); 111 assertEquals(XmlPullParser.TEXT, parser.next()); 112 assertEquals("A", parser.getText()); 113 assertEquals(XmlPullParser.END_TAG, parser.next()); 114 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 115 } 116 117 public void testFoolishlyRecursiveInternalEntities() throws Exception { 118 String xml = "<!DOCTYPE foo [" 119 + " <!ENTITY a \"&#38;#38;#38;\">" // expand & to '&' only twice 120 + "]><foo>&a;</foo>"; 121 XmlPullParser parser = newPullParser(xml); 122 assertEquals(XmlPullParser.START_TAG, parser.next()); 123 assertEquals(XmlPullParser.TEXT, parser.next()); 124 assertEquals("&#38;", parser.getText()); 125 assertEquals(XmlPullParser.END_TAG, parser.next()); 126 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 127 } 128 129 /** 130 * Test that the output of {@code &} is parsed, but {@code &} isn't. 131 * http://www.w3.org/TR/2008/REC-xml-20081126/#sec-entexpand 132 */ 133 public void testExpansionOfEntityAndCharacterReferences() throws Exception { 134 String xml = "<!DOCTYPE foo [" 135 + "<!ENTITY example \"<p>An ampersand (&#38;) may be escaped\n" 136 + "numerically (&#38;#38;) or with a general entity\n" 137 + "(&amp;).</p>\" >" 138 + "]><foo>&example;</foo>"; 139 XmlPullParser parser = newPullParser(xml); 140 assertEquals(XmlPullParser.START_TAG, parser.next()); 141 assertEquals(XmlPullParser.START_TAG, parser.next()); 142 assertEquals("p", parser.getName()); 143 assertEquals(XmlPullParser.TEXT, parser.next()); 144 assertEquals("An ampersand (&) may be escaped\n" 145 + "numerically (&) or with a general entity\n" 146 + "(&).", parser.getText()); 147 assertEquals(XmlPullParser.END_TAG, parser.next()); 148 assertEquals("p", parser.getName()); 149 assertEquals(XmlPullParser.END_TAG, parser.next()); 150 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 151 } 152 153 public void testGeneralEntitiesAreStoredUnresolved() throws Exception { 154 String xml = "<!DOCTYPE foo [" 155 + "<!ENTITY b \"&a;\" >" 156 + "<!ENTITY a \"android\" >" 157 + "]><foo>&a;</foo>"; 158 XmlPullParser parser = newPullParser(xml); 159 assertEquals(XmlPullParser.START_TAG, parser.next()); 160 assertEquals(XmlPullParser.TEXT, parser.next()); 161 assertEquals("android", parser.getText()); 162 assertEquals(XmlPullParser.END_TAG, parser.next()); 163 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 164 } 165 166 public void testStructuredEntityAndNextToken() throws Exception { 167 String xml = "<!DOCTYPE foo [<!ENTITY bb \"<bar>baz<!--quux--></bar>\">]><foo>a&bb;c</foo>"; 168 XmlPullParser parser = newPullParser(xml); 169 assertEquals(XmlPullParser.DOCDECL, parser.nextToken()); 170 assertEquals(XmlPullParser.START_TAG, parser.nextToken()); 171 assertEquals("foo", parser.getName()); 172 assertEquals(XmlPullParser.TEXT, parser.nextToken()); 173 assertEquals("a", parser.getText()); 174 assertEquals(XmlPullParser.ENTITY_REF, parser.nextToken()); 175 assertEquals("bb", parser.getName()); 176 assertEquals("", parser.getText()); 177 assertEquals(XmlPullParser.START_TAG, parser.nextToken()); 178 assertEquals("bar", parser.getName()); 179 assertEquals(XmlPullParser.TEXT, parser.nextToken()); 180 assertEquals("baz", parser.getText()); 181 assertEquals(XmlPullParser.COMMENT, parser.nextToken()); 182 assertEquals("quux", parser.getText()); 183 assertEquals(XmlPullParser.END_TAG, parser.nextToken()); 184 assertEquals("bar", parser.getName()); 185 assertEquals(XmlPullParser.TEXT, parser.nextToken()); 186 assertEquals("c", parser.getText()); 187 assertEquals(XmlPullParser.END_TAG, parser.next()); 188 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 189 } 190 191 /** 192 * Android's Expat replaces external entities with the empty string. 193 */ 194 public void testUsingExternalEntities() throws Exception { 195 String xml = "<!DOCTYPE foo [" 196 + " <!ENTITY a SYSTEM \"http://localhost:1/no-such-file.xml\">" 197 + "]><foo>&a;</foo>"; 198 XmlPullParser parser = newPullParser(xml); 199 assertEquals(XmlPullParser.START_TAG, parser.next()); 200 // &a; is dropped! 201 assertEquals(XmlPullParser.END_TAG, parser.next()); 202 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 203 } 204 205 /** 206 * Android's ExpatPullParser replaces missing entities with the empty string 207 * when an external DTD is declared. 208 */ 209 public void testExternalDtdAndMissingEntity() throws Exception { 210 String xml = "<!DOCTYPE foo SYSTEM \"http://127.0.0.1:1/no-such-file.dtd\">" 211 + "<foo>&a;</foo>"; 212 XmlPullParser parser = newPullParser(xml); 213 assertEquals(XmlPullParser.START_TAG, parser.next()); 214 assertEquals(XmlPullParser.END_TAG, parser.next()); 215 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 216 } 217 218 219 public void testExternalIdIsCaseSensitive() throws Exception { 220 // The spec requires 'SYSTEM' in upper case 221 assertParseFailure("<!DOCTYPE foo [" 222 + " <!ENTITY a system \"http://localhost:1/no-such-file.xml\">" 223 + "]><foo/>"); 224 } 225 226 /** 227 * Use a DTD to specify that {@code <foo>} only contains {@code <bar>} tags. 228 * Validating parsers react to this by dropping whitespace between the two 229 * tags. 230 */ 231 public void testDtdDoesNotInformIgnorableWhitespace() throws Exception { 232 String xml = "<!DOCTYPE foo [\n" 233 + " <!ELEMENT foo (bar)*>\n" 234 + " <!ELEMENT bar ANY>\n" 235 + "]>" 236 + "<foo> \n <bar></bar> \t </foo>"; 237 XmlPullParser parser = newPullParser(xml); 238 assertEquals(XmlPullParser.START_TAG, parser.next()); 239 assertEquals("foo", parser.getName()); 240 assertEquals(XmlPullParser.TEXT, parser.next()); 241 assertEquals(" \n ", parser.getText()); 242 assertEquals(XmlPullParser.START_TAG, parser.next()); 243 assertEquals("bar", parser.getName()); 244 assertEquals(XmlPullParser.END_TAG, parser.next()); 245 assertEquals("bar", parser.getName()); 246 assertEquals(XmlPullParser.TEXT, parser.next()); 247 assertEquals(" \t ", parser.getText()); 248 assertEquals(XmlPullParser.END_TAG, parser.next()); 249 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 250 } 251 252 public void testEmptyDoesNotInformIgnorableWhitespace() throws Exception { 253 String xml = "<!DOCTYPE foo [\n" 254 + " <!ELEMENT foo EMPTY>\n" 255 + "]>" 256 + "<foo> \n </foo>"; 257 XmlPullParser parser = newPullParser(xml); 258 assertEquals(XmlPullParser.START_TAG, parser.next()); 259 assertEquals("foo", parser.getName()); 260 assertEquals(XmlPullParser.TEXT, parser.next()); 261 assertEquals(" \n ", parser.getText()); 262 assertEquals(XmlPullParser.END_TAG, parser.next()); 263 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 264 } 265 266 /** 267 * Test that the parser doesn't expand the entity attributes. 268 */ 269 public void testAttributeOfTypeEntity() throws Exception { 270 String xml = "<!DOCTYPE foo [\n" 271 + " <!ENTITY a \"android\">" 272 + " <!ELEMENT foo ANY>\n" 273 + " <!ATTLIST foo\n" 274 + " bar ENTITY #IMPLIED>" 275 + "]>" 276 + "<foo bar=\"a\"></foo>"; 277 XmlPullParser parser = newPullParser(xml); 278 assertEquals(XmlPullParser.START_TAG, parser.next()); 279 assertEquals("foo", parser.getName()); 280 assertEquals("a", parser.getAttributeValue(null, "bar")); 281 assertEquals(XmlPullParser.END_TAG, parser.next()); 282 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 283 } 284 285 public void testTagStructureNotValidated() throws Exception { 286 String xml = "<!DOCTYPE foo [\n" 287 + " <!ELEMENT foo (bar)*>\n" 288 + " <!ELEMENT bar ANY>\n" 289 + " <!ELEMENT baz ANY>\n" 290 + "]>" 291 + "<foo><bar/><bar/><baz/></foo>"; 292 XmlPullParser parser = newPullParser(xml); 293 assertEquals(XmlPullParser.START_TAG, parser.next()); 294 assertEquals("foo", parser.getName()); 295 assertEquals(XmlPullParser.START_TAG, parser.next()); 296 assertEquals("bar", parser.getName()); 297 assertEquals(XmlPullParser.END_TAG, parser.next()); 298 assertEquals(XmlPullParser.START_TAG, parser.next()); 299 assertEquals("bar", parser.getName()); 300 assertEquals(XmlPullParser.END_TAG, parser.next()); 301 assertEquals(XmlPullParser.START_TAG, parser.next()); 302 assertEquals("baz", parser.getName()); 303 assertEquals(XmlPullParser.END_TAG, parser.next()); 304 assertEquals(XmlPullParser.END_TAG, parser.next()); 305 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 306 } 307 308 public void testAttributeDefaultValues() throws Exception { 309 String xml = "<!DOCTYPE foo [\n" 310 + " <!ATTLIST bar\n" 311 + " baz (a|b|c) \"c\">" 312 + "]>" 313 + "<foo>" 314 + "<bar/>" 315 + "<bar baz=\"a\"/>" 316 + "</foo>"; 317 XmlPullParser parser = newPullParser(xml); 318 assertEquals(XmlPullParser.START_TAG, parser.next()); 319 assertEquals(XmlPullParser.START_TAG, parser.next()); 320 assertEquals("bar", parser.getName()); 321 assertEquals("c", parser.getAttributeValue(null, "baz")); 322 assertEquals(XmlPullParser.END_TAG, parser.next()); 323 assertEquals(XmlPullParser.START_TAG, parser.next()); 324 assertEquals("bar", parser.getName()); 325 assertEquals("a", parser.getAttributeValue(null, "baz")); 326 assertEquals(XmlPullParser.END_TAG, parser.next()); 327 assertEquals(XmlPullParser.END_TAG, parser.next()); 328 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 329 } 330 331 public void testAttributeDefaultValueEntitiesExpanded() throws Exception { 332 String xml = "<!DOCTYPE foo [\n" 333 + " <!ENTITY g \"ghi\">" 334 + " <!ELEMENT foo ANY>\n" 335 + " <!ATTLIST foo\n" 336 + " bar CDATA \"abc & def &g; jk\">" 337 + "]>" 338 + "<foo></foo>"; 339 XmlPullParser parser = newPullParser(xml); 340 assertEquals(XmlPullParser.START_TAG, parser.next()); 341 assertEquals("foo", parser.getName()); 342 assertEquals("abc & def ghi jk", parser.getAttributeValue(null, "bar")); 343 assertEquals(XmlPullParser.END_TAG, parser.next()); 344 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 345 } 346 347 public void testAttributeDefaultValuesAndNamespaces() throws Exception { 348 String xml = "<!DOCTYPE foo [\n" 349 + " <!ATTLIST foo\n" 350 + " bar:a CDATA \"android\">" 351 + "]>" 352 + "<foo xmlns:bar='http://bar'></foo>"; 353 XmlPullParser parser = newPullParser(xml); 354 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 355 assertEquals(XmlPullParser.START_TAG, parser.next()); 356 assertEquals("foo", parser.getName()); 357 // In Expat, namespaces don't apply to default attributes 358 int index = indexOfAttributeWithName(parser, "bar:a"); 359 assertEquals("", parser.getAttributeNamespace(index)); 360 assertEquals("bar:a", parser.getAttributeName(index)); 361 assertEquals("android", parser.getAttributeValue(index)); 362 assertEquals("CDATA", parser.getAttributeType(index)); 363 assertEquals(XmlPullParser.END_TAG, parser.next()); 364 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 365 } 366 367 private int indexOfAttributeWithName(XmlPullParser parser, String name) { 368 for (int i = 0; i < parser.getAttributeCount(); i++) { 369 if (parser.getAttributeName(i).equals(name)) { 370 return i; 371 } 372 } 373 return -1; 374 } 375 376 public void testAttributeEntitiesExpandedEagerly() throws Exception { 377 assertParseFailure("<!DOCTYPE foo [\n" 378 + " <!ELEMENT foo ANY>\n" 379 + " <!ATTLIST foo\n" 380 + " bar CDATA \"abc & def &g; jk\">" 381 + " <!ENTITY g \"ghi\">" 382 + "]>" 383 + "<foo></foo>"); 384 } 385 386 public void testRequiredAttributesOmitted() throws Exception { 387 String xml = "<!DOCTYPE foo [\n" 388 + " <!ELEMENT foo ANY>\n" 389 + " <!ATTLIST foo\n" 390 + " bar (a|b|c) #REQUIRED>" 391 + "]>" 392 + "<foo></foo>"; 393 XmlPullParser parser = newPullParser(xml); 394 assertEquals(XmlPullParser.START_TAG, parser.next()); 395 assertEquals("foo", parser.getName()); 396 assertEquals(null, parser.getAttributeValue(null, "bar")); 397 assertEquals(XmlPullParser.END_TAG, parser.next()); 398 } 399 400 public void testFixedAttributesWithConflictingValues() throws Exception { 401 String xml = "<!DOCTYPE foo [\n" 402 + " <!ELEMENT foo ANY>\n" 403 + " <!ATTLIST foo\n" 404 + " bar (a|b|c) #FIXED \"c\">" 405 + "]>" 406 + "<foo bar=\"a\"></foo>"; 407 XmlPullParser parser = newPullParser(xml); 408 assertEquals(XmlPullParser.START_TAG, parser.next()); 409 assertEquals("foo", parser.getName()); 410 assertEquals("a", parser.getAttributeValue(null, "bar")); 411 assertEquals(XmlPullParser.END_TAG, parser.next()); 412 } 413 414 public void testParsingNotations() throws Exception { 415 String xml = "<!DOCTYPE foo [\n" 416 + " <!NOTATION type-a PUBLIC \"application/a\"> \n" 417 + " <!NOTATION type-b PUBLIC \"image/b\">\n" 418 + " <!NOTATION type-c PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n" 419 + " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"> \n" 420 + " <!ENTITY file SYSTEM \"d.xml\">\n" 421 + " <!ENTITY fileWithNdata SYSTEM \"e.bin\" NDATA type-b>\n" 422 + "]>" 423 + "<foo type=\"type-a\"/>"; 424 XmlPullParser parser = newPullParser(xml); 425 assertEquals(XmlPullParser.START_TAG, parser.next()); 426 assertEquals("foo", parser.getName()); 427 assertEquals(XmlPullParser.END_TAG, parser.next()); 428 } 429 430 public void testCommentsInDoctype() throws Exception { 431 String xml = "<!DOCTYPE foo [" 432 + " <!-- ' -->" 433 + "]><foo>android</foo>"; 434 XmlPullParser parser = newPullParser(xml); 435 assertEquals(XmlPullParser.START_TAG, parser.next()); 436 assertEquals(XmlPullParser.TEXT, parser.next()); 437 assertEquals("android", parser.getText()); 438 assertEquals(XmlPullParser.END_TAG, parser.next()); 439 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 440 } 441 442 public void testDoctypeNameOnly() throws Exception { 443 String xml = "<!DOCTYPE foo><foo></foo>"; 444 XmlPullParser parser = newPullParser(xml); 445 assertEquals(XmlPullParser.START_TAG, parser.next()); 446 assertEquals("foo", parser.getName()); 447 assertEquals(XmlPullParser.END_TAG, parser.next()); 448 assertEquals("foo", parser.getName()); 449 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 450 } 451 452 public void testVeryLongEntities() throws Exception { 453 String a = repeat('a', READ_BUFFER_SIZE + 1); 454 String b = repeat('b', READ_BUFFER_SIZE + 1); 455 String c = repeat('c', READ_BUFFER_SIZE + 1); 456 457 String xml = "<!DOCTYPE foo [\n" 458 + " <!ENTITY " + a + " \"d &" + b + "; e\">" 459 + " <!ENTITY " + b + " \"f " + c + " g\">" 460 + "]>" 461 + "<foo>h &" + a + "; i</foo>"; 462 XmlPullParser parser = newPullParser(xml); 463 assertEquals(XmlPullParser.START_TAG, parser.next()); 464 assertEquals(XmlPullParser.TEXT, parser.next()); 465 assertEquals("h d f " + c + " g e i", parser.getText()); 466 assertEquals(XmlPullParser.END_TAG, parser.next()); 467 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 468 } 469 470 public void testManuallyRegisteredEntitiesWithDoctypeParsing() throws Exception { 471 String xml = "<foo>&a;</foo>"; 472 XmlPullParser parser = newPullParser(xml); 473 try { 474 parser.defineEntityReplacementText("a", "android"); 475 fail(); 476 } catch (UnsupportedOperationException expected) { 477 } catch (IllegalStateException expected) { 478 } 479 } 480 481 public void testDoctypeWithNextToken() throws Exception { 482 String xml = "<!DOCTYPE foo [<!ENTITY bb \"bar baz\">]><foo>a&bb;c</foo>"; 483 XmlPullParser parser = newPullParser(xml); 484 assertEquals(XmlPullParser.DOCDECL, parser.nextToken()); 485 assertEquals(" foo [<!ENTITY bb \"bar baz\">]", parser.getText()); 486 assertNull(parser.getName()); 487 assertEquals(XmlPullParser.START_TAG, parser.nextToken()); 488 assertEquals(XmlPullParser.TEXT, parser.next()); 489 assertEquals("abar bazc", parser.getText()); 490 assertEquals(XmlPullParser.END_TAG, parser.next()); 491 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 492 } 493 494 public void testDoctypeSpansBuffers() throws Exception { 495 char[] doctypeChars = new char[READ_BUFFER_SIZE + 1]; 496 Arrays.fill(doctypeChars, 'x'); 497 String doctypeBody = " foo [<!--" + new String(doctypeChars) + "-->]"; 498 String xml = "<!DOCTYPE" + doctypeBody + "><foo/>"; 499 XmlPullParser parser = newPullParser(xml); 500 assertEquals(XmlPullParser.DOCDECL, parser.nextToken()); 501 assertEquals(doctypeBody, parser.getText()); 502 assertEquals(XmlPullParser.START_TAG, parser.next()); 503 assertEquals(XmlPullParser.END_TAG, parser.next()); 504 assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); 505 } 506 507 public void testDoctypeInDocumentElement() throws Exception { 508 assertParseFailure("<foo><!DOCTYPE foo></foo>"); 509 } 510 511 public void testDoctypeAfterDocumentElement() throws Exception { 512 assertParseFailure("<foo/><!DOCTYPE foo>"); 513 } 514 515 private void assertParseFailure(String xml) throws Exception { 516 XmlPullParser parser = newPullParser(); 517 parser.setInput(new StringReader(xml)); 518 try { 519 while (parser.next() != XmlPullParser.END_DOCUMENT) { 520 } 521 fail(); 522 } catch (XmlPullParserException expected) { 523 } 524 } 525 526 private String repeat(char c, int length) { 527 char[] chars = new char[length]; 528 Arrays.fill(chars, c); 529 return new String(chars); 530 } 531 532 private XmlPullParser newPullParser(String xml) throws XmlPullParserException { 533 XmlPullParser result = newPullParser(); 534 result.setInput(new StringReader(xml)); 535 return result; 536 } 537 538 /** 539 * Creates a new pull parser. 540 */ 541 abstract XmlPullParser newPullParser() throws XmlPullParserException; 542} 543