// // System.Xml.XmlWriter // // Authors: // Kral Ferch // Atsushi Enomoto // // (C) 2002 Kral Ferch // (C) 2002-2003 Atsushi Enomoto // (C) 2004-2007 Novell, Inc. // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections; using System.IO; using System.Text; #if !MOONLIGHT using System.Xml.XPath; #endif namespace System.Xml { public abstract class XmlWriter : IDisposable { #if NET_2_0 XmlWriterSettings settings; #endif #region Constructors protected XmlWriter () { } #endregion #region Properties #if NET_2_0 public virtual XmlWriterSettings Settings { get { if (settings == null) settings = new XmlWriterSettings (); return settings; } } #endif public abstract WriteState WriteState { get; } #if NET_2_0 public virtual string XmlLang { get { return null; } } public virtual XmlSpace XmlSpace { get { return XmlSpace.None; } } #else public abstract string XmlLang { get; } public abstract XmlSpace XmlSpace { get; } #endif #endregion #region Methods public abstract void Close (); #if NET_2_0 public static XmlWriter Create (Stream stream) { return Create (stream, null); } public static XmlWriter Create (string file) { return Create (file, null); } public static XmlWriter Create (TextWriter writer) { return Create (writer, null); } public static XmlWriter Create (XmlWriter writer) { return Create (writer, null); } public static XmlWriter Create (StringBuilder builder) { return Create (builder, null); } public static XmlWriter Create (Stream stream, XmlWriterSettings settings) { Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8; return Create (new StreamWriter (stream, enc), settings); } public static XmlWriter Create (string file, XmlWriterSettings settings) { Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8; return CreateTextWriter (new StreamWriter (file, false, enc), settings, true); } public static XmlWriter Create (StringBuilder builder, XmlWriterSettings settings) { return Create (new StringWriter (builder), settings); } public static XmlWriter Create (TextWriter writer, XmlWriterSettings settings) { if (settings == null) settings = new XmlWriterSettings (); return CreateTextWriter (writer, settings, settings.CloseOutput); } public static XmlWriter Create (XmlWriter writer, XmlWriterSettings settings) { if (settings == null) settings = new XmlWriterSettings (); writer.settings = settings; return writer; } private static XmlWriter CreateTextWriter (TextWriter writer, XmlWriterSettings settings, bool closeOutput) { if (settings == null) settings = new XmlWriterSettings (); XmlTextWriter xtw = new XmlTextWriter (writer, settings, closeOutput); return Create (xtw, settings); } protected virtual void Dispose (bool disposing) { Close (); } void IDisposable.Dispose () { Dispose (false); } #endif public abstract void Flush (); public abstract string LookupPrefix (string ns); private void WriteAttribute (XmlReader reader, bool defattr) { if (!defattr && reader.IsDefault) return; WriteStartAttribute (reader.Prefix, reader.LocalName, reader.NamespaceURI); #if MOONLIGHT // no ReadAttributeValue() in 2.1 profile. WriteString (reader.Value); #else while (reader.ReadAttributeValue ()) { switch (reader.NodeType) { case XmlNodeType.Text: WriteString (reader.Value); break; case XmlNodeType.EntityReference: WriteEntityRef (reader.Name); break; } } #endif WriteEndAttribute (); } public virtual void WriteAttributes (XmlReader reader, bool defattr) { if(reader == null) throw new ArgumentException("null XmlReader specified.", "reader"); switch (reader.NodeType) { case XmlNodeType.XmlDeclaration: WriteAttributeString ("version", reader ["version"]); if (reader ["encoding"] != null) WriteAttributeString ("encoding", reader ["encoding"]); if (reader ["standalone"] != null) WriteAttributeString ("standalone", reader ["standalone"]); break; case XmlNodeType.Element: if (reader.MoveToFirstAttribute ()) goto case XmlNodeType.Attribute; break; case XmlNodeType.Attribute: do { WriteAttribute (reader, defattr); } while (reader.MoveToNextAttribute ()); reader.MoveToElement (); break; default: throw new XmlException("NodeType is not one of Element, Attribute, nor XmlDeclaration."); } } public void WriteAttributeString (string localName, string value) { WriteAttributeString ("", localName, null, value); } public void WriteAttributeString (string localName, string ns, string value) { WriteAttributeString ("", localName, ns, value); } public void WriteAttributeString (string prefix, string localName, string ns, string value) { // In MS.NET (1.0), this check is done *here*, not at WriteStartAttribute. // (XmlTextWriter.WriteStartAttribute("xmlns", "anyname", null) throws an exception. WriteStartAttribute (prefix, localName, ns); if (value != null && value.Length > 0) WriteString (value); WriteEndAttribute (); } public abstract void WriteBase64 (byte[] buffer, int index, int count); #if NET_2_0 public virtual void WriteBinHex (byte [] buffer, int index, int count) { StringWriter sw = new StringWriter (); XmlConvert.WriteBinHex (buffer, index, count, sw); WriteString (sw.ToString ()); } #else public abstract void WriteBinHex (byte[] buffer, int index, int count); #endif public abstract void WriteCData (string text); public abstract void WriteCharEntity (char ch); public abstract void WriteChars (char[] buffer, int index, int count); public abstract void WriteComment (string text); public abstract void WriteDocType (string name, string pubid, string sysid, string subset); public void WriteElementString (string localName, string value) { WriteStartElement(localName); if (value != null && value.Length > 0) WriteString(value); WriteEndElement(); } public void WriteElementString (string localName, string ns, string value) { WriteStartElement(localName, ns); if (value != null && value.Length > 0) WriteString(value); WriteEndElement(); } #if NET_2_0 public void WriteElementString (string prefix, string localName, string ns, string value) { WriteStartElement(prefix, localName, ns); if (value != null && value.Length > 0) WriteString(value); WriteEndElement(); } #endif public abstract void WriteEndAttribute (); public abstract void WriteEndDocument (); public abstract void WriteEndElement (); public abstract void WriteEntityRef (string name); public abstract void WriteFullEndElement (); #if NET_2_0 public virtual void WriteName (string name) { WriteNameInternal (name); } public virtual void WriteNmToken (string name) { WriteNmTokenInternal (name); } public virtual void WriteQualifiedName (string localName, string ns) { WriteQualifiedNameInternal (localName, ns); } #else public abstract void WriteName (string name); public abstract void WriteNmToken (string name); public abstract void WriteQualifiedName (string localName, string ns); #endif internal void WriteNameInternal (string name) { #if NET_2_0 switch (Settings.ConformanceLevel) { case ConformanceLevel.Document: case ConformanceLevel.Fragment: XmlConvert.VerifyName (name); break; } #else XmlConvert.VerifyName (name); #endif WriteString (name); } internal virtual void WriteNmTokenInternal (string name) { bool valid = true; #if NET_2_0 switch (Settings.ConformanceLevel) { case ConformanceLevel.Document: case ConformanceLevel.Fragment: valid = XmlChar.IsNmToken (name); break; } #else valid = XmlChar.IsNmToken (name); #endif if (!valid) throw new ArgumentException ("Argument name is not a valid NMTOKEN."); WriteString (name); } internal void WriteQualifiedNameInternal (string localName, string ns) { if (localName == null || localName == String.Empty) throw new ArgumentException (); if (ns == null) ns = String.Empty; #if NET_2_0 switch (Settings.ConformanceLevel) { case ConformanceLevel.Document: case ConformanceLevel.Fragment: XmlConvert.VerifyNCName (localName); break; } #else XmlConvert.VerifyNCName (localName); #endif string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty; if (prefix == null) throw new ArgumentException (String.Format ("Namespace '{0}' is not declared.", ns)); if (prefix != String.Empty) { WriteString (prefix); WriteString (":"); WriteString (localName); } else WriteString (localName); } #if !MOONLIGHT public virtual void WriteNode (XPathNavigator navigator, bool defattr) { if (navigator == null) throw new ArgumentNullException ("navigator"); switch (navigator.NodeType) { case XPathNodeType.Attribute: // no operation break; case XPathNodeType.Namespace: // no operation break; case XPathNodeType.Text: WriteString (navigator.Value); break; case XPathNodeType.SignificantWhitespace: WriteWhitespace (navigator.Value); break; case XPathNodeType.Whitespace: WriteWhitespace (navigator.Value); break; case XPathNodeType.Comment: WriteComment (navigator.Value); break; case XPathNodeType.ProcessingInstruction: WriteProcessingInstruction (navigator.Name, navigator.Value); break; case XPathNodeType.Root: if (navigator.MoveToFirstChild ()) { do { WriteNode (navigator, defattr); } while (navigator.MoveToNext ()); navigator.MoveToParent (); } break; case XPathNodeType.Element: WriteStartElement (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI); if (navigator.MoveToFirstNamespace (XPathNamespaceScope.Local)) { do { if (defattr || navigator.SchemaInfo == null || navigator.SchemaInfo.IsDefault) WriteAttributeString (navigator.Prefix, navigator.LocalName == String.Empty ? "xmlns" : navigator.LocalName, "http://www.w3.org/2000/xmlns/", navigator.Value); } while (navigator.MoveToNextNamespace (XPathNamespaceScope.Local)); navigator.MoveToParent (); } if (navigator.MoveToFirstAttribute ()) { do { if (defattr || navigator.SchemaInfo == null || navigator.SchemaInfo.IsDefault) WriteAttributeString (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI, navigator.Value); } while (navigator.MoveToNextAttribute ()); navigator.MoveToParent (); } if (navigator.MoveToFirstChild ()) { do { WriteNode (navigator, defattr); } while (navigator.MoveToNext ()); navigator.MoveToParent (); } if (navigator.IsEmptyElement) WriteEndElement (); else WriteFullEndElement (); break; default: throw new NotSupportedException (); } } #endif public virtual void WriteNode (XmlReader reader, bool defattr) { if (reader == null) throw new ArgumentException (); if (reader.ReadState == ReadState.Initial) { reader.Read (); do { WriteNode (reader, defattr); } while (!reader.EOF); return; } switch (reader.NodeType) { case XmlNodeType.Element: WriteStartElement (reader.Prefix, reader.LocalName, reader.NamespaceURI); #if false WriteAttributes (reader, defattr); reader.MoveToElement (); #else // Well, I found that MS.NET took this way, since // there was a error-prone SgmlReader that fails // MoveToNextAttribute(). if (reader.HasAttributes) { for (int i = 0; i < reader.AttributeCount; i++) { reader.MoveToAttribute (i); WriteAttribute (reader, defattr); } reader.MoveToElement (); } #endif if (reader.IsEmptyElement) WriteEndElement (); else { int depth = reader.Depth; reader.Read (); if (reader.NodeType != XmlNodeType.EndElement) { do { WriteNode (reader, defattr); } while (depth < reader.Depth); } WriteFullEndElement (); } break; // In case of XmlAttribute, don't proceed reader, and it will never be written. case XmlNodeType.Attribute: return; case XmlNodeType.Text: WriteString (reader.Value); break; case XmlNodeType.CDATA: WriteCData (reader.Value); break; case XmlNodeType.EntityReference: WriteEntityRef (reader.Name); break; case XmlNodeType.XmlDeclaration: // LAMESPEC: It means that XmlWriter implementation _must not_ check // whether PI name is "xml" (it is XML error) or not. case XmlNodeType.ProcessingInstruction: WriteProcessingInstruction (reader.Name, reader.Value); break; case XmlNodeType.Comment: WriteComment (reader.Value); break; case XmlNodeType.DocumentType: WriteDocType (reader.Name, reader ["PUBLIC"], reader ["SYSTEM"], reader.Value); break; case XmlNodeType.SignificantWhitespace: goto case XmlNodeType.Whitespace; case XmlNodeType.Whitespace: WriteWhitespace (reader.Value); break; case XmlNodeType.EndElement: WriteFullEndElement (); break; case XmlNodeType.EndEntity: break; case XmlNodeType.None: break; // Do nothing, nor reporting errors. default: throw new XmlException ("Unexpected node " + reader.Name + " of type " + reader.NodeType); } reader.Read (); } public abstract void WriteProcessingInstruction (string name, string text); public abstract void WriteRaw (string data); public abstract void WriteRaw (char[] buffer, int index, int count); #if NET_2_0 public void WriteStartAttribute (string localName) { WriteStartAttribute (null, localName, null); } #endif public void WriteStartAttribute (string localName, string ns) { WriteStartAttribute (null, localName, ns); } public abstract void WriteStartAttribute (string prefix, string localName, string ns); public abstract void WriteStartDocument (); public abstract void WriteStartDocument (bool standalone); public void WriteStartElement (string localName) { WriteStartElement (null, localName, null); } public void WriteStartElement (string localName, string ns) { WriteStartElement (null, localName, ns); } public abstract void WriteStartElement (string prefix, string localName, string ns); public abstract void WriteString (string text); public abstract void WriteSurrogateCharEntity (char lowChar, char highChar); public abstract void WriteWhitespace (string ws); #if NET_2_0 public virtual void WriteValue (bool value) { WriteString (XQueryConvert.BooleanToString (value)); } public virtual void WriteValue (DateTime value) { WriteString (XmlConvert.ToString (value)); } public virtual void WriteValue (decimal value) { WriteString (XQueryConvert.DecimalToString (value)); } public virtual void WriteValue (double value) { WriteString (XQueryConvert.DoubleToString (value)); } public virtual void WriteValue (int value) { WriteString (XQueryConvert.IntToString (value)); } public virtual void WriteValue (long value) { WriteString (XQueryConvert.IntegerToString (value)); } public virtual void WriteValue (object value) { if (value == null) throw new ArgumentNullException ("value"); if (value is string) WriteString ((string) value); else if (value is bool) WriteValue ((bool) value); else if (value is byte) WriteValue ((int) value); else if (value is byte []) WriteBase64 ((byte []) value, 0, ((byte []) value).Length); else if (value is char []) WriteChars ((char []) value, 0, ((char []) value).Length); else if (value is DateTime) WriteValue ((DateTime) value); else if (value is decimal) WriteValue ((decimal) value); else if (value is double) WriteValue ((double) value); else if (value is short) WriteValue ((int) value); else if (value is int) WriteValue ((int) value); else if (value is long) WriteValue ((long) value); else if (value is float) WriteValue ((float) value); else if (value is TimeSpan) // undocumented WriteString (XmlConvert.ToString ((TimeSpan) value)); else if (value is XmlQualifiedName) { XmlQualifiedName qname = (XmlQualifiedName) value; if (!qname.Equals (XmlQualifiedName.Empty)) { if (qname.Namespace.Length > 0 && LookupPrefix (qname.Namespace) == null) throw new InvalidCastException (String.Format ("The QName '{0}' cannot be written. No corresponding prefix is declared", qname)); WriteQualifiedName (qname.Name, qname.Namespace); } else WriteString (String.Empty); } else if (value is IEnumerable) { bool follow = false; foreach (object obj in (IEnumerable) value) { if (follow) WriteString (" "); else follow = true; WriteValue (obj); } } else throw new InvalidCastException (String.Format ("Type '{0}' cannot be cast to string", value.GetType ())); } public virtual void WriteValue (float value) { WriteString (XQueryConvert.FloatToString (value)); } public virtual void WriteValue (string value) { WriteString (value); } #endif #endregion } }