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