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