{
#region Fields
- protected TextWriter w;
- protected bool openWriter = true;
- protected bool openStartElement;
- protected Stack openElements = new Stack();
+ TextWriter w;
+ bool nullEncoding = false;
+ bool openWriter = true;
+ bool openStartElement = false;
+ bool openStartAttribute = false;
+ bool documentStarted = false;
+ bool namespaces = true;
+ bool openAttribute = false;
+ bool attributeWrittenForElement = false;
+ Stack openElements = new Stack ();
+ Formatting formatting = Formatting.None;
+ int indentation = 2;
+ char indentChar = ' ';
+ string indentChars = " ";
+ char quoteChar = '\"';
+ int indentLevel = 0;
+ string indentFormatting;
+ Stream baseStream = null;
+ string xmlLang = null;
+ XmlSpace xmlSpace = XmlSpace.None;
+ bool openXmlLang = false;
+ bool openXmlSpace = false;
+ string openElementPrefix;
+ string openElementNS;
+ bool hasRoot = false;
#endregion
public XmlTextWriter (TextWriter w) : base ()
{
this.w = w;
+ nullEncoding = (w.Encoding == null);
+
+ try {
+ baseStream = ((StreamWriter)w).BaseStream;
+ }
+ catch (Exception) { }
}
public XmlTextWriter (Stream w, Encoding encoding) : base ()
{
+ if (encoding == null) {
+ nullEncoding = true;
+ encoding = new UTF8Encoding ();
+ }
+
this.w = new StreamWriter(w, encoding);
+ baseStream = w;
}
- public XmlTextWriter (string filename, Encoding encoding) : base ()
+ public XmlTextWriter (string filename, Encoding encoding) :
+ this (new FileStream (filename, FileMode.Create, FileAccess.Write, FileShare.None), encoding)
{
- this.w = new StreamWriter(filename, false, encoding);
}
#endregion
#region Properties
- [MonoTODO]
public Stream BaseStream {
- get { throw new NotImplementedException(); }
+ get { return baseStream; }
}
- [MonoTODO]
public Formatting Formatting {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
+ get { return formatting; }
+ set { formatting = value; }
+ }
+
+ private bool IndentingOverriden
+ {
+ get {
+ if (openElements.Count == 0)
+ return false;
+ else
+ return (((XmlTextWriterOpenElement)openElements.Peek()).IndentingOverriden);
+ }
+ set {
+ if (openElements.Count > 0)
+ ((XmlTextWriterOpenElement)openElements.Peek()).IndentingOverriden = value;
+ }
}
- [MonoTODO]
public int Indentation {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
+ get { return indentation; }
+ set {
+ indentation = value;
+ UpdateIndentChars ();
+ }
}
- [MonoTODO]
public char IndentChar {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
+ get { return indentChar; }
+ set {
+ indentChar = value;
+ UpdateIndentChars ();
+ }
}
- [MonoTODO]
public bool Namespaces {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
+ get { return namespaces; }
+ set {
+ if (ws != WriteState.Start)
+ throw new InvalidOperationException ("NotInWriteState.");
+
+ namespaces = value;
+ }
}
- [MonoTODO]
public char QuoteChar {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
+ get { return quoteChar; }
+ set {
+ if ((value != '\'') && (value != '\"'))
+ throw new ArgumentException ("This is an invalid XML attribute quote character. Valid attribute quote characters are ' and \".");
+
+ quoteChar = value;
+ }
}
- [MonoTODO]
public override WriteState WriteState {
- get { throw new NotImplementedException(); }
+ get { return ws; }
}
- [MonoTODO]
public override string XmlLang {
- get { throw new NotImplementedException(); }
+ get {
+ string xmlLang = null;
+ int i;
+
+ for (i = 0; i < openElements.Count; i++)
+ {
+ xmlLang = ((XmlTextWriterOpenElement)openElements.ToArray().GetValue(i)).XmlLang;
+ if (xmlLang != null)
+ break;
+ }
+
+ return xmlLang;
+ }
}
- [MonoTODO]
public override XmlSpace XmlSpace {
- get { throw new NotImplementedException(); }
+ get {
+ XmlSpace xmlSpace = XmlSpace.None;
+ int i;
+
+ for (i = 0; i < openElements.Count; i++)
+ {
+ xmlSpace = ((XmlTextWriterOpenElement)openElements.ToArray().GetValue(i)).XmlSpace;
+ if (xmlSpace != XmlSpace.None)
+ break;
+ }
+
+ return xmlSpace;
+ }
}
#endregion
#region Methods
+ private void AddMissingElementXmlns ()
+ {
+ // output namespace declaration if not exist.
+ string prefix = openElementPrefix;
+ string ns = openElementNS;
+ if (ns != null/* && LookupPrefix (ns) != prefix*/)
+ {
+ string formatXmlns = String.Empty;
+ if (ns != String.Empty)
+ {
+ string existingPrefix = namespaceManager.LookupPrefix (ns);
+ bool addDefaultNamespace = false;
+
+ if (existingPrefix == null)
+ {
+ namespaceManager.AddNamespace (prefix, ns);
+ addDefaultNamespace = true;
+ }
+
+ if (prefix == String.Empty)
+ prefix = existingPrefix;
+
+ if (prefix != existingPrefix)
+ formatXmlns = String.Format (" xmlns:{0}={1}{2}{1}", prefix, quoteChar, ns);
+ else if (addDefaultNamespace)
+ formatXmlns = String.Format (" xmlns={0}{1}{0}", quoteChar, ns);
+ }
+ else if ((prefix == String.Empty) && (namespaceManager.LookupNamespace (prefix) != String.Empty))
+ {
+ namespaceManager.AddNamespace (prefix, ns);
+ formatXmlns = String.Format (" xmlns={0}{0}", quoteChar);
+ }
+ if(formatXmlns != String.Empty)
+ w.Write(formatXmlns);
+ openElementPrefix = null;
+ openElementNS = null;
+ }
+ }
- private void CheckOpenWriter ()
+ private void CheckState ()
{
if (!openWriter) {
- throw new InvalidOperationException ();
+ throw new InvalidOperationException ("The Writer is closed.");
+ }
+ if ((documentStarted == true) && (formatting == Formatting.Indented) && (!IndentingOverriden)) {
+ indentFormatting = w.NewLine;
+ if (indentLevel > 0) {
+ for (int i = 0; i < indentLevel; i++)
+ indentFormatting += indentChars;
+ }
}
+ else
+ indentFormatting = "";
+
+ documentStarted = true;
}
- [MonoTODO("Need to close all open elements and close the underlying streams.")]
public override void Close ()
{
+ CloseOpenAttributeAndElements ();
+
+ w.Close();
+ ws = WriteState.Closed;
openWriter = false;
}
- private void CloseStartElement ()
+ private void CloseOpenAttributeAndElements ()
{
- if (openStartElement)
- {
- w.Write(">");
- openStartElement = false;
+ if (openAttribute)
+ WriteEndAttribute ();
+
+ while (openElements.Count > 0) {
+ WriteEndElement();
}
}
- [MonoTODO]
+ private void CloseStartElement ()
+ {
+ if (!openStartElement)
+ return;
+
+ AddMissingElementXmlns ();
+
+ w.Write (">");
+ ws = WriteState.Content;
+ openStartElement = false;
+ attributeWrittenForElement = false;
+ }
+
public override void Flush ()
{
- throw new NotImplementedException ();
+ w.Flush ();
}
- [MonoTODO]
public override string LookupPrefix (string ns)
{
- throw new NotImplementedException ();
+ string prefix = namespaceManager.LookupPrefix (ns);
+
+ // XmlNamespaceManager has changed to return null when NSURI not found.
+ // (Contradiction to the documentation.)
+ //if (prefix == String.Empty)
+ // prefix = null;
+ return prefix;
+ }
+
+ private void UpdateIndentChars ()
+ {
+ indentChars = "";
+ for (int i = 0; i < indentation; i++)
+ indentChars += indentChar;
}
- [MonoTODO]
public override void WriteBase64 (byte[] buffer, int index, int count)
{
- throw new NotImplementedException ();
+ w.Write (Convert.ToBase64String (buffer, index, count));
}
[MonoTODO]
public override void WriteCData (string text)
{
+ if (text.IndexOf("]]>") > 0)
+ throw new ArgumentException ();
+
+ CheckState ();
+ CloseStartElement ();
+
w.Write("<![CDATA[{0}]]>", text);
}
- [MonoTODO]
public override void WriteCharEntity (char ch)
{
- throw new NotImplementedException ();
+ Int16 intCh = (Int16)ch;
+
+ // Make sure the character is not in the surrogate pair
+ // character range, 0xd800- 0xdfff
+ if ((intCh >= -10240) && (intCh <= -8193))
+ throw new ArgumentException ("Surrogate Pair is invalid.");
+
+ w.Write("&#x{0:X};", intCh);
}
[MonoTODO]
public override void WriteComment (string text)
{
- w.Write("<!--{0}-->", text);
+ if ((text.EndsWith("-")) || (text.IndexOf("-->") > 0)) {
+ throw new ArgumentException ();
+ }
+
+ CheckState ();
+ CloseStartElement ();
+
+ w.Write ("<!--{0}-->", text);
}
- [MonoTODO]
public override void WriteDocType (string name, string pubid, string sysid, string subset)
{
- throw new NotImplementedException ();
+ if (name == null || name.Trim ().Length == 0)
+ throw new ArgumentException ("Invalid DOCTYPE name", "name");
+
+ w.Write ("<!DOCTYPE ");
+ w.Write (name);
+ if (pubid != null) {
+ w.Write (String.Format (" PUBLIC {0}{1}{0} {0}{2}{0}", quoteChar, pubid, sysid));
+ } else if (sysid != null) {
+ w.Write (String.Format (" SYSTEM {0}{1}{0}", quoteChar, sysid));
+ }
+
+ if (subset != null)
+ w.Write ("[" + subset + "]");
+
+ w.Write('>');
}
- [MonoTODO]
public override void WriteEndAttribute ()
{
- throw new NotImplementedException ();
+ if (!openAttribute)
+ throw new InvalidOperationException("Token EndAttribute in state Start would result in an invalid XML document.");
+
+ CheckState ();
+
+ if (openXmlLang) {
+ w.Write (xmlLang);
+ openXmlLang = false;
+ ((XmlTextWriterOpenElement)openElements.Peek()).XmlLang = xmlLang;
+ }
+
+ if (openXmlSpace)
+ {
+ w.Write (xmlSpace.ToString ().ToLower ());
+ openXmlSpace = false;
+ ((XmlTextWriterOpenElement)openElements.Peek()).XmlSpace = xmlSpace;
+ }
+
+ w.Write ("{0}", quoteChar);
+
+ openAttribute = false;
}
- [MonoTODO]
public override void WriteEndDocument ()
{
- throw new NotImplementedException ();
+ CloseOpenAttributeAndElements ();
+
+ if (!hasRoot)
+ throw new ArgumentException ("This document does not have a root element.");
+
+ ws = WriteState.Start;
+ hasRoot = false;
}
public override void WriteEndElement ()
{
+ WriteEndElementInternal (false);
+ }
+
+ private void WriteEndElementInternal (bool fullEndElement)
+ {
+ if (openElements.Count == 0)
+ throw new InvalidOperationException("There was no XML start tag open.");
+
+ indentLevel--;
+ CheckState ();
+ AddMissingElementXmlns ();
+
if (openStartElement) {
- w.Write(" />");
- }
- else {
- w.Write("</{0}>", openElements.Pop());
+ if (openAttribute)
+ WriteEndAttribute ();
+ if (fullEndElement)
+ w.Write ("></{0}>", ((XmlTextWriterOpenElement)openElements.Peek ()).Name);
+ else
+ w.Write (" />");
+
+ openElements.Pop ();
+ openStartElement = false;
+ } else {
+ w.Write ("{0}</{1}>", indentFormatting, openElements.Pop ());
}
+
+ namespaceManager.PopScope();
}
[MonoTODO]
throw new NotImplementedException ();
}
- [MonoTODO]
public override void WriteFullEndElement ()
{
- throw new NotImplementedException ();
+ WriteEndElementInternal (true);
+ }
+
+ private void CheckValidChars (string name, bool firstOnlyLetter)
+ {
+ foreach (char c in name) {
+ if (XmlConvert.IsInvalid (c, firstOnlyLetter))
+ throw new ArgumentException ("There is an invalid character: '" + c +
+ "'", "name");
+ }
}
- [MonoTODO]
public override void WriteName (string name)
{
- throw new NotImplementedException ();
+ CheckValidChars (name, true);
+ w.Write (name);
}
- [MonoTODO]
public override void WriteNmToken (string name)
{
- throw new NotImplementedException ();
+ CheckValidChars (name, false);
+ w.Write (name);
}
public override void WriteProcessingInstruction (string name, string text)
{
if ((name == null) || (name == string.Empty) || (name.IndexOf("?>") > 0) || (text.IndexOf("?>") > 0)) {
- throw new ArgumentException();
+ throw new ArgumentException ();
}
- w.Write("<?{0} {1}?>", name, text);
+ CheckState ();
+ CloseStartElement ();
+
+ w.Write ("{0}<?{1} {2}?>", indentFormatting, name, text);
}
[MonoTODO]
public override void WriteQualifiedName (string localName, string ns)
{
- throw new NotImplementedException ();
+ if (localName == null || localName == String.Empty)
+ throw new ArgumentException ();
+
+ CheckState ();
+ w.Write ("{0}:{1}", ns, localName);
}
- [MonoTODO]
public override void WriteRaw (string data)
{
- throw new NotImplementedException ();
+ WriteStringInternal (data, false);
}
- [MonoTODO]
public override void WriteRaw (char[] buffer, int index, int count)
{
- throw new NotImplementedException ();
+ WriteStringInternal (new string (buffer, index, count), false);
}
- [MonoTODO]
public override void WriteStartAttribute (string prefix, string localName, string ns)
{
- throw new NotImplementedException ();
+ if ((prefix == "xml") && (localName == "lang"))
+ openXmlLang = true;
+
+ if ((prefix == "xml") && (localName == "space"))
+ openXmlSpace = true;
+
+ if ((prefix == "xmlns") && (localName == "xmlns"))
+ throw new ArgumentException ("Prefixes beginning with \"xml\" (regardless of whether the characters are uppercase, lowercase, or some combination thereof) are reserved for use by XML.");
+
+ CheckState ();
+
+ if (ws == WriteState.Content)
+ throw new InvalidOperationException ("Token StartAttribute in state " + WriteState + " would result in an invalid XML document.");
+
+ if (prefix == null)
+ prefix = String.Empty;
+
+ if (ns == null)
+ ns = String.Empty;
+
+ string formatPrefix = "";
+ string formatSpace = "";
+
+ if (ns != String.Empty)
+ {
+ string existingPrefix = namespaceManager.LookupPrefix (ns);
+
+ if (prefix == String.Empty)
+ prefix = (existingPrefix == null) ?
+ String.Empty : existingPrefix;
+ }
+
+ if (prefix != String.Empty)
+ {
+ formatPrefix = prefix + ":";
+ }
+
+ if (openStartElement || attributeWrittenForElement)
+ formatSpace = " ";
+
+ w.Write ("{0}{1}{2}={3}", formatSpace, formatPrefix, localName, quoteChar);
+
+ openAttribute = true;
+ attributeWrittenForElement = true;
+ ws = WriteState.Attribute;
}
- [MonoTODO]
public override void WriteStartDocument ()
{
- throw new NotImplementedException ();
+ WriteStartDocument ("");
}
- [MonoTODO]
public override void WriteStartDocument (bool standalone)
{
- throw new NotImplementedException ();
+ string standaloneFormatting;
+
+ if (standalone == true)
+ standaloneFormatting = String.Format (" standalone={0}yes{0}", quoteChar);
+ else
+ standaloneFormatting = String.Format (" standalone={0}no{0}", quoteChar);
+
+ WriteStartDocument (standaloneFormatting);
+ }
+
+ private void WriteStartDocument (string standaloneFormatting)
+ {
+ if (documentStarted == true)
+ throw new InvalidOperationException("WriteStartDocument should be the first call.");
+
+ if (hasRoot)
+ throw new XmlException ("WriteStartDocument called twice.");
+
+ hasRoot = true;
+
+ CheckState ();
+
+ string encodingFormatting = "";
+
+ if (!nullEncoding)
+ encodingFormatting = String.Format (" encoding={0}{1}{0}", quoteChar, w.Encoding.HeaderName);
+
+ w.Write("<?xml version={0}1.0{0}{1}{2}?>", quoteChar, encodingFormatting, standaloneFormatting);
+ ws = WriteState.Prolog;
}
- [MonoTODO("Not dealing with prefix and ns yet.")]
public override void WriteStartElement (string prefix, string localName, string ns)
{
- CheckOpenWriter();
- CloseStartElement();
- w.Write("<{0}", localName);
- openElements.Push(localName);
+ if (!Namespaces && (((prefix != null) && (prefix != String.Empty))
+ || ((ns != null) && (ns != String.Empty))))
+ throw new ArgumentException ("Cannot set the namespace if Namespaces is 'false'.");
+
+ WriteStartElementInternal (prefix, localName, ns);
+ }
+
+ private void WriteStartElementInternal (string prefix, string localName, string ns)
+ {
+ if ((prefix != null && prefix != String.Empty) && ((ns == null) || (ns == String.Empty)))
+ throw new ArgumentException ("Cannot use a prefix with an empty namespace.");
+
+ CheckState ();
+ CloseStartElement ();
+
+ if (prefix == null)
+ prefix = namespaceManager.LookupPrefix (ns);
+ if (prefix == null)
+ prefix = String.Empty;
+
+ string formatXmlns = "";
+ string formatPrefix = "";
+
+ if(ns != null) {
+ if (prefix != String.Empty)
+ formatPrefix = prefix + ":";
+ }
+
+ w.Write ("{0}<{1}{2}{3}", indentFormatting, formatPrefix, localName, formatXmlns);
+
+
+ openElements.Push (new XmlTextWriterOpenElement (formatPrefix + localName));
+ ws = WriteState.Element;
openStartElement = true;
+ openElementNS = ns;
+ openElementPrefix = prefix;
+
+ namespaceManager.PushScope ();
+// if(ns != null)
+// namespaceManager.AddNamespace (prefix, ns);
+ indentLevel++;
}
- [MonoTODO("Haven't done any entity replacements yet.")]
public override void WriteString (string text)
{
- CheckOpenWriter();
- CloseStartElement();
- w.Write(text);
+ if (ws == WriteState.Prolog)
+ throw new InvalidOperationException ("Token content in state Prolog would result in an invalid XML document.");
+
+ WriteStringInternal (text, true);
+ }
+
+ private void WriteStringInternal (string text, bool entitize)
+ {
+ if (text == null)
+ text = String.Empty;
+
+ if (text != String.Empty)
+ {
+ CheckState ();
+
+ if (entitize)
+ {
+ text = text.Replace ("&", "&");
+ text = text.Replace ("<", "<");
+ text = text.Replace (">", ">");
+
+ if (openAttribute)
+ {
+ if (quoteChar == '"')
+ text = text.Replace ("\"", """);
+ else
+ text = text.Replace ("'", "'");
+ }
+ }
+
+ if (!openAttribute)
+ {
+ IndentingOverriden = true;
+ CloseStartElement ();
+ }
+ if (!openXmlLang && !openXmlSpace)
+ w.Write (text);
+ else
+ {
+ if (openXmlLang)
+ xmlLang = text;
+ else
+ {
+ switch (text)
+ {
+ case "default":
+ xmlSpace = XmlSpace.Default;
+ break;
+ case "preserve":
+ xmlSpace = XmlSpace.Preserve;
+ break;
+ default:
+ throw new ArgumentException ("'{0}' is an invalid xml:space value.");
+ }
+ }
+ }
+ }
}
[MonoTODO]
throw new NotImplementedException ();
}
- [MonoTODO]
public override void WriteWhitespace (string ws)
{
- throw new NotImplementedException ();
+ foreach (char c in ws) {
+ if ((c != ' ') && (c != '\t') && (c != '\r') && (c != '\n'))
+ throw new ArgumentException ();
+ }
+
+ w.Write (ws);
}
#endregion