Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Xml / System / Xml / Core / XmlTextWriter.cs / 1 / XmlTextWriter.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //----------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using System.Diagnostics; using System.Globalization; namespace System.Xml { // Specifies formatting options for XmlTextWriter. public enum Formatting { // No special formatting is done (this is the default). None, //This option causes child elements to be indented using the Indentation and IndentChar properties. // It only indents Element Content (http://www.w3.org/TR/1998/REC-xml-19980210#sec-element-content) // and not Mixed Content (http://www.w3.org/TR/1998/REC-xml-19980210#sec-mixed-content) // according to the XML 1.0 definitions of these terms. Indented, }; // Represents a writer that provides fast non-cached forward-only way of generating XML streams // containing XML documents that conform to the W3CExtensible Markup Language (XML) 1.0 specification // and the Namespaces in XML specification. public class XmlTextWriter : XmlWriter { // // Private types // enum NamespaceState { Uninitialized, NotDeclaredButInScope, DeclaredButNotWrittenOut, DeclaredAndWrittenOut } struct TagInfo { internal string name; internal string prefix; internal string defaultNs; internal NamespaceState defaultNsState; internal XmlSpace xmlSpace; internal string xmlLang; internal int prevNsTop; internal int prefixCount; internal bool mixed; // whether to pretty print the contents of this element. internal void Init( int nsTop ) { name = null; defaultNs = String.Empty; defaultNsState = NamespaceState.Uninitialized; xmlSpace = XmlSpace.None; xmlLang = null; prevNsTop = nsTop; prefixCount = 0; mixed = false; } } struct Namespace { internal string prefix; internal string ns; internal bool declared; internal int prevNsIndex; internal void Set( string prefix, string ns, bool declared ) { this.prefix = prefix; this.ns = ns; this.declared = declared; this.prevNsIndex = -1; } } enum SpecialAttr { None, XmlSpace, XmlLang, XmlNs }; // State machine is working through autocomplete private enum State { Start, Prolog, PostDTD, Element, Attribute, Content, AttrOnly, Epilog, Error, Closed, } private enum Token { PI, Doctype, Comment, CData, StartElement, EndElement, LongEndElement, StartAttribute, EndAttribute, Content, Base64, RawData, Whitespace, Empty } // // Fields // // output TextWriter textWriter; XmlTextEncoder xmlEncoder; Encoding encoding; // formatting Formatting formatting; bool indented; // perf - faster to check a boolean. int indentation; char indentChar; // element stack TagInfo[] stack; int top; // state machine for AutoComplete State[] stateTable; State currentState; Token lastToken; // Base64 content XmlTextWriterBase64Encoder base64Encoder; // misc char quoteChar; char curQuoteChar; bool namespaces; SpecialAttr specialAttr; string prefixForXmlNs; bool flush; // namespaces Namespace[] nsStack; int nsTop; DictionarynsHashtable; bool useNsHashtable; // char types XmlCharType xmlCharType = XmlCharType.Instance; // // Constants and constant tables // const int NamespaceStackInitialSize = 8; #if DEBUG const int MaxNamespacesWalkCount = 3; #else const int MaxNamespacesWalkCount = 16; #endif static string[] stateName = { "Start", "Prolog", "PostDTD", "Element", "Attribute", "Content", "AttrOnly", "Epilog", "Error", "Closed", }; static string[] tokenName = { "PI", "Doctype", "Comment", "CData", "StartElement", "EndElement", "LongEndElement", "StartAttribute", "EndAttribute", "Content", "Base64", "RawData", "Whitespace", "Empty" }; static readonly State[] stateTableDefault = { // State.Start State.Prolog State.PostDTD State.Element State.Attribute State.Content State.AttrOnly State.Epilog // /* Token.PI */ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog, /* Token.Doctype */ State.PostDTD, State.PostDTD, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, /* Token.Comment */ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog, /* Token.CData */ State.Content, State.Content, State.Error, State.Content, State.Content, State.Content, State.Error, State.Epilog, /* Token.StartElement */ State.Element, State.Element, State.Element, State.Element, State.Element, State.Element, State.Error, State.Element, /* Token.EndElement */ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, /* Token.LongEndElement */ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, /* Token.StartAttribute */ State.AttrOnly, State.Error, State.Error, State.Attribute, State.Attribute, State.Error, State.Error, State.Error, /* Token.EndAttribute */ State.Error, State.Error, State.Error, State.Error, State.Element, State.Error, State.Epilog, State.Error, /* Token.Content */ State.Content, State.Content, State.Error, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog, /* Token.Base64 */ State.Content, State.Content, State.Error, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog, /* Token.RawData */ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog, /* Token.Whitespace */ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog, }; static readonly State[] stateTableDocument = { // State.Start State.Prolog State.PostDTD State.Element State.Attribute State.Content State.AttrOnly State.Epilog // /* Token.PI */ State.Error, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog, /* Token.Doctype */ State.Error, State.PostDTD, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, /* Token.Comment */ State.Error, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog, /* Token.CData */ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, /* Token.StartElement */ State.Error, State.Element, State.Element, State.Element, State.Element, State.Element, State.Error, State.Error, /* Token.EndElement */ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, /* Token.LongEndElement */ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, /* Token.StartAttribute */ State.Error, State.Error, State.Error, State.Attribute, State.Attribute, State.Error, State.Error, State.Error, /* Token.EndAttribute */ State.Error, State.Error, State.Error, State.Error, State.Element, State.Error, State.Error, State.Error, /* Token.Content */ State.Error, State.Error, State.Error, State.Content, State.Attribute, State.Content, State.Error, State.Error, /* Token.Base64 */ State.Error, State.Error, State.Error, State.Content, State.Attribute, State.Content, State.Error, State.Error, /* Token.RawData */ State.Error, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Error, State.Epilog, /* Token.Whitespace */ State.Error, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Error, State.Epilog, }; // // Constructors // internal XmlTextWriter() { namespaces = true; formatting = Formatting.None; indentation = 2; indentChar = ' '; // namespaces nsStack = new Namespace[NamespaceStackInitialSize]; nsTop = -1; // element stack stack = new TagInfo[10]; top = 0;// 0 is an empty sentanial element stack[top].Init( -1 ); quoteChar = '"'; stateTable = stateTableDefault; currentState = State.Start; lastToken = Token.Empty; } // Creates an instance of the XmlTextWriter class using the specified stream. public XmlTextWriter(Stream w, Encoding encoding) : this() { this.encoding = encoding; if (encoding != null) textWriter = new StreamWriter(w, encoding); else textWriter = new StreamWriter(w); xmlEncoder = new XmlTextEncoder(textWriter); xmlEncoder.QuoteChar = this.quoteChar; } // Creates an instance of the XmlTextWriter class using the specified file. public XmlTextWriter(String filename, Encoding encoding) : this(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read), encoding) { } // Creates an instance of the XmlTextWriter class using the specified TextWriter. public XmlTextWriter(TextWriter w) : this() { textWriter = w; encoding = w.Encoding; xmlEncoder = new XmlTextEncoder(w); xmlEncoder.QuoteChar = this.quoteChar; } // // XmlTextWriter properties // // Gets the XmlTextWriter base stream. public Stream BaseStream { get { StreamWriter streamWriter = textWriter as StreamWriter; return (streamWriter == null ? null : streamWriter.BaseStream); } } // Gets or sets a value indicating whether to do namespace support. public bool Namespaces { get { return this.namespaces;} set { if (this.currentState != State.Start) throw new InvalidOperationException(Res.GetString(Res.Xml_NotInWriteState)); this.namespaces = value; } } // Indicates how the output is formatted. public Formatting Formatting { get { return this.formatting;} set { this.formatting = value; this.indented = value == Formatting.Indented;} } // Gets or sets how many IndentChars to write for each level in the hierarchy when Formatting is set to "Indented". public int Indentation { get { return this.indentation;} set { if (value < 0) throw new ArgumentException(Res.GetString(Res.Xml_InvalidIndentation)); this.indentation = value; } } // Gets or sets which character to use for indenting when Formatting is set to "Indented". public char IndentChar { get { return this.indentChar;} set { this.indentChar = value;} } // Gets or sets which character to use to quote attribute values. public char QuoteChar { get { return this.quoteChar;} set { if (value != '"' && value != '\'') { throw new ArgumentException(Res.GetString(Res.Xml_InvalidQuote)); } this.quoteChar = value; this.xmlEncoder.QuoteChar = value; } } // // XmlWriter implementation // // Writes out the XML declaration with the version "1.0". public override void WriteStartDocument() { StartDocument(-1); } // Writes out the XML declaration with the version "1.0" and the standalone attribute. public override void WriteStartDocument(bool standalone) { StartDocument(standalone ? 1 : 0); } // Closes any open elements or attributes and puts the writer back in the Start state. public override void WriteEndDocument() { try { AutoCompleteAll(); if (this.currentState != State.Epilog) { throw new ArgumentException(Res.GetString(Res.Xml_NoRoot)); } this.stateTable = stateTableDefault; this.currentState = State.Start; this.lastToken = Token.Empty; } catch { currentState = State.Error; throw; } } // Writes out the DOCTYPE declaration with the specified name and optional attributes. public override void WriteDocType(string name, string pubid, string sysid, string subset) { try { ValidateName(name, false); AutoComplete(Token.Doctype); textWriter.Write("'); } catch { currentState = State.Error; throw; } } // Writes out the specified start tag and associates it with the given namespace and prefix. public override void WriteStartElement(string prefix, string localName, string ns) { try { AutoComplete(Token.StartElement); PushStack(); textWriter.Write('<'); if (this.namespaces) { // Propagate default namespace and mix model down the stack. stack[top].defaultNs = stack[top-1].defaultNs; if (stack[top-1].defaultNsState != NamespaceState.Uninitialized) stack[top].defaultNsState = NamespaceState.NotDeclaredButInScope; stack[top].mixed = stack[top-1].mixed; if (ns == null) { // use defined prefix if (prefix != null && prefix.Length != 0 && (LookupNamespace(prefix) == -1)) { throw new ArgumentException(Res.GetString(Res.Xml_UndefPrefix)); } } else { if (prefix == null) { string definedPrefix = FindPrefix(ns); if (definedPrefix != null) { prefix = definedPrefix; } else { PushNamespace(null, ns, false); // new default } } else if (prefix.Length == 0) { PushNamespace(null, ns, false); // new default } else { if (ns.Length == 0) { prefix = null; } VerifyPrefixXml(prefix, ns); PushNamespace(prefix, ns, false); // define } } stack[top].prefix = null; if (prefix != null && prefix.Length != 0) { stack[top].prefix = prefix; textWriter.Write(prefix); textWriter.Write(':'); } } else { if ((ns != null && ns.Length != 0) || (prefix != null && prefix.Length != 0)) { throw new ArgumentException(Res.GetString(Res.Xml_NoNamespaces)); } } stack[top].name = localName; textWriter.Write(localName); } catch { currentState = State.Error; throw; } } // Closes one element and pops the corresponding namespace scope. public override void WriteEndElement() { InternalWriteEndElement(false); } // Closes one element and pops the corresponding namespace scope. public override void WriteFullEndElement() { InternalWriteEndElement(true); } // Writes the start of an attribute. public override void WriteStartAttribute(string prefix, string localName, string ns) { try { AutoComplete(Token.StartAttribute); this.specialAttr = SpecialAttr.None; if (this.namespaces) { if (prefix != null && prefix.Length == 0) { prefix = null; } if (ns == XmlReservedNs.NsXmlNs && prefix == null && localName != "xmlns") { prefix = "xmlns"; } if (prefix == "xml") { if (localName == "lang") { this.specialAttr = SpecialAttr.XmlLang; } else if (localName == "space") { this.specialAttr = SpecialAttr.XmlSpace; } /* bug54408. to be fwd compatible we need to treat xml prefix as reserved and not really insist on a specific value. Who knows in the future it might be OK to say xml:blabla else { throw new ArgumentException(Res.GetString(Res.Xml_InvalidPrefix)); }*/ } else if (prefix == "xmlns") { if (XmlReservedNs.NsXmlNs != ns && ns != null) { throw new ArgumentException(Res.GetString(Res.Xml_XmlnsBelongsToReservedNs)); } if (localName == null || localName.Length == 0) { localName = prefix; prefix = null; this.prefixForXmlNs = null; } else { this.prefixForXmlNs = localName; } this.specialAttr = SpecialAttr.XmlNs; } else if (prefix == null && localName == "xmlns") { if (XmlReservedNs.NsXmlNs != ns && ns != null) { // add the below line back in when DOM is fixed throw new ArgumentException(Res.GetString(Res.Xml_XmlnsBelongsToReservedNs)); } this.specialAttr = SpecialAttr.XmlNs; this.prefixForXmlNs = null; } else { if (ns == null) { // use defined prefix if (prefix != null && (LookupNamespace(prefix) == -1)) { throw new ArgumentException(Res.GetString(Res.Xml_UndefPrefix)); } } else if (ns.Length == 0) { // empty namespace require null prefix prefix = string.Empty; } else { // ns.Length != 0 VerifyPrefixXml(prefix, ns); if (prefix != null && LookupNamespaceInCurrentScope(prefix) != -1) { prefix = null; } // Now verify prefix validity string definedPrefix = FindPrefix(ns); if (definedPrefix != null && (prefix == null || prefix == definedPrefix)) { prefix = definedPrefix; } else { if (prefix == null) { prefix = GeneratePrefix(); // need a prefix if } PushNamespace(prefix, ns, false); } } } if (prefix != null && prefix.Length != 0) { textWriter.Write(prefix); textWriter.Write(':'); } } else { if ((ns != null && ns.Length != 0) || (prefix != null && prefix.Length != 0)) { throw new ArgumentException(Res.GetString(Res.Xml_NoNamespaces)); } if (localName == "xml:lang") { this.specialAttr = SpecialAttr.XmlLang; } else if (localName == "xml:space") { this.specialAttr = SpecialAttr.XmlSpace; } } xmlEncoder.StartAttribute(this.specialAttr != SpecialAttr.None); textWriter.Write(localName); textWriter.Write('='); if (this.curQuoteChar != this.quoteChar) { this.curQuoteChar = this.quoteChar; xmlEncoder.QuoteChar = this.quoteChar; } textWriter.Write(this.curQuoteChar); } catch { currentState = State.Error; throw; } } // Closes the attribute opened by WriteStartAttribute. public override void WriteEndAttribute() { try { AutoComplete(Token.EndAttribute); } catch { currentState = State.Error; throw; } } // Writes out a <![CDATA[...]]> block containing the specified text. public override void WriteCData(string text) { try { AutoComplete(Token.CData); if (null != text && text.IndexOf("]]>", StringComparison.Ordinal) >= 0) { throw new ArgumentException(Res.GetString(Res.Xml_InvalidCDataChars)); } textWriter.Write(""); } catch { currentState = State.Error; throw; } } // Writes out a comment containing the specified text. public override void WriteComment(string text) { try { if (null != text && (text.IndexOf("--", StringComparison.Ordinal)>=0 || (text.Length != 0 && text[text.Length-1] == '-'))) { throw new ArgumentException(Res.GetString(Res.Xml_InvalidCommentChars)); } AutoComplete(Token.Comment); textWriter.Write(""); } catch { currentState = State.Error; throw; } } // Writes out a processing instruction with a space between the name and text as follows: public override void WriteProcessingInstruction(string name, string text) { try { if (null != text && text.IndexOf("?>", StringComparison.Ordinal)>=0) { throw new ArgumentException(Res.GetString(Res.Xml_InvalidPiChars)); } if (0 == String.Compare(name, "xml", StringComparison.OrdinalIgnoreCase) && this.stateTable == stateTableDocument) { throw new ArgumentException(Res.GetString(Res.Xml_DupXmlDecl)); } AutoComplete(Token.PI); InternalWriteProcessingInstruction(name, text); } catch { currentState = State.Error; throw; } } // Writes out an entity reference as follows: "&"+name+";". public override void WriteEntityRef(string name) { try { ValidateName(name, false); AutoComplete(Token.Content); xmlEncoder.WriteEntityRef(name); } catch { currentState = State.Error; throw; } } // Forces the generation of a character entity for the specified Unicode character value. public override void WriteCharEntity(char ch) { try { AutoComplete(Token.Content); xmlEncoder.WriteCharEntity(ch); } catch { currentState = State.Error; throw; } } // Writes out the given whitespace. public override void WriteWhitespace(string ws) { try { if (null == ws || ws.Length == 0) { throw new ArgumentException(Res.GetString(Res.Xml_NonWhitespace)); } if (!xmlCharType.IsOnlyWhitespace(ws)) { throw new ArgumentException(Res.GetString(Res.Xml_NonWhitespace)); } AutoComplete(Token.Whitespace); xmlEncoder.Write(ws); } catch { currentState = State.Error; throw; } } // Writes out the specified text content. public override void WriteString(string text) { try { if (null != text && text.Length != 0 ) { AutoComplete(Token.Content); xmlEncoder.Write(text); } } catch { currentState = State.Error; throw; } } // Writes out the specified surrogate pair as a character entity. public override void WriteSurrogateCharEntity(char lowChar, char highChar){ try { AutoComplete(Token.Content); xmlEncoder.WriteSurrogateCharEntity(lowChar, highChar); } catch { currentState = State.Error; throw; } } // Writes out the specified text content. public override void WriteChars(Char[] buffer, int index, int count) { try { AutoComplete(Token.Content); xmlEncoder.Write(buffer, index, count); } catch { currentState = State.Error; throw; } } // Writes raw markup from the specified character buffer. public override void WriteRaw(Char[] buffer, int index, int count) { try { AutoComplete(Token.RawData); xmlEncoder.WriteRaw(buffer, index, count); } catch { currentState = State.Error; throw; } } // Writes raw markup from the specified character string. public override void WriteRaw(String data) { try { AutoComplete(Token.RawData); xmlEncoder.WriteRawWithSurrogateChecking(data); } catch { currentState = State.Error; throw; } } // Encodes the specified binary bytes as base64 and writes out the resulting text. public override void WriteBase64(byte[] buffer, int index, int count) { try { if (!this.flush) { AutoComplete(Token.Base64); } this.flush = true; // No need for us to explicitly validate the args. The StreamWriter will do // it for us. if (null == this.base64Encoder) { this.base64Encoder = new XmlTextWriterBase64Encoder( xmlEncoder ); } // Encode will call WriteRaw to write out the encoded characters this.base64Encoder.Encode( buffer, index, count ); } catch { currentState = State.Error; throw; } } // Encodes the specified binary bytes as binhex and writes out the resulting text. public override void WriteBinHex( byte[] buffer, int index, int count ) { try { AutoComplete( Token.Content ); BinHexEncoder.Encode( buffer, index, count, this ); } catch { currentState = State.Error; throw; } } // Returns the state of the XmlWriter. public override WriteState WriteState { get { switch (this.currentState) { case State.Start : return WriteState.Start; case State.Prolog : case State.PostDTD : return WriteState.Prolog; case State.Element : return WriteState.Element; case State.Attribute : case State.AttrOnly: return WriteState.Attribute; case State.Content : case State.Epilog : return WriteState.Content; case State.Error: return WriteState.Error; case State.Closed: return WriteState.Closed; default: Debug.Assert( false ); return WriteState.Error; } } } // Closes the XmlWriter and the underlying stream/TextWriter. public override void Close() { try { AutoCompleteAll(); } catch { // never fail } finally { this.currentState = State.Closed; textWriter.Close(); } } // Flushes whatever is in the buffer to the underlying stream/TextWriter and flushes the underlying stream/TextWriter. public override void Flush() { textWriter.Flush(); } // Writes out the specified name, ensuring it is a valid Name according to the XML specification // (http://www.w3.org/TR/1998/REC-xml-19980210#NT-Name public override void WriteName(string name) { try { AutoComplete(Token.Content); InternalWriteName(name, false); } catch { currentState = State.Error; throw; } } // Writes out the specified namespace-qualified name by looking up the prefix that is in scope for the given namespace. public override void WriteQualifiedName(string localName, string ns) { try { AutoComplete(Token.Content); if (this.namespaces) { if (ns != null && ns.Length != 0 && ns != stack[top].defaultNs) { string prefix = FindPrefix(ns); if (prefix == null) { if (this.currentState != State.Attribute) { throw new ArgumentException(Res.GetString(Res.Xml_UndefNamespace, ns)); } prefix = GeneratePrefix(); // need a prefix if PushNamespace(prefix, ns, false); } if (prefix.Length != 0) { InternalWriteName(prefix, true); textWriter.Write(':'); } } } else if (ns != null && ns.Length != 0) { throw new ArgumentException(Res.GetString(Res.Xml_NoNamespaces)); } InternalWriteName(localName, true); } catch { currentState = State.Error; throw; } } // Returns the closest prefix defined in the current namespace scope for the specified namespace URI. public override string LookupPrefix(string ns) { if (ns == null || ns.Length == 0) { throw new ArgumentException(Res.GetString(Res.Xml_EmptyName)); } string s = FindPrefix(ns); if (s == null && ns == stack[top].defaultNs) { s = string.Empty; } return s; } // Gets an XmlSpace representing the current xml:space scope. public override XmlSpace XmlSpace { get { for (int i = top; i > 0; i--) { XmlSpace xs = stack[i].xmlSpace; if (xs != XmlSpace.None) return xs; } return XmlSpace.None; } } // Gets the current xml:lang scope. public override string XmlLang { get { for (int i = top; i > 0; i--) { String xlang = stack[i].xmlLang; if (xlang != null) return xlang; } return null; } } // Writes out the specified name, ensuring it is a valid NmToken // according to the XML specification (http://www.w3.org/TR/1998/REC-xml-19980210#NT-Name). public override void WriteNmToken(string name) { try { AutoComplete(Token.Content); if (name == null || name.Length == 0) { throw new ArgumentException(Res.GetString(Res.Xml_EmptyName)); } if (!xmlCharType.IsNmToken(name)) { throw new ArgumentException(Res.GetString(Res.Xml_InvalidNameChars, name)); } textWriter.Write(name); } catch { currentState = State.Error; throw; } } // // Private implementation methods // void StartDocument(int standalone) { try { if (this.currentState != State.Start) { throw new InvalidOperationException(Res.GetString(Res.Xml_NotTheFirst)); } this.stateTable = stateTableDocument; this.currentState = State.Prolog; StringBuilder bufBld = new StringBuilder(128); bufBld.Append("version=" + quoteChar + "1.0" + quoteChar); if (this.encoding != null) { bufBld.Append(" encoding="); bufBld.Append(quoteChar); bufBld.Append(this.encoding.WebName); bufBld.Append(quoteChar); } if (standalone >= 0) { bufBld.Append(" standalone="); bufBld.Append(quoteChar); bufBld.Append(standalone == 0 ? "no" : "yes"); bufBld.Append(quoteChar); } InternalWriteProcessingInstruction("xml", bufBld.ToString()); } catch { currentState = State.Error; throw; } } void AutoComplete(Token token) { if (this.currentState == State.Closed) { throw new InvalidOperationException(Res.GetString(Res.Xml_Closed)); } else if (this.currentState == State.Error) { throw new InvalidOperationException(Res.GetString(Res.Xml_WrongToken, tokenName[(int)token], stateName[(int)State.Error])); } State newState = this.stateTable[(int)token * 8 + (int)this.currentState]; if (newState == State.Error) { throw new InvalidOperationException(Res.GetString(Res.Xml_WrongToken, tokenName[(int)token], stateName[(int)this.currentState])); } switch (token) { case Token.Doctype: if (this.indented && this.currentState != State.Start) { Indent(false); } break; case Token.StartElement: case Token.Comment: case Token.PI: case Token.CData: if (this.currentState == State.Attribute) { WriteEndAttributeQuote(); WriteEndStartTag(false); } else if (this.currentState == State.Element) { WriteEndStartTag(false); } if (token == Token.CData) { stack[top].mixed = true; } else if (this.indented && this.currentState != State.Start) { Indent(false); } break; case Token.EndElement: case Token.LongEndElement: if (this.flush) { FlushEncoders(); } if (this.currentState == State.Attribute) { WriteEndAttributeQuote(); } if (this.currentState == State.Content) { token = Token.LongEndElement; } else { WriteEndStartTag(token == Token.EndElement); } if (stateTableDocument == this.stateTable && top == 1) { newState = State.Epilog; } break; case Token.StartAttribute: if (this.flush) { FlushEncoders(); } if (this.currentState == State.Attribute) { WriteEndAttributeQuote(); textWriter.Write(' '); } else if (this.currentState == State.Element) { textWriter.Write(' '); } break; case Token.EndAttribute: if (this.flush) { FlushEncoders(); } WriteEndAttributeQuote(); break; case Token.Whitespace: case Token.Content: case Token.RawData: case Token.Base64: if (token != Token.Base64 && this.flush) { FlushEncoders(); } if (this.currentState == State.Element && this.lastToken != Token.Content) { WriteEndStartTag(false); } if (newState == State.Content) { stack[top].mixed = true; } break; default: throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation)); } this.currentState = newState; this.lastToken = token; } void AutoCompleteAll() { if (this.flush) { FlushEncoders(); } while (top > 0) { WriteEndElement(); } } void InternalWriteEndElement(bool longFormat) { try { if (top <= 0) { throw new InvalidOperationException(Res.GetString(Res.Xml_NoStartTag)); } // if we are in the element, we need to close it. AutoComplete(longFormat ? Token.LongEndElement : Token.EndElement); if (this.lastToken == Token.LongEndElement) { if (this.indented) { Indent(true); } textWriter.Write('<'); textWriter.Write('/'); if (this.namespaces && stack[top].prefix != null) { textWriter.Write(stack[top].prefix); textWriter.Write(':'); } textWriter.Write(stack[top].name); textWriter.Write('>'); } // pop namespaces int prevNsTop = stack[top].prevNsTop; if (useNsHashtable && prevNsTop < nsTop) { PopNamespaces(prevNsTop + 1, nsTop); } nsTop = prevNsTop; top--; } catch { currentState = State.Error; throw; } } void WriteEndStartTag(bool empty) { xmlEncoder.StartAttribute(false); for (int i = nsTop; i > stack[top].prevNsTop; i--) { if (!nsStack[i].declared) { textWriter.Write(" xmlns"); textWriter.Write(':'); textWriter.Write(nsStack[i].prefix); textWriter.Write('='); textWriter.Write(this.quoteChar); xmlEncoder.Write(nsStack[i].ns); textWriter.Write(this.quoteChar); } } // Default if ((stack[top].defaultNs != stack[top - 1].defaultNs) && (stack[top].defaultNsState == NamespaceState.DeclaredButNotWrittenOut)) { textWriter.Write(" xmlns"); textWriter.Write('='); textWriter.Write(this.quoteChar); xmlEncoder.Write(stack[top].defaultNs); textWriter.Write(this.quoteChar); stack[top].defaultNsState = NamespaceState.DeclaredAndWrittenOut; } xmlEncoder.EndAttribute(); if (empty) { textWriter.Write(" /"); } textWriter.Write('>'); } void WriteEndAttributeQuote() { if (this.specialAttr != SpecialAttr.None) { // Ok, now to handle xmlspace, etc. HandleSpecialAttribute(); } xmlEncoder.EndAttribute(); textWriter.Write(this.curQuoteChar); } void Indent(bool beforeEndElement) { // pretty printing. if (top == 0) { textWriter.WriteLine(); } else if (!stack[top].mixed) { textWriter.WriteLine(); int i = beforeEndElement ? top - 1 : top; for (i *= this.indentation; i > 0; i--) { textWriter.Write(this.indentChar); } } } // pushes new namespace scope, and returns generated prefix, if one // was needed to resolve conflicts. void PushNamespace(string prefix, string ns, bool declared) { if (XmlReservedNs.NsXmlNs == ns) { throw new ArgumentException(Res.GetString(Res.Xml_CanNotBindToReservedNamespace)); } if (prefix == null) { switch (stack[top].defaultNsState) { case NamespaceState.DeclaredButNotWrittenOut: Debug.Assert (declared == true, "Unexpected situation!!"); // the first namespace that the user gave us is what we // like to keep. break; case NamespaceState.Uninitialized: case NamespaceState.NotDeclaredButInScope: // we now got a brand new namespace that we need to remember stack[top].defaultNs = ns; break; default: Debug.Assert(false, "Should have never come here"); return; } stack[top].defaultNsState = (declared ? NamespaceState.DeclaredAndWrittenOut : NamespaceState.DeclaredButNotWrittenOut); } else { if (prefix.Length != 0 && ns.Length == 0) { throw new ArgumentException(Res.GetString(Res.Xml_PrefixForEmptyNs)); } int existingNsIndex = LookupNamespace(prefix); if (existingNsIndex != -1 && nsStack[existingNsIndex].ns == ns) { // it is already in scope. if (declared) { nsStack[existingNsIndex].declared = true; } } else { // see if prefix conflicts for the current element if (declared) { if (existingNsIndex != -1 && existingNsIndex > stack[top].prevNsTop) { nsStack[existingNsIndex].declared = true; // old one is silenced now } } AddNamespace(prefix, ns, declared); } } } void AddNamespace(string prefix, string ns, bool declared) { int nsIndex = ++nsTop; if ( nsIndex == nsStack.Length ) { Namespace[] newStack = new Namespace[nsIndex * 2]; Array.Copy(nsStack, newStack, nsIndex); nsStack = newStack; } nsStack[nsIndex].Set(prefix, ns, declared); if (useNsHashtable) { AddToNamespaceHashtable(nsIndex); } else if (nsIndex == MaxNamespacesWalkCount) { // add all nsHashtable = new Dictionary (new SecureStringHasher()); for (int i = 0; i <= nsIndex; i++) { AddToNamespaceHashtable(i); } useNsHashtable = true; } } void AddToNamespaceHashtable(int namespaceIndex) { string prefix = nsStack[namespaceIndex].prefix; int existingNsIndex; if ( nsHashtable.TryGetValue(prefix, out existingNsIndex)) { nsStack[namespaceIndex].prevNsIndex = existingNsIndex; } nsHashtable[prefix] = namespaceIndex; } private void PopNamespaces(int indexFrom, int indexTo) { Debug.Assert(useNsHashtable); for (int i = indexTo; i >= indexFrom; i--) { Debug.Assert(nsHashtable.ContainsKey(nsStack[i].prefix)); if (nsStack[i].prevNsIndex == -1) { nsHashtable.Remove(nsStack[i].prefix); } else { nsHashtable[nsStack[i].prefix] = nsStack[i].prevNsIndex; } } } string GeneratePrefix() { int temp = stack[top].prefixCount++ + 1; return "d" + top.ToString("d", CultureInfo.InvariantCulture) + "p" + temp.ToString("d", CultureInfo.InvariantCulture); } void InternalWriteProcessingInstruction(string name, string text) { textWriter.Write(""); ValidateName(name, false); textWriter.Write(name); textWriter.Write(' '); if (null != text) { xmlEncoder.WriteRawWithSurrogateChecking(text); } textWriter.Write("?>"); } int LookupNamespace( string prefix ) { if ( useNsHashtable ) { int nsIndex; if ( nsHashtable.TryGetValue( prefix, out nsIndex ) ) { return nsIndex; } } else { for ( int i = nsTop; i >= 0; i-- ) { if ( nsStack[i].prefix == prefix ) { return i; } } } return -1; } int LookupNamespaceInCurrentScope( string prefix ) { if ( useNsHashtable ) { int nsIndex; if ( nsHashtable.TryGetValue( prefix, out nsIndex ) ) { if ( nsIndex > stack[top].prevNsTop ) { return nsIndex; } } } else { for ( int i = nsTop; i > stack[top].prevNsTop; i-- ) { if ( nsStack[i].prefix == prefix ) { return i; } } } return -1; } string FindPrefix(string ns) { for (int i = nsTop; i >= 0; i--) { if (nsStack[i].ns == ns) { if (LookupNamespace(nsStack[i].prefix) == i) { return nsStack[i].prefix; } } } return null; } // There are three kind of strings we write out - Name, LocalName and Prefix. // Both LocalName and Prefix can be represented with NCName == false and Name // can be represented as NCName == true void InternalWriteName(string name, bool NCName) { ValidateName(name, NCName); textWriter.Write(name); } private unsafe void ValidateName(string name, bool NCName) { if (name == null || name.Length == 0) { throw new ArgumentException(Res.GetString(Res.Xml_EmptyName)); } int nameLength = name.Length; int position = 0; int colonPosition = -1; if (namespaces) { if ((xmlCharType.charProperties[name[position]] & XmlCharType.fNCStartName) != 0) { // if (xmlCharType.IsStartNCNameChar(name[position])) { Continue: position ++; while (position < nameLength && (xmlCharType.charProperties[name[position]] & XmlCharType.fNCName) != 0) { // xmlCharType.IsNCNameChar(name[position])) { position ++; } if (position == nameLength) { return; } if (name[position] == ':') { if (!NCName) { if (colonPosition == -1) { if (position + 1 < nameLength) { colonPosition = position; // We should check if the character after ':' is a valid start name // character but we can't do that because of backward compatilibity goto Continue; } } } } } } else { if ((xmlCharType.charProperties[name[0]] & XmlCharType.fNCStartName) != 0 || name[0] == ':') { // if (xmlCharType.IsStartNameChar(name[0])) { position ++; while (position < nameLength && ((xmlCharType.charProperties[name[position]] & XmlCharType.fNCName) != 0 || name[position] == ':')) { // xmlCharType.IsNameChar(name[position])) { position ++; } if (position == nameLength) { return; } } } throw new ArgumentException(Res.GetString(Res.Xml_InvalidNameChars, name)); } void HandleSpecialAttribute() { string value = xmlEncoder.AttributeValue; switch (this.specialAttr) { case SpecialAttr.XmlLang: stack[top].xmlLang = value; break; case SpecialAttr.XmlSpace: // validate XmlSpace attribute value = XmlConvert.TrimString(value); if (value == "default") { stack[top].xmlSpace = XmlSpace.Default; } else if (value == "preserve") { stack[top].xmlSpace = XmlSpace.Preserve; } else { throw new ArgumentException(Res.GetString(Res.Xml_InvalidXmlSpace, value)); } break; case SpecialAttr.XmlNs: VerifyPrefixXml(this.prefixForXmlNs, value); PushNamespace(this.prefixForXmlNs, value, true); break; } } void VerifyPrefixXml(string prefix, string ns) { if (prefix != null && prefix.Length == 3) { if ( (prefix[0] == 'x' || prefix[0] == 'X') && (prefix[1] == 'm' || prefix[1] == 'M') && (prefix[2] == 'l' || prefix[2] == 'L') ) { if (XmlReservedNs.NsXml != ns) { throw new ArgumentException(Res.GetString(Res.Xml_InvalidPrefix)); } } } } void PushStack() { if (top == stack.Length - 1) { TagInfo[] na = new TagInfo[stack.Length + 10]; if (top > 0) Array.Copy(stack,na,top + 1); stack = na; } top++; // Move up stack stack[top].Init(nsTop); } void FlushEncoders() { if (null != this.base64Encoder) { // The Flush will call WriteRaw to write out the rest of the encoded characters this.base64Encoder.Flush(); } this.flush = false; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ListItem.cs
- RegexCaptureCollection.cs
- MonthChangedEventArgs.cs
- FileLevelControlBuilderAttribute.cs
- TrustManagerPromptUI.cs
- ConfigurationSchemaErrors.cs
- PackWebResponse.cs
- RoutedPropertyChangedEventArgs.cs
- StandardToolWindows.cs
- PenThreadWorker.cs
- CommandDevice.cs
- BackStopAuthenticationModule.cs
- ExternalFile.cs
- IconBitmapDecoder.cs
- FastEncoder.cs
- PropertyPushdownHelper.cs
- DataTemplateKey.cs
- WindowsEditBoxRange.cs
- BitmapCacheBrush.cs
- PropertyChangeTracker.cs
- NativeObjectSecurity.cs
- InkPresenter.cs
- DataGridViewLinkCell.cs
- SmiSettersStream.cs
- ConnectionOrientedTransportChannelFactory.cs
- AuthenticationManager.cs
- SerializerWriterEventHandlers.cs
- BamlBinaryReader.cs
- ConfigUtil.cs
- PointLightBase.cs
- _ScatterGatherBuffers.cs
- ClientBase.cs
- XsdCachingReader.cs
- DataTrigger.cs
- QilGeneratorEnv.cs
- ValidatorUtils.cs
- InlinedLocationReference.cs
- WebPartMenuStyle.cs
- SafeProcessHandle.cs
- HttpFileCollection.cs
- TextViewElement.cs
- SqlDataSourceConnectionPanel.cs
- BuildProvidersCompiler.cs
- HttpEncoder.cs
- NumericPagerField.cs
- MultiTouchSystemGestureLogic.cs
- CodeRegionDirective.cs
- SqlIdentifier.cs
- ExplicitDiscriminatorMap.cs
- RtfControls.cs
- SSmlParser.cs
- ResourceIDHelper.cs
- StopStoryboard.cs
- SqlCacheDependencyDatabase.cs
- ControlTemplate.cs
- ServerValidateEventArgs.cs
- OleTxTransaction.cs
- LazyLoadBehavior.cs
- FormViewModeEventArgs.cs
- DataGridViewRowPostPaintEventArgs.cs
- AssociationSetEnd.cs
- XmlSchemaAttributeGroup.cs
- XMLSyntaxException.cs
- EventLogger.cs
- ToolStripSeparator.cs
- ImageClickEventArgs.cs
- ArcSegment.cs
- _TLSstream.cs
- ImageCodecInfoPrivate.cs
- MsmqBindingFilter.cs
- ComponentManagerBroker.cs
- ResolveDuplexAsyncResult.cs
- Confirm.cs
- WindowsListViewGroupHelper.cs
- ByteStreamGeometryContext.cs
- hresults.cs
- CompilationRelaxations.cs
- X509CertificateCollection.cs
- ComponentResourceManager.cs
- BitmapFrameDecode.cs
- Resources.Designer.cs
- ListControlBoundActionList.cs
- StrictAndMessageFilter.cs
- ObjectDisposedException.cs
- ScaleTransform3D.cs
- ModelChangedEventArgsImpl.cs
- HyperLink.cs
- StrongNameUtility.cs
- VariantWrapper.cs
- Types.cs
- ResourceManagerWrapper.cs
- CredentialCache.cs
- TableCellCollection.cs
- DefaultTextStoreTextComposition.cs
- CatalogZoneBase.cs
- ToolStripItemCollection.cs
- Brush.cs
- GraphicsContext.cs
- mactripleDES.cs
- DataGridRowHeaderAutomationPeer.cs