//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // [....] //------------------------------------------------------------------------------ using System; using System.Threading; using System.IO; using System.Collections; using System.Globalization; using System.Text; using System.Diagnostics; using System.Xml; using System.Xml.XPath; using System.Xml.Schema; namespace System.Xml.Xsl.Runtime { /// /// RtfNavigators store Xslt result-tree-fragments. At runtime, the Xslt library tests to see if a Navigator /// is an RtfNavigator in order to enforce certain restrictions, such as prohibiting querying into Rtfs. /// Furthermore, Rtfs must store extra serialization information required in order to properly implement the /// Xslt disable-output-escaping flag. /// internal abstract class RtfNavigator : XPathNavigator { //----------------------------------------------- // RtfNavigator //----------------------------------------------- /// /// Preserve serialization hints when deep copying. /// public abstract void CopyToWriter(XmlWriter writer); /// /// Discard serialization hints and return a navigator that actually allows navigation. /// public abstract XPathNavigator ToNavigator(); //----------------------------------------------- // XPathNavigator //----------------------------------------------- /// /// Get the XPath node type of the current node. /// public override XPathNodeType NodeType { get { return XPathNodeType.Root; } } /// /// Get the local name portion of the current node's name. /// public override string LocalName { get { return string.Empty; } } /// /// Get the namespace portion of the current node's name. /// public override string NamespaceURI { get { return string.Empty; } } /// /// Get the name of the current node. /// public override string Name { get { return string.Empty; } } /// /// Get the prefix portion of the current node's name. /// public override string Prefix { get { return string.Empty; } } /// /// Return true if this is an element which used a shortcut tag in its Xml 1.0 serialized form. /// public override bool IsEmptyElement { get { return false; } } /// /// Return the xml name table which was used to atomize all prefixes, local-names, and /// namespace uris in the document. /// public override XmlNameTable NameTable { get { throw new NotSupportedException(); } } /// /// Position the navigator on the first attribute of the current node and return true. If no attributes /// can be found, return false. /// public override bool MoveToFirstAttribute() { throw new NotSupportedException(); } /// /// If positioned on an attribute, move to its next sibling attribute. If no attributes can be found, /// return false. /// public override bool MoveToNextAttribute() { throw new NotSupportedException(); } /// /// Position the navigator on the namespace within the specified scope. If no matching namespace /// can be found, return false. /// public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) { throw new NotSupportedException(); } /// /// Position the navigator on the next namespace within the specified scope. If no matching namespace /// can be found, return false. /// public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) { throw new NotSupportedException(); } /// /// If the current node is an attribute or namespace (not content), return false. Otherwise, /// move to the next content node. Return false if there are no more content nodes. /// public override bool MoveToNext() { throw new NotSupportedException(); } /// /// If the current node is an attribute or namespace (not content), return false. Otherwise, /// move to the previous (sibling) content node. Return false if there are no previous content nodes. /// public override bool MoveToPrevious() { throw new NotSupportedException(); } /// /// Move to the first content-typed child of the current node. Return false if the current /// node has no content children. /// public override bool MoveToFirstChild() { throw new NotSupportedException(); } /// /// Position the navigator on the parent of the current node. If the current node has no parent, /// return false. /// public override bool MoveToParent() { throw new NotSupportedException(); } /// /// Position to the navigator to the element whose id is equal to the specified "id" string. /// public override bool MoveToId(string id) { throw new NotSupportedException(); } /// /// Returns true if this navigator is positioned to the same node as the "other" navigator. Returns false /// if not, or if the "other" navigator is not the same type as this navigator. /// public override bool IsSamePosition(XPathNavigator other) { throw new NotSupportedException(); } } /// /// This navigator is a cursor over a cache that stores Xslt disable-output-escaping flags. /// internal sealed class RtfTreeNavigator : RtfNavigator { private XmlEventCache events; private NavigatorConstructor constr; private XmlNameTable nameTable; //----------------------------------------------- // Constructors //----------------------------------------------- /// /// Create a new navigator over the specified cache of Xml events. /// public RtfTreeNavigator(XmlEventCache events, XmlNameTable nameTable) { this.events = events; this.constr = new NavigatorConstructor(); this.nameTable = nameTable; } /// /// Copy constructor. /// public RtfTreeNavigator(RtfTreeNavigator that) { this.events = that.events; this.constr = that.constr; this.nameTable = that.nameTable; } //----------------------------------------------- // RtfNavigator //----------------------------------------------- /// /// Preserve serialization hints when deep copying. /// public override void CopyToWriter(XmlWriter writer) { this.events.EventsToWriter(writer); } /// /// Discard serialization hints and return a navigator that actually allows navigation. /// public override XPathNavigator ToNavigator() { return this.constr.GetNavigator(this.events, this.nameTable); } //----------------------------------------------- // XPathItem //----------------------------------------------- /// /// Get the string value of the current node, computed using data model dm:string-value rules. /// If the node has a typed value, return the string representation of the value. If the node /// is not a parent type (comment, text, pi, etc.), get its simple text value. Otherwise, /// concatenate all text node descendants of the current node. /// public override string Value { get { return this.events.EventsToString(); } } //----------------------------------------------- // XPathNavigator //----------------------------------------------- /// /// Get the base URI of the Rtf. /// public override string BaseURI { get { return this.events.BaseUri; } } /// /// Create a copy of this navigator, positioned to the same node in the tree. /// public override XPathNavigator Clone() { return new RtfTreeNavigator(this); } /// /// Position this navigator to the same position as the "other" navigator. If the "other" navigator /// is not of the same type as this navigator, then return false. /// public override bool MoveTo(XPathNavigator other) { RtfTreeNavigator that = other as RtfTreeNavigator; if (that != null) { this.events = that.events; this.constr = that.constr; this.nameTable = that.nameTable; return true; } return false; } } /// /// This RtfNavigator specializes the case of a root node having a single text node child. This is a very common /// case, such as in bar. /// internal sealed class RtfTextNavigator : RtfNavigator { private string text, baseUri; private NavigatorConstructor constr; //----------------------------------------------- // Constructors //----------------------------------------------- /// /// Create a new navigator having a text node with value = "text" string. /// public RtfTextNavigator(string text, string baseUri) { this.text = text; this.baseUri = baseUri; this.constr = new NavigatorConstructor(); } /// /// Copy constructor. /// public RtfTextNavigator(RtfTextNavigator that) { this.text = that.text; this.baseUri = that.baseUri; this.constr = that.constr; } //----------------------------------------------- // RtfNavigator //----------------------------------------------- /// /// Preserve serialization hints when deep copying. /// public override void CopyToWriter(XmlWriter writer) { writer.WriteString(Value); } /// /// Discard serialization hints and return a navigator that actually allows navigation. /// public override XPathNavigator ToNavigator() { return this.constr.GetNavigator(this.text, this.baseUri, new NameTable()); } //----------------------------------------------- // XPathItem //----------------------------------------------- /// /// Get the string value of the current node, computed using data model dm:string-value rules. /// If the node has a typed value, return the string representation of the value. If the node /// is not a parent type (comment, text, pi, etc.), get its simple text value. Otherwise, /// concatenate all text node descendants of the current node. /// public override string Value { get { return this.text; } } //----------------------------------------------- // XPathNavigator //----------------------------------------------- /// /// Get the base URI of the Rtf. /// public override string BaseURI { get { return this.baseUri; } } /// /// Create a copy of this navigator, positioned to the same node in the tree. /// public override XPathNavigator Clone() { return new RtfTextNavigator(this); } /// /// Position this navigator to the same position as the "other" navigator. If the "other" navigator /// is not of the same type as this navigator, then return false. /// public override bool MoveTo(XPathNavigator other) { RtfTextNavigator that = other as RtfTextNavigator; if (that != null) { this.text = that.text; this.baseUri = that.baseUri; this.constr = that.constr; return true; } return false; } } /// /// This class creates a document on the first call to GetNavigator(), and returns a Navigator from it. On /// subsequent calls, Navigators from the same document are returned (no new document is created). /// internal sealed class NavigatorConstructor { object cache; /// /// Create a document from the cache of events. If a document has already been created previously, return it. /// This method is thread-safe, and is always guaranteed to return the exact same document, no matter how many /// threads have called it concurrently. /// public XPathNavigator GetNavigator(XmlEventCache events, XmlNameTable nameTable) { if (this.cache == null) { // Create XPathDocument from event cache XPathDocument doc = new XPathDocument(nameTable); XmlRawWriter writer = doc.LoadFromWriter(XPathDocument.LoadFlags.AtomizeNames | (events.HasRootNode ? XPathDocument.LoadFlags.None : XPathDocument.LoadFlags.Fragment), events.BaseUri); events.EventsToWriter(writer); writer.Close(); this.cache = doc; } return ((XPathDocument) this.cache).CreateNavigator(); } /// /// Create a document containing a root node and a single text node child with "text" as its text value. /// This method is thread-safe, and is always guaranteed to return the exact same document, no matter how many /// threads have called it concurrently. /// public XPathNavigator GetNavigator(string text, string baseUri, XmlNameTable nameTable) { if (this.cache == null) { // Create XPathDocument XPathDocument doc = new XPathDocument(nameTable); XmlRawWriter writer = doc.LoadFromWriter(XPathDocument.LoadFlags.AtomizeNames, baseUri); writer.WriteString(text); writer.Close(); this.cache = doc; } return ((XPathDocument) this.cache).CreateNavigator(); } } }