The Labs \ Source Viewer \ SSCLI \ System.Xml \ DocumentXmlWriterType

  1. //------------------------------------------------------------------------------
  2. // <copyright file="DocumentXmlWriter.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Xml.Schema;
  20. namespace System.Xml
  21. {
  22.     enum DocumentXmlWriterType
  23.     {
  24.         InsertSiblingAfter,
  25.         InsertSiblingBefore,
  26.         PrependChild,
  27.         AppendChild,
  28.         AppendAttribute,
  29.         ReplaceToFollowingSibling
  30.     }
  31.    
  32.     // Implements a XmlWriter that augments a XmlDocument.
  33.     sealed class DocumentXmlWriter : XmlRawWriter, IXmlNamespaceResolver
  34.     {
  35.         enum State
  36.         {
  37.             Error,
  38.             Attribute,
  39.             Prolog,
  40.             Fragment,
  41.             Content,
  42.            
  43.             Last
  44.             // always last
  45.         }
  46.        
  47.         enum Method
  48.         {
  49.             WriteXmlDeclaration,
  50.             WriteStartDocument,
  51.             WriteEndDocument,
  52.             WriteDocType,
  53.             WriteStartElement,
  54.             WriteEndElement,
  55.             WriteFullEndElement,
  56.             WriteStartAttribute,
  57.             WriteEndAttribute,
  58.             WriteNamespaceDeclaration,
  59.             WriteCData,
  60.             WriteComment,
  61.             WriteProcessingInstruction,
  62.             WriteEntityRef,
  63.             WriteWhitespace,
  64.             WriteString
  65.         }
  66.        
  67.         DocumentXmlWriterType type;
  68.         // writer type
  69.         XmlNode start;
  70.         // context node
  71.         XmlDocument document;
  72.         // context document
  73.         XmlNamespaceManager namespaceManager;
  74.         // context namespace manager
  75.         State state;
  76.         // current state
  77.         XmlNode write;
  78.         // current node
  79.         List<XmlNode> fragment;
  80.         // top level node cache
  81.         XmlWriterSettings settings;
  82.         // wrapping writer settings
  83.         DocumentXPathNavigator navigator;
  84.         // context for replace
  85.         XmlNode end;
  86.         // context for replace
  87.         public DocumentXmlWriter(DocumentXmlWriterType type, XmlNode start, XmlDocument document)
  88.         {
  89.             this.type = type;
  90.             this.start = start;
  91.             this.document = document;
  92.            
  93.             state = StartState();
  94.             fragment = new List<XmlNode>();
  95.             settings = new XmlWriterSettings();
  96.             settings.ReadOnly = false;
  97.             settings.CheckCharacters = false;
  98.             settings.CloseOutput = false;
  99.             settings.ConformanceLevel = (state == State.Prolog ? ConformanceLevel.Document : ConformanceLevel.Fragment);
  100.             settings.ReadOnly = true;
  101.         }
  102.        
  103.         public XmlNamespaceManager NamespaceManager {
  104.             set { namespaceManager = value; }
  105.         }
  106.        
  107.         public override XmlWriterSettings Settings {
  108.             get { return settings; }
  109.         }
  110.        
  111.         internal void SetSettings(XmlWriterSettings value)
  112.         {
  113.             settings = value;
  114.         }
  115.        
  116.         public DocumentXPathNavigator Navigator {
  117.             set { navigator = value; }
  118.         }
  119.        
  120.         public XmlNode EndNode {
  121.             set { end = value; }
  122.         }
  123.        
  124.         internal override void WriteXmlDeclaration(XmlStandalone standalone)
  125.         {
  126.             VerifyState(Method.WriteXmlDeclaration);
  127.             if (standalone != XmlStandalone.Omit) {
  128.                 XmlNode node = document.CreateXmlDeclaration("1.0", string.Empty, standalone == XmlStandalone.Yes ? "yes" : "no");
  129.                 AddChild(node, write);
  130.             }
  131.         }
  132.        
  133.         internal override void WriteXmlDeclaration(string xmldecl)
  134.         {
  135.             VerifyState(Method.WriteXmlDeclaration);
  136.             string version;
  137.             string encoding;
  138.             string standalone;
  139.             XmlLoader.ParseXmlDeclarationValue(xmldecl, out version, out encoding, out standalone);
  140.             XmlNode node = document.CreateXmlDeclaration(version, encoding, standalone);
  141.             AddChild(node, write);
  142.         }
  143.        
  144.         public override void WriteStartDocument()
  145.         {
  146.             VerifyState(Method.WriteStartDocument);
  147.         }
  148.        
  149.         public override void WriteStartDocument(bool standalone)
  150.         {
  151.             VerifyState(Method.WriteStartDocument);
  152.         }
  153.        
  154.         public override void WriteEndDocument()
  155.         {
  156.             VerifyState(Method.WriteEndDocument);
  157.         }
  158.        
  159.         public override void WriteDocType(string name, string pubid, string sysid, string subset)
  160.         {
  161.             VerifyState(Method.WriteDocType);
  162.             XmlNode node = document.CreateDocumentType(name, pubid, sysid, subset);
  163.             AddChild(node, write);
  164.         }
  165.        
  166.         public override void WriteStartElement(string prefix, string localName, string ns)
  167.         {
  168.             VerifyState(Method.WriteStartElement);
  169.             XmlNode node = document.CreateElement(prefix, localName, ns);
  170.             AddChild(node, write);
  171.             write = node;
  172.         }
  173.        
  174.         public override void WriteEndElement()
  175.         {
  176.             VerifyState(Method.WriteEndElement);
  177.             if (write == null) {
  178.                 throw new InvalidOperationException();
  179.             }
  180.             write = write.ParentNode;
  181.         }
  182.        
  183.         internal override void WriteEndElement(string prefix, string localName, string ns)
  184.         {
  185.             WriteEndElement();
  186.         }
  187.        
  188.         public override void WriteFullEndElement()
  189.         {
  190.             VerifyState(Method.WriteFullEndElement);
  191.             XmlElement elem = write as XmlElement;
  192.             if (elem == null) {
  193.                 throw new InvalidOperationException();
  194.             }
  195.             elem.IsEmpty = false;
  196.             write = elem.ParentNode;
  197.         }
  198.        
  199.         internal override void WriteFullEndElement(string prefix, string localName, string ns)
  200.         {
  201.             WriteFullEndElement();
  202.         }
  203.        
  204.         internal override void StartElementContent()
  205.         {
  206.             // nop
  207.         }
  208.        
  209.         public override void WriteStartAttribute(string prefix, string localName, string ns)
  210.         {
  211.             VerifyState(Method.WriteStartAttribute);
  212.             XmlAttribute attr = document.CreateAttribute(prefix, localName, ns);
  213.             AddAttribute(attr, write);
  214.             write = attr;
  215.         }
  216.        
  217.         public override void WriteEndAttribute()
  218.         {
  219.             VerifyState(Method.WriteEndAttribute);
  220.             XmlAttribute attr = write as XmlAttribute;
  221.             if (attr == null) {
  222.                 throw new InvalidOperationException();
  223.             }
  224.             if (!attr.HasChildNodes) {
  225.                 XmlNode node = document.CreateTextNode(string.Empty);
  226.                 AddChild(node, attr);
  227.             }
  228.             write = attr.OwnerElement;
  229.         }
  230.        
  231.         internal override void WriteNamespaceDeclaration(string prefix, string ns)
  232.         {
  233.             VerifyState(Method.WriteNamespaceDeclaration);
  234.             XmlAttribute attr;
  235.             if (prefix.Length == 0) {
  236.                 attr = document.CreateAttribute(prefix, document.strXmlns, document.strReservedXmlns);
  237.             }
  238.             else {
  239.                 attr = document.CreateAttribute(document.strXmlns, prefix, document.strReservedXmlns);
  240.             }
  241.             AddAttribute(attr, write);
  242.             XmlNode node = document.CreateTextNode(ns);
  243.             AddChild(node, attr);
  244.         }
  245.        
  246.         public override void WriteCData(string text)
  247.         {
  248.             VerifyState(Method.WriteCData);
  249.             XmlConvert.VerifyCharData(text, ExceptionType.ArgumentException);
  250.             XmlNode node = document.CreateCDataSection(text);
  251.             AddChild(node, write);
  252.         }
  253.        
  254.         public override void WriteComment(string text)
  255.         {
  256.             VerifyState(Method.WriteComment);
  257.             XmlConvert.VerifyCharData(text, ExceptionType.ArgumentException);
  258.             XmlNode node = document.CreateComment(text);
  259.             AddChild(node, write);
  260.         }
  261.        
  262.         public override void WriteProcessingInstruction(string name, string text)
  263.         {
  264.             VerifyState(Method.WriteProcessingInstruction);
  265.             XmlConvert.VerifyCharData(text, ExceptionType.ArgumentException);
  266.             XmlNode node = document.CreateProcessingInstruction(name, text);
  267.             AddChild(node, write);
  268.         }
  269.        
  270.         public override void WriteEntityRef(string name)
  271.         {
  272.             VerifyState(Method.WriteEntityRef);
  273.             XmlNode node = document.CreateEntityReference(name);
  274.             AddChild(node, write);
  275.         }
  276.        
  277.         public override void WriteCharEntity(char ch)
  278.         {
  279.             WriteString(new string(ch, 1));
  280.         }
  281.        
  282.         public override void WriteWhitespace(string text)
  283.         {
  284.             VerifyState(Method.WriteWhitespace);
  285.             XmlConvert.VerifyCharData(text, ExceptionType.ArgumentException);
  286.             if (document.PreserveWhitespace) {
  287.                 XmlNode node = document.CreateWhitespace(text);
  288.                 AddChild(node, write);
  289.             }
  290.         }
  291.        
  292.         public override void WriteString(string text)
  293.         {
  294.             VerifyState(Method.WriteString);
  295.             XmlConvert.VerifyCharData(text, ExceptionType.ArgumentException);
  296.             XmlNode node = document.CreateTextNode(text);
  297.             AddChild(node, write);
  298.         }
  299.        
  300.         public override void WriteSurrogateCharEntity(char lowCh, char highCh)
  301.         {
  302.             WriteString(new string(new char[] {highCh, lowCh}));
  303.         }
  304.        
  305.         public override void WriteChars(char[] buffer, int index, int count)
  306.         {
  307.             WriteString(new string(buffer, index, count));
  308.         }
  309.        
  310.         public override void WriteRaw(char[] buffer, int index, int count)
  311.         {
  312.             WriteString(new string(buffer, index, count));
  313.         }
  314.        
  315.         public override void WriteRaw(string data)
  316.         {
  317.             WriteString(data);
  318.         }
  319.        
  320.         public override void Close()
  321.         {
  322.             // nop
  323.         }
  324.        
  325.         internal override void Close(WriteState currentState)
  326.         {
  327.             if (currentState == WriteState.Error) {
  328.                 return;
  329.             }
  330.             try {
  331.                 switch (type) {
  332.                     case DocumentXmlWriterType.InsertSiblingAfter:
  333.                         XmlNode parent = start.ParentNode;
  334.                         if (parent == null) {
  335.                             throw new InvalidOperationException(Res.GetString(Res.Xpn_MissingParent));
  336.                         }
  337.                         for (int i = fragment.Count - 1; i >= 0; i--) {
  338.                             parent.InsertAfter(fragment[i], start);
  339.                         }
  340.                         break;
  341.                     case DocumentXmlWriterType.InsertSiblingBefore:
  342.                         parent = start.ParentNode;
  343.                         if (parent == null) {
  344.                             throw new InvalidOperationException(Res.GetString(Res.Xpn_MissingParent));
  345.                         }
  346.                         for (int i = 0; i < fragment.Count; i++) {
  347.                             parent.InsertBefore(fragment[i], start);
  348.                         }
  349.                         break;
  350.                     case DocumentXmlWriterType.PrependChild:
  351.                         for (int i = fragment.Count - 1; i >= 0; i--) {
  352.                             start.PrependChild(fragment[i]);
  353.                         }
  354.                         break;
  355.                     case DocumentXmlWriterType.AppendChild:
  356.                         for (int i = 0; i < fragment.Count; i++) {
  357.                             start.AppendChild(fragment[i]);
  358.                         }
  359.                         break;
  360.                     case DocumentXmlWriterType.AppendAttribute:
  361.                         CloseWithAppendAttribute();
  362.                         break;
  363.                     case DocumentXmlWriterType.ReplaceToFollowingSibling:
  364.                         if (fragment.Count == 0) {
  365.                             throw new InvalidOperationException(Res.GetString(Res.Xpn_NoContent));
  366.                         }
  367.                         CloseWithReplaceToFollowingSibling();
  368.                         break;
  369.                 }
  370.             }
  371.             finally {
  372.                 fragment.Clear();
  373.             }
  374.         }
  375.        
  376.         private void CloseWithAppendAttribute()
  377.         {
  378.             XmlElement elem = start as XmlElement;
  379.             Debug.Assert(elem != null);
  380.             XmlAttributeCollection attrs = elem.Attributes;
  381.             for (int i = 0; i < fragment.Count; i++) {
  382.                 XmlAttribute attr = fragment[i] as XmlAttribute;
  383.                 Debug.Assert(attr != null);
  384.                 int offset = attrs.FindNodeOffsetNS(attr);
  385.                 if (offset != -1 && ((XmlAttribute)attrs.Nodes[offset]).Specified) {
  386.                     throw new XmlException(Res.Xml_DupAttributeName, attr.Prefix.Length == 0 ? attr.LocalName : string.Concat(attr.Prefix, ":", attr.LocalName));
  387.                 }
  388.             }
  389.             for (int i = 0; i < fragment.Count; i++) {
  390.                 XmlAttribute attr = fragment[i] as XmlAttribute;
  391.                 Debug.Assert(attr != null);
  392.                 attrs.Append(attr);
  393.             }
  394.         }
  395.        
  396.         private void CloseWithReplaceToFollowingSibling()
  397.         {
  398.             XmlNode parent = start.ParentNode;
  399.             if (parent == null) {
  400.                 throw new InvalidOperationException(Res.GetString(Res.Xpn_MissingParent));
  401.             }
  402.             if (start != end) {
  403.                 if (!DocumentXPathNavigator.IsFollowingSibling(start, end)) {
  404.                     throw new InvalidOperationException(Res.GetString(Res.Xpn_BadPosition));
  405.                 }
  406.                 if (start.IsReadOnly) {
  407.                     throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Modify_ReadOnly));
  408.                 }
  409.                 DocumentXPathNavigator.DeleteToFollowingSibling(start.NextSibling, end);
  410.             }
  411.             XmlNode fragment0 = fragment[0];
  412.             parent.ReplaceChild(fragment0, start);
  413.             for (int i = fragment.Count - 1; i >= 1; i--) {
  414.                 parent.InsertAfter(fragment[i], fragment0);
  415.             }
  416.             navigator.ResetPosition(fragment0);
  417.         }
  418.        
  419.         public override void Flush()
  420.         {
  421.             // nop
  422.         }
  423.        
  424.         IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope(XmlNamespaceScope scope)
  425.         {
  426.             return namespaceManager.GetNamespacesInScope(scope);
  427.         }
  428.        
  429.         string IXmlNamespaceResolver.LookupNamespace(string prefix)
  430.         {
  431.             return namespaceManager.LookupNamespace(prefix);
  432.         }
  433.        
  434.         string IXmlNamespaceResolver.LookupPrefix(string namespaceName)
  435.         {
  436.             return namespaceManager.LookupPrefix(namespaceName);
  437.         }
  438.        
  439.         void AddAttribute(XmlAttribute attr, XmlNode parent)
  440.         {
  441.             if (parent == null) {
  442.                 fragment.Add(attr);
  443.             }
  444.             else {
  445.                 XmlElement elem = parent as XmlElement;
  446.                 if (elem == null) {
  447.                     throw new InvalidOperationException();
  448.                 }
  449.                 elem.Attributes.Append(attr);
  450.             }
  451.         }
  452.        
  453.         void AddChild(XmlNode node, XmlNode parent)
  454.         {
  455.             if (parent == null) {
  456.                 fragment.Add(node);
  457.             }
  458.             else {
  459.                 parent.AppendChild(node);
  460.             }
  461.         }
  462.        
  463.         State StartState()
  464.         {
  465.             XmlNodeType nodeType = XmlNodeType.None;
  466.            
  467.             switch (type) {
  468.                 case DocumentXmlWriterType.InsertSiblingAfter:
  469.                 case DocumentXmlWriterType.InsertSiblingBefore:
  470.                     XmlNode parent = start.ParentNode;
  471.                     if (parent != null) {
  472.                         nodeType = parent.NodeType;
  473.                     }
  474.                     if (nodeType == XmlNodeType.Document) {
  475.                         return State.Prolog;
  476.                     }
  477.                     else if (nodeType == XmlNodeType.DocumentFragment) {
  478.                         return State.Fragment;
  479.                     }
  480.                     break;
  481.                 case DocumentXmlWriterType.PrependChild:
  482.                 case DocumentXmlWriterType.AppendChild:
  483.                     nodeType = start.NodeType;
  484.                     if (nodeType == XmlNodeType.Document) {
  485.                         return State.Prolog;
  486.                     }
  487.                     else if (nodeType == XmlNodeType.DocumentFragment) {
  488.                         return State.Fragment;
  489.                     }
  490.                     break;
  491.                 case DocumentXmlWriterType.AppendAttribute:
  492.                     return State.Attribute;
  493.                 case DocumentXmlWriterType.ReplaceToFollowingSibling:
  494.                     break;
  495.             }
  496.             return State.Content;
  497.         }
  498.        
  499.         static State[] changeState = {State.Error, State.Error, State.Prolog, State.Content, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error,
  500.         State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Prolog, State.Error, State.Error,
  501.         State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, State.Error, State.Error, State.Content,
  502.         State.Error, State.Error, State.Error, State.Error, State.Content, State.Error, State.Content, State.Error, State.Error, State.Content,
  503.         State.Error, State.Error, State.Error, State.Error, State.Content, State.Error, State.Content, State.Error, State.Error, State.Content,
  504.         State.Error, State.Error, State.Error, State.Content, State.Content, State.Error, State.Error, State.Prolog, State.Content, State.Content,
  505.         State.Error, State.Error, State.Prolog, State.Content, State.Content, State.Error, State.Error, State.Error, State.Content, State.Content,
  506.         State.Error, State.Error, State.Prolog, State.Content, State.Content, State.Error, State.Error, State.Error, State.Content, State.Content
  507.             // State.Error, State.Attribute,State.Prolog, State.Fragment, State.Content,
  508.            
  509.             // Method.XmlDeclaration:
  510.             // Method.StartDocument:
  511.             // Method.EndDocument:
  512.             // Method.DocType:
  513.             // Method.StartElement:
  514.             // Method.EndElement:
  515.             // Method.FullEndElement:
  516.             // Method.StartAttribute:
  517.             // Method.EndAttribute:
  518.             // Method.NamespaceDeclaration:
  519.             // Method.CData:
  520.             // Method.Comment:
  521.             // Method.ProcessingInstruction:
  522.             // Method.EntityRef:
  523.             // Method.Whitespace:
  524.             // Method.String:
  525.         };
  526.        
  527.         void VerifyState(Method method)
  528.         {
  529.             state = changeState[(int)method * (int)State.Last + (int)state];
  530.             if (state == State.Error) {
  531.                 throw new InvalidOperationException(Res.GetString(Res.Xml_ClosedOrError));
  532.             }
  533.         }
  534.     }
  535. }

Developer Fusion