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