001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: DOM3TreeWalker.java 1225446 2011-12-29 06:12:59Z mrglavas $ 020 */ 021 022 package org.apache.xml.serializer.dom3; 023 024 import java.io.File; 025 import java.io.IOException; 026 import java.io.Writer; 027 import java.util.Enumeration; 028 import java.util.Hashtable; 029 import java.util.Properties; 030 031 import org.apache.xml.serializer.dom3.NamespaceSupport; 032 import org.apache.xml.serializer.OutputPropertiesFactory; 033 import org.apache.xml.serializer.SerializationHandler; 034 import org.apache.xml.serializer.utils.MsgKey; 035 import org.apache.xml.serializer.utils.Utils; 036 import org.apache.xml.serializer.utils.XML11Char; 037 import org.apache.xml.serializer.utils.XMLChar; 038 import org.w3c.dom.Attr; 039 import org.w3c.dom.CDATASection; 040 import org.w3c.dom.Comment; 041 import org.w3c.dom.DOMError; 042 import org.w3c.dom.DOMErrorHandler; 043 import org.w3c.dom.Document; 044 import org.w3c.dom.DocumentType; 045 import org.w3c.dom.Element; 046 import org.w3c.dom.Entity; 047 import org.w3c.dom.EntityReference; 048 import org.w3c.dom.NamedNodeMap; 049 import org.w3c.dom.Node; 050 import org.w3c.dom.NodeList; 051 import org.w3c.dom.ProcessingInstruction; 052 import org.w3c.dom.Text; 053 import org.w3c.dom.ls.LSSerializerFilter; 054 import org.w3c.dom.traversal.NodeFilter; 055 import org.xml.sax.Locator; 056 import org.xml.sax.SAXException; 057 import org.xml.sax.ext.LexicalHandler; 058 import org.xml.sax.helpers.LocatorImpl; 059 060 /** 061 * Built on org.apache.xml.serializer.TreeWalker and adds functionality to 062 * traverse and serialize a DOM Node (Level 2 or Level 3) as specified in 063 * the DOM Level 3 LS Recommedation by evaluating and applying DOMConfiguration 064 * parameters and filters if any during serialization. 065 * 066 * @xsl.usage internal 067 */ 068 final class DOM3TreeWalker { 069 070 /** 071 * The SerializationHandler, it extends ContentHandler and when 072 * this class is instantiated via the constructor provided, a 073 * SerializationHandler object is passed to it. 074 */ 075 private SerializationHandler fSerializer = null; 076 077 /** We do not need DOM2Helper since DOM Level 3 LS applies to DOM Level 2 or newer */ 078 079 /** Locator object for this TreeWalker */ 080 private LocatorImpl fLocator = new LocatorImpl(); 081 082 /** ErrorHandler */ 083 private DOMErrorHandler fErrorHandler = null; 084 085 /** LSSerializerFilter */ 086 private LSSerializerFilter fFilter = null; 087 088 /** If the serializer is an instance of a LexicalHandler */ 089 private LexicalHandler fLexicalHandler = null; 090 091 private int fWhatToShowFilter; 092 093 /** New Line character to use in serialization */ 094 private String fNewLine = null; 095 096 /** DOMConfiguration Properties */ 097 private Properties fDOMConfigProperties = null; 098 099 /** Keeps track if we are in an entity reference when entities=true */ 100 private boolean fInEntityRef = false; 101 102 /** Stores the version of the XML document to be serialize */ 103 private String fXMLVersion = null; 104 105 /** XML Version, default 1.0 */ 106 private boolean fIsXMLVersion11 = false; 107 108 /** Is the Node a Level 3 DOM node */ 109 private boolean fIsLevel3DOM = false; 110 111 /** DOM Configuration Parameters */ 112 private int fFeatures = 0; 113 114 /** Flag indicating whether following text to be processed is raw text */ 115 boolean fNextIsRaw = false; 116 117 // 118 private static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; 119 120 // 121 private static final String XMLNS_PREFIX = "xmlns"; 122 123 // 124 private static final String XML_URI = "http://www.w3.org/XML/1998/namespace"; 125 126 // 127 private static final String XML_PREFIX = "xml"; 128 129 /** stores namespaces in scope */ 130 protected NamespaceSupport fNSBinder; 131 132 /** stores all namespace bindings on the current element */ 133 protected NamespaceSupport fLocalNSBinder; 134 135 /** stores the current element depth */ 136 private int fElementDepth = 0; 137 138 // *********************************************************************** 139 // DOMConfiguration paramter settings 140 // *********************************************************************** 141 // Parameter canonical-form, true [optional] - NOT SUPPORTED 142 private final static int CANONICAL = 0x1 << 0; 143 144 // Parameter cdata-sections, true [required] (default) 145 private final static int CDATA = 0x1 << 1; 146 147 // Parameter check-character-normalization, true [optional] - NOT SUPPORTED 148 private final static int CHARNORMALIZE = 0x1 << 2; 149 150 // Parameter comments, true [required] (default) 151 private final static int COMMENTS = 0x1 << 3; 152 153 // Parameter datatype-normalization, true [optional] - NOT SUPPORTED 154 private final static int DTNORMALIZE = 0x1 << 4; 155 156 // Parameter element-content-whitespace, true [required] (default) - value - false [optional] NOT SUPPORTED 157 private final static int ELEM_CONTENT_WHITESPACE = 0x1 << 5; 158 159 // Parameter entities, true [required] (default) 160 private final static int ENTITIES = 0x1 << 6; 161 162 // Parameter infoset, true [required] (default), false has no effect --> True has no effect for the serializer 163 private final static int INFOSET = 0x1 << 7; 164 165 // Parameter namespaces, true [required] (default) 166 private final static int NAMESPACES = 0x1 << 8; 167 168 // Parameter namespace-declarations, true [required] (default) 169 private final static int NAMESPACEDECLS = 0x1 << 9; 170 171 // Parameter normalize-characters, true [optional] - NOT SUPPORTED 172 private final static int NORMALIZECHARS = 0x1 << 10; 173 174 // Parameter split-cdata-sections, true [required] (default) 175 private final static int SPLITCDATA = 0x1 << 11; 176 177 // Parameter validate, true [optional] - NOT SUPPORTED 178 private final static int VALIDATE = 0x1 << 12; 179 180 // Parameter validate-if-schema, true [optional] - NOT SUPPORTED 181 private final static int SCHEMAVALIDATE = 0x1 << 13; 182 183 // Parameter split-cdata-sections, true [required] (default) 184 private final static int WELLFORMED = 0x1 << 14; 185 186 // Parameter discard-default-content, true [required] (default) 187 // Not sure how this will be used in level 2 Documents 188 private final static int DISCARDDEFAULT = 0x1 << 15; 189 190 // Parameter format-pretty-print, true [optional] 191 private final static int PRETTY_PRINT = 0x1 << 16; 192 193 // Parameter ignore-unknown-character-denormalizations, true [required] (default) 194 // We currently do not support XML 1.1 character normalization 195 private final static int IGNORE_CHAR_DENORMALIZE = 0x1 << 17; 196 197 // Parameter discard-default-content, true [required] (default) 198 private final static int XMLDECL = 0x1 << 18; 199 200 /** 201 * Constructor. 202 * @param contentHandler serialHandler The implemention of the SerializationHandler interface 203 */ 204 DOM3TreeWalker( 205 SerializationHandler serialHandler, 206 DOMErrorHandler errHandler, 207 LSSerializerFilter filter, 208 String newLine) { 209 fSerializer = serialHandler; 210 //fErrorHandler = errHandler == null ? new DOMErrorHandlerImpl() : errHandler; // Should we be using the default? 211 fErrorHandler = errHandler; 212 fFilter = filter; 213 fLexicalHandler = null; 214 fNewLine = newLine; 215 216 fNSBinder = new NamespaceSupport(); 217 fLocalNSBinder = new NamespaceSupport(); 218 219 fDOMConfigProperties = fSerializer.getOutputFormat(); 220 fSerializer.setDocumentLocator(fLocator); 221 initProperties(fDOMConfigProperties); 222 223 try { 224 // Bug see Bugzilla 26741 225 fLocator.setSystemId( 226 System.getProperty("user.dir") + File.separator + "dummy.xsl"); 227 } catch (SecurityException se) { // user.dir not accessible from applet 228 229 } 230 } 231 232 /** 233 * Perform a pre-order traversal non-recursive style. 234 * 235 * Note that TreeWalker assumes that the subtree is intended to represent 236 * a complete (though not necessarily well-formed) document and, during a 237 * traversal, startDocument and endDocument will always be issued to the 238 * SAX listener. 239 * 240 * @param pos Node in the tree where to start traversal 241 * 242 * @throws TransformerException 243 */ 244 public void traverse(Node pos) throws org.xml.sax.SAXException { 245 this.fSerializer.startDocument(); 246 247 // Determine if the Node is a DOM Level 3 Core Node. 248 if (pos.getNodeType() != Node.DOCUMENT_NODE) { 249 Document ownerDoc = pos.getOwnerDocument(); 250 if (ownerDoc != null 251 && ownerDoc.getImplementation().hasFeature("Core", "3.0")) { 252 fIsLevel3DOM = true; 253 } 254 } else { 255 if (((Document) pos) 256 .getImplementation() 257 .hasFeature("Core", "3.0")) { 258 fIsLevel3DOM = true; 259 } 260 } 261 262 if (fSerializer instanceof LexicalHandler) { 263 fLexicalHandler = ((LexicalHandler) this.fSerializer); 264 } 265 266 if (fFilter != null) 267 fWhatToShowFilter = fFilter.getWhatToShow(); 268 269 Node top = pos; 270 271 while (null != pos) { 272 startNode(pos); 273 274 Node nextNode = null; 275 276 nextNode = pos.getFirstChild(); 277 278 while (null == nextNode) { 279 endNode(pos); 280 281 if (top.equals(pos)) 282 break; 283 284 nextNode = pos.getNextSibling(); 285 286 if (null == nextNode) { 287 pos = pos.getParentNode(); 288 289 if ((null == pos) || (top.equals(pos))) { 290 if (null != pos) 291 endNode(pos); 292 293 nextNode = null; 294 295 break; 296 } 297 } 298 } 299 300 pos = nextNode; 301 } 302 this.fSerializer.endDocument(); 303 } 304 305 /** 306 * Perform a pre-order traversal non-recursive style. 307 308 * Note that TreeWalker assumes that the subtree is intended to represent 309 * a complete (though not necessarily well-formed) document and, during a 310 * traversal, startDocument and endDocument will always be issued to the 311 * SAX listener. 312 * 313 * @param pos Node in the tree where to start traversal 314 * @param top Node in the tree where to end traversal 315 * 316 * @throws TransformerException 317 */ 318 public void traverse(Node pos, Node top) throws org.xml.sax.SAXException { 319 320 this.fSerializer.startDocument(); 321 322 // Determine if the Node is a DOM Level 3 Core Node. 323 if (pos.getNodeType() != Node.DOCUMENT_NODE) { 324 Document ownerDoc = pos.getOwnerDocument(); 325 if (ownerDoc != null 326 && ownerDoc.getImplementation().hasFeature("Core", "3.0")) { 327 fIsLevel3DOM = true; 328 } 329 } else { 330 if (((Document) pos) 331 .getImplementation() 332 .hasFeature("Core", "3.0")) { 333 fIsLevel3DOM = true; 334 } 335 } 336 337 if (fSerializer instanceof LexicalHandler) { 338 fLexicalHandler = ((LexicalHandler) this.fSerializer); 339 } 340 341 if (fFilter != null) 342 fWhatToShowFilter = fFilter.getWhatToShow(); 343 344 while (null != pos) { 345 startNode(pos); 346 347 Node nextNode = null; 348 349 nextNode = pos.getFirstChild(); 350 351 while (null == nextNode) { 352 endNode(pos); 353 354 if ((null != top) && top.equals(pos)) 355 break; 356 357 nextNode = pos.getNextSibling(); 358 359 if (null == nextNode) { 360 pos = pos.getParentNode(); 361 362 if ((null == pos) || ((null != top) && top.equals(pos))) { 363 nextNode = null; 364 365 break; 366 } 367 } 368 } 369 370 pos = nextNode; 371 } 372 this.fSerializer.endDocument(); 373 } 374 375 /** 376 * Optimized dispatch of characters. 377 */ 378 private final void dispatachChars(Node node) 379 throws org.xml.sax.SAXException { 380 if (fSerializer != null) { 381 this.fSerializer.characters(node); 382 } else { 383 String data = ((Text) node).getData(); 384 this.fSerializer.characters(data.toCharArray(), 0, data.length()); 385 } 386 } 387 388 /** 389 * Start processing given node 390 * 391 * @param node Node to process 392 * 393 * @throws org.xml.sax.SAXException 394 */ 395 protected void startNode(Node node) throws org.xml.sax.SAXException { 396 if (node instanceof Locator) { 397 Locator loc = (Locator) node; 398 fLocator.setColumnNumber(loc.getColumnNumber()); 399 fLocator.setLineNumber(loc.getLineNumber()); 400 fLocator.setPublicId(loc.getPublicId()); 401 fLocator.setSystemId(loc.getSystemId()); 402 } else { 403 fLocator.setColumnNumber(0); 404 fLocator.setLineNumber(0); 405 } 406 407 switch (node.getNodeType()) { 408 case Node.DOCUMENT_TYPE_NODE : 409 serializeDocType((DocumentType) node, true); 410 break; 411 case Node.COMMENT_NODE : 412 serializeComment((Comment) node); 413 break; 414 case Node.DOCUMENT_FRAGMENT_NODE : 415 // Children are traversed 416 break; 417 case Node.DOCUMENT_NODE : 418 break; 419 case Node.ELEMENT_NODE : 420 serializeElement((Element) node, true); 421 break; 422 case Node.PROCESSING_INSTRUCTION_NODE : 423 serializePI((ProcessingInstruction) node); 424 break; 425 case Node.CDATA_SECTION_NODE : 426 serializeCDATASection((CDATASection) node); 427 break; 428 case Node.TEXT_NODE : 429 serializeText((Text) node); 430 break; 431 case Node.ENTITY_REFERENCE_NODE : 432 serializeEntityReference((EntityReference) node, true); 433 break; 434 default : 435 } 436 } 437 438 /** 439 * End processing of given node 440 * 441 * 442 * @param node Node we just finished processing 443 * 444 * @throws org.xml.sax.SAXException 445 */ 446 protected void endNode(Node node) throws org.xml.sax.SAXException { 447 448 switch (node.getNodeType()) { 449 case Node.DOCUMENT_NODE : 450 break; 451 case Node.DOCUMENT_TYPE_NODE : 452 serializeDocType((DocumentType) node, false); 453 break; 454 case Node.ELEMENT_NODE : 455 serializeElement((Element) node, false); 456 break; 457 case Node.CDATA_SECTION_NODE : 458 break; 459 case Node.ENTITY_REFERENCE_NODE : 460 serializeEntityReference((EntityReference) node, false); 461 break; 462 default : 463 } 464 } 465 466 // *********************************************************************** 467 // Node serialization methods 468 // *********************************************************************** 469 /** 470 * Applies a filter on the node to serialize 471 * 472 * @param node The Node to serialize 473 * @return True if the node is to be serialized else false if the node 474 * is to be rejected or skipped. 475 */ 476 protected boolean applyFilter(Node node, int nodeType) { 477 if (fFilter != null && (fWhatToShowFilter & nodeType) != 0) { 478 479 short code = fFilter.acceptNode(node); 480 switch (code) { 481 case NodeFilter.FILTER_REJECT : 482 case NodeFilter.FILTER_SKIP : 483 return false; // skip the node 484 default : // fall through.. 485 } 486 } 487 return true; 488 } 489 490 /** 491 * Serializes a Document Type Node. 492 * 493 * @param node The Docuemnt Type Node to serialize 494 * @param bStart Invoked at the start or end of node. Default true. 495 */ 496 protected void serializeDocType(DocumentType node, boolean bStart) 497 throws SAXException { 498 // The DocType and internalSubset can not be modified in DOM and is 499 // considered to be well-formed as the outcome of successful parsing. 500 String docTypeName = node.getNodeName(); 501 String publicId = node.getPublicId(); 502 String systemId = node.getSystemId(); 503 String internalSubset = node.getInternalSubset(); 504 505 //DocumentType nodes are never passed to the filter 506 507 if (internalSubset != null && !"".equals(internalSubset)) { 508 509 if (bStart) { 510 try { 511 // The Serializer does not provide a way to write out the 512 // DOCTYPE internal subset via an event call, so we write it 513 // out here. 514 Writer writer = fSerializer.getWriter(); 515 StringBuffer dtd = new StringBuffer(); 516 517 dtd.append("<!DOCTYPE "); 518 dtd.append(docTypeName); 519 if (null != publicId) { 520 dtd.append(" PUBLIC \""); 521 dtd.append(publicId); 522 dtd.append('\"'); 523 } 524 525 if (null != systemId) { 526 if (null == publicId) { 527 dtd.append(" SYSTEM \""); 528 } else { 529 dtd.append(" \""); 530 } 531 dtd.append(systemId); 532 dtd.append('\"'); 533 } 534 535 dtd.append(" [ "); 536 537 dtd.append(fNewLine); 538 dtd.append(internalSubset); 539 dtd.append("]>"); 540 dtd.append(fNewLine); 541 542 writer.write(dtd.toString()); 543 writer.flush(); 544 545 } catch (IOException e) { 546 throw new SAXException(Utils.messages.createMessage( 547 MsgKey.ER_WRITING_INTERNAL_SUBSET, null), e); 548 } 549 } // else if !bStart do nothing 550 551 } else { 552 553 if (bStart) { 554 if (fLexicalHandler != null) { 555 fLexicalHandler.startDTD(docTypeName, publicId, systemId); 556 } 557 } else { 558 if (fLexicalHandler != null) { 559 fLexicalHandler.endDTD(); 560 } 561 } 562 } 563 } 564 565 /** 566 * Serializes a Comment Node. 567 * 568 * @param node The Comment Node to serialize 569 */ 570 protected void serializeComment(Comment node) throws SAXException { 571 // comments=true 572 if ((fFeatures & COMMENTS) != 0) { 573 String data = node.getData(); 574 575 // well-formed=true 576 if ((fFeatures & WELLFORMED) != 0) { 577 isCommentWellFormed(data); 578 } 579 580 if (fLexicalHandler != null) { 581 // apply the LSSerializer filter after the operations requested by the 582 // DOMConfiguration parameters have been applied 583 if (!applyFilter(node, NodeFilter.SHOW_COMMENT)) { 584 return; 585 } 586 587 fLexicalHandler.comment(data.toCharArray(), 0, data.length()); 588 } 589 } 590 } 591 592 /** 593 * Serializes an Element Node. 594 * 595 * @param node The Element Node to serialize 596 * @param bStart Invoked at the start or end of node. 597 */ 598 protected void serializeElement(Element node, boolean bStart) 599 throws SAXException { 600 if (bStart) { 601 fElementDepth++; 602 603 // We use the Xalan specific startElement and starPrefixMapping calls 604 // (and addAttribute and namespaceAfterStartElement) as opposed to 605 // SAX specific, for performance reasons as they reduce the overhead 606 // of creating an AttList object upfront. 607 608 // well-formed=true 609 if ((fFeatures & WELLFORMED) != 0) { 610 isElementWellFormed(node); 611 } 612 613 // REVISIT: We apply the LSSerializer filter for elements before 614 // namesapce fixup 615 if (!applyFilter(node, NodeFilter.SHOW_ELEMENT)) { 616 return; 617 } 618 619 // namespaces=true, record and fixup namspaced element 620 if ((fFeatures & NAMESPACES) != 0) { 621 fNSBinder.pushContext(); 622 fLocalNSBinder.reset(); 623 624 recordLocalNSDecl(node); 625 fixupElementNS(node); 626 } 627 628 // Namespace normalization 629 fSerializer.startElement( 630 node.getNamespaceURI(), 631 node.getLocalName(), 632 node.getNodeName()); 633 634 serializeAttList(node); 635 636 } else { 637 fElementDepth--; 638 639 // apply the LSSerializer filter 640 if (!applyFilter(node, NodeFilter.SHOW_ELEMENT)) { 641 return; 642 } 643 644 this.fSerializer.endElement( 645 node.getNamespaceURI(), 646 node.getLocalName(), 647 node.getNodeName()); 648 // since endPrefixMapping was not used by SerializationHandler it was removed 649 // for performance reasons. 650 651 if ((fFeatures & NAMESPACES) != 0 ) { 652 fNSBinder.popContext(); 653 } 654 655 } 656 } 657 658 /** 659 * Serializes the Attr Nodes of an Element. 660 * 661 * @param node The OwnerElement whose Attr Nodes are to be serialized. 662 */ 663 protected void serializeAttList(Element node) throws SAXException { 664 NamedNodeMap atts = node.getAttributes(); 665 int nAttrs = atts.getLength(); 666 667 for (int i = 0; i < nAttrs; i++) { 668 Node attr = atts.item(i); 669 670 String localName = attr.getLocalName(); 671 String attrName = attr.getNodeName(); 672 String attrPrefix = attr.getPrefix() == null ? "" : attr.getPrefix(); 673 String attrValue = attr.getNodeValue(); 674 675 // Determine the Attr's type. 676 String type = null; 677 if (fIsLevel3DOM) { 678 type = ((Attr) attr).getSchemaTypeInfo().getTypeName(); 679 } 680 type = type == null ? "CDATA" : type; 681 682 String attrNS = attr.getNamespaceURI(); 683 if (attrNS !=null && attrNS.length() == 0) { 684 attrNS=null; 685 // we must remove prefix for this attribute 686 attrName=attr.getLocalName(); 687 } 688 689 boolean isSpecified = ((Attr) attr).getSpecified(); 690 boolean addAttr = true; 691 boolean applyFilter = false; 692 boolean xmlnsAttr = 693 attrName.equals("xmlns") || attrName.startsWith("xmlns:"); 694 695 // well-formed=true 696 if ((fFeatures & WELLFORMED) != 0) { 697 isAttributeWellFormed(attr); 698 } 699 700 //----------------------------------------------------------------- 701 // start Attribute namespace fixup 702 //----------------------------------------------------------------- 703 // namespaces=true, normalize all non-namespace attributes 704 // Step 3. Attribute 705 if ((fFeatures & NAMESPACES) != 0 && !xmlnsAttr) { 706 707 // If the Attr has a namespace URI 708 if (attrNS != null) { 709 attrPrefix = attrPrefix == null ? "" : attrPrefix; 710 711 String declAttrPrefix = fNSBinder.getPrefix(attrNS); 712 String declAttrNS = fNSBinder.getURI(attrPrefix); 713 714 // attribute has no prefix (default namespace decl does not apply to 715 // attributes) 716 // OR 717 // attribute prefix is not declared 718 // OR 719 // conflict: attribute has a prefix that conflicts with a binding 720 if ("".equals(attrPrefix) || "".equals(declAttrPrefix) 721 || !attrPrefix.equals(declAttrPrefix)) { 722 723 // namespaceURI matches an in scope declaration of one or 724 // more prefixes 725 if (declAttrPrefix != null && !"".equals(declAttrPrefix)) { 726 // pick the prefix that was found and change attribute's 727 // prefix and nodeName. 728 attrPrefix = declAttrPrefix; 729 730 if (declAttrPrefix.length() > 0 ) { 731 attrName = declAttrPrefix + ":" + localName; 732 } else { 733 attrName = localName; 734 } 735 } else { 736 // The current prefix is not null and it has no in scope 737 // declaration 738 if (attrPrefix != null && !"".equals(attrPrefix) 739 && declAttrNS == null) { 740 // declare this prefix 741 if ((fFeatures & NAMESPACEDECLS) != 0) { 742 fSerializer.addAttribute(XMLNS_URI, attrPrefix, 743 XMLNS_PREFIX + ":" + attrPrefix, "CDATA", 744 attrNS); 745 fNSBinder.declarePrefix(attrPrefix, attrNS); 746 fLocalNSBinder.declarePrefix(attrPrefix, attrNS); 747 } 748 } else { 749 // find a prefix following the pattern "NS" +index 750 // (starting at 1) 751 // make sure this prefix is not declared in the current 752 // scope. 753 int counter = 1; 754 attrPrefix = "NS" + counter++; 755 756 while (fLocalNSBinder.getURI(attrPrefix) != null) { 757 attrPrefix = "NS" + counter++; 758 } 759 // change attribute's prefix and Name 760 attrName = attrPrefix + ":" + localName; 761 762 // create a local namespace declaration attribute 763 // Add the xmlns declaration attribute 764 if ((fFeatures & NAMESPACEDECLS) != 0) { 765 766 fSerializer.addAttribute(XMLNS_URI, attrPrefix, 767 XMLNS_PREFIX + ":" + attrPrefix, "CDATA", 768 attrNS); 769 fNSBinder.declarePrefix(attrPrefix, attrNS); 770 fLocalNSBinder.declarePrefix(attrPrefix, attrNS); 771 } 772 } 773 } 774 } 775 776 } else { // if the Attr has no namespace URI 777 // Attr has no localName 778 if (localName == null) { 779 // DOM Level 1 node! 780 String msg = Utils.messages.createMessage( 781 MsgKey.ER_NULL_LOCAL_ELEMENT_NAME, 782 new Object[] { attrName }); 783 784 if (fErrorHandler != null) { 785 fErrorHandler 786 .handleError(new DOMErrorImpl( 787 DOMError.SEVERITY_ERROR, msg, 788 MsgKey.ER_NULL_LOCAL_ELEMENT_NAME, null, 789 null, null)); 790 } 791 792 } else { // uri=null and no colon 793 // attr has no namespace URI and no prefix 794 // no action is required, since attrs don't use default 795 } 796 } 797 798 } 799 800 801 // discard-default-content=true 802 // Default attr's are not passed to the filter and this contraint 803 // is applied only when discard-default-content=true 804 // What about default xmlns attributes???? check for xmlnsAttr 805 if ((((fFeatures & DISCARDDEFAULT) != 0) && isSpecified) 806 || ((fFeatures & DISCARDDEFAULT) == 0)) { 807 applyFilter = true; 808 } else { 809 addAttr = false; 810 } 811 812 if (applyFilter) { 813 // apply the filter for Attributes that are not default attributes 814 // or namespace decl attributes 815 if (fFilter != null 816 && (fFilter.getWhatToShow() & NodeFilter.SHOW_ATTRIBUTE) 817 != 0) { 818 819 if (!xmlnsAttr) { 820 short code = fFilter.acceptNode(attr); 821 switch (code) { 822 case NodeFilter.FILTER_REJECT : 823 case NodeFilter.FILTER_SKIP : 824 addAttr = false; 825 break; 826 default : //fall through.. 827 } 828 } 829 } 830 } 831 832 // if the node is a namespace node 833 if (addAttr && xmlnsAttr) { 834 // If namespace-declarations=true, add the node , else don't add it 835 if ((fFeatures & NAMESPACEDECLS) != 0) { 836 // The namespace may have been fixed up, in that case don't add it. 837 if (localName != null && !"".equals(localName)) { 838 fSerializer.addAttribute(attrNS, localName, attrName, type, attrValue); 839 } 840 } 841 } else if ( 842 addAttr && !xmlnsAttr) { // if the node is not a namespace node 843 // If namespace-declarations=true, add the node with the Attr nodes namespaceURI 844 // else add the node setting it's namespace to null or else the serializer will later 845 // attempt to add a xmlns attr for the prefixed attribute 846 if (((fFeatures & NAMESPACEDECLS) != 0) && (attrNS != null)) { 847 fSerializer.addAttribute( 848 attrNS, 849 localName, 850 attrName, 851 type, 852 attrValue); 853 } else { 854 fSerializer.addAttribute( 855 "", 856 localName, 857 attrName, 858 type, 859 attrValue); 860 } 861 } 862 863 // 864 if (xmlnsAttr && ((fFeatures & NAMESPACEDECLS) != 0)) { 865 int index; 866 // Use "" instead of null, as Xerces likes "" for the 867 // name of the default namespace. Fix attributed 868 // to "Steven Murray" <smurray@ebt.com>. 869 String prefix = 870 (index = attrName.indexOf(":")) < 0 871 ? "" 872 : attrName.substring(index + 1); 873 874 if (!"".equals(prefix)) { 875 fSerializer.namespaceAfterStartElement(prefix, attrValue); 876 } 877 } 878 } 879 880 } 881 882 /** 883 * Serializes an ProcessingInstruction Node. 884 * 885 * @param node The ProcessingInstruction Node to serialize 886 */ 887 protected void serializePI(ProcessingInstruction node) 888 throws SAXException { 889 ProcessingInstruction pi = node; 890 String name = pi.getNodeName(); 891 892 // well-formed=true 893 if ((fFeatures & WELLFORMED) != 0) { 894 isPIWellFormed(node); 895 } 896 897 // apply the LSSerializer filter 898 if (!applyFilter(node, NodeFilter.SHOW_PROCESSING_INSTRUCTION)) { 899 return; 900 } 901 902 // String data = pi.getData(); 903 if (name.equals("xslt-next-is-raw")) { 904 fNextIsRaw = true; 905 } else { 906 this.fSerializer.processingInstruction(name, pi.getData()); 907 } 908 } 909 910 /** 911 * Serializes an CDATASection Node. 912 * 913 * @param node The CDATASection Node to serialize 914 */ 915 protected void serializeCDATASection(CDATASection node) 916 throws SAXException { 917 // well-formed=true 918 if ((fFeatures & WELLFORMED) != 0) { 919 isCDATASectionWellFormed(node); 920 } 921 922 // cdata-sections = true 923 if ((fFeatures & CDATA) != 0) { 924 925 // split-cdata-sections = true 926 // Assumption: This parameter has an effect only when 927 // cdata-sections=true 928 // ToStream, by default splits cdata-sections. Hence the check 929 // below. 930 String nodeValue = node.getNodeValue(); 931 int endIndex = nodeValue.indexOf("]]>"); 932 if ((fFeatures & SPLITCDATA) != 0) { 933 if (endIndex >= 0) { 934 // The first node split will contain the ]] markers 935 String relatedData = nodeValue.substring(0, endIndex + 2); 936 937 String msg = 938 Utils.messages.createMessage( 939 MsgKey.ER_CDATA_SECTIONS_SPLIT, 940 null); 941 942 if (fErrorHandler != null) { 943 fErrorHandler.handleError( 944 new DOMErrorImpl( 945 DOMError.SEVERITY_WARNING, 946 msg, 947 MsgKey.ER_CDATA_SECTIONS_SPLIT, 948 null, 949 relatedData, 950 null)); 951 } 952 } 953 } else { 954 if (endIndex >= 0) { 955 // The first node split will contain the ]] markers 956 String relatedData = nodeValue.substring(0, endIndex + 2); 957 958 String msg = 959 Utils.messages.createMessage( 960 MsgKey.ER_CDATA_SECTIONS_SPLIT, 961 null); 962 963 if (fErrorHandler != null) { 964 fErrorHandler.handleError( 965 new DOMErrorImpl( 966 DOMError.SEVERITY_ERROR, 967 msg, 968 MsgKey.ER_CDATA_SECTIONS_SPLIT)); 969 } 970 // Report an error and return. What error??? 971 return; 972 } 973 } 974 975 // apply the LSSerializer filter 976 if (!applyFilter(node, NodeFilter.SHOW_CDATA_SECTION)) { 977 return; 978 } 979 980 // splits the cdata-section 981 if (fLexicalHandler != null) { 982 fLexicalHandler.startCDATA(); 983 } 984 dispatachChars(node); 985 if (fLexicalHandler != null) { 986 fLexicalHandler.endCDATA(); 987 } 988 } else { 989 dispatachChars(node); 990 } 991 } 992 993 /** 994 * Serializes an Text Node. 995 * 996 * @param node The Text Node to serialize 997 */ 998 protected void serializeText(Text node) throws SAXException { 999 if (fNextIsRaw) { 1000 fNextIsRaw = false; 1001 fSerializer.processingInstruction( 1002 javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, 1003 ""); 1004 dispatachChars(node); 1005 fSerializer.processingInstruction( 1006 javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, 1007 ""); 1008 } else { 1009 // keep track of dispatch or not to avoid duplicaiton of filter code 1010 boolean bDispatch = false; 1011 1012 // well-formed=true 1013 if ((fFeatures & WELLFORMED) != 0) { 1014 isTextWellFormed(node); 1015 } 1016 1017 // if the node is whitespace 1018 // Determine the Attr's type. 1019 boolean isElementContentWhitespace = false; 1020 if (fIsLevel3DOM) { 1021 isElementContentWhitespace = 1022 node.isElementContentWhitespace(); 1023 } 1024 1025 if (isElementContentWhitespace) { 1026 // element-content-whitespace=true 1027 if ((fFeatures & ELEM_CONTENT_WHITESPACE) != 0) { 1028 bDispatch = true; 1029 } 1030 } else { 1031 bDispatch = true; 1032 } 1033 1034 // apply the LSSerializer filter 1035 if (!applyFilter(node, NodeFilter.SHOW_TEXT)) { 1036 return; 1037 } 1038 1039 if (bDispatch) { 1040 dispatachChars(node); 1041 } 1042 } 1043 } 1044 1045 /** 1046 * Serializes an EntityReference Node. 1047 * 1048 * @param node The EntityReference Node to serialize 1049 * @param bStart Inicates if called from start or endNode 1050 */ 1051 protected void serializeEntityReference( 1052 EntityReference node, 1053 boolean bStart) 1054 throws SAXException { 1055 if (bStart) { 1056 EntityReference eref = node; 1057 // entities=true 1058 if ((fFeatures & ENTITIES) != 0) { 1059 1060 // perform well-formedness and other checking only if 1061 // entities = true 1062 1063 // well-formed=true 1064 if ((fFeatures & WELLFORMED) != 0) { 1065 isEntityReferneceWellFormed(node); 1066 } 1067 1068 // check "unbound-prefix-in-entity-reference" [fatal] 1069 // Raised if the configuration parameter "namespaces" is set to true 1070 if ((fFeatures & NAMESPACES) != 0) { 1071 checkUnboundPrefixInEntRef(node); 1072 } 1073 1074 // The filter should not apply in this case, since the 1075 // EntityReference is not being expanded. 1076 // should we pass entity reference nodes to the filter??? 1077 } 1078 1079 if (fLexicalHandler != null) { 1080 1081 // startEntity outputs only Text but not Element, Attr, Comment 1082 // and PI child nodes. It does so by setting the m_inEntityRef 1083 // in ToStream and using this to decide if a node is to be 1084 // serialized or not. 1085 fLexicalHandler.startEntity(eref.getNodeName()); 1086 } 1087 1088 } else { 1089 EntityReference eref = node; 1090 // entities=true or false, 1091 if (fLexicalHandler != null) { 1092 fLexicalHandler.endEntity(eref.getNodeName()); 1093 } 1094 } 1095 } 1096 1097 1098 // *********************************************************************** 1099 // Methods to check well-formedness 1100 // *********************************************************************** 1101 /** 1102 * Taken from org.apache.xerces.dom.CoreDocumentImpl 1103 * 1104 * Check the string against XML's definition of acceptable names for 1105 * elements and attributes and so on using the XMLCharacterProperties 1106 * utility class 1107 */ 1108 protected boolean isXMLName(String s, boolean xml11Version) { 1109 1110 if (s == null) { 1111 return false; 1112 } 1113 if (!xml11Version) 1114 return XMLChar.isValidName(s); 1115 else 1116 return XML11Char.isXML11ValidName(s); 1117 } 1118 1119 /** 1120 * Taken from org.apache.xerces.dom.CoreDocumentImpl 1121 * 1122 * Checks if the given qualified name is legal with respect 1123 * to the version of XML to which this document must conform. 1124 * 1125 * @param prefix prefix of qualified name 1126 * @param local local part of qualified name 1127 */ 1128 protected boolean isValidQName( 1129 String prefix, 1130 String local, 1131 boolean xml11Version) { 1132 1133 // check that both prefix and local part match NCName 1134 if (local == null) 1135 return false; 1136 boolean validNCName = false; 1137 1138 if (!xml11Version) { 1139 validNCName = 1140 (prefix == null || XMLChar.isValidNCName(prefix)) 1141 && XMLChar.isValidNCName(local); 1142 } else { 1143 validNCName = 1144 (prefix == null || XML11Char.isXML11ValidNCName(prefix)) 1145 && XML11Char.isXML11ValidNCName(local); 1146 } 1147 1148 return validNCName; 1149 } 1150 1151 /** 1152 * Checks if a XML character is well-formed 1153 * 1154 * @param characters A String of characters to be checked for Well-Formedness 1155 * @param refInvalidChar A reference to the character to be returned that was determined invalid. 1156 */ 1157 protected boolean isWFXMLChar(String chardata, Character refInvalidChar) { 1158 if (chardata == null || (chardata.length() == 0)) { 1159 return true; 1160 } 1161 1162 char[] dataarray = chardata.toCharArray(); 1163 int datalength = dataarray.length; 1164 1165 // version of the document is XML 1.1 1166 if (fIsXMLVersion11) { 1167 //we need to check all characters as per production rules of XML11 1168 int i = 0; 1169 while (i < datalength) { 1170 if (XML11Char.isXML11Invalid(dataarray[i++])) { 1171 // check if this is a supplemental character 1172 char ch = dataarray[i - 1]; 1173 if (XMLChar.isHighSurrogate(ch) && i < datalength) { 1174 char ch2 = dataarray[i++]; 1175 if (XMLChar.isLowSurrogate(ch2) 1176 && XMLChar.isSupplemental( 1177 XMLChar.supplemental(ch, ch2))) { 1178 continue; 1179 } 1180 } 1181 // Reference to invalid character which is returned 1182 refInvalidChar = new Character(ch); 1183 return false; 1184 } 1185 } 1186 } // version of the document is XML 1.0 1187 else { 1188 // we need to check all characters as per production rules of XML 1.0 1189 int i = 0; 1190 while (i < datalength) { 1191 if (XMLChar.isInvalid(dataarray[i++])) { 1192 // check if this is a supplemental character 1193 char ch = dataarray[i - 1]; 1194 if (XMLChar.isHighSurrogate(ch) && i < datalength) { 1195 char ch2 = dataarray[i++]; 1196 if (XMLChar.isLowSurrogate(ch2) 1197 && XMLChar.isSupplemental( 1198 XMLChar.supplemental(ch, ch2))) { 1199 continue; 1200 } 1201 } 1202 // Reference to invalid character which is returned 1203 refInvalidChar = new Character(ch); 1204 return false; 1205 } 1206 } 1207 } // end-else fDocument.isXMLVersion() 1208 1209 return true; 1210 } // isXMLCharWF 1211 1212 /** 1213 * Checks if a XML character is well-formed. If there is a problem with 1214 * the character a non-null Character is returned else null is returned. 1215 * 1216 * @param characters A String of characters to be checked for Well-Formedness 1217 * @return Character A reference to the character to be returned that was determined invalid. 1218 */ 1219 protected Character isWFXMLChar(String chardata) { 1220 Character refInvalidChar; 1221 if (chardata == null || (chardata.length() == 0)) { 1222 return null; 1223 } 1224 1225 char[] dataarray = chardata.toCharArray(); 1226 int datalength = dataarray.length; 1227 1228 // version of the document is XML 1.1 1229 if (fIsXMLVersion11) { 1230 //we need to check all characters as per production rules of XML11 1231 int i = 0; 1232 while (i < datalength) { 1233 if (XML11Char.isXML11Invalid(dataarray[i++])) { 1234 // check if this is a supplemental character 1235 char ch = dataarray[i - 1]; 1236 if (XMLChar.isHighSurrogate(ch) && i < datalength) { 1237 char ch2 = dataarray[i++]; 1238 if (XMLChar.isLowSurrogate(ch2) 1239 && XMLChar.isSupplemental( 1240 XMLChar.supplemental(ch, ch2))) { 1241 continue; 1242 } 1243 } 1244 // Reference to invalid character which is returned 1245 refInvalidChar = new Character(ch); 1246 return refInvalidChar; 1247 } 1248 } 1249 } // version of the document is XML 1.0 1250 else { 1251 // we need to check all characters as per production rules of XML 1.0 1252 int i = 0; 1253 while (i < datalength) { 1254 if (XMLChar.isInvalid(dataarray[i++])) { 1255 // check if this is a supplemental character 1256 char ch = dataarray[i - 1]; 1257 if (XMLChar.isHighSurrogate(ch) && i < datalength) { 1258 char ch2 = dataarray[i++]; 1259 if (XMLChar.isLowSurrogate(ch2) 1260 && XMLChar.isSupplemental( 1261 XMLChar.supplemental(ch, ch2))) { 1262 continue; 1263 } 1264 } 1265 // Reference to invalid character which is returned 1266 refInvalidChar = new Character(ch); 1267 return refInvalidChar; 1268 } 1269 } 1270 } // end-else fDocument.isXMLVersion() 1271 1272 return null; 1273 } // isXMLCharWF 1274 1275 /** 1276 * Checks if a comment node is well-formed 1277 * 1278 * @param data The contents of the comment node 1279 * @return a boolean indiacating if the comment is well-formed or not. 1280 */ 1281 protected void isCommentWellFormed(String data) { 1282 if (data == null || (data.length() == 0)) { 1283 return; 1284 } 1285 1286 char[] dataarray = data.toCharArray(); 1287 int datalength = dataarray.length; 1288 1289 // version of the document is XML 1.1 1290 if (fIsXMLVersion11) { 1291 // we need to check all chracters as per production rules of XML11 1292 int i = 0; 1293 while (i < datalength) { 1294 char c = dataarray[i++]; 1295 if (XML11Char.isXML11Invalid(c)) { 1296 // check if this is a supplemental character 1297 if (XMLChar.isHighSurrogate(c) && i < datalength) { 1298 char c2 = dataarray[i++]; 1299 if (XMLChar.isLowSurrogate(c2) 1300 && XMLChar.isSupplemental( 1301 XMLChar.supplemental(c, c2))) { 1302 continue; 1303 } 1304 } 1305 String msg = 1306 Utils.messages.createMessage( 1307 MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT, 1308 new Object[] { new Character(c)}); 1309 1310 if (fErrorHandler != null) { 1311 fErrorHandler.handleError( 1312 new DOMErrorImpl( 1313 DOMError.SEVERITY_FATAL_ERROR, 1314 msg, 1315 MsgKey.ER_WF_INVALID_CHARACTER, 1316 null, 1317 null, 1318 null)); 1319 } 1320 } else if (c == '-' && i < datalength && dataarray[i] == '-') { 1321 String msg = 1322 Utils.messages.createMessage( 1323 MsgKey.ER_WF_DASH_IN_COMMENT, 1324 null); 1325 1326 if (fErrorHandler != null) { 1327 fErrorHandler.handleError( 1328 new DOMErrorImpl( 1329 DOMError.SEVERITY_FATAL_ERROR, 1330 msg, 1331 MsgKey.ER_WF_INVALID_CHARACTER, 1332 null, 1333 null, 1334 null)); 1335 } 1336 } 1337 } 1338 } // version of the document is XML 1.0 1339 else { 1340 // we need to check all chracters as per production rules of XML 1.0 1341 int i = 0; 1342 while (i < datalength) { 1343 char c = dataarray[i++]; 1344 if (XMLChar.isInvalid(c)) { 1345 // check if this is a supplemental character 1346 if (XMLChar.isHighSurrogate(c) && i < datalength) { 1347 char c2 = dataarray[i++]; 1348 if (XMLChar.isLowSurrogate(c2) 1349 && XMLChar.isSupplemental( 1350 XMLChar.supplemental(c, c2))) { 1351 continue; 1352 } 1353 } 1354 String msg = 1355 Utils.messages.createMessage( 1356 MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT, 1357 new Object[] { new Character(c)}); 1358 1359 if (fErrorHandler != null) { 1360 fErrorHandler.handleError( 1361 new DOMErrorImpl( 1362 DOMError.SEVERITY_FATAL_ERROR, 1363 msg, 1364 MsgKey.ER_WF_INVALID_CHARACTER, 1365 null, 1366 null, 1367 null)); 1368 } 1369 } else if (c == '-' && i < datalength && dataarray[i] == '-') { 1370 String msg = 1371 Utils.messages.createMessage( 1372 MsgKey.ER_WF_DASH_IN_COMMENT, 1373 null); 1374 1375 if (fErrorHandler != null) { 1376 fErrorHandler.handleError( 1377 new DOMErrorImpl( 1378 DOMError.SEVERITY_FATAL_ERROR, 1379 msg, 1380 MsgKey.ER_WF_INVALID_CHARACTER, 1381 null, 1382 null, 1383 null)); 1384 } 1385 } 1386 } 1387 } 1388 return; 1389 } 1390 1391 /** 1392 * Checks if an element node is well-formed, by checking its Name for well-formedness. 1393 * 1394 * @param data The contents of the comment node 1395 * @return a boolean indiacating if the comment is well-formed or not. 1396 */ 1397 protected void isElementWellFormed(Node node) { 1398 boolean isNameWF = false; 1399 if ((fFeatures & NAMESPACES) != 0) { 1400 isNameWF = 1401 isValidQName( 1402 node.getPrefix(), 1403 node.getLocalName(), 1404 fIsXMLVersion11); 1405 } else { 1406 isNameWF = isXMLName(node.getNodeName(), fIsXMLVersion11); 1407 } 1408 1409 if (!isNameWF) { 1410 String msg = 1411 Utils.messages.createMessage( 1412 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME, 1413 new Object[] { "Element", node.getNodeName()}); 1414 1415 if (fErrorHandler != null) { 1416 fErrorHandler.handleError( 1417 new DOMErrorImpl( 1418 DOMError.SEVERITY_FATAL_ERROR, 1419 msg, 1420 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME, 1421 null, 1422 null, 1423 null)); 1424 } 1425 } 1426 } 1427 1428 /** 1429 * Checks if an attr node is well-formed, by checking it's Name and value 1430 * for well-formedness. 1431 * 1432 * @param data The contents of the comment node 1433 * @return a boolean indiacating if the comment is well-formed or not. 1434 */ 1435 protected void isAttributeWellFormed(Node node) { 1436 boolean isNameWF = false; 1437 if ((fFeatures & NAMESPACES) != 0) { 1438 isNameWF = 1439 isValidQName( 1440 node.getPrefix(), 1441 node.getLocalName(), 1442 fIsXMLVersion11); 1443 } else { 1444 isNameWF = isXMLName(node.getNodeName(), fIsXMLVersion11); 1445 } 1446 1447 if (!isNameWF) { 1448 String msg = 1449 Utils.messages.createMessage( 1450 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME, 1451 new Object[] { "Attr", node.getNodeName()}); 1452 1453 if (fErrorHandler != null) { 1454 fErrorHandler.handleError( 1455 new DOMErrorImpl( 1456 DOMError.SEVERITY_FATAL_ERROR, 1457 msg, 1458 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME, 1459 null, 1460 null, 1461 null)); 1462 } 1463 } 1464 1465 // Check the Attr's node value 1466 // WFC: No < in Attribute Values 1467 String value = node.getNodeValue(); 1468 if (value.indexOf('<') >= 0) { 1469 String msg = 1470 Utils.messages.createMessage( 1471 MsgKey.ER_WF_LT_IN_ATTVAL, 1472 new Object[] { 1473 ((Attr) node).getOwnerElement().getNodeName(), 1474 node.getNodeName()}); 1475 1476 if (fErrorHandler != null) { 1477 fErrorHandler.handleError( 1478 new DOMErrorImpl( 1479 DOMError.SEVERITY_FATAL_ERROR, 1480 msg, 1481 MsgKey.ER_WF_LT_IN_ATTVAL, 1482 null, 1483 null, 1484 null)); 1485 } 1486 } 1487 1488 // we need to loop through the children of attr nodes and check their values for 1489 // well-formedness 1490 NodeList children = node.getChildNodes(); 1491 for (int i = 0; i < children.getLength(); i++) { 1492 Node child = children.item(i); 1493 // An attribute node with no text or entity ref child for example 1494 // doc.createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ns"); 1495 // followes by 1496 // element.setAttributeNodeNS(attribute); 1497 // can potentially lead to this situation. If the attribute 1498 // was a prefix Namespace attribute declaration then then DOM Core 1499 // should have some exception defined for this. 1500 if (child == null) { 1501 // we should probably report an error 1502 continue; 1503 } 1504 switch (child.getNodeType()) { 1505 case Node.TEXT_NODE : 1506 isTextWellFormed((Text) child); 1507 break; 1508 case Node.ENTITY_REFERENCE_NODE : 1509 isEntityReferneceWellFormed((EntityReference) child); 1510 break; 1511 default : 1512 } 1513 } 1514 1515 // TODO: 1516 // WFC: Check if the attribute prefix is bound to 1517 // http://www.w3.org/2000/xmlns/ 1518 1519 // WFC: Unique Att Spec 1520 // Perhaps pass a seen boolean value to this method. serializeAttList will determine 1521 // if the attr was seen before. 1522 } 1523 1524 /** 1525 * Checks if a PI node is well-formed, by checking it's Name and data 1526 * for well-formedness. 1527 * 1528 * @param data The contents of the comment node 1529 */ 1530 protected void isPIWellFormed(ProcessingInstruction node) { 1531 // Is the PI Target a valid XML name 1532 if (!isXMLName(node.getNodeName(), fIsXMLVersion11)) { 1533 String msg = 1534 Utils.messages.createMessage( 1535 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME, 1536 new Object[] { "ProcessingInstruction", node.getTarget()}); 1537 1538 if (fErrorHandler != null) { 1539 fErrorHandler.handleError( 1540 new DOMErrorImpl( 1541 DOMError.SEVERITY_FATAL_ERROR, 1542 msg, 1543 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME, 1544 null, 1545 null, 1546 null)); 1547 } 1548 } 1549 1550 // Does the PI Data carry valid XML characters 1551 1552 // REVISIT: Should we check if the PI DATA contains a ?> ??? 1553 Character invalidChar = isWFXMLChar(node.getData()); 1554 if (invalidChar != null) { 1555 String msg = 1556 Utils.messages.createMessage( 1557 MsgKey.ER_WF_INVALID_CHARACTER_IN_PI, 1558 new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) }); 1559 1560 if (fErrorHandler != null) { 1561 fErrorHandler.handleError( 1562 new DOMErrorImpl( 1563 DOMError.SEVERITY_FATAL_ERROR, 1564 msg, 1565 MsgKey.ER_WF_INVALID_CHARACTER, 1566 null, 1567 null, 1568 null)); 1569 } 1570 } 1571 } 1572 1573 /** 1574 * Checks if an CDATASection node is well-formed, by checking it's data 1575 * for well-formedness. Note that the presence of a CDATA termination mark 1576 * in the contents of a CDATASection is handled by the parameter 1577 * spli-cdata-sections 1578 * 1579 * @param data The contents of the comment node 1580 */ 1581 protected void isCDATASectionWellFormed(CDATASection node) { 1582 // Does the data valid XML character data 1583 Character invalidChar = isWFXMLChar(node.getData()); 1584 //if (!isWFXMLChar(node.getData(), invalidChar)) { 1585 if (invalidChar != null) { 1586 String msg = 1587 Utils.messages.createMessage( 1588 MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA, 1589 new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) }); 1590 1591 if (fErrorHandler != null) { 1592 fErrorHandler.handleError( 1593 new DOMErrorImpl( 1594 DOMError.SEVERITY_FATAL_ERROR, 1595 msg, 1596 MsgKey.ER_WF_INVALID_CHARACTER, 1597 null, 1598 null, 1599 null)); 1600 } 1601 } 1602 } 1603 1604 /** 1605 * Checks if an Text node is well-formed, by checking if it contains invalid 1606 * XML characters. 1607 * 1608 * @param data The contents of the comment node 1609 */ 1610 protected void isTextWellFormed(Text node) { 1611 // Does the data valid XML character data 1612 Character invalidChar = isWFXMLChar(node.getData()); 1613 if (invalidChar != null) { 1614 String msg = 1615 Utils.messages.createMessage( 1616 MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT, 1617 new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) }); 1618 1619 if (fErrorHandler != null) { 1620 fErrorHandler.handleError( 1621 new DOMErrorImpl( 1622 DOMError.SEVERITY_FATAL_ERROR, 1623 msg, 1624 MsgKey.ER_WF_INVALID_CHARACTER, 1625 null, 1626 null, 1627 null)); 1628 } 1629 } 1630 } 1631 1632 /** 1633 * Checks if an EntityRefernece node is well-formed, by checking it's node name. Then depending 1634 * on whether it is referenced in Element content or in an Attr Node, checks if the EntityReference 1635 * references an unparsed entity or a external entity and if so throws raises the 1636 * appropriate well-formedness error. 1637 * 1638 * @param data The contents of the comment node 1639 * @parent The parent of the EntityReference Node 1640 */ 1641 protected void isEntityReferneceWellFormed(EntityReference node) { 1642 // Is the EntityReference name a valid XML name 1643 if (!isXMLName(node.getNodeName(), fIsXMLVersion11)) { 1644 String msg = 1645 Utils.messages.createMessage( 1646 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME, 1647 new Object[] { "EntityReference", node.getNodeName()}); 1648 1649 if (fErrorHandler != null) { 1650 fErrorHandler.handleError( 1651 new DOMErrorImpl( 1652 DOMError.SEVERITY_FATAL_ERROR, 1653 msg, 1654 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME, 1655 null, 1656 null, 1657 null)); 1658 } 1659 } 1660 1661 // determine the parent node 1662 Node parent = node.getParentNode(); 1663 1664 // Traverse the declared entities and check if the nodeName and namespaceURI 1665 // of the EntityReference matches an Entity. If so, check the if the notationName 1666 // is not null, if so, report an error. 1667 DocumentType docType = node.getOwnerDocument().getDoctype(); 1668 if (docType != null) { 1669 NamedNodeMap entities = docType.getEntities(); 1670 for (int i = 0; i < entities.getLength(); i++) { 1671 Entity ent = (Entity) entities.item(i); 1672 1673 String nodeName = 1674 node.getNodeName() == null ? "" : node.getNodeName(); 1675 String nodeNamespaceURI = 1676 node.getNamespaceURI() == null 1677 ? "" 1678 : node.getNamespaceURI(); 1679 String entName = 1680 ent.getNodeName() == null ? "" : ent.getNodeName(); 1681 String entNamespaceURI = 1682 ent.getNamespaceURI() == null ? "" : ent.getNamespaceURI(); 1683 // If referenced in Element content 1684 // WFC: Parsed Entity 1685 if (parent.getNodeType() == Node.ELEMENT_NODE) { 1686 if (entNamespaceURI.equals(nodeNamespaceURI) 1687 && entName.equals(nodeName)) { 1688 1689 if (ent.getNotationName() != null) { 1690 String msg = 1691 Utils.messages.createMessage( 1692 MsgKey.ER_WF_REF_TO_UNPARSED_ENT, 1693 new Object[] { node.getNodeName()}); 1694 1695 if (fErrorHandler != null) { 1696 fErrorHandler.handleError( 1697 new DOMErrorImpl( 1698 DOMError.SEVERITY_FATAL_ERROR, 1699 msg, 1700 MsgKey.ER_WF_REF_TO_UNPARSED_ENT, 1701 null, 1702 null, 1703 null)); 1704 } 1705 } 1706 } 1707 } // end if WFC: Parsed Entity 1708 1709 // If referenced in an Attr value 1710 // WFC: No External Entity References 1711 if (parent.getNodeType() == Node.ATTRIBUTE_NODE) { 1712 if (entNamespaceURI.equals(nodeNamespaceURI) 1713 && entName.equals(nodeName)) { 1714 1715 if (ent.getPublicId() != null 1716 || ent.getSystemId() != null 1717 || ent.getNotationName() != null) { 1718 String msg = 1719 Utils.messages.createMessage( 1720 MsgKey.ER_WF_REF_TO_EXTERNAL_ENT, 1721 new Object[] { node.getNodeName()}); 1722 1723 if (fErrorHandler != null) { 1724 fErrorHandler.handleError( 1725 new DOMErrorImpl( 1726 DOMError.SEVERITY_FATAL_ERROR, 1727 msg, 1728 MsgKey.ER_WF_REF_TO_EXTERNAL_ENT, 1729 null, 1730 null, 1731 null)); 1732 } 1733 } 1734 } 1735 } //end if WFC: No External Entity References 1736 } 1737 } 1738 } // isEntityReferneceWellFormed 1739 1740 /** 1741 * If the configuration parameter "namespaces" is set to true, this methods 1742 * checks if an entity whose replacement text contains unbound namespace 1743 * prefixes is referenced in a location where there are no bindings for 1744 * the namespace prefixes and if so raises a LSException with the error-type 1745 * "unbound-prefix-in-entity-reference" 1746 * 1747 * @param Node, The EntityReference nodes whose children are to be checked 1748 */ 1749 protected void checkUnboundPrefixInEntRef(Node node) { 1750 Node child, next; 1751 for (child = node.getFirstChild(); child != null; child = next) { 1752 next = child.getNextSibling(); 1753 1754 if (child.getNodeType() == Node.ELEMENT_NODE) { 1755 1756 //If a NamespaceURI is not declared for the current 1757 //node's prefix, raise a fatal error. 1758 String prefix = child.getPrefix(); 1759 if (prefix != null 1760 && fNSBinder.getURI(prefix) == null) { 1761 String msg = 1762 Utils.messages.createMessage( 1763 MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF, 1764 new Object[] { 1765 node.getNodeName(), 1766 child.getNodeName(), 1767 prefix }); 1768 1769 if (fErrorHandler != null) { 1770 fErrorHandler.handleError( 1771 new DOMErrorImpl( 1772 DOMError.SEVERITY_FATAL_ERROR, 1773 msg, 1774 MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF, 1775 null, 1776 null, 1777 null)); 1778 } 1779 } 1780 1781 NamedNodeMap attrs = child.getAttributes(); 1782 1783 for (int i = 0; i < attrs.getLength(); i++) { 1784 String attrPrefix = attrs.item(i).getPrefix(); 1785 if (attrPrefix != null 1786 && fNSBinder.getURI(attrPrefix) == null) { 1787 String msg = 1788 Utils.messages.createMessage( 1789 MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF, 1790 new Object[] { 1791 node.getNodeName(), 1792 child.getNodeName(), 1793 attrs.item(i)}); 1794 1795 if (fErrorHandler != null) { 1796 fErrorHandler.handleError( 1797 new DOMErrorImpl( 1798 DOMError.SEVERITY_FATAL_ERROR, 1799 msg, 1800 MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF, 1801 null, 1802 null, 1803 null)); 1804 } 1805 } 1806 } 1807 } 1808 1809 if (child.hasChildNodes()) { 1810 checkUnboundPrefixInEntRef(child); 1811 } 1812 } 1813 } 1814 1815 // *********************************************************************** 1816 // Namespace normalization 1817 // *********************************************************************** 1818 /** 1819 * Records local namespace declarations, to be used for normalization later 1820 * 1821 * @param Node, The element node, whose namespace declarations are to be recorded 1822 */ 1823 protected void recordLocalNSDecl(Node node) { 1824 NamedNodeMap atts = ((Element) node).getAttributes(); 1825 int length = atts.getLength(); 1826 1827 for (int i = 0; i < length; i++) { 1828 Node attr = atts.item(i); 1829 1830 String localName = attr.getLocalName(); 1831 String attrPrefix = attr.getPrefix(); 1832 String attrValue = attr.getNodeValue(); 1833 String attrNS = attr.getNamespaceURI(); 1834 1835 localName = 1836 localName == null 1837 || XMLNS_PREFIX.equals(localName) ? "" : localName; 1838 attrPrefix = attrPrefix == null ? "" : attrPrefix; 1839 attrValue = attrValue == null ? "" : attrValue; 1840 attrNS = attrNS == null ? "" : attrNS; 1841 1842 // check if attribute is a namespace decl 1843 if (XMLNS_URI.equals(attrNS)) { 1844 1845 // No prefix may be bound to http://www.w3.org/2000/xmlns/. 1846 if (XMLNS_URI.equals(attrValue)) { 1847 String msg = 1848 Utils.messages.createMessage( 1849 MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND, 1850 new Object[] { attrPrefix, XMLNS_URI }); 1851 1852 if (fErrorHandler != null) { 1853 fErrorHandler.handleError( 1854 new DOMErrorImpl( 1855 DOMError.SEVERITY_ERROR, 1856 msg, 1857 MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND, 1858 null, 1859 null, 1860 null)); 1861 } 1862 } else { 1863 // store the namespace-declaration 1864 if (XMLNS_PREFIX.equals(attrPrefix) ) { 1865 // record valid decl 1866 if (attrValue.length() != 0) { 1867 fNSBinder.declarePrefix(localName, attrValue); 1868 } else { 1869 // Error; xmlns:prefix="" 1870 } 1871 } else { // xmlns 1872 // empty prefix is always bound ("" or some string) 1873 fNSBinder.declarePrefix("", attrValue); 1874 } 1875 } 1876 1877 } 1878 } 1879 } 1880 1881 /** 1882 * Fixes an element's namespace 1883 * 1884 * @param Node, The element node, whose namespace is to be fixed 1885 */ 1886 protected void fixupElementNS(Node node) throws SAXException { 1887 String namespaceURI = ((Element) node).getNamespaceURI(); 1888 String prefix = ((Element) node).getPrefix(); 1889 String localName = ((Element) node).getLocalName(); 1890 1891 if (namespaceURI != null) { 1892 //if ( Element's prefix/namespace pair (or default namespace, 1893 // if no prefix) are within the scope of a binding ) 1894 prefix = prefix == null ? "" : prefix; 1895 String inScopeNamespaceURI = fNSBinder.getURI(prefix); 1896 1897 if ((inScopeNamespaceURI != null 1898 && inScopeNamespaceURI.equals(namespaceURI))) { 1899 // do nothing, declaration in scope is inherited 1900 1901 } else { 1902 // Create a local namespace declaration attr for this namespace, 1903 // with Element's current prefix (or a default namespace, if 1904 // no prefix). If there's a conflicting local declaration 1905 // already present, change its value to use this namespace. 1906 1907 // Add the xmlns declaration attribute 1908 //fNSBinder.pushNamespace(prefix, namespaceURI, fElementDepth); 1909 if ((fFeatures & NAMESPACEDECLS) != 0) { 1910 if ("".equals(prefix) || "".equals(namespaceURI)) { 1911 ((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX, namespaceURI); 1912 } else { 1913 ((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX + ":" + prefix, namespaceURI); 1914 } 1915 } 1916 fLocalNSBinder.declarePrefix(prefix, namespaceURI); 1917 fNSBinder.declarePrefix(prefix, namespaceURI); 1918 1919 } 1920 } else { 1921 // Element has no namespace 1922 // DOM Level 1 1923 if (localName == null || "".equals(localName)) { 1924 // DOM Level 1 node! 1925 String msg = 1926 Utils.messages.createMessage( 1927 MsgKey.ER_NULL_LOCAL_ELEMENT_NAME, 1928 new Object[] { node.getNodeName()}); 1929 1930 if (fErrorHandler != null) { 1931 fErrorHandler.handleError( 1932 new DOMErrorImpl( 1933 DOMError.SEVERITY_ERROR, 1934 msg, 1935 MsgKey.ER_NULL_LOCAL_ELEMENT_NAME, 1936 null, 1937 null, 1938 null)); 1939 } 1940 } else { 1941 namespaceURI = fNSBinder.getURI(""); 1942 if (namespaceURI !=null && namespaceURI.length() > 0) { 1943 ((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX, ""); 1944 fLocalNSBinder.declarePrefix("", ""); 1945 fNSBinder.declarePrefix("", ""); 1946 } 1947 } 1948 } 1949 } 1950 /** 1951 * This table is a quick lookup of a property key (String) to the integer that 1952 * is the bit to flip in the fFeatures field, so the integers should have 1953 * values 1,2,4,8,16... 1954 * 1955 */ 1956 private static final Hashtable s_propKeys = new Hashtable(); 1957 static { 1958 1959 // Initialize the mappings of property keys to bit values (Integer objects) 1960 // or mappings to a String object "", which indicates we are interested 1961 // in the property, but it does not have a simple bit value to flip 1962 1963 // cdata-sections 1964 int i = CDATA; 1965 Integer val = new Integer(i); 1966 s_propKeys.put( 1967 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CDATA_SECTIONS, 1968 val); 1969 1970 // comments 1971 int i1 = COMMENTS; 1972 val = new Integer(i1); 1973 s_propKeys.put( 1974 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_COMMENTS, 1975 val); 1976 1977 // element-content-whitespace 1978 int i2 = ELEM_CONTENT_WHITESPACE; 1979 val = new Integer(i2); 1980 s_propKeys.put( 1981 DOMConstants.S_DOM3_PROPERTIES_NS 1982 + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, 1983 val); 1984 int i3 = ENTITIES; 1985 1986 // entities 1987 val = new Integer(i3); 1988 s_propKeys.put( 1989 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, 1990 val); 1991 1992 // namespaces 1993 int i4 = NAMESPACES; 1994 val = new Integer(i4); 1995 s_propKeys.put( 1996 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACES, 1997 val); 1998 1999 // namespace-declarations 2000 int i5 = NAMESPACEDECLS; 2001 val = new Integer(i5); 2002 s_propKeys.put( 2003 DOMConstants.S_DOM3_PROPERTIES_NS 2004 + DOMConstants.DOM_NAMESPACE_DECLARATIONS, 2005 val); 2006 2007 // split-cdata-sections 2008 int i6 = SPLITCDATA; 2009 val = new Integer(i6); 2010 s_propKeys.put( 2011 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_SPLIT_CDATA, 2012 val); 2013 2014 // discard-default-content 2015 int i7 = WELLFORMED; 2016 val = new Integer(i7); 2017 s_propKeys.put( 2018 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_WELLFORMED, 2019 val); 2020 2021 // discard-default-content 2022 int i8 = DISCARDDEFAULT; 2023 val = new Integer(i8); 2024 s_propKeys.put( 2025 DOMConstants.S_DOM3_PROPERTIES_NS 2026 + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT, 2027 val); 2028 2029 // We are interested in these properties, but they don't have a simple 2030 // bit value to deal with. 2031 s_propKeys.put( 2032 DOMConstants.S_DOM3_PROPERTIES_NS 2033 + DOMConstants.DOM_FORMAT_PRETTY_PRINT, 2034 ""); 2035 s_propKeys.put(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, ""); 2036 s_propKeys.put( 2037 DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION, 2038 ""); 2039 s_propKeys.put(DOMConstants.S_XSL_OUTPUT_ENCODING, ""); 2040 s_propKeys.put(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, ""); 2041 } 2042 2043 /** 2044 * Initializes fFeatures based on the DOMConfiguration Parameters set. 2045 * 2046 * @param properties DOMConfiguraiton properties that were set and which are 2047 * to be used while serializing the DOM. 2048 */ 2049 protected void initProperties(Properties properties) { 2050 2051 for (Enumeration keys = properties.keys(); keys.hasMoreElements();) { 2052 2053 final String key = (String) keys.nextElement(); 2054 2055 // caonical-form 2056 // Other features will be enabled or disabled when this is set to true or false. 2057 2058 // error-handler; set via the constructor 2059 2060 // infoset 2061 // Other features will be enabled or disabled when this is set to true 2062 2063 // A quick lookup for the given set of properties (cdata-sections ...) 2064 final Object iobj = s_propKeys.get(key); 2065 if (iobj != null) { 2066 if (iobj instanceof Integer) { 2067 // Dealing with a property that has a simple bit value that 2068 // we need to set 2069 2070 // cdata-sections 2071 // comments 2072 // element-content-whitespace 2073 // entities 2074 // namespaces 2075 // namespace-declarations 2076 // split-cdata-sections 2077 // well-formed 2078 // discard-default-content 2079 final int BITFLAG = ((Integer) iobj).intValue(); 2080 if ((properties.getProperty(key).endsWith("yes"))) { 2081 fFeatures = fFeatures | BITFLAG; 2082 } else { 2083 fFeatures = fFeatures & ~BITFLAG; 2084 } 2085 } else { 2086 // We are interested in the property, but it is not 2087 // a simple bit that we need to set. 2088 2089 if ((DOMConstants.S_DOM3_PROPERTIES_NS 2090 + DOMConstants.DOM_FORMAT_PRETTY_PRINT) 2091 .equals(key)) { 2092 // format-pretty-print; set internally on the serializers via xsl:output properties in LSSerializer 2093 if ((properties.getProperty(key).endsWith("yes"))) { 2094 fSerializer.setIndent(true); 2095 fSerializer.setIndentAmount(3); 2096 } else { 2097 fSerializer.setIndent(false); 2098 } 2099 } else if ( 2100 (DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL).equals( 2101 key)) { 2102 // omit-xml-declaration; set internally on the serializers via xsl:output properties in LSSerializer 2103 if ((properties.getProperty(key).endsWith("yes"))) { 2104 fSerializer.setOmitXMLDeclaration(true); 2105 } else { 2106 fSerializer.setOmitXMLDeclaration(false); 2107 } 2108 } else if ( 2109 ( 2110 DOMConstants.S_XERCES_PROPERTIES_NS 2111 + DOMConstants.S_XML_VERSION).equals( 2112 key)) { 2113 // Retreive the value of the XML Version attribute via the xml-version 2114 String version = properties.getProperty(key); 2115 if ("1.1".equals(version)) { 2116 fIsXMLVersion11 = true; 2117 fSerializer.setVersion(version); 2118 } else { 2119 fSerializer.setVersion("1.0"); 2120 } 2121 } else if ( 2122 (DOMConstants.S_XSL_OUTPUT_ENCODING).equals(key)) { 2123 // Retreive the value of the XML Encoding attribute 2124 String encoding = properties.getProperty(key); 2125 if (encoding != null) { 2126 fSerializer.setEncoding(encoding); 2127 } 2128 } else if ((DOMConstants.S_XERCES_PROPERTIES_NS 2129 + DOMConstants.DOM_ENTITIES).equals(key)) { 2130 // Preserve entity references in the document 2131 if ((properties.getProperty(key).endsWith("yes"))) { 2132 fSerializer.setDTDEntityExpansion(false); 2133 } 2134 else { 2135 fSerializer.setDTDEntityExpansion(true); 2136 } 2137 } else { 2138 // We shouldn't get here, ever, now what? 2139 } 2140 } 2141 } 2142 } 2143 // Set the newLine character to use 2144 if (fNewLine != null) { 2145 fSerializer.setOutputProperty(OutputPropertiesFactory.S_KEY_LINE_SEPARATOR, fNewLine); 2146 } 2147 } 2148 2149 } //TreeWalker