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 17 package tests.xml; 18 19 import dalvik.annotation.KnownFailure; 20 import junit.framework.AssertionFailedError; 21 import junit.framework.TestCase; 22 import org.w3c.dom.Attr; 23 import org.w3c.dom.CDATASection; 24 import org.w3c.dom.Comment; 25 import org.w3c.dom.DOMException; 26 import org.w3c.dom.DOMImplementation; 27 import org.w3c.dom.Document; 28 import org.w3c.dom.DocumentType; 29 import org.w3c.dom.Element; 30 import org.w3c.dom.Entity; 31 import org.w3c.dom.EntityReference; 32 import org.w3c.dom.NamedNodeMap; 33 import org.w3c.dom.Node; 34 import org.w3c.dom.NodeList; 35 import org.w3c.dom.Notation; 36 import org.w3c.dom.ProcessingInstruction; 37 import org.w3c.dom.Text; 38 import org.w3c.dom.TypeInfo; 39 import org.w3c.dom.UserDataHandler; 40 import org.xml.sax.InputSource; 41 import org.xml.sax.SAXException; 42 43 import javax.xml.parsers.DocumentBuilder; 44 import javax.xml.parsers.DocumentBuilderFactory; 45 import javax.xml.transform.OutputKeys; 46 import javax.xml.transform.Transformer; 47 import javax.xml.transform.TransformerException; 48 import javax.xml.transform.TransformerFactory; 49 import javax.xml.transform.dom.DOMSource; 50 import javax.xml.transform.stream.StreamResult; 51 import java.io.File; 52 import java.io.FileWriter; 53 import java.io.IOException; 54 import java.io.StringReader; 55 import java.io.StringWriter; 56 import java.util.ArrayList; 57 import java.util.Arrays; 58 import java.util.HashSet; 59 import java.util.List; 60 import java.util.Set; 61 import java.util.regex.Matcher; 62 import java.util.regex.Pattern; 63 64 import static org.w3c.dom.UserDataHandler.NODE_ADOPTED; 65 import static org.w3c.dom.UserDataHandler.NODE_CLONED; 66 import static org.w3c.dom.UserDataHandler.NODE_IMPORTED; 67 import static org.w3c.dom.UserDataHandler.NODE_RENAMED; 68 69 /** 70 * Construct a DOM and then interrogate it. 71 */ 72 public class DomTest extends TestCase { 73 74 private Transformer transformer; 75 private DocumentBuilder builder; 76 private DOMImplementation domImplementation; 77 78 private final String xml 79 = "<!DOCTYPE menu [" 80 + " <!ENTITY sp \"Maple Syrup\">" 81 + " <!NOTATION png SYSTEM \"image/png\">" 82 + "]>" 83 + "<menu>\n" 84 + " <item xmlns=\"http://food\" xmlns:a=\"http://addons\">\n" 85 + " <name a:standard=\"strawberry\" deluxe=\"&sp;\">Waffles</name>\n" 86 + " <description xmlns=\"http://marketing\">Belgian<![CDATA[ waffles & strawberries (< 5g ]]>of fat)</description>\n" 87 + " <a:option>Whipped Cream</a:option>\n" 88 + " <a:option>&sp;</a:option>\n" 89 + " <?wafflemaker square shape?>\n" 90 + " <nutrition>\n" 91 + " <a:vitamins xmlns:a=\"http://usda\">\n" 92 + " <!-- add other vitamins? --> \n" 93 + " <a:vitaminc>60%</a:vitaminc>\n" 94 + " </a:vitamins>\n" 95 + " </nutrition>\n" 96 + " </item>\n" 97 + "</menu>"; 98 99 private Document document; 100 private DocumentType doctype; 101 private Entity sp; 102 private Notation png; 103 private Element menu; 104 private Element item; 105 private Attr itemXmlns; 106 private Attr itemXmlnsA; 107 private Element name; 108 private Attr standard; 109 private Attr deluxe; 110 private Text waffles; 111 private Element description; 112 private Text descriptionText1; 113 private CDATASection descriptionText2; 114 private Text descriptionText3; 115 private Element option1; 116 private Element option2; 117 private Node option2Reference; // resolved to Text on RI, an EntityReference on Dalvik 118 private ProcessingInstruction wafflemaker; 119 private Element nutrition; 120 private Element vitamins; 121 private Attr vitaminsXmlnsA; 122 private Comment comment; 123 private Element vitaminc; 124 private Text vitamincText; 125 private List<Node> allNodes; 126 127 @Override protected void setUp() throws Exception { 128 transformer = TransformerFactory.newInstance().newTransformer(); 129 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 130 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 131 factory.setNamespaceAware(true); 132 builder = factory.newDocumentBuilder(); 133 domImplementation = builder.getDOMImplementation(); 134 document = builder.parse(new InputSource(new StringReader(xml))); 135 136 // doctype nodes 137 doctype = document.getDoctype(); 138 if (doctype.getEntities() != null) { 139 sp = (Entity) doctype.getEntities().item(0); 140 } 141 if (doctype.getNotations() != null) { 142 png = (Notation) doctype.getNotations().item(0); 143 } 144 145 // document nodes 146 menu = document.getDocumentElement(); 147 item = (Element) menu.getChildNodes().item(1); 148 itemXmlns = item.getAttributeNode("xmlns"); 149 itemXmlnsA = item.getAttributeNode("xmlns:a"); 150 name = (Element) item.getChildNodes().item(1); 151 standard = name.getAttributeNode("a:standard"); 152 deluxe = name.getAttributeNode("deluxe"); 153 waffles = (Text) name.getChildNodes().item(0); 154 description = (Element) item.getChildNodes().item(3); 155 descriptionText1 = (Text) description.getChildNodes().item(0); 156 descriptionText2 = (CDATASection) description.getChildNodes().item(1); 157 descriptionText3 = (Text) description.getChildNodes().item(2); 158 option1 = (Element) item.getChildNodes().item(5); 159 option2 = (Element) item.getChildNodes().item(7); 160 option2Reference = option2.getChildNodes().item(0); 161 wafflemaker = (ProcessingInstruction) item.getChildNodes().item(9); 162 nutrition = (Element) item.getChildNodes().item(11); 163 vitamins = (Element) nutrition.getChildNodes().item(1); 164 vitaminsXmlnsA = vitamins.getAttributeNode("xmlns:a"); 165 comment = (Comment) vitamins.getChildNodes().item(1); 166 vitaminc = (Element) vitamins.getChildNodes().item(3); 167 vitamincText = (Text) vitaminc.getChildNodes().item(0); 168 169 allNodes = new ArrayList<Node>(); 170 171 if (sp != null) { 172 allNodes.add(sp); 173 } 174 if (png != null) { 175 allNodes.add(png); 176 } 177 178 allNodes.addAll(Arrays.asList(document, doctype, menu, item, itemXmlns, 179 itemXmlnsA, name, standard, deluxe, waffles, description, 180 descriptionText1, descriptionText2, descriptionText3, option1, 181 option2, option2Reference, wafflemaker, nutrition, vitamins, 182 vitaminsXmlnsA, comment, vitaminc, vitamincText)); 183 } 184 185 /** 186 * Android's parsed DOM doesn't include entity declarations. These nodes will 187 * only be tested for implementations that support them. 188 */ 189 @KnownFailure("Dalvik doesn't parse entity declarations") 190 public void testEntityDeclarations() { 191 assertNotNull("This implementation does not parse entity declarations", sp); 192 } 193 194 /** 195 * Android's parsed DOM doesn't include notations. These nodes will only be 196 * tested for implementations that support them. 197 */ 198 @KnownFailure("Dalvik doesn't parse notations") 199 public void testNotations() { 200 assertNotNull("This implementation does not parse notations", png); 201 } 202 203 public void testLookupNamespaceURIByPrefix() { 204 assertEquals(null, doctype.lookupNamespaceURI("a")); 205 if (sp != null) { 206 assertEquals(null, sp.lookupNamespaceURI("a")); 207 } 208 if (png != null) { 209 assertEquals(null, png.lookupNamespaceURI("a")); 210 } 211 assertEquals(null, document.lookupNamespaceURI("a")); 212 assertEquals(null, menu.lookupNamespaceURI("a")); 213 assertEquals("http://addons", item.lookupNamespaceURI("a")); 214 assertEquals("http://addons", itemXmlns.lookupNamespaceURI("a")); 215 assertEquals("http://addons", itemXmlnsA.lookupNamespaceURI("a")); 216 assertEquals("http://addons", name.lookupNamespaceURI("a")); 217 assertEquals("http://addons", standard.lookupNamespaceURI("a")); 218 assertEquals("http://addons", deluxe.lookupNamespaceURI("a")); 219 assertEquals("http://addons", description.lookupNamespaceURI("a")); 220 assertEquals("http://addons", descriptionText1.lookupNamespaceURI("a")); 221 assertEquals("http://addons", descriptionText2.lookupNamespaceURI("a")); 222 assertEquals("http://addons", descriptionText3.lookupNamespaceURI("a")); 223 assertEquals("http://addons", option1.lookupNamespaceURI("a")); 224 assertEquals("http://addons", option2.lookupNamespaceURI("a")); 225 assertEquals("http://addons", option2Reference.lookupNamespaceURI("a")); 226 assertEquals("http://addons", wafflemaker.lookupNamespaceURI("a")); 227 assertEquals("http://addons", nutrition.lookupNamespaceURI("a")); 228 assertEquals("http://usda", vitamins.lookupNamespaceURI("a")); 229 assertEquals("http://usda", vitaminsXmlnsA.lookupNamespaceURI("a")); 230 assertEquals("http://usda", comment.lookupNamespaceURI("a")); 231 assertEquals("http://usda", vitaminc.lookupNamespaceURI("a")); 232 assertEquals("http://usda", vitamincText.lookupNamespaceURI("a")); 233 } 234 235 public void testLookupNamespaceURIWithNullPrefix() { 236 assertEquals(null, document.lookupNamespaceURI(null)); 237 assertEquals(null, doctype.lookupNamespaceURI(null)); 238 if (sp != null) { 239 assertEquals(null, sp.lookupNamespaceURI(null)); 240 } 241 if (png != null) { 242 assertEquals(null, png.lookupNamespaceURI(null)); 243 } 244 assertEquals(null, menu.lookupNamespaceURI(null)); 245 assertEquals("http://food", item.lookupNamespaceURI(null)); 246 assertEquals("http://food", itemXmlns.lookupNamespaceURI(null)); 247 assertEquals("http://food", itemXmlnsA.lookupNamespaceURI(null)); 248 assertEquals("http://food", name.lookupNamespaceURI(null)); 249 assertEquals("http://food", standard.lookupNamespaceURI(null)); 250 assertEquals("http://food", deluxe.lookupNamespaceURI(null)); 251 assertEquals("http://marketing", description.lookupNamespaceURI(null)); 252 assertEquals("http://marketing", descriptionText1.lookupNamespaceURI(null)); 253 assertEquals("http://marketing", descriptionText2.lookupNamespaceURI(null)); 254 assertEquals("http://marketing", descriptionText3.lookupNamespaceURI(null)); 255 assertEquals("http://food", option1.lookupNamespaceURI(null)); 256 assertEquals("http://food", option2.lookupNamespaceURI(null)); 257 assertEquals("http://food", option2Reference.lookupNamespaceURI(null)); 258 assertEquals("http://food", wafflemaker.lookupNamespaceURI(null)); 259 assertEquals("http://food", nutrition.lookupNamespaceURI(null)); 260 assertEquals("http://food", vitamins.lookupNamespaceURI(null)); 261 assertEquals("http://food", vitaminsXmlnsA.lookupNamespaceURI(null)); 262 assertEquals("http://food", comment.lookupNamespaceURI(null)); 263 assertEquals("http://food", vitaminc.lookupNamespaceURI(null)); 264 assertEquals("http://food", vitamincText.lookupNamespaceURI(null)); 265 } 266 267 public void testLookupNamespaceURIWithXmlnsPrefix() { 268 for (Node node : allNodes) { 269 assertEquals(null, node.lookupNamespaceURI("xmlns")); 270 } 271 } 272 273 public void testLookupPrefixWithShadowedUri() { 274 assertEquals(null, document.lookupPrefix("http://addons")); 275 assertEquals(null, doctype.lookupPrefix("http://addons")); 276 if (sp != null) { 277 assertEquals(null, sp.lookupPrefix("http://addons")); 278 } 279 if (png != null) { 280 assertEquals(null, png.lookupPrefix("http://addons")); 281 } 282 assertEquals(null, menu.lookupPrefix("http://addons")); 283 assertEquals("a", item.lookupPrefix("http://addons")); 284 assertEquals("a", itemXmlns.lookupPrefix("http://addons")); 285 assertEquals("a", itemXmlnsA.lookupPrefix("http://addons")); 286 assertEquals("a", name.lookupPrefix("http://addons")); 287 assertEquals("a", standard.lookupPrefix("http://addons")); 288 assertEquals("a", deluxe.lookupPrefix("http://addons")); 289 assertEquals("a", description.lookupPrefix("http://addons")); 290 assertEquals("a", descriptionText1.lookupPrefix("http://addons")); 291 assertEquals("a", descriptionText2.lookupPrefix("http://addons")); 292 assertEquals("a", descriptionText3.lookupPrefix("http://addons")); 293 assertEquals("a", option1.lookupPrefix("http://addons")); 294 assertEquals("a", option2.lookupPrefix("http://addons")); 295 assertEquals("a", option2Reference.lookupPrefix("http://addons")); 296 assertEquals("a", wafflemaker.lookupPrefix("http://addons")); 297 assertEquals("a", nutrition.lookupPrefix("http://addons")); 298 assertEquals(null, vitamins.lookupPrefix("http://addons")); 299 assertEquals(null, vitaminsXmlnsA.lookupPrefix("http://addons")); 300 assertEquals(null, comment.lookupPrefix("http://addons")); 301 assertEquals(null, vitaminc.lookupPrefix("http://addons")); 302 assertEquals(null, vitamincText.lookupPrefix("http://addons")); 303 } 304 305 public void testLookupPrefixWithUnusedUri() { 306 for (Node node : allNodes) { 307 assertEquals(null, node.lookupPrefix("http://unused")); 308 } 309 } 310 311 public void testLookupPrefixWithNullUri() { 312 for (Node node : allNodes) { 313 assertEquals(null, node.lookupPrefix(null)); 314 } 315 } 316 317 public void testLookupPrefixWithShadowingUri() { 318 assertEquals(null, document.lookupPrefix("http://usda")); 319 assertEquals(null, doctype.lookupPrefix("http://usda")); 320 if (sp != null) { 321 assertEquals(null, sp.lookupPrefix("http://usda")); 322 } 323 if (png != null) { 324 assertEquals(null, png.lookupPrefix("http://usda")); 325 } 326 assertEquals(null, menu.lookupPrefix("http://usda")); 327 assertEquals(null, item.lookupPrefix("http://usda")); 328 assertEquals(null, itemXmlns.lookupPrefix("http://usda")); 329 assertEquals(null, itemXmlnsA.lookupPrefix("http://usda")); 330 assertEquals(null, name.lookupPrefix("http://usda")); 331 assertEquals(null, standard.lookupPrefix("http://usda")); 332 assertEquals(null, deluxe.lookupPrefix("http://usda")); 333 assertEquals(null, description.lookupPrefix("http://usda")); 334 assertEquals(null, descriptionText1.lookupPrefix("http://usda")); 335 assertEquals(null, descriptionText2.lookupPrefix("http://usda")); 336 assertEquals(null, descriptionText3.lookupPrefix("http://usda")); 337 assertEquals(null, option1.lookupPrefix("http://usda")); 338 assertEquals(null, option2.lookupPrefix("http://usda")); 339 assertEquals(null, option2Reference.lookupPrefix("http://usda")); 340 assertEquals(null, wafflemaker.lookupPrefix("http://usda")); 341 assertEquals(null, nutrition.lookupPrefix("http://usda")); 342 assertEquals("a", vitamins.lookupPrefix("http://usda")); 343 assertEquals("a", vitaminsXmlnsA.lookupPrefix("http://usda")); 344 assertEquals("a", comment.lookupPrefix("http://usda")); 345 assertEquals("a", vitaminc.lookupPrefix("http://usda")); 346 assertEquals("a", vitamincText.lookupPrefix("http://usda")); 347 } 348 349 public void testIsDefaultNamespace() { 350 assertFalse(document.isDefaultNamespace("http://food")); 351 assertFalse(doctype.isDefaultNamespace("http://food")); 352 if (sp != null) { 353 assertFalse(sp.isDefaultNamespace("http://food")); 354 } 355 if (png != null) { 356 assertFalse(png.isDefaultNamespace("http://food")); 357 } 358 assertFalse(menu.isDefaultNamespace("http://food")); 359 assertTrue(item.isDefaultNamespace("http://food")); 360 assertTrue(itemXmlns.isDefaultNamespace("http://food")); 361 assertTrue(itemXmlnsA.isDefaultNamespace("http://food")); 362 assertTrue(name.isDefaultNamespace("http://food")); 363 assertTrue(standard.isDefaultNamespace("http://food")); 364 assertTrue(deluxe.isDefaultNamespace("http://food")); 365 assertFalse(description.isDefaultNamespace("http://food")); 366 assertFalse(descriptionText1.isDefaultNamespace("http://food")); 367 assertFalse(descriptionText2.isDefaultNamespace("http://food")); 368 assertFalse(descriptionText3.isDefaultNamespace("http://food")); 369 assertTrue(option1.isDefaultNamespace("http://food")); 370 assertTrue(option2.isDefaultNamespace("http://food")); 371 assertTrue(option2Reference.isDefaultNamespace("http://food")); 372 assertTrue(wafflemaker.isDefaultNamespace("http://food")); 373 assertTrue(nutrition.isDefaultNamespace("http://food")); 374 assertTrue(vitamins.isDefaultNamespace("http://food")); 375 assertTrue(vitaminsXmlnsA.isDefaultNamespace("http://food")); 376 assertTrue(comment.isDefaultNamespace("http://food")); 377 assertTrue(vitaminc.isDefaultNamespace("http://food")); 378 assertTrue(vitamincText.isDefaultNamespace("http://food")); 379 } 380 381 /** 382 * Xerces fails this test. It returns false always for entity, notation, 383 * document fragment and document type nodes. This contradicts its own 384 * behaviour on lookupNamespaceURI(null). 385 */ 386 public void testIsDefaultNamespaceNull_XercesBugs() { 387 String message = "isDefaultNamespace() should be consistent with lookupNamespaceURI(null)"; 388 assertTrue(message, doctype.isDefaultNamespace(null)); 389 if (sp != null) { 390 assertTrue(message, sp.isDefaultNamespace(null)); 391 } 392 if (png != null) { 393 assertTrue(message, png.isDefaultNamespace(null)); 394 } 395 } 396 397 public void testIsDefaultNamespaceNull() { 398 assertTrue(document.isDefaultNamespace(null)); 399 assertTrue(menu.isDefaultNamespace(null)); 400 assertFalse(item.isDefaultNamespace(null)); 401 assertFalse(itemXmlns.isDefaultNamespace(null)); 402 assertFalse(itemXmlnsA.isDefaultNamespace(null)); 403 assertFalse(name.isDefaultNamespace(null)); 404 assertFalse(standard.isDefaultNamespace(null)); 405 assertFalse(deluxe.isDefaultNamespace(null)); 406 assertFalse(description.isDefaultNamespace(null)); 407 assertFalse(descriptionText1.isDefaultNamespace(null)); 408 assertFalse(descriptionText2.isDefaultNamespace(null)); 409 assertFalse(descriptionText3.isDefaultNamespace(null)); 410 assertFalse(option1.isDefaultNamespace(null)); 411 assertFalse(option2.isDefaultNamespace(null)); 412 assertFalse(option2Reference.isDefaultNamespace(null)); 413 assertFalse(wafflemaker.isDefaultNamespace(null)); 414 assertFalse(nutrition.isDefaultNamespace(null)); 415 assertFalse(vitamins.isDefaultNamespace(null)); 416 assertFalse(vitaminsXmlnsA.isDefaultNamespace(null)); 417 assertFalse(comment.isDefaultNamespace(null)); 418 assertFalse(vitaminc.isDefaultNamespace(null)); 419 assertFalse(vitamincText.isDefaultNamespace(null)); 420 } 421 422 public void testDoctypeSetTextContent() throws TransformerException { 423 String original = domToString(document); 424 doctype.setTextContent("foobar"); // strangely, this is specified to no-op 425 assertEquals(original, domToString(document)); 426 } 427 428 public void testDocumentSetTextContent() throws TransformerException { 429 String original = domToString(document); 430 document.setTextContent("foobar"); // strangely, this is specified to no-op 431 assertEquals(original, domToString(document)); 432 } 433 434 public void testElementSetTextContent() throws TransformerException { 435 String original = domToString(document); 436 nutrition.setTextContent("foobar"); 437 String expected = original.replaceFirst( 438 "(?s)<nutrition>.*</nutrition>", "<nutrition>foobar</nutrition>"); 439 assertEquals(expected, domToString(document)); 440 } 441 442 public void testEntitySetTextContent() throws TransformerException { 443 if (sp == null) { 444 return; 445 } 446 try { 447 sp.setTextContent("foobar"); 448 fail(); // is this implementation-specific behaviour? 449 } catch (DOMException e) { 450 } 451 } 452 453 public void testNotationSetTextContent() throws TransformerException { 454 if (png == null) { 455 return; 456 } 457 String original = domToString(document); 458 png.setTextContent("foobar"); 459 String expected = original.replace("image/png", "foobar"); 460 assertEquals(expected, domToString(document)); 461 } 462 463 /** 464 * Tests setTextContent on entity references. Although the other tests can 465 * act on a parsed DOM, this needs to use a programmatically constructed DOM 466 * because the parser may have replaced the entity reference with the 467 * corresponding text. 468 */ 469 public void testEntityReferenceSetTextContent() throws TransformerException { 470 document = builder.newDocument(); 471 Element root = document.createElement("menu"); 472 document.appendChild(root); 473 474 EntityReference entityReference = document.createEntityReference("sp"); 475 root.appendChild(entityReference); 476 477 try { 478 entityReference.setTextContent("Lite Syrup"); 479 fail(); 480 } catch (DOMException e) { 481 } 482 } 483 484 public void testAttributeSetTextContent() throws TransformerException { 485 String original = domToString(document); 486 standard.setTextContent("foobar"); 487 String expected = original.replace("standard=\"strawberry\"", "standard=\"foobar\""); 488 assertEquals(expected, domToString(document)); 489 } 490 491 public void testTextSetTextContent() throws TransformerException { 492 String original = domToString(document); 493 descriptionText1.setTextContent("foobar"); 494 String expected = original.replace(">Belgian<!", ">foobar<!"); 495 assertEquals(expected, domToString(document)); 496 } 497 498 public void testCdataSetTextContent() throws TransformerException { 499 String original = domToString(document); 500 descriptionText2.setTextContent("foobar"); 501 String expected = original.replace( 502 " waffles & strawberries (< 5g ", "foobar"); 503 assertEquals(expected, domToString(document)); 504 } 505 506 public void testProcessingInstructionSetTextContent() throws TransformerException { 507 String original = domToString(document); 508 wafflemaker.setTextContent("foobar"); 509 String expected = original.replace(" square shape?>", " foobar?>"); 510 assertEquals(expected, domToString(document)); 511 } 512 513 public void testCommentSetTextContent() throws TransformerException { 514 String original = domToString(document); 515 comment.setTextContent("foobar"); 516 String expected = original.replace("-- add other vitamins? --", "--foobar--"); 517 assertEquals(expected, domToString(document)); 518 } 519 520 public void testCoreFeature() { 521 assertFeature("Core", null); 522 assertFeature("Core", ""); 523 assertFeature("Core", "1.0"); 524 assertFeature("Core", "2.0"); 525 assertFeature("Core", "3.0"); 526 assertFeature("CORE", "3.0"); 527 assertFeature("+Core", "3.0"); 528 assertNoFeature("Core", "4.0"); 529 } 530 531 public void testXmlFeature() { 532 assertFeature("XML", null); 533 assertFeature("XML", ""); 534 assertFeature("XML", "1.0"); 535 assertFeature("XML", "2.0"); 536 assertFeature("XML", "3.0"); 537 assertFeature("Xml", "3.0"); 538 assertFeature("+XML", "3.0"); 539 assertNoFeature("XML", "4.0"); 540 } 541 542 /** 543 * The RI fails this test. 544 * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#Document3-version 545 */ 546 public void testXmlVersionFeature() { 547 assertFeature("XMLVersion", null); 548 assertFeature("XMLVersion", ""); 549 assertFeature("XMLVersion", "1.0"); 550 assertFeature("XMLVersion", "1.1"); 551 assertFeature("XMLVERSION", "1.1"); 552 assertFeature("+XMLVersion", "1.1"); 553 assertNoFeature("XMLVersion", "1.2"); 554 assertNoFeature("XMLVersion", "2.0"); 555 assertNoFeature("XMLVersion", "2.0"); 556 } 557 558 @KnownFailure("Dalvik doesn't support load/save") 559 public void testLoadSaveFeature() { 560 assertFeature("LS", "3.0"); 561 } 562 563 @KnownFailure("Dalvik doesn't support the element traversal feature") 564 public void testElementTraversalFeature() { 565 assertFeature("ElementTraversal", "1.0"); 566 } 567 568 private void assertFeature(String feature, String version) { 569 String message = "This implementation is expected to support " 570 + feature + " v. " + version + " but does not."; 571 assertTrue(message, domImplementation.hasFeature(feature, version)); 572 assertNotNull(message, domImplementation.getFeature(feature, version)); 573 } 574 575 private void assertNoFeature(String feature, String version) { 576 assertFalse(domImplementation.hasFeature(feature, version)); 577 assertNull(domImplementation.getFeature(feature, version)); 578 } 579 580 public void testIsSupported() { 581 // we don't independently test the features; instead just assume the 582 // implementation calls through to hasFeature (as tested above) 583 for (Node node : allNodes) { 584 assertTrue(node.isSupported("XML", null)); 585 assertTrue(node.isSupported("XML", "3.0")); 586 assertFalse(node.isSupported("foo", null)); 587 assertFalse(node.isSupported("foo", "bar")); 588 } 589 } 590 591 public void testGetFeature() { 592 // we don't independently test the features; instead just assume the 593 // implementation calls through to hasFeature (as tested above) 594 for (Node node : allNodes) { 595 assertSame(node, node.getFeature("XML", null)); 596 assertSame(node, node.getFeature("XML", "3.0")); 597 assertNull(node.getFeature("foo", null)); 598 assertNull(node.getFeature("foo", "bar")); 599 } 600 } 601 602 public void testNodeEqualsPositive() throws Exception { 603 DomTest copy = new DomTest(); 604 copy.setUp(); 605 606 for (int i = 0; i < allNodes.size(); i++) { 607 Node a = allNodes.get(i); 608 Node b = copy.allNodes.get(i); 609 assertTrue(a.isEqualNode(b)); 610 } 611 } 612 613 public void testNodeEqualsNegative() throws Exception { 614 for (Node a : allNodes) { 615 for (Node b : allNodes) { 616 assertEquals(a == b, a.isEqualNode(b)); 617 } 618 } 619 } 620 621 public void testNodeEqualsNegativeRecursive() throws Exception { 622 DomTest copy = new DomTest(); 623 copy.setUp(); 624 copy.vitaminc.setTextContent("55%"); 625 626 // changing anything about a node should break equality for all parents 627 assertFalse(document.isEqualNode(copy.document)); 628 assertFalse(menu.isEqualNode(copy.menu)); 629 assertFalse(item.isEqualNode(copy.item)); 630 assertFalse(nutrition.isEqualNode(copy.nutrition)); 631 assertFalse(vitamins.isEqualNode(copy.vitamins)); 632 assertFalse(vitaminc.isEqualNode(copy.vitaminc)); 633 634 // but not siblings 635 assertTrue(doctype.isEqualNode(copy.doctype)); 636 assertTrue(description.isEqualNode(copy.description)); 637 assertTrue(option1.isEqualNode(copy.option1)); 638 } 639 640 public void testNodeEqualsNull() { 641 for (Node node : allNodes) { 642 try { 643 node.isEqualNode(null); 644 fail(); 645 } catch (NullPointerException e) { 646 } 647 } 648 } 649 650 public void testIsElementContentWhitespaceWithoutDeclaration() throws Exception { 651 String xml = "<menu> <item/> </menu>"; 652 653 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 654 Text text = (Text) factory.newDocumentBuilder() 655 .parse(new InputSource(new StringReader(xml))) 656 .getDocumentElement().getChildNodes().item(0); 657 assertFalse(text.isElementContentWhitespace()); 658 } 659 660 @KnownFailure("Dalvik doesn't recognize element content whitespace") 661 public void testIsElementContentWhitespaceWithDeclaration() throws Exception { 662 String xml = "<!DOCTYPE menu [\n" 663 + " <!ELEMENT menu (item)*>\n" 664 + " <!ELEMENT item (#PCDATA)>\n" 665 + "]><menu> <item/> </menu>"; 666 667 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 668 Text text = (Text) factory.newDocumentBuilder() 669 .parse(new InputSource(new StringReader(xml))) 670 .getDocumentElement().getChildNodes().item(0); 671 assertTrue("This implementation does not recognize element content whitespace", 672 text.isElementContentWhitespace()); 673 } 674 675 public void testGetWholeTextFirst() { 676 assertEquals("Belgian waffles & strawberries (< 5g of fat)", 677 descriptionText1.getWholeText()); 678 } 679 680 public void testGetWholeTextMiddle() { 681 assertEquals("This implementation doesn't include preceding nodes in getWholeText()", 682 "Belgian waffles & strawberries (< 5g of fat)", descriptionText2.getWholeText()); 683 } 684 685 public void testGetWholeTextLast() { 686 assertEquals("This implementation doesn't include preceding nodes in getWholeText()", 687 "Belgian waffles & strawberries (< 5g of fat)", descriptionText3.getWholeText()); 688 } 689 690 public void testGetWholeTextOnly() { 691 assertEquals("60%", vitamincText.getWholeText()); 692 } 693 694 @KnownFailure("Dalvik doesn't resolve entity references") 695 public void testGetWholeTextWithEntityReference() { 696 EntityReference spReference = document.createEntityReference("sp"); 697 description.insertBefore(spReference, descriptionText2); 698 699 assertEquals("This implementation doesn't resolve entity references in getWholeText()", 700 "BelgianMaple Syrup waffles & strawberries (< 5g of fat)", 701 descriptionText1.getWholeText()); 702 } 703 704 public void testReplaceWholeTextFirst() throws TransformerException { 705 String original = domToString(document); 706 Text replacement = descriptionText1.replaceWholeText("Eggos"); 707 assertSame(descriptionText1, replacement); 708 String expected = original.replace( 709 "Belgian<![CDATA[ waffles & strawberries (< 5g ]]>of fat)", "Eggos"); 710 assertEquals(expected, domToString(document)); 711 } 712 713 public void testReplaceWholeTextMiddle() throws TransformerException { 714 String original = domToString(document); 715 Text replacement = descriptionText2.replaceWholeText("Eggos"); 716 assertSame(descriptionText2, replacement); 717 String expected = original.replace( 718 "Belgian<![CDATA[ waffles & strawberries (< 5g ]]>of fat)", "<![CDATA[Eggos]]>"); 719 assertEquals("This implementation doesn't remove preceding nodes in replaceWholeText()", 720 expected, domToString(document)); 721 } 722 723 public void testReplaceWholeTextLast() throws TransformerException { 724 String original = domToString(document); 725 Text replacement = descriptionText3.replaceWholeText("Eggos"); 726 assertSame(descriptionText3, replacement); 727 String expected = original.replace( 728 "Belgian<![CDATA[ waffles & strawberries (< 5g ]]>of fat)", "Eggos"); 729 assertEquals("This implementation doesn't remove preceding nodes in replaceWholeText()", 730 expected, domToString(document)); 731 } 732 733 public void testReplaceWholeTextOnly() throws TransformerException { 734 String original = domToString(document); 735 Text replacement = vitamincText.replaceWholeText("70%"); 736 assertEquals(Node.TEXT_NODE, replacement.getNodeType()); 737 assertSame(vitamincText, replacement); 738 String expected = original.replace("60%", "70%"); 739 assertEquals(expected, domToString(document)); 740 } 741 742 public void testReplaceWholeTextFirstWithNull() throws TransformerException { 743 String original = domToString(document); 744 assertNull(descriptionText1.replaceWholeText(null)); 745 String expected = original.replaceFirst(">.*</description>", "/>"); 746 assertEquals("This implementation doesn't remove adjacent nodes in replaceWholeText(null)", 747 expected, domToString(document)); 748 } 749 750 public void testReplaceWholeTextMiddleWithNull() throws TransformerException { 751 String original = domToString(document); 752 assertNull(descriptionText2.replaceWholeText(null)); 753 String expected = original.replaceFirst(">.*</description>", "/>"); 754 assertEquals("This implementation doesn't remove adjacent nodes in replaceWholeText(null)", 755 expected, domToString(document)); 756 } 757 758 public void testReplaceWholeTextLastWithNull() throws TransformerException { 759 String original = domToString(document); 760 assertNull(descriptionText3.replaceWholeText(null)); 761 String expected = original.replaceFirst(">.*</description>", "/>"); 762 assertEquals("This implementation doesn't remove adjacent nodes in replaceWholeText(null)", 763 expected, domToString(document)); 764 } 765 766 public void testReplaceWholeTextFirstWithEmptyString() throws TransformerException { 767 String original = domToString(document); 768 assertNull(descriptionText1.replaceWholeText("")); 769 String expected = original.replaceFirst(">.*</description>", "/>"); 770 assertEquals("This implementation doesn't remove adjacent nodes in replaceWholeText(null)", 771 expected, domToString(document)); 772 } 773 774 public void testReplaceWholeTextOnlyWithEmptyString() throws TransformerException { 775 String original = domToString(document); 776 assertNull(vitamincText.replaceWholeText("")); 777 String expected = original.replaceFirst(">.*</a:vitaminc>", "/>"); 778 assertEquals(expected, domToString(document)); 779 } 780 781 public void testUserDataAttachments() { 782 Object a = new Object(); 783 Object b = new Object(); 784 for (Node node : allNodes) { 785 node.setUserData("a", a, null); 786 node.setUserData("b", b, null); 787 } 788 for (Node node : allNodes) { 789 assertSame(a, node.getUserData("a")); 790 assertSame(b, node.getUserData("b")); 791 assertEquals(null, node.getUserData("c")); 792 assertEquals(null, node.getUserData("A")); 793 } 794 } 795 796 public void testUserDataRejectsNullKey() { 797 try { 798 menu.setUserData(null, "apple", null); 799 fail(); 800 } catch (NullPointerException e) { 801 } 802 try { 803 menu.getUserData(null); 804 fail(); 805 } catch (NullPointerException e) { 806 } 807 } 808 809 /** 810 * A shallow clone requires cloning the attributes but not the child nodes. 811 */ 812 public void testUserDataHandlerNotifiedOfShallowClones() { 813 RecordingHandler handler = new RecordingHandler(); 814 name.setUserData("a", "apple", handler); 815 name.setUserData("b", "banana", handler); 816 standard.setUserData("c", "cat", handler); 817 waffles.setUserData("d", "dog", handler); 818 819 Element clonedName = (Element) name.cloneNode(false); 820 Attr clonedStandard = clonedName.getAttributeNode("a:standard"); 821 822 Set<String> expected = new HashSet<String>(); 823 expected.add(notification(NODE_CLONED, "a", "apple", name, clonedName)); 824 expected.add(notification(NODE_CLONED, "b", "banana", name, clonedName)); 825 expected.add(notification(NODE_CLONED, "c", "cat", standard, clonedStandard)); 826 assertEquals(expected, handler.calls); 827 } 828 829 /** 830 * A deep clone requires cloning both the attributes and the child nodes. 831 */ 832 public void testUserDataHandlerNotifiedOfDeepClones() { 833 RecordingHandler handler = new RecordingHandler(); 834 name.setUserData("a", "apple", handler); 835 name.setUserData("b", "banana", handler); 836 standard.setUserData("c", "cat", handler); 837 waffles.setUserData("d", "dog", handler); 838 839 Element clonedName = (Element) name.cloneNode(true); 840 Attr clonedStandard = clonedName.getAttributeNode("a:standard"); 841 Text clonedWaffles = (Text) clonedName.getChildNodes().item(0); 842 843 Set<String> expected = new HashSet<String>(); 844 expected.add(notification(NODE_CLONED, "a", "apple", name, clonedName)); 845 expected.add(notification(NODE_CLONED, "b", "banana", name, clonedName)); 846 expected.add(notification(NODE_CLONED, "c", "cat", standard, clonedStandard)); 847 expected.add(notification(NODE_CLONED, "d", "dog", waffles, clonedWaffles)); 848 assertEquals(expected, handler.calls); 849 } 850 851 /** 852 * A shallow import requires importing the attributes but not the child 853 * nodes. 854 */ 855 public void testUserDataHandlerNotifiedOfShallowImports() { 856 RecordingHandler handler = new RecordingHandler(); 857 name.setUserData("a", "apple", handler); 858 name.setUserData("b", "banana", handler); 859 standard.setUserData("c", "cat", handler); 860 waffles.setUserData("d", "dog", handler); 861 862 Document newDocument = builder.newDocument(); 863 Element importedName = (Element) newDocument.importNode(name, false); 864 Attr importedStandard = importedName.getAttributeNode("a:standard"); 865 866 Set<String> expected = new HashSet<String>(); 867 expected.add(notification(NODE_IMPORTED, "a", "apple", name, importedName)); 868 expected.add(notification(NODE_IMPORTED, "b", "banana", name, importedName)); 869 expected.add(notification(NODE_IMPORTED, "c", "cat", standard, importedStandard)); 870 assertEquals(expected, handler.calls); 871 } 872 873 /** 874 * A deep import requires cloning both the attributes and the child nodes. 875 */ 876 public void testUserDataHandlerNotifiedOfDeepImports() { 877 RecordingHandler handler = new RecordingHandler(); 878 name.setUserData("a", "apple", handler); 879 name.setUserData("b", "banana", handler); 880 standard.setUserData("c", "cat", handler); 881 waffles.setUserData("d", "dog", handler); 882 883 Document newDocument = builder.newDocument(); 884 Element importedName = (Element) newDocument.importNode(name, true); 885 Attr importedStandard = importedName.getAttributeNode("a:standard"); 886 Text importedWaffles = (Text) importedName.getChildNodes().item(0); 887 888 Set<String> expected = new HashSet<String>(); 889 expected.add(notification(NODE_IMPORTED, "a", "apple", name, importedName)); 890 expected.add(notification(NODE_IMPORTED, "b", "banana", name, importedName)); 891 expected.add(notification(NODE_IMPORTED, "c", "cat", standard, importedStandard)); 892 expected.add(notification(NODE_IMPORTED, "d", "dog", waffles, importedWaffles)); 893 assertEquals(expected, handler.calls); 894 } 895 896 public void testImportNodeDeep() throws TransformerException { 897 String original = domToStringStripElementWhitespace(document); 898 899 Document newDocument = builder.newDocument(); 900 Element importedItem = (Element) newDocument.importNode(item, true); 901 assertDetached(item.getParentNode(), importedItem); 902 903 newDocument.appendChild(importedItem); 904 String expected = original.replaceAll("</?menu>", ""); 905 assertEquals(expected, domToStringStripElementWhitespace(newDocument)); 906 } 907 908 public void testImportNodeShallow() throws TransformerException { 909 Document newDocument = builder.newDocument(); 910 Element importedItem = (Element) newDocument.importNode(item, false); 911 assertDetached(item.getParentNode(), importedItem); 912 913 newDocument.appendChild(importedItem); 914 assertEquals("<item xmlns=\"http://food\" xmlns:a=\"http://addons\"/>", 915 domToString(newDocument)); 916 } 917 918 public void testNodeAdoption() throws Exception { 919 for (Node node : allNodes) { 920 if (node == document || node == doctype || node == sp || node == png) { 921 assertNotAdoptable(node); 922 } else { 923 adoptAndCheck(node); 924 } 925 } 926 } 927 928 private void assertNotAdoptable(Node node) { 929 try { 930 builder.newDocument().adoptNode(node); 931 fail(); 932 } catch (DOMException e) { 933 } 934 } 935 936 /** 937 * Adopts the node into another document, then adopts the root element, and 938 * then attaches the adopted node in the proper place. The net result should 939 * be that the document's entire contents have moved to another document. 940 */ 941 private void adoptAndCheck(Node node) throws Exception { 942 String original = domToString(document); 943 Document newDocument = builder.newDocument(); 944 945 // remember where to insert the node in the new document 946 boolean isAttribute = node.getNodeType() == Node.ATTRIBUTE_NODE; 947 Node parent = isAttribute 948 ? ((Attr) node).getOwnerElement() : node.getParentNode(); 949 Node nextSibling = node.getNextSibling(); 950 951 // move the node and make sure it was detached 952 assertSame(node, newDocument.adoptNode(node)); 953 assertDetached(parent, node); 954 955 // move the rest of the document and wire the adopted back into place 956 assertSame(menu, newDocument.adoptNode(menu)); 957 newDocument.appendChild(menu); 958 if (isAttribute) { 959 ((Element) parent).setAttributeNodeNS((Attr) node); 960 } else if (nextSibling != null) { 961 parent.insertBefore(node, nextSibling); 962 } else if (parent != document) { 963 parent.appendChild(node); 964 } 965 966 assertEquals(original, domToString(newDocument)); 967 document = newDocument; 968 } 969 970 private void assertDetached(Node formerParent, Node node) { 971 assertNull(node.getParentNode()); 972 NodeList children = formerParent.getChildNodes(); 973 for (int i = 0; i < children.getLength(); i++) { 974 assertTrue(children.item(i) != node); 975 } 976 if (node.getNodeType() == Node.ATTRIBUTE_NODE) { 977 assertNull(((Attr) node).getOwnerElement()); 978 NamedNodeMap attributes = formerParent.getAttributes(); 979 for (int i = 0; i < attributes.getLength(); i++) { 980 assertTrue(attributes.item(i) != node); 981 } 982 } 983 } 984 985 public void testAdoptionImmediatelyAfterParsing() throws Exception { 986 Document newDocument = builder.newDocument(); 987 try { 988 assertSame(name, newDocument.adoptNode(name)); 989 assertSame(newDocument, name.getOwnerDocument()); 990 assertSame(newDocument, standard.getOwnerDocument()); 991 assertSame(newDocument, waffles.getOwnerDocument()); 992 } catch (Throwable e) { 993 AssertionFailedError failure = new AssertionFailedError( 994 "This implementation fails to adopt nodes before the " 995 + "document has been traversed"); 996 failure.initCause(e); 997 throw failure; 998 } 999 } 1000 1001 /** 1002 * There should be notifications for adopted node itself but none of its 1003 * children. The DOM spec is vague on this, so we're consistent with the RI. 1004 */ 1005 public void testUserDataHandlerNotifiedOfOnlyShallowAdoptions() throws Exception { 1006 /* 1007 * Force a traversal of the document, otherwise this test may fail for 1008 * an unrelated reason on version 5 of the RI. That behavior is 1009 * exercised by testAdoptionImmediatelyAfterParsing(). 1010 */ 1011 domToString(document); 1012 1013 RecordingHandler handler = new RecordingHandler(); 1014 name.setUserData("a", "apple", handler); 1015 name.setUserData("b", "banana", handler); 1016 standard.setUserData("c", "cat", handler); 1017 waffles.setUserData("d", "dog", handler); 1018 1019 Document newDocument = builder.newDocument(); 1020 assertSame(name, newDocument.adoptNode(name)); 1021 assertSame(newDocument, name.getOwnerDocument()); 1022 assertSame(newDocument, standard.getOwnerDocument()); 1023 assertSame(newDocument, waffles.getOwnerDocument()); 1024 1025 Set<String> expected = new HashSet<String>(); 1026 expected.add(notification(NODE_ADOPTED, "a", "apple", name, null)); 1027 expected.add(notification(NODE_ADOPTED, "b", "banana", name, null)); 1028 assertEquals(expected, handler.calls); 1029 } 1030 1031 public void testBaseUriRelativeUriResolution() throws Exception { 1032 File file = File.createTempFile("DomTest.java", "xml"); 1033 File parentFile = file.getParentFile(); 1034 FileWriter writer = new FileWriter(file); 1035 writer.write("<a>" 1036 + " <b xml:base=\"b1/b2\">" 1037 + " <c>" 1038 + " <d xml:base=\"../d1/d2\"><e/></d>" 1039 + " </c>" 1040 + " </b>" 1041 + " <h xml:base=\"h1/h2/\">" 1042 + " <i xml:base=\"../i1/i2\"/>" 1043 + " </h>" 1044 + "</a>"); 1045 writer.close(); 1046 document = builder.parse(file); 1047 1048 assertFileUriEquals("", file.getPath(), document.getBaseURI()); 1049 assertFileUriEquals("", file.getPath(), document.getDocumentURI()); 1050 Element a = document.getDocumentElement(); 1051 assertFileUriEquals("", file.getPath(), a.getBaseURI()); 1052 1053 String message = "This implementation's getBaseURI() doesn't handle relative URIs"; 1054 Element b = (Element) a.getChildNodes().item(1); 1055 Element c = (Element) b.getChildNodes().item(1); 1056 Element d = (Element) c.getChildNodes().item(1); 1057 Element e = (Element) d.getChildNodes().item(0); 1058 Element h = (Element) a.getChildNodes().item(3); 1059 Element i = (Element) h.getChildNodes().item(1); 1060 assertFileUriEquals(message, parentFile + "/b1/b2", b.getBaseURI()); 1061 assertFileUriEquals(message, parentFile + "/b1/b2", c.getBaseURI()); 1062 assertFileUriEquals(message, parentFile + "/d1/d2", d.getBaseURI()); 1063 assertFileUriEquals(message, parentFile + "/d1/d2", e.getBaseURI()); 1064 assertFileUriEquals(message, parentFile + "/h1/h2/", h.getBaseURI()); 1065 assertFileUriEquals(message, parentFile + "/h1/i1/i2", i.getBaseURI()); 1066 } 1067 1068 /** 1069 * Regrettably both "file:/tmp/foo.txt" and "file:///tmp/foo.txt" are 1070 * legal URIs, and different implementations emit different forms. 1071 */ 1072 private void assertFileUriEquals( 1073 String message, String expectedFile, String actual) { 1074 if (!("file:" + expectedFile).equals(actual) 1075 && !("file://" + expectedFile).equals(actual)) { 1076 fail("Expected URI for: " + expectedFile 1077 + " but was " + actual + ". " + message); 1078 } 1079 } 1080 1081 /** 1082 * According to the <a href="http://www.w3.org/TR/xmlbase/">XML Base</a> 1083 * spec, fragments (like "#frag" or "") should not be dereferenced. 1084 */ 1085 public void testBaseUriResolutionWithHashes() throws Exception { 1086 document = builder.parse(new InputSource(new StringReader( 1087 "<a xml:base=\"http://a1/a2\">" 1088 + " <b xml:base=\"b1#b2\"/>" 1089 + " <c xml:base=\"#c1\">" 1090 + " <d xml:base=\"\"/>" 1091 + " </c>" 1092 + " <e xml:base=\"\"/>" 1093 + "</a>"))); 1094 Element a = document.getDocumentElement(); 1095 assertEquals("http://a1/a2", a.getBaseURI()); 1096 1097 String message = "This implementation's getBaseURI() doesn't handle " 1098 + "relative URIs with hashes"; 1099 Element b = (Element) a.getChildNodes().item(1); 1100 Element c = (Element) a.getChildNodes().item(3); 1101 Element d = (Element) c.getChildNodes().item(1); 1102 Element e = (Element) a.getChildNodes().item(5); 1103 assertEquals(message, "http://a1/b1#b2", b.getBaseURI()); 1104 assertEquals(message, "http://a1/a2#c1", c.getBaseURI()); 1105 assertEquals(message, "http://a1/a2#c1", d.getBaseURI()); 1106 assertEquals(message, "http://a1/a2", e.getBaseURI()); 1107 } 1108 1109 public void testBaseUriInheritedForProcessingInstructions() { 1110 document.setDocumentURI("http://d1/d2"); 1111 assertEquals("http://d1/d2", wafflemaker.getBaseURI()); 1112 } 1113 1114 public void testBaseUriInheritedForEntities() { 1115 if (sp == null) { 1116 return; 1117 } 1118 document.setDocumentURI("http://d1/d2"); 1119 assertEquals("http://d1/d2", sp.getBaseURI()); 1120 } 1121 1122 public void testBaseUriNotInheritedForNotations() { 1123 if (png == null) { 1124 return; 1125 } 1126 document.setDocumentURI("http://d1/d2"); 1127 assertNull(png.getBaseURI()); 1128 } 1129 1130 public void testBaseUriNotInheritedForDoctypes() { 1131 document.setDocumentURI("http://d1/d2"); 1132 assertNull(doctype.getBaseURI()); 1133 } 1134 1135 public void testBaseUriNotInheritedForAttributes() { 1136 document.setDocumentURI("http://d1/d2"); 1137 assertNull(itemXmlns.getBaseURI()); 1138 assertNull(itemXmlnsA.getBaseURI()); 1139 assertNull(standard.getBaseURI()); 1140 assertNull(vitaminsXmlnsA.getBaseURI()); 1141 } 1142 1143 public void testBaseUriNotInheritedForTextsOrCdatas() { 1144 document.setDocumentURI("http://d1/d2"); 1145 assertNull(descriptionText1.getBaseURI()); 1146 assertNull(descriptionText2.getBaseURI()); 1147 assertNull(option2Reference.getBaseURI()); 1148 } 1149 1150 public void testBaseUriNotInheritedForComments() { 1151 document.setDocumentURI("http://d1/d2"); 1152 assertNull(descriptionText1.getBaseURI()); 1153 assertNull(descriptionText2.getBaseURI()); 1154 } 1155 1156 public void testBaseUriNotInheritedForEntityReferences() { 1157 document.setDocumentURI("http://d1/d2"); 1158 assertNull(option2Reference.getBaseURI()); 1159 } 1160 1161 public void testProgrammaticElementIds() { 1162 vitaminc.setAttribute("name", "c"); 1163 assertFalse(vitaminc.getAttributeNode("name").isId()); 1164 assertNull(document.getElementById("c")); 1165 1166 // set the ID attribute... 1167 vitaminc.setIdAttribute("name", true); 1168 assertTrue(vitaminc.getAttributeNode("name").isId()); 1169 assertSame(vitaminc, document.getElementById("c")); 1170 1171 // ... and then take it away 1172 vitaminc.setIdAttribute("name", false); 1173 assertFalse(vitaminc.getAttributeNode("name").isId()); 1174 assertNull(document.getElementById("c")); 1175 } 1176 1177 public void testMultipleIdsOnOneElement() { 1178 vitaminc.setAttribute("name", "c"); 1179 vitaminc.setIdAttribute("name", true); 1180 vitaminc.setAttribute("atc", "a11g"); 1181 vitaminc.setIdAttribute("atc", true); 1182 1183 assertTrue(vitaminc.getAttributeNode("name").isId()); 1184 assertTrue(vitaminc.getAttributeNode("atc").isId()); 1185 assertSame(vitaminc, document.getElementById("c")); 1186 assertSame(vitaminc, document.getElementById("a11g")); 1187 assertNull(document.getElementById("g")); 1188 } 1189 1190 @KnownFailure("Dalvik treats id attributes as identifiers") 1191 public void testAttributeNamedIdIsNotAnIdByDefault() { 1192 String message = "This implementation incorrectly interprets the " 1193 + "\"id\" attribute as an identifier by default."; 1194 vitaminc.setAttribute("id", "c"); 1195 assertNull(message, document.getElementById("c")); 1196 } 1197 1198 public void testElementTypeInfo() { 1199 TypeInfo typeInfo = description.getSchemaTypeInfo(); 1200 assertNull(typeInfo.getTypeName()); 1201 assertNull(typeInfo.getTypeNamespace()); 1202 assertFalse(typeInfo.isDerivedFrom("x", "y", TypeInfo.DERIVATION_UNION)); 1203 } 1204 1205 public void testAttributeTypeInfo() { 1206 TypeInfo typeInfo = standard.getSchemaTypeInfo(); 1207 assertNull(typeInfo.getTypeName()); 1208 assertNull(typeInfo.getTypeNamespace()); 1209 assertFalse(typeInfo.isDerivedFrom("x", "y", TypeInfo.DERIVATION_UNION)); 1210 } 1211 1212 public void testRenameElement() { 1213 document.renameNode(description, null, "desc"); 1214 assertEquals("desc", description.getTagName()); 1215 assertEquals("desc", description.getLocalName()); 1216 assertEquals(null, description.getPrefix()); 1217 assertEquals(null, description.getNamespaceURI()); 1218 } 1219 1220 public void testRenameElementWithPrefix() { 1221 try { 1222 document.renameNode(description, null, "a:desc"); 1223 fail(); 1224 } catch (DOMException e) { 1225 } 1226 } 1227 1228 public void testRenameElementWithNamespace() { 1229 document.renameNode(description, "http://sales", "desc"); 1230 assertEquals("desc", description.getTagName()); 1231 assertEquals("desc", description.getLocalName()); 1232 assertEquals(null, description.getPrefix()); 1233 assertEquals("http://sales", description.getNamespaceURI()); 1234 } 1235 1236 public void testRenameElementWithPrefixAndNamespace() { 1237 document.renameNode(description, "http://sales", "a:desc"); 1238 assertEquals("a:desc", description.getTagName()); 1239 assertEquals("desc", description.getLocalName()); 1240 assertEquals("a", description.getPrefix()); 1241 assertEquals("http://sales", description.getNamespaceURI()); 1242 } 1243 1244 public void testRenameAttribute() { 1245 document.renameNode(deluxe, null, "special"); 1246 assertEquals("special", deluxe.getName()); 1247 assertEquals("special", deluxe.getLocalName()); 1248 assertEquals(null, deluxe.getPrefix()); 1249 assertEquals(null, deluxe.getNamespaceURI()); 1250 } 1251 1252 public void testRenameAttributeWithPrefix() { 1253 try { 1254 document.renameNode(deluxe, null, "a:special"); 1255 fail(); 1256 } catch (DOMException e) { 1257 } 1258 } 1259 1260 public void testRenameAttributeWithNamespace() { 1261 document.renameNode(deluxe, "http://sales", "special"); 1262 assertEquals("special", deluxe.getName()); 1263 assertEquals("special", deluxe.getLocalName()); 1264 assertEquals(null, deluxe.getPrefix()); 1265 assertEquals("http://sales", deluxe.getNamespaceURI()); 1266 } 1267 1268 public void testRenameAttributeWithPrefixAndNamespace() { 1269 document.renameNode(deluxe, "http://sales", "a:special"); 1270 assertEquals("a:special", deluxe.getName()); 1271 assertEquals("special", deluxe.getLocalName()); 1272 assertEquals("a", deluxe.getPrefix()); 1273 assertEquals("http://sales", deluxe.getNamespaceURI()); 1274 } 1275 1276 public void testUserDataHandlerNotifiedOfRenames() { 1277 RecordingHandler handler = new RecordingHandler(); 1278 description.setUserData("a", "apple", handler); 1279 deluxe.setUserData("b", "banana", handler); 1280 standard.setUserData("c", "cat", handler); 1281 1282 document.renameNode(deluxe, null, "special"); 1283 document.renameNode(description, null, "desc"); 1284 1285 Set<String> expected = new HashSet<String>(); 1286 expected.add(notification(NODE_RENAMED, "a", "apple", description, null)); 1287 expected.add(notification(NODE_RENAMED, "b", "banana", deluxe, null)); 1288 assertEquals(expected, handler.calls); 1289 } 1290 1291 public void testRenameToInvalid() { 1292 try { 1293 document.renameNode(description, null, "xmlns:foo"); 1294 fail(); 1295 } catch (DOMException e) { 1296 } 1297 try { 1298 document.renameNode(description, null, "xml:foo"); 1299 fail(); 1300 } catch (DOMException e) { 1301 } 1302 try { 1303 document.renameNode(deluxe, null, "xmlns"); 1304 fail(); 1305 } catch (DOMException e) { 1306 } 1307 } 1308 1309 public void testRenameNodeOtherThanElementOrAttribute() { 1310 for (Node node : allNodes) { 1311 if (node.getNodeType() == Node.ATTRIBUTE_NODE 1312 || node.getNodeType() == Node.ELEMENT_NODE) { 1313 continue; 1314 } 1315 1316 try { 1317 document.renameNode(node, null, "foo"); 1318 fail(); 1319 } catch (DOMException e) { 1320 } 1321 } 1322 } 1323 1324 public void testDocumentDoesNotHaveWhitespaceChildren() 1325 throws IOException, SAXException { 1326 String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n" 1327 + " <foo/>\n" 1328 + " \n"; 1329 document = builder.parse(new InputSource(new StringReader(xml))); 1330 assertEquals("Document nodes shouldn't have text children", 1331 1, document.getChildNodes().getLength()); 1332 } 1333 1334 @KnownFailure("Dalvik document nodes accept arbitrary child nodes") 1335 public void testDocumentAddChild() 1336 throws IOException, SAXException { 1337 try { 1338 document.appendChild(document.createTextNode(" ")); 1339 fail("Document nodes shouldn't accept child nodes"); 1340 } catch (DOMException e) { 1341 } 1342 } 1343 1344 public void testIterateForwardsThroughInnerNodeSiblings() throws Exception { 1345 document = builder.parse(new InputSource(new StringReader( 1346 "<root><child/><child/></root>"))); 1347 Node root = document.getDocumentElement(); 1348 Node current = root.getChildNodes().item(0); 1349 while (current.getNextSibling() != null) { 1350 current = current.getNextSibling(); 1351 } 1352 assertEquals(root.getChildNodes().item(root.getChildNodes().getLength() - 1), current); 1353 } 1354 1355 public void testIterateBackwardsThroughInnerNodeSiblings() throws Exception { 1356 document = builder.parse(new InputSource(new StringReader( 1357 "<root><child/><child/></root>"))); 1358 Node root = document.getDocumentElement(); 1359 Node current = root.getChildNodes().item(root.getChildNodes().getLength() - 1); 1360 while (current.getPreviousSibling() != null) { 1361 current = current.getPreviousSibling(); 1362 } 1363 assertEquals(root.getChildNodes().item(0), current); 1364 } 1365 1366 public void testIterateForwardsThroughLeafNodeSiblings() throws Exception { 1367 document = builder.parse(new InputSource(new StringReader( 1368 "<root> <!-- --> </root>"))); 1369 Node root = document.getDocumentElement(); 1370 Node current = root.getChildNodes().item(0); 1371 while (current.getNextSibling() != null) { 1372 current = current.getNextSibling(); 1373 } 1374 assertEquals(root.getChildNodes().item(root.getChildNodes().getLength() - 1), current); 1375 } 1376 1377 public void testIterateBackwardsThroughLeafNodeSiblings() throws Exception { 1378 document = builder.parse(new InputSource(new StringReader( 1379 "<root> <!-- --> </root>"))); 1380 Node root = document.getDocumentElement(); 1381 Node current = root.getChildNodes().item(root.getChildNodes().getLength() - 1); 1382 while (current.getPreviousSibling() != null) { 1383 current = current.getPreviousSibling(); 1384 } 1385 assertEquals(root.getChildNodes().item(0), current); 1386 } 1387 1388 private class RecordingHandler implements UserDataHandler { 1389 final Set<String> calls = new HashSet<String>(); 1390 public void handle(short operation, String key, Object data, Node src, Node dst) { 1391 calls.add(notification(operation, key, data, src, dst)); 1392 } 1393 } 1394 1395 private String notification(short operation, String key, Object data, Node src, Node dst) { 1396 return "op:" + operation + " key:" + key + " data:" + data + " src:" + src + " dst:" + dst; 1397 } 1398 1399 private String domToString(Document document) throws TransformerException { 1400 StringWriter writer = new StringWriter(); 1401 transformer.transform(new DOMSource(document), new StreamResult(writer)); 1402 String result = writer.toString(); 1403 1404 /* 1405 * Hack: swap <name>'s a:standard attribute and deluxe attribute if 1406 * they're out of order. Some document transformations reorder the 1407 * attributes, which causes pain when we try to use String comparison on 1408 * them. 1409 */ 1410 Matcher attributeMatcher = Pattern.compile(" a:standard=\"[^\"]+\"").matcher(result); 1411 if (attributeMatcher.find()) { 1412 result = result.substring(0, attributeMatcher.start()) 1413 + result.substring(attributeMatcher.end()); 1414 int insertionPoint = result.indexOf(" deluxe=\""); 1415 result = result.substring(0, insertionPoint) 1416 + attributeMatcher.group() 1417 + result.substring(insertionPoint); 1418 } 1419 1420 return result; 1421 } 1422 1423 private String domToStringStripElementWhitespace(Document document) 1424 throws TransformerException { 1425 return domToString(document).replaceAll("(?m)>\\s+<", "><"); 1426 } 1427 } 1428