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: LSSerializerImpl.java 1225426 2011-12-29 04:13:08Z mrglavas $
020     */
021    
022    package org.apache.xml.serializer.dom3;
023    
024    import java.io.FileOutputStream;
025    import java.io.OutputStream;
026    import java.io.StringWriter;
027    import java.io.UnsupportedEncodingException;
028    import java.io.Writer;
029    import java.net.HttpURLConnection;
030    import java.net.URL;
031    import java.net.URLConnection;
032    import java.security.AccessController;
033    import java.security.PrivilegedAction;
034    import java.util.Properties;
035    import java.util.StringTokenizer;
036    
037    import org.apache.xml.serializer.DOM3Serializer;
038    import org.apache.xml.serializer.Encodings;
039    import org.apache.xml.serializer.OutputPropertiesFactory;
040    import org.apache.xml.serializer.Serializer;
041    import org.apache.xml.serializer.SerializerFactory;
042    import org.apache.xml.serializer.utils.MsgKey;
043    import org.apache.xml.serializer.utils.SystemIDResolver;
044    import org.apache.xml.serializer.utils.Utils;
045    import org.w3c.dom.DOMConfiguration;
046    import org.w3c.dom.DOMError;
047    import org.w3c.dom.DOMErrorHandler;
048    import org.w3c.dom.DOMException;
049    import org.w3c.dom.DOMStringList;
050    import org.w3c.dom.Document;
051    import org.w3c.dom.Node;
052    import org.w3c.dom.ls.LSException;
053    import org.w3c.dom.ls.LSOutput;
054    import org.w3c.dom.ls.LSSerializer;
055    import org.w3c.dom.ls.LSSerializerFilter;
056    
057    /**
058     * Implemenatation of DOM Level 3 org.w3c.ls.LSSerializer and 
059     * org.w3c.dom.ls.DOMConfiguration.  Serialization is achieved by delegating 
060     * serialization calls to <CODE>org.apache.xml.serializer.ToStream</CODE> or 
061     * one of its derived classes depending on the serialization method, while walking
062     * the DOM in DOM3TreeWalker.  
063     * @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html#LS-LSSerializer">org.w3c.dom.ls.LSSerializer</a>
064     * @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#DOMConfiguration">org.w3c.dom.DOMConfiguration</a>
065     *  
066     * @version $Id:  
067     * 
068     * @xsl.usage internal 
069     */
070    final public class LSSerializerImpl implements DOMConfiguration, LSSerializer {
071        
072        // The default end-of-line character sequence used in serialization.
073        private static final String DEFAULT_END_OF_LINE;
074        static {
075            String lineSeparator = (String) AccessController.doPrivileged(new PrivilegedAction() {
076                public Object run() {
077                    try {
078                        return System.getProperty("line.separator");
079                    }
080                    catch (SecurityException ex) {}
081                    return null;
082                }
083            });
084            // The DOM Level 3 Load and Save specification requires that implementations choose a default
085            // sequence which matches one allowed by XML 1.0 (or XML 1.1). If the value of "line.separator" 
086            // isn't one of the XML 1.0 end-of-line sequences then we select "\n" as the default value.
087            DEFAULT_END_OF_LINE = lineSeparator != null && 
088                (lineSeparator.equals("\r\n") || lineSeparator.equals("\r")) ? lineSeparator : "\n";
089        }
090        
091        /** private data members */
092        private Serializer fXMLSerializer = null;
093        
094        // Tracks DOMConfiguration features. 
095        protected int fFeatures = 0;
096        
097        // Common DOM serializer
098        private  DOM3Serializer fDOMSerializer = null;
099        
100        // A filter set on the LSSerializer
101        private LSSerializerFilter fSerializerFilter = null;  
102        
103        // Stores the nodeArg parameter to speed up multiple writes of the same node.
104        private Node fVisitedNode = null;
105        
106        // The end-of-line character sequence used in serialization. "\n" is whats used on the web.
107        private String fEndOfLine = DEFAULT_END_OF_LINE;
108        
109        // The DOMErrorhandler.
110        private DOMErrorHandler fDOMErrorHandler = null;
111        
112        // The Configuration parameter to pass to the Underlying serilaizer.
113        private Properties fDOMConfigProperties = null;
114        
115        // The encoding to use during serialization.
116        private String fEncoding; 
117            
118        // ************************************************************************
119        // DOM Level 3 DOM Configuration parameter names
120        // ************************************************************************    
121        // Parameter canonical-form, true [optional] - NOT SUPPORTED 
122        private final static int CANONICAL = 0x1 << 0;
123        
124        // Parameter cdata-sections, true [required] (default)
125        private final static int CDATA = 0x1 << 1;
126        
127        // Parameter check-character-normalization, true [optional] - NOT SUPPORTED 
128        private final static int CHARNORMALIZE = 0x1 << 2;
129        
130        // Parameter comments, true [required] (default)
131        private final static int COMMENTS = 0x1 << 3;
132        
133        // Parameter datatype-normalization, true [optional] - NOT SUPPORTED
134        private final static int DTNORMALIZE = 0x1 << 4;    
135        
136        // Parameter element-content-whitespace, true [required] (default) - value - false [optional] NOT SUPPORTED
137        private final static int ELEM_CONTENT_WHITESPACE = 0x1 << 5;
138        
139        // Parameter entities, true [required] (default)
140        private final static int ENTITIES = 0x1 << 6;
141        
142        // Parameter infoset, true [required] (default), false has no effect --> True has no effect for the serializer
143        private final static int INFOSET = 0x1 << 7;
144        
145        // Parameter namespaces, true [required] (default)
146        private final static int NAMESPACES = 0x1 << 8;
147        
148        // Parameter namespace-declarations, true [required] (default)
149        private final static int NAMESPACEDECLS = 0x1 << 9;
150        
151        // Parameter normalize-characters, true [optional] - NOT SUPPORTED
152        private final static int NORMALIZECHARS = 0x1 << 10;
153        
154        // Parameter split-cdata-sections, true [required] (default)
155        private final static int SPLITCDATA = 0x1 << 11;   
156        
157        // Parameter validate, true [optional] - NOT SUPPORTED
158        private final static int VALIDATE = 0x1 << 12;   
159        
160        // Parameter validate-if-schema, true [optional] - NOT SUPPORTED
161        private final static int SCHEMAVALIDATE = 0x1 << 13;
162        
163        // Parameter split-cdata-sections, true [required] (default)
164        private final static int WELLFORMED = 0x1 << 14;   
165        
166        // Parameter discard-default-content, true [required] (default)
167        // Not sure how this will be used in level 2 Documents
168        private final static int DISCARDDEFAULT = 0x1 << 15;       
169        
170        // Parameter format-pretty-print, true [optional] 
171        private final static int PRETTY_PRINT = 0x1 << 16;
172        
173        // Parameter ignore-unknown-character-denormalizations, true [required] (default)
174        // We currently do not support XML 1.1 character normalization
175        private final static int IGNORE_CHAR_DENORMALIZE = 0x1 << 17;
176        
177        // Parameter discard-default-content, true [required] (default)
178        private final static int XMLDECL = 0x1 << 18;    
179        // ************************************************************************
180        
181        // Recognized parameters for which atleast one value can be set
182        private String fRecognizedParameters [] = {
183                DOMConstants.DOM_CANONICAL_FORM,
184                DOMConstants.DOM_CDATA_SECTIONS,
185                DOMConstants.DOM_CHECK_CHAR_NORMALIZATION,
186                DOMConstants.DOM_COMMENTS,
187                DOMConstants.DOM_DATATYPE_NORMALIZATION,
188                DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE,
189                DOMConstants.DOM_ENTITIES,
190                DOMConstants.DOM_INFOSET,
191                DOMConstants.DOM_NAMESPACES,
192                DOMConstants.DOM_NAMESPACE_DECLARATIONS,
193                //DOMConstants.DOM_NORMALIZE_CHARACTERS,
194                DOMConstants.DOM_SPLIT_CDATA,
195                DOMConstants.DOM_VALIDATE,
196                DOMConstants.DOM_VALIDATE_IF_SCHEMA,
197                DOMConstants.DOM_WELLFORMED,
198                DOMConstants.DOM_DISCARD_DEFAULT_CONTENT,
199                DOMConstants.DOM_FORMAT_PRETTY_PRINT,
200                DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS,
201                DOMConstants.DOM_XMLDECL,
202                DOMConstants.DOM_ERROR_HANDLER
203        };
204        
205        
206        /**
207         * Constructor:  Creates a LSSerializerImpl object.  The underlying
208         * XML 1.0 or XML 1.1 org.apache.xml.serializer.Serializer object is
209         * created and initialized the first time any of the write methods are  
210         * invoked to serialize the Node.  Subsequent write methods on the same
211         * LSSerializerImpl object will use the previously created Serializer object.
212         */
213        public LSSerializerImpl () {
214            // set default parameters
215            fFeatures |= CDATA;
216            fFeatures |= COMMENTS;
217            fFeatures |= ELEM_CONTENT_WHITESPACE;
218            fFeatures |= ENTITIES;
219            fFeatures |= NAMESPACES;
220            fFeatures |= NAMESPACEDECLS;
221            fFeatures |= SPLITCDATA;
222            fFeatures |= WELLFORMED;
223            fFeatures |= DISCARDDEFAULT;
224            fFeatures |= XMLDECL;
225            
226            // New OutputFormat properties
227            fDOMConfigProperties = new Properties();
228            
229            // Initialize properties to be passed on the underlying serializer
230            initializeSerializerProps();
231            
232            // Create the underlying serializer.
233            Properties  configProps = OutputPropertiesFactory.getDefaultMethodProperties("xml");
234            
235            // change xml version from 1.0 to 1.1
236            //configProps.setProperty("version", "1.1");
237            
238            // Get a serializer that seriailizes according the the properties,
239            // which in this case is to xml
240            fXMLSerializer = SerializerFactory.getSerializer(configProps);
241            
242            // Initialize Serializer
243            fXMLSerializer.setOutputFormat(fDOMConfigProperties);
244        }
245        
246        /**
247         * Initializes the underlying serializer's configuration depending on the
248         * default DOMConfiguration parameters. This method must be called before a
249         * node is to be serialized.
250         * 
251         * @xsl.usage internal
252         */
253        public void initializeSerializerProps () {
254            // canonical-form
255            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
256                    + DOMConstants.DOM_CANONICAL_FORM, DOMConstants.DOM3_DEFAULT_FALSE);
257            
258            // cdata-sections
259            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
260                    + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_DEFAULT_TRUE);
261            
262            // "check-character-normalization"
263            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
264                    + DOMConstants.DOM_CHECK_CHAR_NORMALIZATION,
265                    DOMConstants.DOM3_DEFAULT_FALSE);
266            
267            // comments
268            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
269                    + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_DEFAULT_TRUE);
270            
271            // datatype-normalization
272            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
273                    + DOMConstants.DOM_DATATYPE_NORMALIZATION,
274                    DOMConstants.DOM3_DEFAULT_FALSE);
275            
276            // element-content-whitespace
277            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
278                    + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE,
279                    DOMConstants.DOM3_DEFAULT_TRUE);
280            
281            // entities
282            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
283                    + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_TRUE);
284            // preserve entities
285            fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS
286                    + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_TRUE);
287    
288            // error-handler
289            // Should we set our default ErrorHandler
290            /*
291             * if (fDOMConfig.getParameter(Constants.DOM_ERROR_HANDLER) != null) {
292             * fDOMErrorHandler =
293             * (DOMErrorHandler)fDOMConfig.getParameter(Constants.DOM_ERROR_HANDLER); }
294             */
295            
296            // infoset
297            if ((fFeatures & INFOSET) != 0) {
298                fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
299                        + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_DEFAULT_TRUE);
300                fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
301                        + DOMConstants.DOM_NAMESPACE_DECLARATIONS,
302                        DOMConstants.DOM3_DEFAULT_TRUE);
303                fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
304                        + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_DEFAULT_TRUE);
305                fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
306                        + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE,
307                        DOMConstants.DOM3_DEFAULT_TRUE);
308                fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
309                        + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_DEFAULT_TRUE);
310                fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
311                        + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_FALSE);
312                // preserve entities
313                fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS
314                        + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_FALSE);
315                fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
316                        + DOMConstants.DOM_CDATA_SECTIONS,
317                        DOMConstants.DOM3_DEFAULT_FALSE);
318                fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
319                        + DOMConstants.DOM_VALIDATE_IF_SCHEMA,
320                        DOMConstants.DOM3_DEFAULT_FALSE);
321                fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
322                        + DOMConstants.DOM_DATATYPE_NORMALIZATION,
323                        DOMConstants.DOM3_DEFAULT_FALSE);
324            }
325            
326            // namespaces
327            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
328                    + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_DEFAULT_TRUE);
329            
330            // namespace-declarations
331            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
332                    + DOMConstants.DOM_NAMESPACE_DECLARATIONS,
333                    DOMConstants.DOM3_DEFAULT_TRUE);
334            
335            // normalize-characters
336            /*
337            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
338                    + DOMConstants.DOM_NORMALIZE_CHARACTERS,
339                    DOMConstants.DOM3_DEFAULT_FALSE);
340            */
341            
342            // split-cdata-sections
343            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
344                    + DOMConstants.DOM_SPLIT_CDATA, DOMConstants.DOM3_DEFAULT_TRUE);
345            
346            // validate
347            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
348                    + DOMConstants.DOM_VALIDATE, DOMConstants.DOM3_DEFAULT_FALSE);
349            
350            // validate-if-schema
351            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
352                    + DOMConstants.DOM_VALIDATE_IF_SCHEMA,
353                    DOMConstants.DOM3_DEFAULT_FALSE);
354            
355            // well-formed
356            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
357                    + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_DEFAULT_TRUE);
358            
359            // pretty-print
360            fDOMConfigProperties.setProperty(
361                    DOMConstants.S_XSL_OUTPUT_INDENT,
362                    DOMConstants.DOM3_DEFAULT_TRUE);
363            fDOMConfigProperties.setProperty(
364                    OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, Integer.toString(3));
365            
366            // 
367            
368            // discard-default-content
369            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS
370                    + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT,
371                    DOMConstants.DOM3_DEFAULT_TRUE);
372            
373            // xml-declaration
374            fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "no");
375            
376        }    
377        
378        // ************************************************************************
379        // DOMConfiguraiton implementation
380        // ************************************************************************
381        
382        /** 
383         * Checks if setting a parameter to a specific value is supported.    
384         *  
385         * @see org.w3c.dom.DOMConfiguration#canSetParameter(java.lang.String, java.lang.Object)
386         * @since DOM Level 3
387         * @param name A String containing the DOMConfiguration parameter name.
388         * @param value An Object specifying the value of the corresponding parameter. 
389         */
390        public boolean canSetParameter(String name, Object value) {
391            if (value instanceof Boolean){
392                if ( name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS)
393                        || name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS)
394                        || name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES)
395                        || name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)
396                        || name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE)
397                        || name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES)    
398                        || name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS)
399                        || name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA)
400                        || name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED)    
401                        || name.equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT)
402                        || name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)                
403                        || name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL)){
404                    // both values supported
405                    return true;
406                }
407                else if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM)
408                        || name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION)
409                        || name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION)
410                        || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)
411                        || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE)
412                        // || name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS)
413                        ) {
414                    // true is not supported
415                    return !((Boolean)value).booleanValue();
416                }
417                else if (name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) {
418                    // false is not supported
419                    return ((Boolean)value).booleanValue();
420                }
421            }
422            else if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER) &&
423                    value == null || value instanceof DOMErrorHandler){
424                return true;
425            }
426            return false;
427        }
428        /**
429         * This method returns the value of a parameter if known.
430         * 
431         * @see org.w3c.dom.DOMConfiguration#getParameter(java.lang.String)
432         * 
433         * @param name A String containing the DOMConfiguration parameter name 
434         *             whose value is to be returned.
435         * @return Object The value of the parameter if known. 
436         */
437        public Object getParameter(String name) throws DOMException {
438            if (name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS)) {
439                return ((fFeatures & COMMENTS) != 0) ? Boolean.TRUE : Boolean.FALSE;
440            } else if (name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS)) {
441                return ((fFeatures & CDATA) != 0) ? Boolean.TRUE : Boolean.FALSE;
442            } else if (name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES)) {
443                return ((fFeatures & ENTITIES) != 0) ? Boolean.TRUE : Boolean.FALSE;
444            } else if (name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES)) {
445                return ((fFeatures & NAMESPACES) != 0) ? Boolean.TRUE : Boolean.FALSE;
446            } else if (name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS)) {
447                return ((fFeatures & NAMESPACEDECLS) != 0) ? Boolean.TRUE : Boolean.FALSE;
448            } else if (name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA)) {
449                return ((fFeatures & SPLITCDATA) != 0) ? Boolean.TRUE : Boolean.FALSE;
450            } else if (name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED)) {
451                return ((fFeatures & WELLFORMED) != 0) ? Boolean.TRUE : Boolean.FALSE;
452            }  else if (name.equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT)) {
453                return ((fFeatures & DISCARDDEFAULT) != 0) ? Boolean.TRUE : Boolean.FALSE;
454            } else if (name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)) {
455                return ((fFeatures & PRETTY_PRINT) != 0) ? Boolean.TRUE : Boolean.FALSE;
456            } else if (name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL)) {
457                return ((fFeatures & XMLDECL) != 0) ? Boolean.TRUE : Boolean.FALSE;
458            } else if (name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE)) {
459                return ((fFeatures & ELEM_CONTENT_WHITESPACE) != 0) ? Boolean.TRUE : Boolean.FALSE;
460            } else if (name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)) {
461                return ((fFeatures & PRETTY_PRINT) != 0) ? Boolean.TRUE : Boolean.FALSE;
462            } else if (name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) {
463                return Boolean.TRUE;
464            } else if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM)
465                    || name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION)
466                    || name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION) 
467                    // || name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS)                
468                    || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE)
469                    || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)) {
470                return Boolean.FALSE;
471            } else if (name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)){
472                if ((fFeatures & ENTITIES) == 0 &&
473                        (fFeatures & CDATA) == 0 &&
474                        (fFeatures & ELEM_CONTENT_WHITESPACE) != 0 &&
475                        (fFeatures & NAMESPACES) != 0 &&
476                        (fFeatures & NAMESPACEDECLS) != 0 &&
477                        (fFeatures & WELLFORMED) != 0 &&
478                        (fFeatures & COMMENTS) != 0) {
479                    return Boolean.TRUE;
480                }                 
481                return Boolean.FALSE;
482            } else if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER)) {
483                return fDOMErrorHandler;
484            } else if (
485                    name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_LOCATION)
486                    || name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_TYPE)) {
487                return null;
488            } else {
489                // Here we have to add the Xalan specific DOM Message Formatter
490                String msg = Utils.messages.createMessage(
491                        MsgKey.ER_FEATURE_NOT_FOUND,
492                        new Object[] { name });
493                throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
494            }
495        }
496        
497        /**
498         * This method returns a of the parameters supported by this DOMConfiguration object 
499         * and for which at least one value can be set by the application
500         * 
501         * @see org.w3c.dom.DOMConfiguration#getParameterNames()
502         * 
503         * @return DOMStringList A list of DOMConfiguration parameters recognized
504         *                       by the serializer
505         */
506        public DOMStringList getParameterNames() {
507            return new DOMStringListImpl(fRecognizedParameters);
508        }
509        
510        /**
511         * This method sets the value of the named parameter.
512         *   
513         * @see org.w3c.dom.DOMConfiguration#setParameter(java.lang.String, java.lang.Object)
514         * 
515         * @param name A String containing the DOMConfiguration parameter name.
516         * @param value An Object contaiing the parameters value to set.
517         */
518        public void setParameter(String name, Object value) throws DOMException {
519            // If the value is a boolean
520            if (value instanceof Boolean) {
521                boolean state = ((Boolean) value).booleanValue();
522                
523                if (name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS)) {
524                    fFeatures = state ? fFeatures | COMMENTS : fFeatures
525                            & ~COMMENTS;
526                    // comments
527                    if (state) {
528                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
529                                + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_EXPLICIT_TRUE);
530                    } else {
531                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
532                                + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_EXPLICIT_FALSE);
533                    }                
534                } else if (name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS)) {
535                    fFeatures =  state ? fFeatures | CDATA : fFeatures
536                            & ~CDATA;
537                    // cdata-sections
538                    if (state) {
539                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
540                                + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_EXPLICIT_TRUE);
541                    } else {
542                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
543                                + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_EXPLICIT_FALSE);
544                    }
545                } else if (name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES)) {
546                    fFeatures = state ? fFeatures | ENTITIES : fFeatures
547                            & ~ENTITIES;
548                    // entities
549                    if (state) {
550                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
551                                + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_TRUE);
552                        fDOMConfigProperties.setProperty(
553                                DOMConstants.S_XERCES_PROPERTIES_NS
554                                + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_TRUE);
555                    } else {
556                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
557                                + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE);
558                        fDOMConfigProperties.setProperty(
559                                DOMConstants.S_XERCES_PROPERTIES_NS
560                                + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE);
561                    }
562                } else if (name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES)) {
563                    fFeatures = state ? fFeatures | NAMESPACES : fFeatures
564                            & ~NAMESPACES;
565                    // namespaces
566                    if (state) {
567                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
568                                + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_EXPLICIT_TRUE);
569                    } else {
570                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
571                                + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_EXPLICIT_FALSE); 
572                    }       
573                } else if (name
574                        .equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS)) {
575                    fFeatures = state ? fFeatures | NAMESPACEDECLS
576                            : fFeatures & ~NAMESPACEDECLS;
577                    // namespace-declarations
578                    if (state) {
579                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
580                                + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_EXPLICIT_TRUE);
581                    } else {
582                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
583                                + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_EXPLICIT_FALSE); 
584                    } 
585                } else if (name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA)) {
586                    fFeatures = state ? fFeatures | SPLITCDATA : fFeatures
587                            & ~SPLITCDATA;
588                    // split-cdata-sections
589                    if (state) {
590                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
591                                + DOMConstants.DOM_SPLIT_CDATA, DOMConstants.DOM3_EXPLICIT_TRUE);
592                    } else {
593                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
594                                + DOMConstants.DOM_SPLIT_CDATA, DOMConstants.DOM3_EXPLICIT_FALSE); 
595                    }  
596                } else if (name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED)) {
597                    fFeatures = state ? fFeatures | WELLFORMED : fFeatures
598                            & ~WELLFORMED;
599                    // well-formed
600                    if (state) {
601                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
602                                + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_EXPLICIT_TRUE);
603                    } else {
604                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
605                                + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_EXPLICIT_FALSE); 
606                    }                  
607                } else if (name
608                        .equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT)) {
609                    fFeatures = state ? fFeatures | DISCARDDEFAULT
610                            : fFeatures & ~DISCARDDEFAULT;
611                    // discard-default-content
612                    if (state) {
613                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
614                                + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT, DOMConstants.DOM3_EXPLICIT_TRUE);
615                    } else {
616                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
617                                + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT, DOMConstants.DOM3_EXPLICIT_FALSE); 
618                    }                    
619                } else if (name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)) {
620                    fFeatures = state ? fFeatures | PRETTY_PRINT : fFeatures
621                            & ~PRETTY_PRINT;
622                    // format-pretty-print
623                    if (state) {
624                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
625                                + DOMConstants.DOM_FORMAT_PRETTY_PRINT, DOMConstants.DOM3_EXPLICIT_TRUE);
626                    }
627                    else {
628                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
629                                + DOMConstants.DOM_FORMAT_PRETTY_PRINT, DOMConstants.DOM3_EXPLICIT_FALSE);
630                    }
631                } else if (name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL)) {
632                    fFeatures = state ? fFeatures | XMLDECL : fFeatures
633                            & ~XMLDECL;
634                    if (state) {
635                        fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "no");
636                    } else {
637                        fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "yes"); 
638                    }       
639                } else if (name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE)) {
640                    fFeatures = state ? fFeatures | ELEM_CONTENT_WHITESPACE : fFeatures
641                            & ~ELEM_CONTENT_WHITESPACE;
642                    // element-content-whitespace
643                    if (state) {
644                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
645                                + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_EXPLICIT_TRUE);
646                    } else {
647                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
648                                + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_EXPLICIT_FALSE);
649                    }            
650                } else if (name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) {
651                    // false is not supported
652                    if (!state) {
653                        // Here we have to add the Xalan specific DOM Message Formatter
654                        String msg = Utils.messages.createMessage(
655                                MsgKey.ER_FEATURE_NOT_SUPPORTED,
656                                new Object[] { name });
657                        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
658                    } else {
659                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
660                                + DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS, DOMConstants.DOM3_EXPLICIT_TRUE);
661                    }
662                } else if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM)
663                        || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)
664                        || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE)
665                        || name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION)
666                        || name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION)
667                        // || name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS)
668                        ) {
669                    // true is not supported
670                    if (state) {
671                        String msg = Utils.messages.createMessage(
672                                MsgKey.ER_FEATURE_NOT_SUPPORTED,
673                                new Object[] { name });
674                        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
675                    } else {
676                        if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM)) {
677                            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
678                                    + DOMConstants.DOM_CANONICAL_FORM, DOMConstants.DOM3_EXPLICIT_FALSE);
679                        } else if (name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)) {
680                            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
681                                    + DOMConstants.DOM_VALIDATE_IF_SCHEMA, DOMConstants.DOM3_EXPLICIT_FALSE);
682                        } else if (name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE)) {
683                            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
684                                    + DOMConstants.DOM_VALIDATE, DOMConstants.DOM3_EXPLICIT_FALSE);
685                        } else if (name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)) {
686                            fDOMConfigProperties.setProperty(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION 
687                                    + DOMConstants.DOM_CHECK_CHAR_NORMALIZATION, DOMConstants.DOM3_EXPLICIT_FALSE);
688                        } else if (name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION)) {
689                            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
690                                    + DOMConstants.DOM_DATATYPE_NORMALIZATION, DOMConstants.DOM3_EXPLICIT_FALSE);
691                        } /* else if (name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS)) {
692                            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
693                                    + DOMConstants.DOM_NORMALIZE_CHARACTERS, DOMConstants.DOM3_EXPLICIT_FALSE);
694                        } */
695                    }
696                } else if (name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)) {
697                    // infoset
698                    if (state) {
699                        fFeatures &= ~ENTITIES;
700                        fFeatures &= ~CDATA;
701                        fFeatures &= ~SCHEMAVALIDATE;
702                        fFeatures &= ~DTNORMALIZE;
703                        fFeatures |= NAMESPACES;
704                        fFeatures |= NAMESPACEDECLS;
705                        fFeatures |= WELLFORMED;
706                        fFeatures |= ELEM_CONTENT_WHITESPACE;
707                        fFeatures |= COMMENTS;
708                        
709                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
710                                + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_EXPLICIT_TRUE); 
711                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
712                                + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_EXPLICIT_TRUE);
713                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
714                                + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_EXPLICIT_TRUE);
715                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
716                                + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_EXPLICIT_TRUE);
717                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
718                                + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_EXPLICIT_TRUE);
719                        
720                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
721                                + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE);
722                        fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS
723                                + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE);
724                        
725                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
726                                + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_EXPLICIT_FALSE);
727                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
728                                + DOMConstants.DOM_VALIDATE_IF_SCHEMA, DOMConstants.DOM3_EXPLICIT_FALSE);            
729                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 
730                                + DOMConstants.DOM_DATATYPE_NORMALIZATION, DOMConstants.DOM3_EXPLICIT_FALSE);
731                    }
732                } else {
733                    // If this is a non-boolean parameter a type mismatch should be thrown.
734                    if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER) ||
735                        name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_LOCATION) ||
736                        name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_TYPE)) {
737                        String msg = Utils.messages.createMessage(
738                                MsgKey.ER_TYPE_MISMATCH_ERR,
739                                new Object[] { name });
740                        throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg);
741                    }
742                    
743                    // Parameter is not recognized
744                    String msg = Utils.messages.createMessage(
745                            MsgKey.ER_FEATURE_NOT_FOUND,
746                            new Object[] { name });
747                    throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
748                }
749            } // If the parameter value is not a boolean 
750            else if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER)) {
751                if (value == null || value instanceof DOMErrorHandler) {
752                    fDOMErrorHandler = (DOMErrorHandler)value;
753                } else {
754                    String msg = Utils.messages.createMessage(
755                            MsgKey.ER_TYPE_MISMATCH_ERR,
756                            new Object[] { name });
757                    throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg);
758                }
759            } else if (
760                    name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_LOCATION)
761                    || name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_TYPE)) {
762                if (value != null) {
763                    if (!(value instanceof String)) {
764                        String msg = Utils.messages.createMessage(
765                                MsgKey.ER_TYPE_MISMATCH_ERR,
766                                new Object[] { name });
767                        throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg);
768                    }
769                    String msg = Utils.messages.createMessage(
770                            MsgKey.ER_FEATURE_NOT_SUPPORTED,
771                            new Object[] { name });
772                    throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
773                }
774            } else {
775                // If this is a boolean parameter a type mismatch should be thrown.
776                if (name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS) ||
777                        name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS) ||
778                        name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES) ||
779                        name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES) ||
780                        name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS) ||
781                        name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA) ||
782                        name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED) ||
783                        name.equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT) ||
784                        name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT) ||
785                        name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL) ||
786                        name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE) ||
787                        name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS) ||
788                        name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM) ||
789                        name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA) ||
790                        name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE) ||
791                        name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION) ||
792                        name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION) ||
793                        name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)) {
794                    String msg = Utils.messages.createMessage(
795                            MsgKey.ER_TYPE_MISMATCH_ERR,
796                            new Object[] { name });
797                    throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg);
798                }
799                
800                // Parameter is not recognized
801                String msg = Utils.messages.createMessage(
802                        MsgKey.ER_FEATURE_NOT_FOUND,
803                        new Object[] { name });
804                throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
805            }
806        }
807        // ************************************************************************
808        
809        
810        // ************************************************************************
811        // DOMConfiguraiton implementation
812        // ************************************************************************
813        
814        /** 
815         * Returns the DOMConfiguration of the LSSerializer.
816         *  
817         * @see org.w3c.dom.ls.LSSerializer#getDomConfig()
818         * @since DOM Level 3
819         * @return A DOMConfiguration object.
820         */
821        public DOMConfiguration getDomConfig() {
822            return (DOMConfiguration)this;
823        }
824        
825        /** 
826         * Returns the DOMConfiguration of the LSSerializer.
827         *  
828         * @see org.w3c.dom.ls.LSSerializer#getFilter()
829         * @since DOM Level 3
830         * @return A LSSerializerFilter object.
831         */
832        public LSSerializerFilter getFilter() {
833            return fSerializerFilter;
834        }
835        
836        /** 
837         * Returns the End-Of-Line sequence of characters to be used in the XML 
838         * being serialized.  If none is set a default "\n" is returned.
839         * 
840         * @see org.w3c.dom.ls.LSSerializer#getNewLine()
841         * @since DOM Level 3
842         * @return A String containing the end-of-line character sequence  used in 
843         * serialization.
844         */
845        public String getNewLine() {
846            return fEndOfLine;
847        }
848        
849        /** 
850         * Set a LSSerilizerFilter on the LSSerializer.  When set, the filter is
851         * called before each node is serialized which depending on its implemention
852         * determines if the node is to be serialized or not.    
853         *  
854         * @see org.w3c.dom.ls.LSSerializer#setFilter
855         * @since DOM Level 3
856         * @param filter A LSSerializerFilter to be applied to the stream to serialize.
857         */
858        public void setFilter(LSSerializerFilter filter) {
859            fSerializerFilter = filter;
860        }
861        
862        /** 
863         * Sets the End-Of-Line sequence of characters to be used in the XML 
864         * being serialized.  Setting this attribute to null will reset its 
865         * value to the default value i.e. "\n".
866         * 
867         * @see org.w3c.dom.ls.LSSerializer#setNewLine
868         * @since DOM Level 3
869         * @param newLine a String that is the end-of-line character sequence to be used in 
870         * serialization.
871         */
872        public void setNewLine(String newLine) {
873            fEndOfLine = (newLine != null) ? newLine : DEFAULT_END_OF_LINE;
874        }
875        
876        /** 
877         * Serializes the specified node to the specified LSOutput and returns true if the Node 
878         * was successfully serialized. 
879         * 
880         * @see org.w3c.dom.ls.LSSerializer#write(org.w3c.dom.Node, org.w3c.dom.ls.LSOutput)
881         * @since DOM Level 3
882         * @param nodeArg The Node to serialize.
883         * @throws org.w3c.dom.ls.LSException SERIALIZE_ERR: Raised if the 
884         * LSSerializer was unable to serialize the node.
885         *      
886         */
887        public boolean write(Node nodeArg, LSOutput destination) throws LSException {
888            // If the destination is null
889            if (destination == null) {
890                String msg = Utils.messages
891                .createMessage(
892                        MsgKey.ER_NO_OUTPUT_SPECIFIED,
893                        null);
894                if (fDOMErrorHandler != null) {
895                    fDOMErrorHandler.handleError(new DOMErrorImpl(
896                            DOMError.SEVERITY_FATAL_ERROR, msg,
897                            MsgKey.ER_NO_OUTPUT_SPECIFIED));
898                }
899                throw new LSException(LSException.SERIALIZE_ERR, msg);
900            } 
901            
902            // If nodeArg is null, return false.  Should we throw and LSException instead?
903            if (nodeArg == null ) {
904                return false;
905            }
906    
907            // Obtain a reference to the serializer to use
908            // Serializer serializer = getXMLSerializer(xmlVersion);
909            Serializer serializer = fXMLSerializer;
910            serializer.reset();
911            
912            // If the node has not been seen
913            if ( nodeArg != fVisitedNode) {
914                // Determine the XML Document version of the Node 
915                String xmlVersion = getXMLVersion(nodeArg);
916                
917                // Determine the encoding: 1.LSOutput.encoding, 2.Document.inputEncoding, 3.Document.xmlEncoding. 
918                fEncoding = destination.getEncoding();
919                if (fEncoding == null ) {
920                    fEncoding = getInputEncoding(nodeArg);
921                    fEncoding = fEncoding != null ? fEncoding : getXMLEncoding(nodeArg) == null? "UTF-8": getXMLEncoding(nodeArg);
922                }
923    
924                // If the encoding is not recognized throw an exception.
925                // Note: The serializer defaults to UTF-8 when created
926                if (!Encodings.isRecognizedEncoding(fEncoding)) {
927                    String msg = Utils.messages
928                    .createMessage(
929                            MsgKey.ER_UNSUPPORTED_ENCODING,
930                            null);
931                    if (fDOMErrorHandler != null) {
932                        fDOMErrorHandler.handleError(new DOMErrorImpl(
933                                DOMError.SEVERITY_FATAL_ERROR, msg,
934                                MsgKey.ER_UNSUPPORTED_ENCODING));
935                    }
936                    throw new LSException(LSException.SERIALIZE_ERR, msg);                  
937                }
938                
939                serializer.getOutputFormat().setProperty("version", xmlVersion);
940    
941                // Set the output encoding and xml version properties
942                fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION, xmlVersion);
943                fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_ENCODING, fEncoding);
944                
945                // If the node to be serialized is not a Document, Element, or Entity
946                // node
947                // then the XML declaration, or text declaration, should be never be
948                // serialized.
949                if ( (nodeArg.getNodeType() != Node.DOCUMENT_NODE
950                        || nodeArg.getNodeType() != Node.ELEMENT_NODE
951                        || nodeArg.getNodeType() != Node.ENTITY_NODE)
952                        && ((fFeatures & XMLDECL) != 0)) {
953                    fDOMConfigProperties.setProperty(
954                            DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL,
955                            DOMConstants.DOM3_DEFAULT_FALSE);
956                }
957    
958                fVisitedNode = nodeArg;
959            } 
960            
961            // Update the serializer properties
962            fXMLSerializer.setOutputFormat(fDOMConfigProperties);
963            
964            // 
965            try {
966                
967                // The LSSerializer will use the LSOutput object to determine 
968                // where to serialize the output to in the following order the  
969                // first one that is not null and not an empty string will be    
970                // used: 1.LSOutput.characterStream, 2.LSOutput.byteStream,   
971                // 3. LSOutput.systemId 
972                // 1.LSOutput.characterStream
973                Writer writer = destination.getCharacterStream();
974                if (writer == null ) {
975                    
976                    // 2.LSOutput.byteStream
977                    OutputStream outputStream = destination.getByteStream();
978                    if ( outputStream == null) {
979                        
980                        // 3. LSOutput.systemId
981                        String uri = destination.getSystemId();
982                        if (uri == null) {
983                            String msg = Utils.messages
984                            .createMessage(
985                                    MsgKey.ER_NO_OUTPUT_SPECIFIED,
986                                    null);
987                            if (fDOMErrorHandler != null) {
988                                fDOMErrorHandler.handleError(new DOMErrorImpl(
989                                        DOMError.SEVERITY_FATAL_ERROR, msg,
990                                        MsgKey.ER_NO_OUTPUT_SPECIFIED));
991                            }
992                            throw new LSException(LSException.SERIALIZE_ERR, msg);
993                            
994                        } else {
995                            // Expand the System Id and obtain an absolute URI for it.
996                            String absoluteURI = SystemIDResolver.getAbsoluteURI(uri);
997                            
998                            URL url = new URL(absoluteURI);
999                            OutputStream urlOutStream = null;
1000                            String protocol = url.getProtocol();
1001                            String host = url.getHost();
1002                            
1003                            // For file protocols, there is no need to use a URL to get its
1004                            // corresponding OutputStream
1005                            
1006                            // Scheme names consist of a sequence of characters. The lower case
1007                            // letters "a"--"z", digits, and the characters plus ("+"), period
1008                            // ("."), and hyphen ("-") are allowed. For resiliency, programs
1009                            // interpreting URLs should treat upper case letters as equivalent to
1010                            // lower case in scheme names (e.g., allow "HTTP" as well as "http").
1011                            if (protocol.equalsIgnoreCase("file") 
1012                                    && (host == null || host.length() == 0 || host.equals("localhost"))) {
1013                                // do we also need to check for host.equals(hostname)
1014                                urlOutStream = new FileOutputStream(getPathWithoutEscapes(url.getPath()));
1015                               
1016                            } else {
1017                                // This should support URL's whose schemes are mentioned in 
1018                                // RFC1738 other than file
1019                                
1020                                URLConnection urlCon = url.openConnection();
1021                                urlCon.setDoInput(false);
1022                                urlCon.setDoOutput(true);
1023                                urlCon.setUseCaches(false); 
1024                                urlCon.setAllowUserInteraction(false);
1025                                
1026                                // When writing to a HTTP URI, a HTTP PUT is performed.
1027                                if (urlCon instanceof HttpURLConnection) {
1028                                    HttpURLConnection httpCon = (HttpURLConnection) urlCon;
1029                                    httpCon.setRequestMethod("PUT");
1030                                }
1031                                urlOutStream = urlCon.getOutputStream();
1032                            }
1033                            // set the OutputStream to that obtained from the systemId
1034                            serializer.setOutputStream(urlOutStream);
1035                        }
1036                    } else {
1037                        // 2.LSOutput.byteStream
1038                        serializer.setOutputStream(outputStream);     
1039                    }
1040                } else {
1041                    // 1.LSOutput.characterStream
1042                    serializer.setWriter(writer);
1043                }
1044                
1045                // The associated media type by default is set to text/xml on 
1046                // org.apache.xml.serializer.SerializerBase.  
1047                
1048                // Get a reference to the serializer then lets you serilize a DOM
1049                // Use this hack till Xalan support JAXP1.3
1050                if (fDOMSerializer == null) {
1051                   fDOMSerializer = (DOM3Serializer)serializer.asDOM3Serializer();
1052                } 
1053                
1054                // Set the error handler on the DOM3Serializer interface implementation
1055                if (fDOMErrorHandler != null) {
1056                    fDOMSerializer.setErrorHandler(fDOMErrorHandler);
1057                }
1058                
1059                // Set the filter on the DOM3Serializer interface implementation
1060                if (fSerializerFilter != null) {
1061                    fDOMSerializer.setNodeFilter(fSerializerFilter);
1062                }
1063                
1064                // Set the NewLine character to be used
1065                fDOMSerializer.setNewLine(fEndOfLine.toCharArray());
1066                
1067                // Serializer your DOM, where node is an org.w3c.dom.Node
1068                // Assuming that Xalan's serializer can serialize any type of DOM node
1069                fDOMSerializer.serializeDOM3(nodeArg);
1070                
1071            } catch( UnsupportedEncodingException ue) {
1072                
1073                String msg = Utils.messages
1074                .createMessage(
1075                        MsgKey.ER_UNSUPPORTED_ENCODING,
1076                        null);
1077                if (fDOMErrorHandler != null) {
1078                    fDOMErrorHandler.handleError(new DOMErrorImpl(
1079                            DOMError.SEVERITY_FATAL_ERROR, msg,
1080                            MsgKey.ER_UNSUPPORTED_ENCODING, ue));
1081                }
1082                throw (LSException) createLSException(LSException.SERIALIZE_ERR, ue).fillInStackTrace();
1083            } catch (LSException lse) {
1084                // Rethrow LSException.
1085                throw lse;
1086            } catch (RuntimeException e) {
1087                throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();
1088            }  catch (Exception e) {
1089                if (fDOMErrorHandler != null) {
1090                    fDOMErrorHandler.handleError(new DOMErrorImpl(
1091                            DOMError.SEVERITY_FATAL_ERROR, e.getMessage(),
1092                            null, e));
1093                }
1094                throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();
1095            }        
1096            return true;
1097        }
1098        
1099        /** 
1100         * Serializes the specified node and returns a String with the serialized
1101         * data to the caller.  
1102         * 
1103         * @see org.w3c.dom.ls.LSSerializer#writeToString(org.w3c.dom.Node)
1104         * @since DOM Level 3
1105         * @param nodeArg The Node to serialize.
1106         * @throws org.w3c.dom.ls.LSException SERIALIZE_ERR: Raised if the 
1107         * LSSerializer was unable to serialize the node.
1108         *      
1109         */
1110        public String writeToString(Node nodeArg) throws DOMException, LSException {
1111            // return null is nodeArg is null.  Should an Exception be thrown instead?
1112            if (nodeArg == null) {
1113                return null;
1114            }
1115    
1116            // Should we reset the serializer configuration before each write operation?
1117            // Obtain a reference to the serializer to use
1118            Serializer serializer = fXMLSerializer;
1119            serializer.reset();
1120            
1121            if (nodeArg != fVisitedNode){
1122                // Determine the XML Document version of the Node 
1123                String xmlVersion = getXMLVersion(nodeArg);
1124                
1125                serializer.getOutputFormat().setProperty("version", xmlVersion);
1126                
1127                // Set the output encoding and xml version properties
1128                fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION, xmlVersion);
1129                fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_ENCODING, "UTF-16");
1130                
1131                // If the node to be serialized is not a Document, Element, or Entity
1132                // node
1133                // then the XML declaration, or text declaration, should be never be
1134                // serialized.
1135                if  ((nodeArg.getNodeType() != Node.DOCUMENT_NODE
1136                        || nodeArg.getNodeType() != Node.ELEMENT_NODE
1137                        || nodeArg.getNodeType() != Node.ENTITY_NODE)
1138                        && ((fFeatures & XMLDECL) != 0)) {
1139                    fDOMConfigProperties.setProperty(
1140                            DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL,
1141                            DOMConstants.DOM3_DEFAULT_FALSE);
1142                }            
1143    
1144                fVisitedNode = nodeArg;       
1145            } 
1146            // Update the serializer properties
1147            fXMLSerializer.setOutputFormat(fDOMConfigProperties);
1148            
1149            // StringWriter to Output to
1150            StringWriter output = new StringWriter();
1151            
1152            // 
1153            try {
1154                
1155                // Set the Serializer's Writer to a StringWriter
1156                serializer.setWriter(output);
1157                
1158                // Get a reference to the serializer then lets you serilize a DOM
1159                // Use this hack till Xalan support JAXP1.3
1160                if (fDOMSerializer == null) {
1161                    fDOMSerializer = (DOM3Serializer)serializer.asDOM3Serializer();
1162                } 
1163                            
1164                // Set the error handler on the DOM3Serializer interface implementation
1165                if (fDOMErrorHandler != null) {
1166                    fDOMSerializer.setErrorHandler(fDOMErrorHandler);
1167                }
1168                
1169                // Set the filter on the DOM3Serializer interface implementation
1170                if (fSerializerFilter != null) {
1171                    fDOMSerializer.setNodeFilter(fSerializerFilter);
1172                }
1173                
1174                // Set the NewLine character to be used
1175                fDOMSerializer.setNewLine(fEndOfLine.toCharArray());
1176                
1177                // Serializer your DOM, where node is an org.w3c.dom.Node
1178                fDOMSerializer.serializeDOM3(nodeArg);
1179            } catch (LSException lse) {
1180                // Rethrow LSException.
1181                throw lse;
1182            } catch (RuntimeException e) {
1183                throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();
1184            }  catch (Exception e) {
1185                if (fDOMErrorHandler != null) {
1186                    fDOMErrorHandler.handleError(new DOMErrorImpl(
1187                            DOMError.SEVERITY_FATAL_ERROR, e.getMessage(),
1188                            null, e));
1189                }
1190                throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();
1191            }        
1192            
1193            // return the serialized string
1194            return output.toString();
1195        }
1196        
1197        /** 
1198         * Serializes the specified node to the specified URI and returns true if the Node 
1199         * was successfully serialized. 
1200         * 
1201         * @see org.w3c.dom.ls.LSSerializer#writeToURI(org.w3c.dom.Node, String)
1202         * @since DOM Level 3
1203         * @param nodeArg The Node to serialize.
1204         * @throws org.w3c.dom.ls.LSException SERIALIZE_ERR: Raised if the 
1205         * LSSerializer was unable to serialize the node.
1206         *      
1207         */
1208        public boolean writeToURI(Node nodeArg, String uri) throws LSException {
1209            // If nodeArg is null, return false.  Should we throw and LSException instead?
1210            if (nodeArg == null ) {
1211                return false;
1212            }
1213    
1214            // Obtain a reference to the serializer to use
1215            Serializer serializer = fXMLSerializer;
1216            serializer.reset();
1217            
1218            if (nodeArg != fVisitedNode) {
1219                // Determine the XML Document version of the Node 
1220                String xmlVersion = getXMLVersion(nodeArg);
1221                
1222                // Determine the encoding: 1.LSOutput.encoding,
1223                // 2.Document.inputEncoding, 3.Document.xmlEncoding.
1224                fEncoding = getInputEncoding(nodeArg);
1225                if (fEncoding == null ) {
1226                    fEncoding = fEncoding != null ? fEncoding : getXMLEncoding(nodeArg) == null? "UTF-8": getXMLEncoding(nodeArg);
1227                }
1228                
1229                serializer.getOutputFormat().setProperty("version", xmlVersion);
1230                
1231                // Set the output encoding and xml version properties
1232                fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION, xmlVersion);
1233                fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_ENCODING, fEncoding);
1234                
1235                // If the node to be serialized is not a Document, Element, or Entity
1236                // node
1237                // then the XML declaration, or text declaration, should be never be
1238                // serialized.
1239                if ( (nodeArg.getNodeType() != Node.DOCUMENT_NODE
1240                        || nodeArg.getNodeType() != Node.ELEMENT_NODE
1241                        || nodeArg.getNodeType() != Node.ENTITY_NODE)
1242                        && ((fFeatures & XMLDECL) != 0))  {
1243                    fDOMConfigProperties.setProperty(
1244                            DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL,
1245                            DOMConstants.DOM3_DEFAULT_FALSE);
1246                }
1247           
1248                fVisitedNode = nodeArg;
1249            } 
1250            
1251            // Update the serializer properties
1252            fXMLSerializer.setOutputFormat(fDOMConfigProperties);
1253            
1254            // 
1255            try {
1256                // If the specified encoding is not supported an
1257                // "unsupported-encoding" fatal error is raised. ??
1258                if (uri == null) {
1259                    String msg = Utils.messages.createMessage(
1260                            MsgKey.ER_NO_OUTPUT_SPECIFIED, null);
1261                    if (fDOMErrorHandler != null) {
1262                        fDOMErrorHandler.handleError(new DOMErrorImpl(
1263                                DOMError.SEVERITY_FATAL_ERROR, msg,
1264                                MsgKey.ER_NO_OUTPUT_SPECIFIED));
1265                    }
1266                    throw new LSException(LSException.SERIALIZE_ERR, msg);
1267                    
1268                } else {
1269                    // REVISIT: Can this be used to get an absolute expanded URI
1270                    String absoluteURI = SystemIDResolver.getAbsoluteURI(uri);
1271                    
1272                    URL url = new URL(absoluteURI);
1273                    OutputStream urlOutStream = null;
1274                    String protocol = url.getProtocol();
1275                    String host = url.getHost();
1276                    
1277                    // For file protocols, there is no need to use a URL to get its
1278                    // corresponding OutputStream
1279                    
1280                    // Scheme names consist of a sequence of characters. The lower 
1281                    // case letters "a"--"z", digits, and the characters plus ("+"), 
1282                    // period ("."), and hyphen ("-") are allowed. For resiliency, 
1283                    // programs interpreting URLs should treat upper case letters as
1284                    // equivalent to lower case in scheme names 
1285                    // (e.g., allow "HTTP" as well as "http").
1286                    if (protocol.equalsIgnoreCase("file")
1287                            && (host == null || host.length() == 0 || host
1288                                    .equals("localhost"))) {
1289                        // do we also need to check for host.equals(hostname)
1290                        urlOutStream = new FileOutputStream(getPathWithoutEscapes(url.getPath()));
1291                        
1292                    } else {
1293                        // This should support URL's whose schemes are mentioned in
1294                        // RFC1738 other than file
1295                        
1296                        URLConnection urlCon = url.openConnection();
1297                        urlCon.setDoInput(false);
1298                        urlCon.setDoOutput(true);
1299                        urlCon.setUseCaches(false);
1300                        urlCon.setAllowUserInteraction(false);
1301                        
1302                        // When writing to a HTTP URI, a HTTP PUT is performed.
1303                        if (urlCon instanceof HttpURLConnection) {
1304                            HttpURLConnection httpCon = (HttpURLConnection) urlCon;
1305                            httpCon.setRequestMethod("PUT");
1306                        }
1307                        urlOutStream = urlCon.getOutputStream();
1308                    }
1309                    // set the OutputStream to that obtained from the systemId
1310                    serializer.setOutputStream(urlOutStream);
1311                }
1312                
1313                // Get a reference to the serializer then lets you serilize a DOM
1314                // Use this hack till Xalan support JAXP1.3
1315                if (fDOMSerializer == null) {
1316                    fDOMSerializer = (DOM3Serializer)serializer.asDOM3Serializer();
1317                } 
1318                
1319                // Set the error handler on the DOM3Serializer interface implementation
1320                if (fDOMErrorHandler != null) {
1321                    fDOMSerializer.setErrorHandler(fDOMErrorHandler);
1322                }
1323                
1324                // Set the filter on the DOM3Serializer interface implementation
1325                if (fSerializerFilter != null) {
1326                    fDOMSerializer.setNodeFilter(fSerializerFilter);
1327                }
1328                
1329                // Set the NewLine character to be used
1330                fDOMSerializer.setNewLine(fEndOfLine.toCharArray());
1331                
1332                // Serializer your DOM, where node is an org.w3c.dom.Node
1333                // Assuming that Xalan's serializer can serialize any type of DOM
1334                // node
1335                fDOMSerializer.serializeDOM3(nodeArg);
1336                
1337            } catch (LSException lse) {
1338                // Rethrow LSException.
1339                throw lse;
1340            } catch (RuntimeException e) {
1341                throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();
1342            }  catch (Exception e) {
1343                if (fDOMErrorHandler != null) {
1344                    fDOMErrorHandler.handleError(new DOMErrorImpl(
1345                            DOMError.SEVERITY_FATAL_ERROR, e.getMessage(),
1346                            null, e));
1347                }
1348                throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();
1349            }        
1350            
1351            return true;
1352        }
1353        // ************************************************************************
1354        
1355        
1356        // ************************************************************************
1357        // Implementaion methods
1358        // ************************************************************************
1359        
1360        /** 
1361         * Determines the XML Version of the Document Node to serialize.  If the Document Node
1362         * is not a DOM Level 3 Node, then the default version returned is 1.0.
1363         * 
1364         * @param  nodeArg The Node to serialize
1365         * @return A String containing the version pseudo-attribute of the XMLDecl.  
1366         * @throws Throwable if the DOM implementation does not implement Document.getXmlVersion()      
1367         */
1368        //protected String getXMLVersion(Node nodeArg) throws Throwable {
1369        protected String getXMLVersion(Node nodeArg) {
1370            Document doc = null;
1371            
1372            // Determine the XML Version of the document
1373            if (nodeArg != null) {
1374                if (nodeArg.getNodeType() == Node.DOCUMENT_NODE) {
1375                    // The Document node is the Node argument
1376                    doc = (Document)nodeArg;
1377                } else { 
1378                    // The Document node is the Node argument's ownerDocument
1379                    doc = nodeArg.getOwnerDocument();
1380                }
1381                
1382                // Determine the DOM Version.
1383                if (doc != null && doc.getImplementation().hasFeature("Core","3.0")) {
1384                    return doc.getXmlVersion();
1385                }
1386            } 
1387            // The version will be treated as "1.0" which may result in
1388            // an ill-formed document being serialized.
1389            // If nodeArg does not have an ownerDocument, treat this as XML 1.0
1390            return "1.0";
1391        }
1392        
1393        /** 
1394         * Determines the XML Encoding of the Document Node to serialize.  If the Document Node
1395         * is not a DOM Level 3 Node, then the default encoding "UTF-8" is returned.
1396         * 
1397         * @param  nodeArg The Node to serialize
1398         * @return A String containing the encoding pseudo-attribute of the XMLDecl.  
1399         * @throws Throwable if the DOM implementation does not implement Document.getXmlEncoding()     
1400         */
1401        protected String getXMLEncoding(Node nodeArg) {
1402            Document doc = null;
1403            
1404            // Determine the XML Encoding of the document
1405            if (nodeArg != null) {
1406                if (nodeArg.getNodeType() == Node.DOCUMENT_NODE) {
1407                    // The Document node is the Node argument
1408                    doc = (Document)nodeArg;
1409                } else { 
1410                    // The Document node is the Node argument's ownerDocument
1411                    doc = nodeArg.getOwnerDocument();
1412                }
1413                
1414                // Determine the XML Version. 
1415                if (doc != null && doc.getImplementation().hasFeature("Core","3.0")) {
1416                    return doc.getXmlEncoding();
1417                }
1418            } 
1419            // The default encoding is UTF-8 except for the writeToString method
1420            return "UTF-8";
1421        }
1422        
1423        /** 
1424         * Determines the Input Encoding of the Document Node to serialize.  If the Document Node
1425         * is not a DOM Level 3 Node, then null is returned.
1426         * 
1427         * @param  nodeArg The Node to serialize
1428         * @return A String containing the input encoding.  
1429         */
1430        protected String getInputEncoding(Node nodeArg)  {
1431            Document doc = null;
1432            
1433            // Determine the Input Encoding of the document
1434            if (nodeArg != null) {
1435                if (nodeArg.getNodeType() == Node.DOCUMENT_NODE) {
1436                    // The Document node is the Node argument
1437                    doc = (Document)nodeArg;
1438                } else { 
1439                    // The Document node is the Node argument's ownerDocument
1440                    doc = nodeArg.getOwnerDocument();
1441                }
1442                
1443                // Determine the DOM Version.
1444                if (doc != null && doc.getImplementation().hasFeature("Core","3.0")) {
1445                    return doc.getInputEncoding();
1446                }
1447            } 
1448            // The default encoding returned is null
1449            return null;
1450        }
1451        
1452        /**
1453         * This method returns the LSSerializer's error handler.
1454         * 
1455         * @return Returns the fDOMErrorHandler.
1456         */
1457        public DOMErrorHandler getErrorHandler() {
1458            return fDOMErrorHandler;
1459        }
1460        
1461        /**
1462         * Replaces all escape sequences in the given path with their literal characters.
1463         */
1464        private static String getPathWithoutEscapes(String origPath) {
1465            if (origPath != null && origPath.length() != 0 && origPath.indexOf('%') != -1) {
1466                // Locate the escape characters
1467                StringTokenizer tokenizer = new StringTokenizer(origPath, "%");
1468                StringBuffer result = new StringBuffer(origPath.length());
1469                int size = tokenizer.countTokens();
1470                result.append(tokenizer.nextToken());
1471                for(int i = 1; i < size; ++i) {
1472                    String token = tokenizer.nextToken();
1473                    if (token.length() >= 2 && isHexDigit(token.charAt(0)) && 
1474                            isHexDigit(token.charAt(1))) {
1475                        // Decode the 2 digit hexadecimal number following % in '%nn'
1476                        result.append((char)Integer.valueOf(token.substring(0, 2), 16).intValue());
1477                        token = token.substring(2);
1478                    }
1479                    result.append(token);
1480                }
1481                return result.toString();
1482            }
1483            return origPath;
1484        }
1485    
1486        /** 
1487         * Returns true if the given character is a valid hex character.
1488         */
1489        private static boolean isHexDigit(char c) {
1490            return (c >= '0' && c <= '9' || 
1491                    c >= 'a' && c <= 'f' || 
1492                    c >= 'A' && c <= 'F');
1493        }
1494        
1495        /**
1496         * Creates an LSException. On J2SE 1.4 and above the cause for the exception will be set.
1497         */
1498        private static LSException createLSException(short code, Throwable cause) {
1499            LSException lse = new LSException(code, cause != null ? cause.getMessage() : null);
1500            if (cause != null && ThrowableMethods.fgThrowableMethodsAvailable) {
1501                try {
1502                    ThrowableMethods.fgThrowableInitCauseMethod.invoke(lse, new Object [] {cause});
1503                }
1504                // Something went wrong. There's not much we can do about it.
1505                catch (Exception e) {}
1506            }
1507            return lse;
1508        }
1509        
1510        /**
1511         * Holder of methods from java.lang.Throwable.
1512         */
1513        static class ThrowableMethods {
1514            
1515            // Method: java.lang.Throwable.initCause(java.lang.Throwable)
1516            private static java.lang.reflect.Method fgThrowableInitCauseMethod = null;
1517            
1518            // Flag indicating whether or not Throwable methods available.
1519            private static boolean fgThrowableMethodsAvailable = false;
1520            
1521            private ThrowableMethods() {}
1522            
1523            // Attempt to get methods for java.lang.Throwable on class initialization.
1524            static {
1525                try {
1526                    fgThrowableInitCauseMethod = Throwable.class.getMethod("initCause", new Class [] {Throwable.class});
1527                    fgThrowableMethodsAvailable = true;
1528                }
1529                // ClassNotFoundException, NoSuchMethodException or SecurityException
1530                // Whatever the case, we cannot use java.lang.Throwable.initCause(java.lang.Throwable).
1531                catch (Exception exc) {
1532                    fgThrowableInitCauseMethod = null;
1533                    fgThrowableMethodsAvailable = false;
1534                }
1535            }
1536        }
1537    }