/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id: $ */ package org.apache.xml.serializer.dom3; import java.io.FileOutputStream; import java.io.OutputStream; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Properties; import java.util.StringTokenizer; import org.apache.xml.serializer.DOM3Serializer; import org.apache.xml.serializer.Encodings; import org.apache.xml.serializer.OutputPropertiesFactory; import org.apache.xml.serializer.Serializer; import org.apache.xml.serializer.SerializerFactory; import org.apache.xml.serializer.utils.MsgKey; import org.apache.xml.serializer.utils.SystemIDResolver; import org.apache.xml.serializer.utils.Utils; import org.w3c.dom.DOMConfiguration; import org.w3c.dom.DOMError; import org.w3c.dom.DOMErrorHandler; import org.w3c.dom.DOMException; import org.w3c.dom.DOMStringList; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.ls.LSException; import org.w3c.dom.ls.LSOutput; import org.w3c.dom.ls.LSSerializer; import org.w3c.dom.ls.LSSerializerFilter; /** * Implemenatation of DOM Level 3 org.w3c.ls.LSSerializer and * org.w3c.dom.ls.DOMConfiguration. Serialization is achieved by delegating * serialization calls to org.apache.xml.serializer.ToStream or * one of its derived classes depending on the serialization method, while walking * the DOM in DOM3TreeWalker. * @see org.w3c.dom.ls.LSSerializer * @see org.w3c.dom.DOMConfiguration * * @version $Id: * * @xsl.usage internal */ final public class LSSerializerImpl implements DOMConfiguration, LSSerializer { // The default end-of-line character sequence used in serialization. private static final String DEFAULT_END_OF_LINE; static { String lineSeparator = (String) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { return System.getProperty("line.separator"); } catch (SecurityException ex) {} return null; } }); // The DOM Level 3 Load and Save specification requires that implementations choose a default // sequence which matches one allowed by XML 1.0 (or XML 1.1). If the value of "line.separator" // isn't one of the XML 1.0 end-of-line sequences then we select "\n" as the default value. DEFAULT_END_OF_LINE = lineSeparator != null && (lineSeparator.equals("\r\n") || lineSeparator.equals("\r")) ? lineSeparator : "\n"; } /** private data members */ private Serializer fXMLSerializer = null; // Tracks DOMConfiguration features. protected int fFeatures = 0; // Common DOM serializer private DOM3Serializer fDOMSerializer = null; // A filter set on the LSSerializer private LSSerializerFilter fSerializerFilter = null; // Stores the nodeArg parameter to speed up multiple writes of the same node. private Node fVisitedNode = null; // The end-of-line character sequence used in serialization. "\n" is whats used on the web. private String fEndOfLine = DEFAULT_END_OF_LINE; // The DOMErrorhandler. private DOMErrorHandler fDOMErrorHandler = null; // The Configuration parameter to pass to the Underlying serilaizer. private Properties fDOMConfigProperties = null; // The encoding to use during serialization. private String fEncoding; // ************************************************************************ // DOM Level 3 DOM Configuration parameter names // ************************************************************************ // Parameter canonical-form, true [optional] - NOT SUPPORTED private final static int CANONICAL = 0x1 << 0; // Parameter cdata-sections, true [required] (default) private final static int CDATA = 0x1 << 1; // Parameter check-character-normalization, true [optional] - NOT SUPPORTED private final static int CHARNORMALIZE = 0x1 << 2; // Parameter comments, true [required] (default) private final static int COMMENTS = 0x1 << 3; // Parameter datatype-normalization, true [optional] - NOT SUPPORTED private final static int DTNORMALIZE = 0x1 << 4; // Parameter element-content-whitespace, true [required] (default) - value - false [optional] NOT SUPPORTED private final static int ELEM_CONTENT_WHITESPACE = 0x1 << 5; // Parameter entities, true [required] (default) private final static int ENTITIES = 0x1 << 6; // Parameter infoset, true [required] (default), false has no effect --> True has no effect for the serializer private final static int INFOSET = 0x1 << 7; // Parameter namespaces, true [required] (default) private final static int NAMESPACES = 0x1 << 8; // Parameter namespace-declarations, true [required] (default) private final static int NAMESPACEDECLS = 0x1 << 9; // Parameter normalize-characters, true [optional] - NOT SUPPORTED private final static int NORMALIZECHARS = 0x1 << 10; // Parameter split-cdata-sections, true [required] (default) private final static int SPLITCDATA = 0x1 << 11; // Parameter validate, true [optional] - NOT SUPPORTED private final static int VALIDATE = 0x1 << 12; // Parameter validate-if-schema, true [optional] - NOT SUPPORTED private final static int SCHEMAVALIDATE = 0x1 << 13; // Parameter split-cdata-sections, true [required] (default) private final static int WELLFORMED = 0x1 << 14; // Parameter discard-default-content, true [required] (default) // Not sure how this will be used in level 2 Documents private final static int DISCARDDEFAULT = 0x1 << 15; // Parameter format-pretty-print, true [optional] private final static int PRETTY_PRINT = 0x1 << 16; // Parameter ignore-unknown-character-denormalizations, true [required] (default) // We currently do not support XML 1.1 character normalization private final static int IGNORE_CHAR_DENORMALIZE = 0x1 << 17; // Parameter discard-default-content, true [required] (default) private final static int XMLDECL = 0x1 << 18; // ************************************************************************ // Recognized parameters for which atleast one value can be set private String fRecognizedParameters [] = { DOMConstants.DOM_CANONICAL_FORM, DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM_CHECK_CHAR_NORMALIZATION, DOMConstants.DOM_COMMENTS, DOMConstants.DOM_DATATYPE_NORMALIZATION, DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM_ENTITIES, DOMConstants.DOM_INFOSET, DOMConstants.DOM_NAMESPACES, DOMConstants.DOM_NAMESPACE_DECLARATIONS, //DOMConstants.DOM_NORMALIZE_CHARACTERS, DOMConstants.DOM_SPLIT_CDATA, DOMConstants.DOM_VALIDATE, DOMConstants.DOM_VALIDATE_IF_SCHEMA, DOMConstants.DOM_WELLFORMED, DOMConstants.DOM_DISCARD_DEFAULT_CONTENT, DOMConstants.DOM_FORMAT_PRETTY_PRINT, DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS, DOMConstants.DOM_XMLDECL, DOMConstants.DOM_ERROR_HANDLER }; /** * Constructor: Creates a LSSerializerImpl object. The underlying * XML 1.0 or XML 1.1 org.apache.xml.serializer.Serializer object is * created and initialized the first time any of the write methods are * invoked to serialize the Node. Subsequent write methods on the same * LSSerializerImpl object will use the previously created Serializer object. */ public LSSerializerImpl () { // set default parameters fFeatures |= CDATA; fFeatures |= COMMENTS; fFeatures |= ELEM_CONTENT_WHITESPACE; fFeatures |= ENTITIES; fFeatures |= NAMESPACES; fFeatures |= NAMESPACEDECLS; fFeatures |= SPLITCDATA; fFeatures |= WELLFORMED; fFeatures |= DISCARDDEFAULT; fFeatures |= XMLDECL; // New OutputFormat properties fDOMConfigProperties = new Properties(); // Initialize properties to be passed on the underlying serializer initializeSerializerProps(); // Create the underlying serializer. Properties configProps = OutputPropertiesFactory.getDefaultMethodProperties("xml"); // change xml version from 1.0 to 1.1 //configProps.setProperty("version", "1.1"); // Get a serializer that seriailizes according the the properties, // which in this case is to xml fXMLSerializer = SerializerFactory.getSerializer(configProps); // Initialize Serializer fXMLSerializer.setOutputFormat(fDOMConfigProperties); } /** * Initializes the underlying serializer's configuration depending on the * default DOMConfiguration parameters. This method must be called before a * node is to be serialized. * * @xsl.usage internal */ public void initializeSerializerProps () { // canonical-form fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CANONICAL_FORM, DOMConstants.DOM3_DEFAULT_FALSE); // cdata-sections fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_DEFAULT_TRUE); // "check-character-normalization" fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CHECK_CHAR_NORMALIZATION, DOMConstants.DOM3_DEFAULT_FALSE); // comments fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_DEFAULT_TRUE); // datatype-normalization fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_DATATYPE_NORMALIZATION, DOMConstants.DOM3_DEFAULT_FALSE); // element-content-whitespace fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_DEFAULT_TRUE); // entities fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_TRUE); // preserve entities fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_TRUE); // error-handler // Should we set our default ErrorHandler /* * if (fDOMConfig.getParameter(Constants.DOM_ERROR_HANDLER) != null) { * fDOMErrorHandler = * (DOMErrorHandler)fDOMConfig.getParameter(Constants.DOM_ERROR_HANDLER); } */ // infoset if ((fFeatures & INFOSET) != 0) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_DEFAULT_TRUE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_DEFAULT_TRUE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_DEFAULT_TRUE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_DEFAULT_TRUE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_DEFAULT_TRUE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_FALSE); // preserve entities fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_FALSE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_DEFAULT_FALSE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_VALIDATE_IF_SCHEMA, DOMConstants.DOM3_DEFAULT_FALSE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_DATATYPE_NORMALIZATION, DOMConstants.DOM3_DEFAULT_FALSE); } // namespaces fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_DEFAULT_TRUE); // namespace-declarations fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_DEFAULT_TRUE); // normalize-characters /* fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NORMALIZE_CHARACTERS, DOMConstants.DOM3_DEFAULT_FALSE); */ // split-cdata-sections fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_SPLIT_CDATA, DOMConstants.DOM3_DEFAULT_TRUE); // validate fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_VALIDATE, DOMConstants.DOM3_DEFAULT_FALSE); // validate-if-schema fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_VALIDATE_IF_SCHEMA, DOMConstants.DOM3_DEFAULT_FALSE); // well-formed fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_DEFAULT_TRUE); // pretty-print fDOMConfigProperties.setProperty( DOMConstants.S_XSL_OUTPUT_INDENT, DOMConstants.DOM3_DEFAULT_TRUE); fDOMConfigProperties.setProperty( OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, Integer.toString(3)); // // discard-default-content fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT, DOMConstants.DOM3_DEFAULT_TRUE); // xml-declaration fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "no"); } // ************************************************************************ // DOMConfiguraiton implementation // ************************************************************************ /** * Checks if setting a parameter to a specific value is supported. * * @see org.w3c.dom.DOMConfiguration#canSetParameter(java.lang.String, java.lang.Object) * @since DOM Level 3 * @param name A String containing the DOMConfiguration parameter name. * @param value An Object specifying the value of the corresponding parameter. */ public boolean canSetParameter(String name, Object value) { if (value instanceof Boolean){ if ( name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS) || name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS) || name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES) || name.equalsIgnoreCase(DOMConstants.DOM_INFOSET) || name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE) || name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES) || name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS) || name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA) || name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED) || name.equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT) || name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT) || name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL)){ // both values supported return true; } else if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM) || name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION) || name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION) || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA) || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE) // || name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS) ) { // true is not supported return !((Boolean)value).booleanValue(); } else if (name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) { // false is not supported return ((Boolean)value).booleanValue(); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER) && value == null || value instanceof DOMErrorHandler){ return true; } return false; } /** * This method returns the value of a parameter if known. * * @see org.w3c.dom.DOMConfiguration#getParameter(java.lang.String) * * @param name A String containing the DOMConfiguration parameter name * whose value is to be returned. * @return Object The value of the parameter if known. */ public Object getParameter(String name) throws DOMException { if (name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS)) { return ((fFeatures & COMMENTS) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS)) { return ((fFeatures & CDATA) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES)) { return ((fFeatures & ENTITIES) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES)) { return ((fFeatures & NAMESPACES) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS)) { return ((fFeatures & NAMESPACEDECLS) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA)) { return ((fFeatures & SPLITCDATA) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED)) { return ((fFeatures & WELLFORMED) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT)) { return ((fFeatures & DISCARDDEFAULT) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)) { return ((fFeatures & PRETTY_PRINT) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL)) { return ((fFeatures & XMLDECL) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE)) { return ((fFeatures & ELEM_CONTENT_WHITESPACE) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)) { return ((fFeatures & PRETTY_PRINT) != 0) ? Boolean.TRUE : Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) { return Boolean.TRUE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM) || name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION) || name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION) // || name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS) || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE) || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)) { return Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)){ if ((fFeatures & ENTITIES) == 0 && (fFeatures & CDATA) == 0 && (fFeatures & ELEM_CONTENT_WHITESPACE) != 0 && (fFeatures & NAMESPACES) != 0 && (fFeatures & NAMESPACEDECLS) != 0 && (fFeatures & WELLFORMED) != 0 && (fFeatures & COMMENTS) != 0) { return Boolean.TRUE; } return Boolean.FALSE; } else if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER)) { return fDOMErrorHandler; } else if ( name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_LOCATION) || name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_TYPE)) { return null; } else { // Here we have to add the Xalan specific DOM Message Formatter String msg = Utils.messages.createMessage( MsgKey.ER_FEATURE_NOT_FOUND, new Object[] { name }); throw new DOMException(DOMException.NOT_FOUND_ERR, msg); } } /** * This method returns a of the parameters supported by this DOMConfiguration object * and for which at least one value can be set by the application * * @see org.w3c.dom.DOMConfiguration#getParameterNames() * * @return DOMStringList A list of DOMConfiguration parameters recognized * by the serializer */ public DOMStringList getParameterNames() { return new DOMStringListImpl(fRecognizedParameters); } /** * This method sets the value of the named parameter. * * @see org.w3c.dom.DOMConfiguration#setParameter(java.lang.String, java.lang.Object) * * @param name A String containing the DOMConfiguration parameter name. * @param value An Object contaiing the parameters value to set. */ public void setParameter(String name, Object value) throws DOMException { // If the value is a boolean if (value instanceof Boolean) { boolean state = ((Boolean) value).booleanValue(); if (name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS)) { fFeatures = state ? fFeatures | COMMENTS : fFeatures & ~COMMENTS; // comments if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_EXPLICIT_TRUE); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_EXPLICIT_FALSE); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS)) { fFeatures = state ? fFeatures | CDATA : fFeatures & ~CDATA; // cdata-sections if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_EXPLICIT_TRUE); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_EXPLICIT_FALSE); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES)) { fFeatures = state ? fFeatures | ENTITIES : fFeatures & ~ENTITIES; // entities if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_TRUE); fDOMConfigProperties.setProperty( DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_TRUE); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE); fDOMConfigProperties.setProperty( DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES)) { fFeatures = state ? fFeatures | NAMESPACES : fFeatures & ~NAMESPACES; // namespaces if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_EXPLICIT_TRUE); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_EXPLICIT_FALSE); } } else if (name .equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS)) { fFeatures = state ? fFeatures | NAMESPACEDECLS : fFeatures & ~NAMESPACEDECLS; // namespace-declarations if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_EXPLICIT_TRUE); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_EXPLICIT_FALSE); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA)) { fFeatures = state ? fFeatures | SPLITCDATA : fFeatures & ~SPLITCDATA; // split-cdata-sections if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_SPLIT_CDATA, DOMConstants.DOM3_EXPLICIT_TRUE); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_SPLIT_CDATA, DOMConstants.DOM3_EXPLICIT_FALSE); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED)) { fFeatures = state ? fFeatures | WELLFORMED : fFeatures & ~WELLFORMED; // well-formed if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_EXPLICIT_TRUE); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_EXPLICIT_FALSE); } } else if (name .equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT)) { fFeatures = state ? fFeatures | DISCARDDEFAULT : fFeatures & ~DISCARDDEFAULT; // discard-default-content if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT, DOMConstants.DOM3_EXPLICIT_TRUE); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT, DOMConstants.DOM3_EXPLICIT_FALSE); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)) { fFeatures = state ? fFeatures | PRETTY_PRINT : fFeatures & ~PRETTY_PRINT; // format-pretty-print if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_FORMAT_PRETTY_PRINT, DOMConstants.DOM3_EXPLICIT_TRUE); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_FORMAT_PRETTY_PRINT, DOMConstants.DOM3_EXPLICIT_FALSE); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL)) { fFeatures = state ? fFeatures | XMLDECL : fFeatures & ~XMLDECL; if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "no"); } else { fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "yes"); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE)) { fFeatures = state ? fFeatures | ELEM_CONTENT_WHITESPACE : fFeatures & ~ELEM_CONTENT_WHITESPACE; // element-content-whitespace if (state) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_EXPLICIT_TRUE); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_EXPLICIT_FALSE); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) { // false is not supported if (!state) { // Here we have to add the Xalan specific DOM Message Formatter String msg = Utils.messages.createMessage( MsgKey.ER_FEATURE_NOT_SUPPORTED, new Object[] { name }); throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); } else { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS, DOMConstants.DOM3_EXPLICIT_TRUE); } } else if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM) || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA) || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE) || name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION) || name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION) // || name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS) ) { // true is not supported if (state) { String msg = Utils.messages.createMessage( MsgKey.ER_FEATURE_NOT_SUPPORTED, new Object[] { name }); throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); } else { if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM)) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CANONICAL_FORM, DOMConstants.DOM3_EXPLICIT_FALSE); } else if (name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_VALIDATE_IF_SCHEMA, DOMConstants.DOM3_EXPLICIT_FALSE); } else if (name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE)) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_VALIDATE, DOMConstants.DOM3_EXPLICIT_FALSE); } else if (name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)) { fDOMConfigProperties.setProperty(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION + DOMConstants.DOM_CHECK_CHAR_NORMALIZATION, DOMConstants.DOM3_EXPLICIT_FALSE); } else if (name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION)) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_DATATYPE_NORMALIZATION, DOMConstants.DOM3_EXPLICIT_FALSE); } /* else if (name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS)) { fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NORMALIZE_CHARACTERS, DOMConstants.DOM3_EXPLICIT_FALSE); } */ } } else if (name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)) { // infoset if (state) { fFeatures &= ~ENTITIES; fFeatures &= ~CDATA; fFeatures &= ~SCHEMAVALIDATE; fFeatures &= ~DTNORMALIZE; fFeatures |= NAMESPACES; fFeatures |= NAMESPACEDECLS; fFeatures |= WELLFORMED; fFeatures |= ELEM_CONTENT_WHITESPACE; fFeatures |= COMMENTS; fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_EXPLICIT_TRUE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_EXPLICIT_TRUE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_EXPLICIT_TRUE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_EXPLICIT_TRUE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_EXPLICIT_TRUE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE); fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_EXPLICIT_FALSE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_VALIDATE_IF_SCHEMA, DOMConstants.DOM3_EXPLICIT_FALSE); fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_DATATYPE_NORMALIZATION, DOMConstants.DOM3_EXPLICIT_FALSE); } } else { // If this is a non-boolean parameter a type mismatch should be thrown. if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER) || name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_LOCATION) || name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_TYPE)) { String msg = Utils.messages.createMessage( MsgKey.ER_TYPE_MISMATCH_ERR, new Object[] { name }); throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg); } // Parameter is not recognized String msg = Utils.messages.createMessage( MsgKey.ER_FEATURE_NOT_FOUND, new Object[] { name }); throw new DOMException(DOMException.NOT_FOUND_ERR, msg); } } // If the parameter value is not a boolean else if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER)) { if (value == null || value instanceof DOMErrorHandler) { fDOMErrorHandler = (DOMErrorHandler)value; } else { String msg = Utils.messages.createMessage( MsgKey.ER_TYPE_MISMATCH_ERR, new Object[] { name }); throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg); } } else if ( name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_LOCATION) || name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_TYPE)) { if (value != null) { if (!(value instanceof String)) { String msg = Utils.messages.createMessage( MsgKey.ER_TYPE_MISMATCH_ERR, new Object[] { name }); throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg); } String msg = Utils.messages.createMessage( MsgKey.ER_FEATURE_NOT_SUPPORTED, new Object[] { name }); throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); } } else { // If this is a boolean parameter a type mismatch should be thrown. if (name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS) || name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS) || name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES) || name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES) || name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS) || name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA) || name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED) || name.equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT) || name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT) || name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL) || name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE) || name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS) || name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM) || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA) || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE) || name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION) || name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION) || name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)) { String msg = Utils.messages.createMessage( MsgKey.ER_TYPE_MISMATCH_ERR, new Object[] { name }); throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg); } // Parameter is not recognized String msg = Utils.messages.createMessage( MsgKey.ER_FEATURE_NOT_FOUND, new Object[] { name }); throw new DOMException(DOMException.NOT_FOUND_ERR, msg); } } // ************************************************************************ // ************************************************************************ // DOMConfiguraiton implementation // ************************************************************************ /** * Returns the DOMConfiguration of the LSSerializer. * * @see org.w3c.dom.ls.LSSerializer#getDomConfig() * @since DOM Level 3 * @return A DOMConfiguration object. */ public DOMConfiguration getDomConfig() { return (DOMConfiguration)this; } /** * Returns the DOMConfiguration of the LSSerializer. * * @see org.w3c.dom.ls.LSSerializer#getFilter() * @since DOM Level 3 * @return A LSSerializerFilter object. */ public LSSerializerFilter getFilter() { return fSerializerFilter; } /** * Returns the End-Of-Line sequence of characters to be used in the XML * being serialized. If none is set a default "\n" is returned. * * @see org.w3c.dom.ls.LSSerializer#getNewLine() * @since DOM Level 3 * @return A String containing the end-of-line character sequence used in * serialization. */ public String getNewLine() { return fEndOfLine; } /** * Set a LSSerilizerFilter on the LSSerializer. When set, the filter is * called before each node is serialized which depending on its implemention * determines if the node is to be serialized or not. * * @see org.w3c.dom.ls.LSSerializer#setFilter * @since DOM Level 3 * @param filter A LSSerializerFilter to be applied to the stream to serialize. */ public void setFilter(LSSerializerFilter filter) { fSerializerFilter = filter; } /** * Sets the End-Of-Line sequence of characters to be used in the XML * being serialized. Setting this attribute to null will reset its * value to the default value i.e. "\n". * * @see org.w3c.dom.ls.LSSerializer#setNewLine * @since DOM Level 3 * @param newLine a String that is the end-of-line character sequence to be used in * serialization. */ public void setNewLine(String newLine) { fEndOfLine = (newLine != null) ? newLine : DEFAULT_END_OF_LINE; } /** * Serializes the specified node to the specified LSOutput and returns true if the Node * was successfully serialized. * * @see org.w3c.dom.ls.LSSerializer#write(org.w3c.dom.Node, org.w3c.dom.ls.LSOutput) * @since DOM Level 3 * @param nodeArg The Node to serialize. * @throws org.w3c.dom.ls.LSException SERIALIZE_ERR: Raised if the * LSSerializer was unable to serialize the node. * */ public boolean write(Node nodeArg, LSOutput destination) throws LSException { // If the destination is null if (destination == null) { String msg = Utils.messages .createMessage( MsgKey.ER_NO_OUTPUT_SPECIFIED, null); if (fDOMErrorHandler != null) { fDOMErrorHandler.handleError(new DOMErrorImpl( DOMError.SEVERITY_FATAL_ERROR, msg, MsgKey.ER_NO_OUTPUT_SPECIFIED)); } throw new LSException(LSException.SERIALIZE_ERR, msg); } // If nodeArg is null, return false. Should we throw and LSException instead? if (nodeArg == null ) { return false; } // Obtain a reference to the serializer to use // Serializer serializer = getXMLSerializer(xmlVersion); Serializer serializer = fXMLSerializer; serializer.reset(); // If the node has not been seen if ( nodeArg != fVisitedNode) { // Determine the XML Document version of the Node String xmlVersion = getXMLVersion(nodeArg); // Determine the encoding: 1.LSOutput.encoding, 2.Document.inputEncoding, 3.Document.xmlEncoding. fEncoding = destination.getEncoding(); if (fEncoding == null ) { fEncoding = getInputEncoding(nodeArg); fEncoding = fEncoding != null ? fEncoding : getXMLEncoding(nodeArg) == null? "UTF-8": getXMLEncoding(nodeArg); } // If the encoding is not recognized throw an exception. // Note: The serializer defaults to UTF-8 when created if (!Encodings.isRecognizedEncoding(fEncoding)) { String msg = Utils.messages .createMessage( MsgKey.ER_UNSUPPORTED_ENCODING, null); if (fDOMErrorHandler != null) { fDOMErrorHandler.handleError(new DOMErrorImpl( DOMError.SEVERITY_FATAL_ERROR, msg, MsgKey.ER_UNSUPPORTED_ENCODING)); } throw new LSException(LSException.SERIALIZE_ERR, msg); } serializer.getOutputFormat().setProperty("version", xmlVersion); // Set the output encoding and xml version properties fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION, xmlVersion); fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_ENCODING, fEncoding); // If the node to be serialized is not a Document, Element, or Entity // node // then the XML declaration, or text declaration, should be never be // serialized. if ( (nodeArg.getNodeType() != Node.DOCUMENT_NODE || nodeArg.getNodeType() != Node.ELEMENT_NODE || nodeArg.getNodeType() != Node.ENTITY_NODE) && ((fFeatures & XMLDECL) != 0)) { fDOMConfigProperties.setProperty( DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, DOMConstants.DOM3_DEFAULT_FALSE); } fVisitedNode = nodeArg; } // Update the serializer properties fXMLSerializer.setOutputFormat(fDOMConfigProperties); // try { // The LSSerializer will use the LSOutput object to determine // where to serialize the output to in the following order the // first one that is not null and not an empty string will be // used: 1.LSOutput.characterStream, 2.LSOutput.byteStream, // 3. LSOutput.systemId // 1.LSOutput.characterStream Writer writer = destination.getCharacterStream(); if (writer == null ) { // 2.LSOutput.byteStream OutputStream outputStream = destination.getByteStream(); if ( outputStream == null) { // 3. LSOutput.systemId String uri = destination.getSystemId(); if (uri == null) { String msg = Utils.messages .createMessage( MsgKey.ER_NO_OUTPUT_SPECIFIED, null); if (fDOMErrorHandler != null) { fDOMErrorHandler.handleError(new DOMErrorImpl( DOMError.SEVERITY_FATAL_ERROR, msg, MsgKey.ER_NO_OUTPUT_SPECIFIED)); } throw new LSException(LSException.SERIALIZE_ERR, msg); } else { // Expand the System Id and obtain an absolute URI for it. String absoluteURI = SystemIDResolver.getAbsoluteURI(uri); URL url = new URL(absoluteURI); OutputStream urlOutStream = null; String protocol = url.getProtocol(); String host = url.getHost(); // For file protocols, there is no need to use a URL to get its // corresponding OutputStream // Scheme names consist of a sequence of characters. The lower case // letters "a"--"z", digits, and the characters plus ("+"), period // ("."), and hyphen ("-") are allowed. For resiliency, programs // interpreting URLs should treat upper case letters as equivalent to // lower case in scheme names (e.g., allow "HTTP" as well as "http"). if (protocol.equalsIgnoreCase("file") && (host == null || host.length() == 0 || host.equals("localhost"))) { // do we also need to check for host.equals(hostname) urlOutStream = new FileOutputStream(getPathWithoutEscapes(url.getPath())); } else { // This should support URL's whose schemes are mentioned in // RFC1738 other than file URLConnection urlCon = url.openConnection(); urlCon.setDoInput(false); urlCon.setDoOutput(true); urlCon.setUseCaches(false); urlCon.setAllowUserInteraction(false); // When writing to a HTTP URI, a HTTP PUT is performed. if (urlCon instanceof HttpURLConnection) { HttpURLConnection httpCon = (HttpURLConnection) urlCon; httpCon.setRequestMethod("PUT"); } urlOutStream = urlCon.getOutputStream(); } // set the OutputStream to that obtained from the systemId serializer.setOutputStream(urlOutStream); } } else { // 2.LSOutput.byteStream serializer.setOutputStream(outputStream); } } else { // 1.LSOutput.characterStream serializer.setWriter(writer); } // The associated media type by default is set to text/xml on // org.apache.xml.serializer.SerializerBase. // Get a reference to the serializer then lets you serilize a DOM // Use this hack till Xalan support JAXP1.3 if (fDOMSerializer == null) { fDOMSerializer = (DOM3Serializer)serializer.asDOM3Serializer(); } // Set the error handler on the DOM3Serializer interface implementation if (fDOMErrorHandler != null) { fDOMSerializer.setErrorHandler(fDOMErrorHandler); } // Set the filter on the DOM3Serializer interface implementation if (fSerializerFilter != null) { fDOMSerializer.setNodeFilter(fSerializerFilter); } // Set the NewLine character to be used fDOMSerializer.setNewLine(fEndOfLine.toCharArray()); // Serializer your DOM, where node is an org.w3c.dom.Node // Assuming that Xalan's serializer can serialize any type of DOM node fDOMSerializer.serializeDOM3(nodeArg); } catch( UnsupportedEncodingException ue) { String msg = Utils.messages .createMessage( MsgKey.ER_UNSUPPORTED_ENCODING, null); if (fDOMErrorHandler != null) { fDOMErrorHandler.handleError(new DOMErrorImpl( DOMError.SEVERITY_FATAL_ERROR, msg, MsgKey.ER_UNSUPPORTED_ENCODING, ue)); } throw (LSException) createLSException(LSException.SERIALIZE_ERR, ue).fillInStackTrace(); } catch (LSException lse) { // Rethrow LSException. throw lse; } catch (RuntimeException e) { throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); } catch (Exception e) { if (fDOMErrorHandler != null) { fDOMErrorHandler.handleError(new DOMErrorImpl( DOMError.SEVERITY_FATAL_ERROR, e.getMessage(), null, e)); } throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); } return true; } /** * Serializes the specified node and returns a String with the serialized * data to the caller. * * @see org.w3c.dom.ls.LSSerializer#writeToString(org.w3c.dom.Node) * @since DOM Level 3 * @param nodeArg The Node to serialize. * @throws org.w3c.dom.ls.LSException SERIALIZE_ERR: Raised if the * LSSerializer was unable to serialize the node. * */ public String writeToString(Node nodeArg) throws DOMException, LSException { // return null is nodeArg is null. Should an Exception be thrown instead? if (nodeArg == null) { return null; } // Should we reset the serializer configuration before each write operation? // Obtain a reference to the serializer to use Serializer serializer = fXMLSerializer; serializer.reset(); if (nodeArg != fVisitedNode){ // Determine the XML Document version of the Node String xmlVersion = getXMLVersion(nodeArg); serializer.getOutputFormat().setProperty("version", xmlVersion); // Set the output encoding and xml version properties fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION, xmlVersion); fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_ENCODING, "UTF-16"); // If the node to be serialized is not a Document, Element, or Entity // node // then the XML declaration, or text declaration, should be never be // serialized. if ((nodeArg.getNodeType() != Node.DOCUMENT_NODE || nodeArg.getNodeType() != Node.ELEMENT_NODE || nodeArg.getNodeType() != Node.ENTITY_NODE) && ((fFeatures & XMLDECL) != 0)) { fDOMConfigProperties.setProperty( DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, DOMConstants.DOM3_DEFAULT_FALSE); } fVisitedNode = nodeArg; } // Update the serializer properties fXMLSerializer.setOutputFormat(fDOMConfigProperties); // StringWriter to Output to StringWriter output = new StringWriter(); // try { // Set the Serializer's Writer to a StringWriter serializer.setWriter(output); // Get a reference to the serializer then lets you serilize a DOM // Use this hack till Xalan support JAXP1.3 if (fDOMSerializer == null) { fDOMSerializer = (DOM3Serializer)serializer.asDOM3Serializer(); } // Set the error handler on the DOM3Serializer interface implementation if (fDOMErrorHandler != null) { fDOMSerializer.setErrorHandler(fDOMErrorHandler); } // Set the filter on the DOM3Serializer interface implementation if (fSerializerFilter != null) { fDOMSerializer.setNodeFilter(fSerializerFilter); } // Set the NewLine character to be used fDOMSerializer.setNewLine(fEndOfLine.toCharArray()); // Serializer your DOM, where node is an org.w3c.dom.Node fDOMSerializer.serializeDOM3(nodeArg); } catch (LSException lse) { // Rethrow LSException. throw lse; } catch (RuntimeException e) { throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); } catch (Exception e) { if (fDOMErrorHandler != null) { fDOMErrorHandler.handleError(new DOMErrorImpl( DOMError.SEVERITY_FATAL_ERROR, e.getMessage(), null, e)); } throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); } // return the serialized string return output.toString(); } /** * Serializes the specified node to the specified URI and returns true if the Node * was successfully serialized. * * @see org.w3c.dom.ls.LSSerializer#writeToURI(org.w3c.dom.Node, String) * @since DOM Level 3 * @param nodeArg The Node to serialize. * @throws org.w3c.dom.ls.LSException SERIALIZE_ERR: Raised if the * LSSerializer was unable to serialize the node. * */ public boolean writeToURI(Node nodeArg, String uri) throws LSException { // If nodeArg is null, return false. Should we throw and LSException instead? if (nodeArg == null ) { return false; } // Obtain a reference to the serializer to use Serializer serializer = fXMLSerializer; serializer.reset(); if (nodeArg != fVisitedNode) { // Determine the XML Document version of the Node String xmlVersion = getXMLVersion(nodeArg); // Determine the encoding: 1.LSOutput.encoding, // 2.Document.inputEncoding, 3.Document.xmlEncoding. fEncoding = getInputEncoding(nodeArg); if (fEncoding == null ) { fEncoding = fEncoding != null ? fEncoding : getXMLEncoding(nodeArg) == null? "UTF-8": getXMLEncoding(nodeArg); } serializer.getOutputFormat().setProperty("version", xmlVersion); // Set the output encoding and xml version properties fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION, xmlVersion); fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_ENCODING, fEncoding); // If the node to be serialized is not a Document, Element, or Entity // node // then the XML declaration, or text declaration, should be never be // serialized. if ( (nodeArg.getNodeType() != Node.DOCUMENT_NODE || nodeArg.getNodeType() != Node.ELEMENT_NODE || nodeArg.getNodeType() != Node.ENTITY_NODE) && ((fFeatures & XMLDECL) != 0)) { fDOMConfigProperties.setProperty( DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, DOMConstants.DOM3_DEFAULT_FALSE); } fVisitedNode = nodeArg; } // Update the serializer properties fXMLSerializer.setOutputFormat(fDOMConfigProperties); // try { // If the specified encoding is not supported an // "unsupported-encoding" fatal error is raised. ?? if (uri == null) { String msg = Utils.messages.createMessage( MsgKey.ER_NO_OUTPUT_SPECIFIED, null); if (fDOMErrorHandler != null) { fDOMErrorHandler.handleError(new DOMErrorImpl( DOMError.SEVERITY_FATAL_ERROR, msg, MsgKey.ER_NO_OUTPUT_SPECIFIED)); } throw new LSException(LSException.SERIALIZE_ERR, msg); } else { // REVISIT: Can this be used to get an absolute expanded URI String absoluteURI = SystemIDResolver.getAbsoluteURI(uri); URL url = new URL(absoluteURI); OutputStream urlOutStream = null; String protocol = url.getProtocol(); String host = url.getHost(); // For file protocols, there is no need to use a URL to get its // corresponding OutputStream // Scheme names consist of a sequence of characters. The lower // case letters "a"--"z", digits, and the characters plus ("+"), // period ("."), and hyphen ("-") are allowed. For resiliency, // programs interpreting URLs should treat upper case letters as // equivalent to lower case in scheme names // (e.g., allow "HTTP" as well as "http"). if (protocol.equalsIgnoreCase("file") && (host == null || host.length() == 0 || host .equals("localhost"))) { // do we also need to check for host.equals(hostname) urlOutStream = new FileOutputStream(getPathWithoutEscapes(url.getPath())); } else { // This should support URL's whose schemes are mentioned in // RFC1738 other than file URLConnection urlCon = url.openConnection(); urlCon.setDoInput(false); urlCon.setDoOutput(true); urlCon.setUseCaches(false); urlCon.setAllowUserInteraction(false); // When writing to a HTTP URI, a HTTP PUT is performed. if (urlCon instanceof HttpURLConnection) { HttpURLConnection httpCon = (HttpURLConnection) urlCon; httpCon.setRequestMethod("PUT"); } urlOutStream = urlCon.getOutputStream(); } // set the OutputStream to that obtained from the systemId serializer.setOutputStream(urlOutStream); } // Get a reference to the serializer then lets you serilize a DOM // Use this hack till Xalan support JAXP1.3 if (fDOMSerializer == null) { fDOMSerializer = (DOM3Serializer)serializer.asDOM3Serializer(); } // Set the error handler on the DOM3Serializer interface implementation if (fDOMErrorHandler != null) { fDOMSerializer.setErrorHandler(fDOMErrorHandler); } // Set the filter on the DOM3Serializer interface implementation if (fSerializerFilter != null) { fDOMSerializer.setNodeFilter(fSerializerFilter); } // Set the NewLine character to be used fDOMSerializer.setNewLine(fEndOfLine.toCharArray()); // Serializer your DOM, where node is an org.w3c.dom.Node // Assuming that Xalan's serializer can serialize any type of DOM // node fDOMSerializer.serializeDOM3(nodeArg); } catch (LSException lse) { // Rethrow LSException. throw lse; } catch (RuntimeException e) { throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); } catch (Exception e) { if (fDOMErrorHandler != null) { fDOMErrorHandler.handleError(new DOMErrorImpl( DOMError.SEVERITY_FATAL_ERROR, e.getMessage(), null, e)); } throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); } return true; } // ************************************************************************ // ************************************************************************ // Implementaion methods // ************************************************************************ /** * Determines the XML Version of the Document Node to serialize. If the Document Node * is not a DOM Level 3 Node, then the default version returned is 1.0. * * @param nodeArg The Node to serialize * @return A String containing the version pseudo-attribute of the XMLDecl. * @throws Throwable if the DOM implementation does not implement Document.getXmlVersion() */ //protected String getXMLVersion(Node nodeArg) throws Throwable { protected String getXMLVersion(Node nodeArg) { Document doc = null; // Determine the XML Version of the document if (nodeArg != null) { if (nodeArg.getNodeType() == Node.DOCUMENT_NODE) { // The Document node is the Node argument doc = (Document)nodeArg; } else { // The Document node is the Node argument's ownerDocument doc = nodeArg.getOwnerDocument(); } // Determine the DOM Version. if (doc != null && doc.getImplementation().hasFeature("Core","3.0")) { return doc.getXmlVersion(); } } // The version will be treated as "1.0" which may result in // an ill-formed document being serialized. // If nodeArg does not have an ownerDocument, treat this as XML 1.0 return "1.0"; } /** * Determines the XML Encoding of the Document Node to serialize. If the Document Node * is not a DOM Level 3 Node, then the default encoding "UTF-8" is returned. * * @param nodeArg The Node to serialize * @return A String containing the encoding pseudo-attribute of the XMLDecl. * @throws Throwable if the DOM implementation does not implement Document.getXmlEncoding() */ protected String getXMLEncoding(Node nodeArg) { Document doc = null; // Determine the XML Encoding of the document if (nodeArg != null) { if (nodeArg.getNodeType() == Node.DOCUMENT_NODE) { // The Document node is the Node argument doc = (Document)nodeArg; } else { // The Document node is the Node argument's ownerDocument doc = nodeArg.getOwnerDocument(); } // Determine the XML Version. if (doc != null && doc.getImplementation().hasFeature("Core","3.0")) { return doc.getXmlEncoding(); } } // The default encoding is UTF-8 except for the writeToString method return "UTF-8"; } /** * Determines the Input Encoding of the Document Node to serialize. If the Document Node * is not a DOM Level 3 Node, then null is returned. * * @param nodeArg The Node to serialize * @return A String containing the input encoding. */ protected String getInputEncoding(Node nodeArg) { Document doc = null; // Determine the Input Encoding of the document if (nodeArg != null) { if (nodeArg.getNodeType() == Node.DOCUMENT_NODE) { // The Document node is the Node argument doc = (Document)nodeArg; } else { // The Document node is the Node argument's ownerDocument doc = nodeArg.getOwnerDocument(); } // Determine the DOM Version. if (doc != null && doc.getImplementation().hasFeature("Core","3.0")) { return doc.getInputEncoding(); } } // The default encoding returned is null return null; } /** * This method returns the LSSerializer's error handler. * * @return Returns the fDOMErrorHandler. */ public DOMErrorHandler getErrorHandler() { return fDOMErrorHandler; } /** * Replaces all escape sequences in the given path with their literal characters. */ private static String getPathWithoutEscapes(String origPath) { if (origPath != null && origPath.length() != 0 && origPath.indexOf('%') != -1) { // Locate the escape characters StringTokenizer tokenizer = new StringTokenizer(origPath, "%"); StringBuffer result = new StringBuffer(origPath.length()); int size = tokenizer.countTokens(); result.append(tokenizer.nextToken()); for(int i = 1; i < size; ++i) { String token = tokenizer.nextToken(); if (token.length() >= 2 && isHexDigit(token.charAt(0)) && isHexDigit(token.charAt(1))) { // Decode the 2 digit hexadecimal number following % in '%nn' result.append((char)Integer.valueOf(token.substring(0, 2), 16).intValue()); token = token.substring(2); } result.append(token); } return result.toString(); } return origPath; } /** * Returns true if the given character is a valid hex character. */ private static boolean isHexDigit(char c) { return (c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F'); } /** * Creates an LSException. On J2SE 1.4 and above the cause for the exception will be set. */ private static LSException createLSException(short code, Throwable cause) { LSException lse = new LSException(code, cause != null ? cause.getMessage() : null); if (cause != null && ThrowableMethods.fgThrowableMethodsAvailable) { try { ThrowableMethods.fgThrowableInitCauseMethod.invoke(lse, new Object [] {cause}); } // Something went wrong. There's not much we can do about it. catch (Exception e) {} } return lse; } /** * Holder of methods from java.lang.Throwable. */ static class ThrowableMethods { // Method: java.lang.Throwable.initCause(java.lang.Throwable) private static java.lang.reflect.Method fgThrowableInitCauseMethod = null; // Flag indicating whether or not Throwable methods available. private static boolean fgThrowableMethodsAvailable = false; private ThrowableMethods() {} // Attempt to get methods for java.lang.Throwable on class initialization. static { try { fgThrowableInitCauseMethod = Throwable.class.getMethod("initCause", new Class [] {Throwable.class}); fgThrowableMethodsAvailable = true; } // ClassNotFoundException, NoSuchMethodException or SecurityException // Whatever the case, we cannot use java.lang.Throwable.initCause(java.lang.Throwable). catch (Exception exc) { fgThrowableInitCauseMethod = null; fgThrowableMethodsAvailable = false; } } } }