X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.XML%2FSystem.Xml%2FXmlTextWriter.cs;h=dba8ed19f329fe4b6284c58b07050214f9e19a6f;hb=2d7a230c66956b5c1d582513201054c1f4659650;hp=13bec99bdaa7489f8425915077b15b79dd9a89ba;hpb=5b7629fbf350b66ceeb3273fb92ff0f6cd2e8851;p=mono.git diff --git a/mcs/class/System.XML/System.Xml/XmlTextWriter.cs b/mcs/class/System.XML/System.Xml/XmlTextWriter.cs index 13bec99bdaa..dba8ed19f32 100644 --- a/mcs/class/System.XML/System.Xml/XmlTextWriter.cs +++ b/mcs/class/System.XML/System.Xml/XmlTextWriter.cs @@ -18,10 +18,28 @@ namespace System.Xml { #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; #endregion @@ -30,115 +48,206 @@ namespace System.Xml 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 CheckOpenWriter () + private void CheckState () { if (!openWriter) { - throw new InvalidOperationException (); + throw new InvalidOperationException ("The Writer is closed."); } + if ((documentStarted == true) && (formatting == Formatting.Indented) && (!IndentingOverriden)) { + indentFormatting = "\r\n"; + 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 CloseOpenAttributeAndElements () + { + if (openAttribute) + WriteEndAttribute (); + + while (openElements.Count > 0) { + WriteEndElement(); + } + } + private void CloseStartElement () { - if (openStartElement) - { + if (openStartElement) { w.Write(">"); + ws = WriteState.Content; openStartElement = false; + attributeWrittenForElement = false; } } - [MonoTODO] public override void Flush () { - throw new NotImplementedException (); + w.Flush (); } - [MonoTODO] public override string LookupPrefix (string ns) { - throw new NotImplementedException (); + string prefix = namespaceManager.LookupPrefix (ns); + + 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] @@ -149,13 +258,25 @@ namespace System.Xml public override void WriteCData (string text) { + if (text.IndexOf("]]>") > 0) + throw new ArgumentException (); + + CheckState (); + CloseStartElement (); + w.Write("", 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] @@ -166,35 +287,98 @@ namespace System.Xml public override void WriteComment (string text) { - w.Write("", text); + if ((text.EndsWith("-")) || (text.IndexOf("-->") > 0)) { + throw new ArgumentException (); + } + + CheckState (); + CloseStartElement (); + + w.Write ("", 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 ("'); } - [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 ((ws == WriteState.Start) || (ws == WriteState.Prolog)) + throw new ArgumentException ("This document does not have a root element."); + + ws = WriteState.Start; } 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 (); + if (openStartElement) { - w.Write(" />"); - } - else { - w.Write("", openElements.Pop()); + if (openAttribute) + WriteEndAttribute (); + if (fullEndElement) + w.Write (">", ((XmlTextWriterOpenElement)openElements.Peek ()).Name); + else + w.Write (" />"); + + openElements.Pop (); + openStartElement = false; + } else { + w.Write ("{0}", indentFormatting, openElements.Pop ()); } + + namespaceManager.PopScope(); } [MonoTODO] @@ -203,43 +387,57 @@ namespace System.Xml 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("", name, text); + CheckState (); + CloseStartElement (); + + w.Write ("{0}", 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] @@ -248,40 +446,213 @@ namespace System.Xml throw new NotImplementedException (); } - [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; + } + + 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."); + + CheckState (); + + string encodingFormatting = ""; + + if (!nullEncoding) + encodingFormatting = String.Format (" encoding={0}{1}{0}", quoteChar, w.Encoding.HeaderName); + + w.Write("", 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; + + if ((prefix != String.Empty) && ((ns == null) || (ns == String.Empty))) + throw new ArgumentException ("Cannot use a prefix with an empty namespace."); + + CheckState (); + CloseStartElement (); + + string formatXmlns = ""; + string formatPrefix = ""; + + if(ns != null) + { + if (ns != String.Empty) + { + string existingPrefix = namespaceManager.LookupPrefix (ns); + bool addDefaultNamespace = false; + + if (existingPrefix == String.Empty && !namespaceManager.HasNamespace (prefix)) { + 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)) { + formatXmlns = String.Format (" xmlns={0}{0}", quoteChar); + } + + 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; + + 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] @@ -290,10 +661,14 @@ namespace System.Xml 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