1 /* 2 * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved. 3 * 4 * This software is open source. 5 * See the bottom of this file for the licence. 6 */ 7 8 package org.dom4j.io; 9 10 import java.lang.reflect.Method; 11 import java.util.ArrayList; 12 import java.util.HashMap; 13 import java.util.List; 14 import java.util.Map; 15 16 import org.dom4j.Branch; 17 import org.dom4j.Document; 18 import org.dom4j.DocumentFactory; 19 import org.dom4j.DocumentType; 20 import org.dom4j.Element; 21 import org.dom4j.ElementHandler; 22 import org.dom4j.Namespace; 23 import org.dom4j.QName; 24 import org.dom4j.dtd.AttributeDecl; 25 import org.dom4j.dtd.ElementDecl; 26 import org.dom4j.dtd.ExternalEntityDecl; 27 import org.dom4j.dtd.InternalEntityDecl; 28 import org.dom4j.tree.AbstractElement; 29 import org.dom4j.tree.NamespaceStack; 30 31 import org.xml.sax.Attributes; 32 import org.xml.sax.DTDHandler; 33 import org.xml.sax.EntityResolver; 34 import org.xml.sax.InputSource; 35 import org.xml.sax.Locator; 36 import org.xml.sax.SAXException; 37 import org.xml.sax.SAXParseException; 38 import org.xml.sax.ext.DeclHandler; 39 import org.xml.sax.ext.LexicalHandler; 40 import org.xml.sax.helpers.DefaultHandler; 41 42 /** 43 * <p> 44 * <code>SAXContentHandler</code> builds a dom4j tree via SAX events. 45 * </p> 46 * 47 * @author <a href="mailto:jstrachan@apache.org">James Strachan </a> 48 * @version $Revision: 1.61 $ 49 */ 50 public class SAXContentHandler extends DefaultHandler implements 51 LexicalHandler, DeclHandler, DTDHandler { 52 /** The factory used to create new <code>Document</code> instances */ 53 private DocumentFactory documentFactory; 54 55 /** The document that is being built */ 56 private Document document; 57 58 /** stack of <code>Element</code> objects */ 59 private ElementStack elementStack; 60 61 /** stack of <code>Namespace</code> and <code>QName</code> objects */ 62 private NamespaceStack namespaceStack; 63 64 /** the <code>ElementHandler</code> called as the elements are complete */ 65 private ElementHandler elementHandler; 66 67 /** the Locator */ 68 private Locator locator; 69 70 /** The name of the current entity */ 71 private String entity; 72 73 /** Flag used to indicate that we are inside a DTD section */ 74 private boolean insideDTDSection; 75 76 /** Flag used to indicate that we are inside a CDATA section */ 77 private boolean insideCDATASection; 78 79 /** 80 * buffer to hold contents of cdata section across multiple characters 81 * events 82 */ 83 private StringBuffer cdataText; 84 85 /** namespaces that are available for use */ 86 private Map availableNamespaceMap = new HashMap(); 87 88 /** declared namespaces that are not yet available for use */ 89 private List declaredNamespaceList = new ArrayList(); 90 91 /** internal DTD declarations */ 92 private List internalDTDDeclarations; 93 94 /** external DTD declarations */ 95 private List externalDTDDeclarations; 96 97 /** The number of namespaces that are declared in the current scope */ 98 private int declaredNamespaceIndex; 99 100 /** The entity resolver */ 101 private EntityResolver entityResolver; 102 103 private InputSource inputSource; 104 105 /** The current element we are on */ 106 private Element currentElement; 107 108 /** Should internal DTD declarations be expanded into a List in the DTD */ 109 private boolean includeInternalDTDDeclarations = false; 110 111 /** Should external DTD declarations be expanded into a List in the DTD */ 112 private boolean includeExternalDTDDeclarations = false; 113 114 /** The number of levels deep we are inside a startEntity/endEntity call */ 115 private int entityLevel; 116 117 /** Are we in an internal DTD subset? */ 118 private boolean internalDTDsubset = false; 119 120 /** Whether adjacent text nodes should be merged */ 121 private boolean mergeAdjacentText = false; 122 123 /** Have we added text to the buffer */ 124 private boolean textInTextBuffer = false; 125 126 /** Should we ignore comments */ 127 private boolean ignoreComments = false; 128 129 /** Buffer used to concatenate text together */ 130 private StringBuffer textBuffer; 131 132 /** Holds value of property stripWhitespaceText. */ 133 private boolean stripWhitespaceText = false; 134 SAXContentHandler()135 public SAXContentHandler() { 136 this(DocumentFactory.getInstance()); 137 } 138 SAXContentHandler(DocumentFactory documentFactory)139 public SAXContentHandler(DocumentFactory documentFactory) { 140 this(documentFactory, null); 141 } 142 SAXContentHandler(DocumentFactory documentFactory, ElementHandler elementHandler)143 public SAXContentHandler(DocumentFactory documentFactory, 144 ElementHandler elementHandler) { 145 this(documentFactory, elementHandler, null); 146 this.elementStack = createElementStack(); 147 } 148 SAXContentHandler(DocumentFactory documentFactory, ElementHandler elementHandler, ElementStack elementStack)149 public SAXContentHandler(DocumentFactory documentFactory, 150 ElementHandler elementHandler, ElementStack elementStack) { 151 this.documentFactory = documentFactory; 152 this.elementHandler = elementHandler; 153 this.elementStack = elementStack; 154 this.namespaceStack = new NamespaceStack(documentFactory); 155 } 156 157 /** 158 * DOCUMENT ME! 159 * 160 * @return the document that has been or is being built 161 */ getDocument()162 public Document getDocument() { 163 if (document == null) { 164 document = createDocument(); 165 } 166 167 return document; 168 } 169 170 // ContentHandler interface 171 // ------------------------------------------------------------------------- setDocumentLocator(Locator documentLocator)172 public void setDocumentLocator(Locator documentLocator) { 173 this.locator = documentLocator; 174 } 175 processingInstruction(String target, String data)176 public void processingInstruction(String target, String data) 177 throws SAXException { 178 if (mergeAdjacentText && textInTextBuffer) { 179 completeCurrentTextNode(); 180 } 181 182 if (currentElement != null) { 183 currentElement.addProcessingInstruction(target, data); 184 } else { 185 getDocument().addProcessingInstruction(target, data); 186 } 187 } 188 startPrefixMapping(String prefix, String uri)189 public void startPrefixMapping(String prefix, String uri) 190 throws SAXException { 191 namespaceStack.push(prefix, uri); 192 } 193 endPrefixMapping(String prefix)194 public void endPrefixMapping(String prefix) throws SAXException { 195 namespaceStack.pop(prefix); 196 declaredNamespaceIndex = namespaceStack.size(); 197 } 198 startDocument()199 public void startDocument() throws SAXException { 200 // document = createDocument(); 201 document = null; 202 currentElement = null; 203 204 elementStack.clear(); 205 206 if ((elementHandler != null) 207 && (elementHandler instanceof DispatchHandler)) { 208 elementStack.setDispatchHandler((DispatchHandler) elementHandler); 209 } 210 211 namespaceStack.clear(); 212 declaredNamespaceIndex = 0; 213 214 if (mergeAdjacentText && (textBuffer == null)) { 215 textBuffer = new StringBuffer(); 216 } 217 218 textInTextBuffer = false; 219 } 220 endDocument()221 public void endDocument() throws SAXException { 222 namespaceStack.clear(); 223 elementStack.clear(); 224 currentElement = null; 225 textBuffer = null; 226 } 227 startElement(String namespaceURI, String localName, String qualifiedName, Attributes attributes)228 public void startElement(String namespaceURI, String localName, 229 String qualifiedName, Attributes attributes) throws SAXException { 230 if (mergeAdjacentText && textInTextBuffer) { 231 completeCurrentTextNode(); 232 } 233 234 QName qName = namespaceStack.getQName(namespaceURI, localName, 235 qualifiedName); 236 237 Branch branch = currentElement; 238 239 if (branch == null) { 240 branch = getDocument(); 241 } 242 243 Element element = branch.addElement(qName); 244 245 // add all declared namespaces 246 addDeclaredNamespaces(element); 247 248 // now lets add all attribute values 249 addAttributes(element, attributes); 250 251 elementStack.pushElement(element); 252 currentElement = element; 253 254 entity = null; // fixes bug527062 255 256 if (elementHandler != null) { 257 elementHandler.onStart(elementStack); 258 } 259 } 260 endElement(String namespaceURI, String localName, String qName)261 public void endElement(String namespaceURI, String localName, String qName) 262 throws SAXException { 263 if (mergeAdjacentText && textInTextBuffer) { 264 completeCurrentTextNode(); 265 } 266 267 if ((elementHandler != null) && (currentElement != null)) { 268 elementHandler.onEnd(elementStack); 269 } 270 271 elementStack.popElement(); 272 currentElement = elementStack.peekElement(); 273 } 274 characters(char[] ch, int start, int end)275 public void characters(char[] ch, int start, int end) throws SAXException { 276 if (end == 0) { 277 return; 278 } 279 280 if (currentElement != null) { 281 if (entity != null) { 282 if (mergeAdjacentText && textInTextBuffer) { 283 completeCurrentTextNode(); 284 } 285 286 currentElement.addEntity(entity, new String(ch, start, end)); 287 entity = null; 288 } else if (insideCDATASection) { 289 if (mergeAdjacentText && textInTextBuffer) { 290 completeCurrentTextNode(); 291 } 292 293 cdataText.append(new String(ch, start, end)); 294 } else { 295 if (mergeAdjacentText) { 296 textBuffer.append(ch, start, end); 297 textInTextBuffer = true; 298 } else { 299 currentElement.addText(new String(ch, start, end)); 300 } 301 } 302 } 303 } 304 305 // ErrorHandler interface 306 // ------------------------------------------------------------------------- 307 308 /** 309 * This method is called when a warning occurs during the parsing of the 310 * document. This method does nothing. 311 * 312 * @param exception 313 * DOCUMENT ME! 314 * 315 * @throws SAXException 316 * DOCUMENT ME! 317 */ warning(SAXParseException exception)318 public void warning(SAXParseException exception) throws SAXException { 319 // ignore warnings by default 320 } 321 322 /** 323 * This method is called when an error is detected during parsing such as a 324 * validation error. This method rethrows the exception 325 * 326 * @param exception 327 * DOCUMENT ME! 328 * 329 * @throws SAXException 330 * DOCUMENT ME! 331 */ error(SAXParseException exception)332 public void error(SAXParseException exception) throws SAXException { 333 throw exception; 334 } 335 336 /** 337 * This method is called when a fatal error occurs during parsing. This 338 * method rethrows the exception 339 * 340 * @param exception 341 * DOCUMENT ME! 342 * 343 * @throws SAXException 344 * DOCUMENT ME! 345 */ fatalError(SAXParseException exception)346 public void fatalError(SAXParseException exception) throws SAXException { 347 throw exception; 348 } 349 350 // LexicalHandler interface 351 // ------------------------------------------------------------------------- startDTD(String name, String publicId, String systemId)352 public void startDTD(String name, String publicId, String systemId) 353 throws SAXException { 354 getDocument().addDocType(name, publicId, systemId); 355 insideDTDSection = true; 356 internalDTDsubset = true; 357 } 358 endDTD()359 public void endDTD() throws SAXException { 360 insideDTDSection = false; 361 362 DocumentType docType = getDocument().getDocType(); 363 364 if (docType != null) { 365 if (internalDTDDeclarations != null) { 366 docType.setInternalDeclarations(internalDTDDeclarations); 367 } 368 369 if (externalDTDDeclarations != null) { 370 docType.setExternalDeclarations(externalDTDDeclarations); 371 } 372 } 373 374 internalDTDDeclarations = null; 375 externalDTDDeclarations = null; 376 } 377 startEntity(String name)378 public void startEntity(String name) throws SAXException { 379 ++entityLevel; 380 381 // Ignore DTD references 382 entity = null; 383 384 if (!insideDTDSection) { 385 if (!isIgnorableEntity(name)) { 386 entity = name; 387 } 388 } 389 390 // internal DTD subsets can only appear outside of a 391 // startEntity/endEntity block 392 // see the startDTD method in 393 // http://dom4j.org/javadoc/org/xml/sax/ext/LexicalHandler.html 394 internalDTDsubset = false; 395 } 396 endEntity(String name)397 public void endEntity(String name) throws SAXException { 398 --entityLevel; 399 entity = null; 400 401 if (entityLevel == 0) { 402 internalDTDsubset = true; 403 } 404 } 405 startCDATA()406 public void startCDATA() throws SAXException { 407 insideCDATASection = true; 408 cdataText = new StringBuffer(); 409 } 410 endCDATA()411 public void endCDATA() throws SAXException { 412 insideCDATASection = false; 413 currentElement.addCDATA(cdataText.toString()); 414 } 415 comment(char[] ch, int start, int end)416 public void comment(char[] ch, int start, int end) throws SAXException { 417 if (!ignoreComments) { 418 if (mergeAdjacentText && textInTextBuffer) { 419 completeCurrentTextNode(); 420 } 421 422 String text = new String(ch, start, end); 423 424 if (!insideDTDSection && (text.length() > 0)) { 425 if (currentElement != null) { 426 currentElement.addComment(text); 427 } else { 428 getDocument().addComment(text); 429 } 430 } 431 } 432 } 433 434 // DeclHandler interface 435 // ------------------------------------------------------------------------- 436 437 /** 438 * Report an element type declaration. 439 * 440 * <p> 441 * The content model will consist of the string "EMPTY", the string "ANY", 442 * or a parenthesised group, optionally followed by an occurrence indicator. 443 * The model will be normalized so that all parameter entities are fully 444 * resolved and all whitespace is removed,and will include the enclosing 445 * parentheses. Other normalization (such as removing redundant parentheses 446 * or simplifying occurrence indicators) is at the discretion of the parser. 447 * </p> 448 * 449 * @param name 450 * The element type name. 451 * @param model 452 * The content model as a normalized string. 453 * 454 * @exception SAXException 455 * The application may raise an exception. 456 */ elementDecl(String name, String model)457 public void elementDecl(String name, String model) throws SAXException { 458 if (internalDTDsubset) { 459 if (includeInternalDTDDeclarations) { 460 addDTDDeclaration(new ElementDecl(name, model)); 461 } 462 } else { 463 if (includeExternalDTDDeclarations) { 464 addExternalDTDDeclaration(new ElementDecl(name, model)); 465 } 466 } 467 } 468 469 /** 470 * Report an attribute type declaration. 471 * 472 * <p> 473 * Only the effective (first) declaration for an attribute will be reported. 474 * The type will be one of the strings "CDATA", "ID", "IDREF", "IDREFS", 475 * "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", a parenthesized token group 476 * with the separator "|" and all whitespace removed, or the word "NOTATION" 477 * followed by a space followed by a parenthesized token group with all 478 * whitespace removed. 479 * </p> 480 * 481 * <p> 482 * Any parameter entities in the attribute value will be expanded, but 483 * general entities will not. 484 * </p> 485 * 486 * @param eName 487 * The name of the associated element. 488 * @param aName 489 * The name of the attribute. 490 * @param type 491 * A string representing the attribute type. 492 * @param valueDefault 493 * A string representing the attribute default ("#IMPLIED", 494 * "#REQUIRED", or "#FIXED") or null if none of these applies. 495 * @param val 496 * A string representing the attribute's default value, or null 497 * if there is none. 498 * 499 * @exception SAXException 500 * The application may raise an exception. 501 */ attributeDecl(String eName, String aName, String type, String valueDefault, String val)502 public void attributeDecl(String eName, String aName, String type, 503 String valueDefault, String val) throws SAXException { 504 if (internalDTDsubset) { 505 if (includeInternalDTDDeclarations) { 506 addDTDDeclaration(new AttributeDecl(eName, aName, type, 507 valueDefault, val)); 508 } 509 } else { 510 if (includeExternalDTDDeclarations) { 511 addExternalDTDDeclaration(new AttributeDecl(eName, aName, type, 512 valueDefault, val)); 513 } 514 } 515 } 516 517 /** 518 * Report an internal entity declaration. 519 * 520 * <p> 521 * Only the effective (first) declaration for each entity will be reported. 522 * All parameter entities in the value will be expanded, but general 523 * entities will not. 524 * </p> 525 * 526 * @param name 527 * The name of the entity. If it is a parameter entity, the name 528 * will begin with '%'. 529 * @param value 530 * The replacement text of the entity. 531 * 532 * @exception SAXException 533 * The application may raise an exception. 534 * 535 * @see #externalEntityDecl 536 * @see org.xml.sax.DTDHandler#unparsedEntityDecl 537 */ internalEntityDecl(String name, String value)538 public void internalEntityDecl(String name, String value) 539 throws SAXException { 540 if (internalDTDsubset) { 541 if (includeInternalDTDDeclarations) { 542 addDTDDeclaration(new InternalEntityDecl(name, value)); 543 } 544 } else { 545 if (includeExternalDTDDeclarations) { 546 addExternalDTDDeclaration(new InternalEntityDecl(name, value)); 547 } 548 } 549 } 550 551 /** 552 * Report a parsed external entity declaration. 553 * 554 * <p> 555 * Only the effective (first) declaration for each entity will be reported. 556 * </p> 557 * 558 * @param name 559 * The name of the entity. If it is a parameter entity, the name 560 * will begin with '%'. 561 * @param publicId 562 * The declared public identifier of the entity, or null if none 563 * was declared. 564 * @param sysId 565 * The declared system identifier of the entity. 566 * 567 * @exception SAXException 568 * The application may raise an exception. 569 * 570 * @see #internalEntityDecl 571 * @see org.xml.sax.DTDHandler#unparsedEntityDecl 572 */ externalEntityDecl(String name, String publicId, String sysId)573 public void externalEntityDecl(String name, String publicId, String sysId) 574 throws SAXException { 575 ExternalEntityDecl declaration = new ExternalEntityDecl(name, publicId, 576 sysId); 577 578 if (internalDTDsubset) { 579 if (includeInternalDTDDeclarations) { 580 addDTDDeclaration(declaration); 581 } 582 } else { 583 if (includeExternalDTDDeclarations) { 584 addExternalDTDDeclaration(declaration); 585 } 586 } 587 } 588 589 // DTDHandler interface 590 // ------------------------------------------------------------------------- 591 592 /** 593 * Receive notification of a notation declaration event. 594 * 595 * <p> 596 * It is up to the application to record the notation for later reference, 597 * if necessary. 598 * </p> 599 * 600 * <p> 601 * At least one of publicId and systemId must be non-null. If a system 602 * identifier is present, and it is a URL, the SAX parser must resolve it 603 * fully before passing it to the application through this event. 604 * </p> 605 * 606 * <p> 607 * There is no guarantee that the notation declaration will be reported 608 * before any unparsed entities that use it. 609 * </p> 610 * 611 * @param name 612 * The notation name. 613 * @param publicId 614 * The notation's public identifier, or null if none was given. 615 * @param systemId 616 * The notation's system identifier, or null if none was given. 617 * 618 * @exception SAXException 619 * Any SAX exception, possibly wrapping another exception. 620 * 621 * @see #unparsedEntityDecl 622 * @see org.xml.sax.AttributeList 623 */ notationDecl(String name, String publicId, String systemId)624 public void notationDecl(String name, String publicId, String systemId) 625 throws SAXException { 626 // #### not supported yet! 627 } 628 629 /** 630 * Receive notification of an unparsed entity declaration event. 631 * 632 * <p> 633 * Note that the notation name corresponds to a notation reported by the 634 * {@link #notationDecl notationDecl}event. It is up to the application to 635 * record the entity for later reference, if necessary. 636 * </p> 637 * 638 * <p> 639 * If the system identifier is a URL, the parser must resolve it fully 640 * before passing it to the application. 641 * </p> 642 * 643 * @param name 644 * The unparsed entity's name. 645 * @param publicId 646 * The entity's public identifier, or null if none was given. 647 * @param systemId 648 * The entity's system identifier. 649 * @param notationName 650 * The name of the associated notation. 651 * 652 * @exception SAXException 653 * Any SAX exception, possibly wrapping another exception. 654 * 655 * @see #notationDecl 656 * @see org.xml.sax.AttributeList 657 */ unparsedEntityDecl(String name, String publicId, String systemId, String notationName)658 public void unparsedEntityDecl(String name, String publicId, 659 String systemId, String notationName) throws SAXException { 660 // #### not supported yet! 661 } 662 663 // Properties 664 // ------------------------------------------------------------------------- getElementStack()665 public ElementStack getElementStack() { 666 return elementStack; 667 } 668 setElementStack(ElementStack elementStack)669 public void setElementStack(ElementStack elementStack) { 670 this.elementStack = elementStack; 671 } 672 getEntityResolver()673 public EntityResolver getEntityResolver() { 674 return entityResolver; 675 } 676 setEntityResolver(EntityResolver entityResolver)677 public void setEntityResolver(EntityResolver entityResolver) { 678 this.entityResolver = entityResolver; 679 } 680 getInputSource()681 public InputSource getInputSource() { 682 return inputSource; 683 } 684 setInputSource(InputSource inputSource)685 public void setInputSource(InputSource inputSource) { 686 this.inputSource = inputSource; 687 } 688 689 /** 690 * DOCUMENT ME! 691 * 692 * @return whether internal DTD declarations should be expanded into the 693 * DocumentType object or not. 694 */ isIncludeInternalDTDDeclarations()695 public boolean isIncludeInternalDTDDeclarations() { 696 return includeInternalDTDDeclarations; 697 } 698 699 /** 700 * Sets whether internal DTD declarations should be expanded into the 701 * DocumentType object or not. 702 * 703 * @param include 704 * whether or not DTD declarations should be expanded and 705 * included into the DocumentType object. 706 */ setIncludeInternalDTDDeclarations(boolean include)707 public void setIncludeInternalDTDDeclarations(boolean include) { 708 this.includeInternalDTDDeclarations = include; 709 } 710 711 /** 712 * DOCUMENT ME! 713 * 714 * @return whether external DTD declarations should be expanded into the 715 * DocumentType object or not. 716 */ isIncludeExternalDTDDeclarations()717 public boolean isIncludeExternalDTDDeclarations() { 718 return includeExternalDTDDeclarations; 719 } 720 721 /** 722 * Sets whether DTD external declarations should be expanded into the 723 * DocumentType object or not. 724 * 725 * @param include 726 * whether or not DTD declarations should be expanded and 727 * included into the DocumentType object. 728 */ setIncludeExternalDTDDeclarations(boolean include)729 public void setIncludeExternalDTDDeclarations(boolean include) { 730 this.includeExternalDTDDeclarations = include; 731 } 732 733 /** 734 * Returns whether adjacent text nodes should be merged together. 735 * 736 * @return Value of property mergeAdjacentText. 737 */ isMergeAdjacentText()738 public boolean isMergeAdjacentText() { 739 return mergeAdjacentText; 740 } 741 742 /** 743 * Sets whether or not adjacent text nodes should be merged together when 744 * parsing. 745 * 746 * @param mergeAdjacentText 747 * New value of property mergeAdjacentText. 748 */ setMergeAdjacentText(boolean mergeAdjacentText)749 public void setMergeAdjacentText(boolean mergeAdjacentText) { 750 this.mergeAdjacentText = mergeAdjacentText; 751 } 752 753 /** 754 * Sets whether whitespace between element start and end tags should be 755 * ignored 756 * 757 * @return Value of property stripWhitespaceText. 758 */ isStripWhitespaceText()759 public boolean isStripWhitespaceText() { 760 return stripWhitespaceText; 761 } 762 763 /** 764 * Sets whether whitespace between element start and end tags should be 765 * ignored. 766 * 767 * @param stripWhitespaceText 768 * New value of property stripWhitespaceText. 769 */ setStripWhitespaceText(boolean stripWhitespaceText)770 public void setStripWhitespaceText(boolean stripWhitespaceText) { 771 this.stripWhitespaceText = stripWhitespaceText; 772 } 773 774 /** 775 * Returns whether we should ignore comments or not. 776 * 777 * @return boolean 778 */ isIgnoreComments()779 public boolean isIgnoreComments() { 780 return ignoreComments; 781 } 782 783 /** 784 * Sets whether we should ignore comments or not. 785 * 786 * @param ignoreComments 787 * whether we should ignore comments or not. 788 */ setIgnoreComments(boolean ignoreComments)789 public void setIgnoreComments(boolean ignoreComments) { 790 this.ignoreComments = ignoreComments; 791 } 792 793 // Implementation methods 794 // ------------------------------------------------------------------------- 795 796 /** 797 * If the current text buffer contains any text then create a new text node 798 * with it and add it to the current element 799 */ completeCurrentTextNode()800 protected void completeCurrentTextNode() { 801 if (stripWhitespaceText) { 802 boolean whitespace = true; 803 804 for (int i = 0, size = textBuffer.length(); i < size; i++) { 805 if (!Character.isWhitespace(textBuffer.charAt(i))) { 806 whitespace = false; 807 808 break; 809 } 810 } 811 812 if (!whitespace) { 813 currentElement.addText(textBuffer.toString()); 814 } 815 } else { 816 currentElement.addText(textBuffer.toString()); 817 } 818 819 textBuffer.setLength(0); 820 textInTextBuffer = false; 821 } 822 823 /** 824 * DOCUMENT ME! 825 * 826 * @return the current document 827 */ createDocument()828 protected Document createDocument() { 829 String encoding = getEncoding(); 830 Document doc = documentFactory.createDocument(encoding); 831 832 // set the EntityResolver 833 doc.setEntityResolver(entityResolver); 834 835 if (inputSource != null) { 836 doc.setName(inputSource.getSystemId()); 837 } 838 839 return doc; 840 } 841 getEncoding()842 private String getEncoding() { 843 if (locator == null) { 844 return null; 845 } 846 847 // use reflection to avoid dependency on Locator2 848 // or other locator implemenations. 849 try { 850 Method m = locator.getClass().getMethod("getEncoding", 851 new Class[] {}); 852 853 if (m != null) { 854 return (String) m.invoke(locator, null); 855 } 856 } catch (Exception e) { 857 // do nothing 858 } 859 860 // couldn't determine encoding, returning null... 861 return null; 862 } 863 864 /** 865 * a Strategy Method to determine if a given entity name is ignorable 866 * 867 * @param name 868 * DOCUMENT ME! 869 * 870 * @return DOCUMENT ME! 871 */ isIgnorableEntity(String name)872 protected boolean isIgnorableEntity(String name) { 873 return "amp".equals(name) || "apos".equals(name) || "gt".equals(name) 874 || "lt".equals(name) || "quot".equals(name); 875 } 876 877 /** 878 * Add all namespaces declared before the startElement() SAX event to the 879 * current element so that they are available to child elements and 880 * attributes 881 * 882 * @param element 883 * DOCUMENT ME! 884 */ addDeclaredNamespaces(Element element)885 protected void addDeclaredNamespaces(Element element) { 886 Namespace elementNamespace = element.getNamespace(); 887 888 for (int size = namespaceStack.size(); declaredNamespaceIndex < size; 889 declaredNamespaceIndex++) { 890 Namespace namespace = namespaceStack 891 .getNamespace(declaredNamespaceIndex); 892 893 // if ( namespace != elementNamespace ) { 894 element.add(namespace); 895 896 // } 897 } 898 } 899 900 /** 901 * Add all the attributes to the given elements 902 * 903 * @param element 904 * DOCUMENT ME! 905 * @param attributes 906 * DOCUMENT ME! 907 */ addAttributes(Element element, Attributes attributes)908 protected void addAttributes(Element element, Attributes attributes) { 909 // XXXX: as an optimisation, we could deduce this value from the current 910 // SAX parser settings, the SAX namespaces-prefixes feature 911 boolean noNamespaceAttributes = false; 912 913 if (element instanceof AbstractElement) { 914 // optimised method 915 AbstractElement baseElement = (AbstractElement) element; 916 baseElement.setAttributes(attributes, namespaceStack, 917 noNamespaceAttributes); 918 } else { 919 int size = attributes.getLength(); 920 921 for (int i = 0; i < size; i++) { 922 String attributeQName = attributes.getQName(i); 923 924 if (noNamespaceAttributes 925 || !attributeQName.startsWith("xmlns")) { 926 String attributeURI = attributes.getURI(i); 927 String attributeLocalName = attributes.getLocalName(i); 928 String attributeValue = attributes.getValue(i); 929 930 QName qName = namespaceStack.getAttributeQName( 931 attributeURI, attributeLocalName, attributeQName); 932 element.addAttribute(qName, attributeValue); 933 } 934 } 935 } 936 } 937 938 /** 939 * Adds an internal DTD declaration to the list of declarations 940 * 941 * @param declaration 942 * DOCUMENT ME! 943 */ addDTDDeclaration(Object declaration)944 protected void addDTDDeclaration(Object declaration) { 945 if (internalDTDDeclarations == null) { 946 internalDTDDeclarations = new ArrayList(); 947 } 948 949 internalDTDDeclarations.add(declaration); 950 } 951 952 /** 953 * Adds an external DTD declaration to the list of declarations 954 * 955 * @param declaration 956 * DOCUMENT ME! 957 */ addExternalDTDDeclaration(Object declaration)958 protected void addExternalDTDDeclaration(Object declaration) { 959 if (externalDTDDeclarations == null) { 960 externalDTDDeclarations = new ArrayList(); 961 } 962 963 externalDTDDeclarations.add(declaration); 964 } 965 createElementStack()966 protected ElementStack createElementStack() { 967 return new ElementStack(); 968 } 969 } 970 971 /* 972 * Redistribution and use of this software and associated documentation 973 * ("Software"), with or without modification, are permitted provided that the 974 * following conditions are met: 975 * 976 * 1. Redistributions of source code must retain copyright statements and 977 * notices. Redistributions must also contain a copy of this document. 978 * 979 * 2. Redistributions in binary form must reproduce the above copyright notice, 980 * this list of conditions and the following disclaimer in the documentation 981 * and/or other materials provided with the distribution. 982 * 983 * 3. The name "DOM4J" must not be used to endorse or promote products derived 984 * from this Software without prior written permission of MetaStuff, Ltd. For 985 * written permission, please contact dom4j-info@metastuff.com. 986 * 987 * 4. Products derived from this Software may not be called "DOM4J" nor may 988 * "DOM4J" appear in their names without prior written permission of MetaStuff, 989 * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd. 990 * 991 * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org 992 * 993 * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND 994 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 995 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 996 * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE 997 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 998 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 999 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 1000 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 1001 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1002 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 1003 * POSSIBILITY OF SUCH DAMAGE. 1004 * 1005 * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved. 1006 */ 1007