1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the  "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/*
19 * $Id:  $
20 */
21
22package org.apache.xml.serializer.dom3;
23
24import java.io.FileOutputStream;
25import java.io.OutputStream;
26import java.io.StringWriter;
27import java.io.UnsupportedEncodingException;
28import java.io.Writer;
29import java.net.HttpURLConnection;
30import java.net.URL;
31import java.net.URLConnection;
32import java.security.AccessController;
33import java.security.PrivilegedAction;
34import java.util.Properties;
35import java.util.StringTokenizer;
36
37import org.apache.xml.serializer.DOM3Serializer;
38import org.apache.xml.serializer.Encodings;
39import org.apache.xml.serializer.OutputPropertiesFactory;
40import org.apache.xml.serializer.Serializer;
41import org.apache.xml.serializer.SerializerFactory;
42import org.apache.xml.serializer.utils.MsgKey;
43import org.apache.xml.serializer.utils.SystemIDResolver;
44import org.apache.xml.serializer.utils.Utils;
45import org.w3c.dom.DOMConfiguration;
46import org.w3c.dom.DOMError;
47import org.w3c.dom.DOMErrorHandler;
48import org.w3c.dom.DOMException;
49import org.w3c.dom.DOMStringList;
50import org.w3c.dom.Document;
51import org.w3c.dom.Node;
52import org.w3c.dom.ls.LSException;
53import org.w3c.dom.ls.LSOutput;
54import org.w3c.dom.ls.LSSerializer;
55import org.w3c.dom.ls.LSSerializerFilter;
56
57/**
58 * Implemenatation of DOM Level 3 org.w3c.ls.LSSerializer and
59 * org.w3c.dom.ls.DOMConfiguration.  Serialization is achieved by delegating
60 * serialization calls to <CODE>org.apache.xml.serializer.ToStream</CODE> or
61 * one of its derived classes depending on the serialization method, while walking
62 * the DOM in DOM3TreeWalker.
63 * @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>
64 * @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#DOMConfiguration">org.w3c.dom.DOMConfiguration</a>
65 *
66 * @version $Id:
67 *
68 * @xsl.usage internal
69 */
70final public class LSSerializerImpl implements DOMConfiguration, LSSerializer {
71
72    // The default end-of-line character sequence used in serialization.
73    private static final String DEFAULT_END_OF_LINE;
74    static {
75        String lineSeparator = (String) AccessController.doPrivileged(new PrivilegedAction() {
76            public Object run() {
77                try {
78                    return System.getProperty("line.separator");
79                }
80                catch (SecurityException ex) {}
81                return null;
82            }
83        });
84        // The DOM Level 3 Load and Save specification requires that implementations choose a default
85        // sequence which matches one allowed by XML 1.0 (or XML 1.1). If the value of "line.separator"
86        // isn't one of the XML 1.0 end-of-line sequences then we select "\n" as the default value.
87        DEFAULT_END_OF_LINE = lineSeparator != null &&
88            (lineSeparator.equals("\r\n") || lineSeparator.equals("\r")) ? lineSeparator : "\n";
89    }
90
91    /** private data members */
92    private Serializer fXMLSerializer = null;
93
94    // Tracks DOMConfiguration features.
95    protected int fFeatures = 0;
96
97    // Common DOM serializer
98    private  DOM3Serializer fDOMSerializer = null;
99
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}
1538