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