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