1 21 22 23 package nu.xom; 24 25 import java.util.Stack ; 26 27 import org.xml.sax.ContentHandler ; 28 import org.xml.sax.DTDHandler ; 29 import org.xml.sax.Locator ; 30 import org.xml.sax.ext.DeclHandler ; 31 import org.xml.sax.ext.LexicalHandler ; 32 33 38 class XOMHandler 39 implements ContentHandler , LexicalHandler , DeclHandler , DTDHandler { 40 41 protected Document document; 42 protected String documentBaseURI; 43 44 protected ParentNode parent; 50 protected ParentNode current; 51 protected Stack parents; 52 protected boolean inProlog; 53 protected boolean inDTD; 54 protected int position; protected Locator locator; 56 protected DocType doctype; 57 protected StringBuffer internalDTDSubset; 58 protected NodeFactory factory; 59 60 61 XOMHandler(NodeFactory factory) { 62 this.factory = factory; 63 } 64 65 66 public void setDocumentLocator(Locator locator) { 67 this.locator = locator; 68 } 69 70 71 Document getDocument() { 72 return document; 73 } 74 75 76 public void startDocument() { 77 78 inDTD = false; 79 document = factory.startMakingDocument(); 80 parent = document; 81 current = document; 82 parents = new Stack (); 83 parents.push(document); 84 inProlog = true; 85 position = 0; 86 buffer = new StringBuffer (); 87 doctype = null; 88 if (locator != null) { 89 documentBaseURI = locator.getSystemId(); 90 document.setBaseURI(documentBaseURI); 95 } 96 97 } 98 99 100 public void endDocument() { 101 factory.finishMakingDocument(document); 102 parents.pop(); 103 } 104 105 106 public void startElement(String namespaceURI, String localName, 107 String qualifiedName, org.xml.sax.Attributes attributes) { 108 109 flushText(); 110 Element element; 111 if (parent != document) { 112 element = factory.startMakingElement(qualifiedName, namespaceURI); 113 } 114 else { element = factory.makeRootElement(qualifiedName, namespaceURI); 116 if (element == null) { throw new NullPointerException ( 118 "Factory failed to create root element." 119 ); 120 } 121 document.setRootElement(element); 122 inProlog = false; 123 } 124 125 current = element; 126 parents.push(element); 128 129 if (element != null) { if (parent != document) { 131 parent.appendChild(element); 133 } 134 if (locator != null) { 140 String baseURI = locator.getSystemId(); 141 if (baseURI != null && !baseURI.equals(documentBaseURI)) { 142 element.setActualBaseURI(baseURI); 143 } 144 } 145 146 150 for (int i = 0; i < attributes.getLength(); i++) { 153 String qName = attributes.getQName(i); 154 if (qName.startsWith("xmlns:") || qName.equals("xmlns")) { 155 continue; 156 } 157 else { 158 String namespace = attributes.getURI(i); 159 String value = attributes.getValue(i); 160 Nodes nodes = factory.makeAttribute( 161 qName, 162 namespace, 163 value, 164 convertStringToType(attributes.getType(i)) 165 ); 166 int numberChildren = 0; 167 for (int j=0; j < nodes.size(); j++) { 168 Node node = nodes.get(j); 169 if (node.isAttribute()) { 170 factory.addAttribute(element, (Attribute) node); 171 } 172 else { 173 factory.insertChild(element, node, numberChildren++); 174 } 175 } 176 } 177 } 178 179 for (int i = 0; i < attributes.getLength(); i++) { 181 String qName = attributes.getQName(i); 182 if (qName.startsWith("xmlns:")) { 183 String namespaceName = attributes.getValue(i); 184 String namespacePrefix = qName.substring(6); 185 String currentValue 186 = element.getNamespaceURI(namespacePrefix); 187 if (!namespaceName.equals(currentValue)) { 188 element.addNamespaceDeclaration( 189 namespacePrefix, namespaceName); 190 } 191 } 192 else if (qName.equals("xmlns")) { 193 String namespaceName = attributes.getValue(i); 194 String namespacePrefix = ""; 195 String currentValue 196 = element.getNamespaceURI(namespacePrefix); 197 if (!namespaceName.equals(currentValue)) { 198 element.addNamespaceDeclaration(namespacePrefix, 199 namespaceName); 200 } 201 } 202 } 203 204 parent = element; 206 } 207 208 } 209 210 211 public void endElement( 212 String namespaceURI, String localName, String qualifiedName) { 213 214 current = (ParentNode) parents.pop(); 217 flushText(); 218 219 if (current != null) { 220 parent = current.getParent(); 221 Nodes result = factory.finishMakingElement((Element) current); 222 223 if (result.size() != 1 || result.get(0) != current) { 225 if (!parent.isDocument()) { 226 parent.removeChild(parent.getChildCount() - 1); 227 for (int i=0; i < result.size(); i++) { 228 Node node = result.get(i); 229 if (node.isAttribute()) { 230 ((Element) parent).addAttribute((Attribute) node); 231 } 232 else { 233 parent.appendChild(node); 234 } 235 } 236 } 237 else { Document doc = (Document) parent; 239 Element currentRoot = doc.getRootElement(); 240 boolean beforeRoot = true; 241 for (int i=0; i < result.size(); i++) { 242 Node node = result.get(i); 243 if (node.isElement()) { 244 if (node != currentRoot) { 245 if (!beforeRoot) { 246 throw new IllegalAddException("Factory returned multiple roots"); 248 } 249 doc.setRootElement((Element) node); 250 } 251 beforeRoot = false; 252 } 253 else if (beforeRoot) { 254 doc.insertChild(node, doc.indexOf(doc.getRootElement())); 255 } 256 else { 257 doc.appendChild(node); 258 } 259 } 260 if (beforeRoot) { 261 throw new WellformednessException( 264 "Factory attempted to remove the root element"); 265 } 266 } 267 } 268 } 269 270 } 271 272 273 static Attribute.Type convertStringToType(String saxType) { 274 275 if (saxType.equals("CDATA")) return Attribute.Type.CDATA; 276 if (saxType.equals("ID")) return Attribute.Type.ID; 277 if (saxType.equals("IDREF")) return Attribute.Type.IDREF; 278 if (saxType.equals("IDREFS")) return Attribute.Type.IDREFS; 279 if (saxType.equals("NMTOKEN")) return Attribute.Type.NMTOKEN; 280 if (saxType.equals("NMTOKENS")) return Attribute.Type.NMTOKENS; 281 if (saxType.equals("ENTITY")) return Attribute.Type.ENTITY; 282 if (saxType.equals("ENTITIES")) return Attribute.Type.ENTITIES; 283 if (saxType.equals("NOTATION")) return Attribute.Type.NOTATION; 284 285 if (saxType.equals("ENUMERATION")) { 287 return Attribute.Type.ENUMERATION; 288 } 289 if (saxType.startsWith("(")) return Attribute.Type.ENUMERATION; 290 291 return Attribute.Type.UNDECLARED; 292 293 } 294 295 296 protected StringBuffer buffer; 297 298 public void characters(char[] text, int start, int length) { 299 buffer.append(text, start, length); 300 if (finishedCDATA && length > 0) inCDATA = false; 301 } 302 303 304 protected void flushText() { 306 307 if (buffer.length() > 0) { 308 Nodes result; 309 if (!inCDATA) { 310 result = factory.makeText(buffer.toString()); 311 } 312 else { 313 result = factory.makeCDATASection(buffer.toString()); 314 } 315 for (int i=0; i < result.size(); i++) { 316 Node node = result.get(i); 317 if (node.isAttribute()) { 318 ((Element) parent).addAttribute((Attribute) node); 319 } 320 else { 321 parent.appendChild(node); 322 } 323 } 324 buffer = new StringBuffer (); 325 } 326 inCDATA = false; 327 finishedCDATA = false; 328 329 } 330 331 332 public void ignorableWhitespace( 333 char[] text, int start, int length) { 334 characters(text, start, length); 335 } 336 337 338 public void processingInstruction(String target, String data) { 339 340 if (!inDTD) flushText(); 341 if (inExternalSubset) return; 342 Nodes result = factory.makeProcessingInstruction(target, data); 343 344 for (int i = 0; i < result.size(); i++) { 345 Node node = result.get(i); 346 if (!inDTD) { 347 if (inProlog) { 348 parent.insertChild(node, position); 349 position++; 350 } 351 else { 352 if (node.isAttribute()) { 353 ((Element) parent).addAttribute((Attribute) node); 354 } 355 else parent.appendChild(node); 356 } 357 } 358 else { 359 if (node.isProcessingInstruction() || node.isComment()) { 360 internalDTDSubset.append(" "); 361 internalDTDSubset.append(node.toXML()); 362 internalDTDSubset.append("\n"); 363 } 364 else { 365 throw new XMLException("Factory tried to put a " 366 + node.getClass().getName() 367 + " in the internal DTD subset"); 368 } 369 } 370 } 371 372 } 373 374 375 public void startPrefixMapping(String prefix, String uri) {} 377 public void endPrefixMapping(String prefix) {} 378 379 public void skippedEntity(String name) { 380 flushText(); 381 throw new XMLException("Could not resolve entity " + name); 382 } 383 384 385 public void startDTD(String rootName, String publicID, 387 String systemID) { 388 389 inDTD = true; 390 Nodes result = factory.makeDocType(rootName, publicID, systemID); 391 for (int i = 0; i < result.size(); i++) { 392 Node node = result.get(i); 393 document.insertChild(node, position); 394 position++; 395 if (node.isDocType()) { 396 DocType doctype = (DocType) node; 397 internalDTDSubset = new StringBuffer (); 398 this.doctype = doctype; 399 } 400 } 401 402 } 403 404 405 public void endDTD() { 406 407 inDTD = false; 408 if (doctype != null) { 409 doctype.setInternalDTDSubset(internalDTDSubset.toString()); 410 } 411 412 } 413 414 415 protected boolean inExternalSubset = false; 416 417 public void startEntity(String name) { 422 if (name.equals("[dtd]")) inExternalSubset = true; 423 } 424 425 426 public void endEntity(String name) { 427 if (name.equals("[dtd]")) inExternalSubset = false; 428 } 429 430 431 protected boolean inCDATA = false; 432 protected boolean finishedCDATA = false; 433 434 public void startCDATA() { 435 if (buffer.length() == 0) inCDATA = true; 436 finishedCDATA = false; 437 } 438 439 440 public void endCDATA() { 441 finishedCDATA = true; 442 } 443 444 445 public void comment(char[] text, int start, int length) { 446 447 if (!inDTD) flushText(); 448 if (inExternalSubset) return; 449 450 Nodes result = factory.makeComment(new String (text, start, length)); 451 452 for (int i = 0; i < result.size(); i++) { 453 Node node = result.get(i); 454 if (!inDTD) { 455 if (inProlog) { 456 parent.insertChild(node, position); 457 position++; 458 } 459 else { 460 if (node instanceof Attribute) { 461 ((Element) parent).addAttribute((Attribute) node); 462 } 463 else parent.appendChild(node); 464 } 465 } 466 else { 467 if (node.isComment() || node.isProcessingInstruction()) { 468 internalDTDSubset.append(" "); 469 internalDTDSubset.append(node.toXML()); 470 internalDTDSubset.append("\n"); 471 } 472 else { 473 throw new XMLException("Factory tried to put a " 474 + node.getClass().getName() 475 + " in the internal DTD subset"); 476 } 477 } 478 } 479 480 } 481 482 483 public void elementDecl(String name, String model) { 484 485 if (!inExternalSubset && doctype != null) { 486 internalDTDSubset.append(" <!ELEMENT "); 487 internalDTDSubset.append(name); 488 internalDTDSubset.append(' '); 489 internalDTDSubset.append(model); 490 internalDTDSubset.append(">\n"); 491 } 492 493 } 494 495 496 public void attributeDecl(String elementName, 497 String attributeName, String type, String mode, 498 String defaultValue) { 499 500 if (!inExternalSubset && doctype != null) { 501 internalDTDSubset.append(" <!ATTLIST "); 502 internalDTDSubset.append(elementName); 503 internalDTDSubset.append(' '); 504 internalDTDSubset.append(attributeName); 505 internalDTDSubset.append(' '); 506 internalDTDSubset.append(type); 507 if (mode != null) { 508 internalDTDSubset.append(' '); 509 internalDTDSubset.append(mode); 510 } 511 if (defaultValue != null) { 512 internalDTDSubset.append(' '); 513 internalDTDSubset.append('"'); 514 internalDTDSubset.append(escapeReservedCharacters(defaultValue)); 515 internalDTDSubset.append("\""); 516 } 517 internalDTDSubset.append(">\n"); 518 } 519 520 } 521 522 523 public void internalEntityDecl(String name, 524 String value) { 525 526 if (!inExternalSubset && doctype != null) { 527 if (!name.startsWith("%")) { internalDTDSubset.append(" <!ENTITY "); 529 internalDTDSubset.append(name); 530 internalDTDSubset.append(" \""); 531 internalDTDSubset.append(escapeCarriageReturns(value)); 532 internalDTDSubset.append("\">\n"); 533 } 534 } 535 536 } 537 538 539 public void externalEntityDecl(String name, 540 String publicID, String systemID) { 541 542 if (!inExternalSubset && doctype != null) { 543 if (!name.startsWith("%")) { internalDTDSubset.append(" <!ENTITY "); 545 if (publicID != null) { 546 internalDTDSubset.append(name); 547 internalDTDSubset.append(" PUBLIC \""); 548 internalDTDSubset.append(publicID); 549 internalDTDSubset.append("\" \""); 550 internalDTDSubset.append(systemID); 551 } 552 else { 553 internalDTDSubset.append(name); 554 internalDTDSubset.append(" SYSTEM \""); 555 internalDTDSubset.append(systemID); 556 } 557 internalDTDSubset.append("\">\n"); 558 } 559 } 560 561 } 562 563 564 public void notationDecl(String name, String publicID, 565 String systemID) { 566 567 if (!inExternalSubset && doctype != null) { 568 internalDTDSubset.append(" <!NOTATION "); 569 internalDTDSubset.append(name); 570 if (publicID != null) { 571 internalDTDSubset.append(" PUBLIC \""); 572 internalDTDSubset.append(publicID); 573 internalDTDSubset.append('"'); 574 if (systemID != null) { 575 internalDTDSubset.append(" \""); 576 internalDTDSubset.append(systemID); 577 internalDTDSubset.append('"'); 578 } 579 } 580 else { 581 internalDTDSubset.append(" SYSTEM \""); 582 internalDTDSubset.append(systemID); 583 internalDTDSubset.append('"'); 584 } 585 internalDTDSubset.append(">\n"); 586 } 587 588 } 589 590 591 public void unparsedEntityDecl(String name, String publicID, 592 String systemID, String notationName) { 593 594 if (!inExternalSubset && doctype != null) { 595 internalDTDSubset.append(" <!ENTITY "); 596 if (publicID != null) { 597 internalDTDSubset.append(name); 598 internalDTDSubset.append(" PUBLIC \""); 599 internalDTDSubset.append(publicID); 600 internalDTDSubset.append("\" \""); 601 internalDTDSubset.append(systemID); 602 internalDTDSubset.append("\" NDATA "); 603 internalDTDSubset.append(notationName); 604 } 605 else { 606 internalDTDSubset.append(name); 607 internalDTDSubset.append(" SYSTEM \""); 608 internalDTDSubset.append(systemID); 609 internalDTDSubset.append("\" NDATA "); 610 internalDTDSubset.append(notationName); 611 } 612 internalDTDSubset.append(">\n"); 613 } 614 615 } 616 617 618 624 private static String escapeCarriageReturns(String s) { 625 626 int length = s.length(); 627 StringBuffer result = new StringBuffer (length); 628 for (int i = 0; i < length; i++) { 629 char c = s.charAt(i); 630 if (c == '\r') result.append("
"); 631 else result.append(c); 632 } 633 634 return result.toString(); 635 636 } 637 638 639 private static String escapeReservedCharacters(String s) { 640 641 int length = s.length(); 642 StringBuffer result = new StringBuffer (length); 643 for (int i = 0; i < length; i++) { 644 char c = s.charAt(i); 645 switch (c) { 647 case '\r': 648 result.append("
"); 649 break; 650 case '&': 651 result.append("&"); 652 break; 653 case '"': 654 result.append("""); 655 break; 656 case '<': 657 result.append("<"); 658 break; 659 default: 660 result.append(c); 661 } 662 } 663 664 return result.toString(); 665 666 } 667 668 669 } | Popular Tags |