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