1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Threading; 7 using System.IO; 8 using System.Collections; 9 using System.Globalization; 10 using System.Text; 11 using System.Diagnostics; 12 using System.Xml; 13 using System.Xml.XPath; 14 using System.Xml.Schema; 15 16 namespace System.Xml.Xsl.Runtime 17 { 18 /// <summary> 19 /// RtfNavigators store Xslt result-tree-fragments. At runtime, the Xslt library tests to see if a Navigator 20 /// is an RtfNavigator in order to enforce certain restrictions, such as prohibiting querying into Rtfs. 21 /// Furthermore, Rtfs must store extra serialization information required in order to properly implement the 22 /// Xslt disable-output-escaping flag. 23 /// </summary> 24 internal abstract class RtfNavigator : XPathNavigator 25 { 26 //----------------------------------------------- 27 // RtfNavigator 28 //----------------------------------------------- 29 30 /// <summary> 31 /// Preserve serialization hints when deep copying. 32 /// </summary> CopyToWriter(XmlWriter writer)33 public abstract void CopyToWriter(XmlWriter writer); 34 35 /// <summary> 36 /// Discard serialization hints and return a navigator that actually allows navigation. 37 /// </summary> ToNavigator()38 public abstract XPathNavigator ToNavigator(); 39 40 41 //----------------------------------------------- 42 // XPathNavigator 43 //----------------------------------------------- 44 45 /// <summary> 46 /// Get the XPath node type of the current node. 47 /// </summary> 48 public override XPathNodeType NodeType 49 { 50 get { return XPathNodeType.Root; } 51 } 52 53 /// <summary> 54 /// Get the local name portion of the current node's name. 55 /// </summary> 56 public override string LocalName 57 { 58 get { return string.Empty; } 59 } 60 61 /// <summary> 62 /// Get the namespace portion of the current node's name. 63 /// </summary> 64 public override string NamespaceURI 65 { 66 get { return string.Empty; } 67 } 68 69 /// <summary> 70 /// Get the name of the current node. 71 /// </summary> 72 public override string Name 73 { 74 get { return string.Empty; } 75 } 76 77 /// <summary> 78 /// Get the prefix portion of the current node's name. 79 /// </summary> 80 public override string Prefix 81 { 82 get { return string.Empty; } 83 } 84 85 /// <summary> 86 /// Return true if this is an element which used a shortcut tag in its Xml 1.0 serialized form. 87 /// </summary> 88 public override bool IsEmptyElement 89 { 90 get { return false; } 91 } 92 93 /// <summary> 94 /// Return the xml name table which was used to atomize all prefixes, local-names, and 95 /// namespace uris in the document. 96 /// </summary> 97 public override XmlNameTable NameTable 98 { 99 get { throw new NotSupportedException(); } 100 } 101 102 /// <summary> 103 /// Position the navigator on the first attribute of the current node and return true. If no attributes 104 /// can be found, return false. 105 /// </summary> MoveToFirstAttribute()106 public override bool MoveToFirstAttribute() 107 { 108 throw new NotSupportedException(); 109 } 110 111 /// <summary> 112 /// If positioned on an attribute, move to its next sibling attribute. If no attributes can be found, 113 /// return false. 114 /// </summary> MoveToNextAttribute()115 public override bool MoveToNextAttribute() 116 { 117 throw new NotSupportedException(); 118 } 119 120 /// <summary> 121 /// Position the navigator on the namespace within the specified scope. If no matching namespace 122 /// can be found, return false. 123 /// </summary> MoveToFirstNamespace(XPathNamespaceScope namespaceScope)124 public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) 125 { 126 throw new NotSupportedException(); 127 } 128 129 /// <summary> 130 /// Position the navigator on the next namespace within the specified scope. If no matching namespace 131 /// can be found, return false. 132 /// </summary> MoveToNextNamespace(XPathNamespaceScope namespaceScope)133 public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) 134 { 135 throw new NotSupportedException(); 136 } 137 138 /// <summary> 139 /// If the current node is an attribute or namespace (not content), return false. Otherwise, 140 /// move to the next content node. Return false if there are no more content nodes. 141 /// </summary> MoveToNext()142 public override bool MoveToNext() 143 { 144 throw new NotSupportedException(); 145 } 146 147 /// <summary> 148 /// If the current node is an attribute or namespace (not content), return false. Otherwise, 149 /// move to the previous (sibling) content node. Return false if there are no previous content nodes. 150 /// </summary> MoveToPrevious()151 public override bool MoveToPrevious() 152 { 153 throw new NotSupportedException(); 154 } 155 156 /// <summary> 157 /// Move to the first content-typed child of the current node. Return false if the current 158 /// node has no content children. 159 /// </summary> MoveToFirstChild()160 public override bool MoveToFirstChild() 161 { 162 throw new NotSupportedException(); 163 } 164 165 /// <summary> 166 /// Position the navigator on the parent of the current node. If the current node has no parent, 167 /// return false. 168 /// </summary> MoveToParent()169 public override bool MoveToParent() 170 { 171 throw new NotSupportedException(); 172 } 173 174 /// <summary> 175 /// Position to the navigator to the element whose id is equal to the specified "id" string. 176 /// </summary> MoveToId(string id)177 public override bool MoveToId(string id) 178 { 179 throw new NotSupportedException(); 180 } 181 182 /// <summary> 183 /// Returns true if this navigator is positioned to the same node as the "other" navigator. Returns false 184 /// if not, or if the "other" navigator is not the same type as this navigator. 185 /// </summary> IsSamePosition(XPathNavigator other)186 public override bool IsSamePosition(XPathNavigator other) 187 { 188 throw new NotSupportedException(); 189 } 190 } 191 192 193 /// <summary> 194 /// This navigator is a cursor over a cache that stores Xslt disable-output-escaping flags. 195 /// </summary> 196 internal sealed class RtfTreeNavigator : RtfNavigator 197 { 198 private XmlEventCache _events; 199 private NavigatorConstructor _constr; 200 private XmlNameTable _nameTable; 201 202 203 //----------------------------------------------- 204 // Constructors 205 //----------------------------------------------- 206 207 /// <summary> 208 /// Create a new navigator over the specified cache of Xml events. 209 /// </summary> RtfTreeNavigator(XmlEventCache events, XmlNameTable nameTable)210 public RtfTreeNavigator(XmlEventCache events, XmlNameTable nameTable) 211 { 212 _events = events; 213 _constr = new NavigatorConstructor(); 214 _nameTable = nameTable; 215 } 216 217 /// <summary> 218 /// Copy constructor. 219 /// </summary> RtfTreeNavigator(RtfTreeNavigator that)220 public RtfTreeNavigator(RtfTreeNavigator that) 221 { 222 _events = that._events; 223 _constr = that._constr; 224 _nameTable = that._nameTable; 225 } 226 227 228 //----------------------------------------------- 229 // RtfNavigator 230 //----------------------------------------------- 231 232 /// <summary> 233 /// Preserve serialization hints when deep copying. 234 /// </summary> CopyToWriter(XmlWriter writer)235 public override void CopyToWriter(XmlWriter writer) 236 { 237 _events.EventsToWriter(writer); 238 } 239 240 /// <summary> 241 /// Discard serialization hints and return a navigator that actually allows navigation. 242 /// </summary> ToNavigator()243 public override XPathNavigator ToNavigator() 244 { 245 return _constr.GetNavigator(_events, _nameTable); 246 } 247 248 249 //----------------------------------------------- 250 // XPathItem 251 //----------------------------------------------- 252 253 /// <summary> 254 /// Get the string value of the current node, computed using data model dm:string-value rules. 255 /// If the node has a typed value, return the string representation of the value. If the node 256 /// is not a parent type (comment, text, pi, etc.), get its simple text value. Otherwise, 257 /// concatenate all text node descendants of the current node. 258 /// </summary> 259 public override string Value 260 { 261 get { return _events.EventsToString(); } 262 } 263 264 265 //----------------------------------------------- 266 // XPathNavigator 267 //----------------------------------------------- 268 269 /// <summary> 270 /// Get the base URI of the Rtf. 271 /// </summary> 272 public override string BaseURI 273 { 274 get { return _events.BaseUri; } 275 } 276 277 /// <summary> 278 /// Create a copy of this navigator, positioned to the same node in the tree. 279 /// </summary> Clone()280 public override XPathNavigator Clone() 281 { 282 return new RtfTreeNavigator(this); 283 } 284 285 /// <summary> 286 /// Position this navigator to the same position as the "other" navigator. If the "other" navigator 287 /// is not of the same type as this navigator, then return false. 288 /// </summary> MoveTo(XPathNavigator other)289 public override bool MoveTo(XPathNavigator other) 290 { 291 RtfTreeNavigator that = other as RtfTreeNavigator; 292 if (that != null) 293 { 294 _events = that._events; 295 _constr = that._constr; 296 _nameTable = that._nameTable; 297 return true; 298 } 299 return false; 300 } 301 } 302 303 304 /// <summary> 305 /// This RtfNavigator specializes the case of a root node having a single text node child. This is a very common 306 /// case, such as in <xsl:variable name="foo">bar</xsl:variable>. 307 /// </summary> 308 internal sealed class RtfTextNavigator : RtfNavigator 309 { 310 private string _text, _baseUri; 311 private NavigatorConstructor _constr; 312 313 314 //----------------------------------------------- 315 // Constructors 316 //----------------------------------------------- 317 318 /// <summary> 319 /// Create a new navigator having a text node with value = "text" string. 320 /// </summary> RtfTextNavigator(string text, string baseUri)321 public RtfTextNavigator(string text, string baseUri) 322 { 323 _text = text; 324 _baseUri = baseUri; 325 _constr = new NavigatorConstructor(); 326 } 327 328 /// <summary> 329 /// Copy constructor. 330 /// </summary> RtfTextNavigator(RtfTextNavigator that)331 public RtfTextNavigator(RtfTextNavigator that) 332 { 333 _text = that._text; 334 _baseUri = that._baseUri; 335 _constr = that._constr; 336 } 337 338 339 //----------------------------------------------- 340 // RtfNavigator 341 //----------------------------------------------- 342 343 /// <summary> 344 /// Preserve serialization hints when deep copying. 345 /// </summary> CopyToWriter(XmlWriter writer)346 public override void CopyToWriter(XmlWriter writer) 347 { 348 writer.WriteString(Value); 349 } 350 351 /// <summary> 352 /// Discard serialization hints and return a navigator that actually allows navigation. 353 /// </summary> ToNavigator()354 public override XPathNavigator ToNavigator() 355 { 356 return _constr.GetNavigator(_text, _baseUri, new NameTable()); 357 } 358 359 360 //----------------------------------------------- 361 // XPathItem 362 //----------------------------------------------- 363 364 /// <summary> 365 /// Get the string value of the current node, computed using data model dm:string-value rules. 366 /// If the node has a typed value, return the string representation of the value. If the node 367 /// is not a parent type (comment, text, pi, etc.), get its simple text value. Otherwise, 368 /// concatenate all text node descendants of the current node. 369 /// </summary> 370 public override string Value 371 { 372 get { return _text; } 373 } 374 375 376 //----------------------------------------------- 377 // XPathNavigator 378 //----------------------------------------------- 379 380 /// <summary> 381 /// Get the base URI of the Rtf. 382 /// </summary> 383 public override string BaseURI 384 { 385 get { return _baseUri; } 386 } 387 388 /// <summary> 389 /// Create a copy of this navigator, positioned to the same node in the tree. 390 /// </summary> Clone()391 public override XPathNavigator Clone() 392 { 393 return new RtfTextNavigator(this); 394 } 395 396 /// <summary> 397 /// Position this navigator to the same position as the "other" navigator. If the "other" navigator 398 /// is not of the same type as this navigator, then return false. 399 /// </summary> MoveTo(XPathNavigator other)400 public override bool MoveTo(XPathNavigator other) 401 { 402 RtfTextNavigator that = other as RtfTextNavigator; 403 if (that != null) 404 { 405 _text = that._text; 406 _baseUri = that._baseUri; 407 _constr = that._constr; 408 return true; 409 } 410 return false; 411 } 412 } 413 414 415 /// <summary> 416 /// This class creates a document on the first call to GetNavigator(), and returns a Navigator from it. On 417 /// subsequent calls, Navigators from the same document are returned (no new document is created). 418 /// </summary> 419 internal sealed class NavigatorConstructor 420 { 421 private object _cache; 422 423 /// <summary> 424 /// Create a document from the cache of events. If a document has already been created previously, return it. 425 /// This method is thread-safe, and is always guaranteed to return the exact same document, no matter how many 426 /// threads have called it concurrently. 427 /// </summary> GetNavigator(XmlEventCache events, XmlNameTable nameTable)428 public XPathNavigator GetNavigator(XmlEventCache events, XmlNameTable nameTable) 429 { 430 if (_cache == null) 431 { 432 // Create XPathDocument from event cache 433 XPathDocument doc = new XPathDocument(nameTable); 434 XmlRawWriter writer = doc.LoadFromWriter(XPathDocument.LoadFlags.AtomizeNames | (events.HasRootNode ? XPathDocument.LoadFlags.None : XPathDocument.LoadFlags.Fragment), events.BaseUri); 435 436 events.EventsToWriter(writer); 437 writer.Close(); 438 439 _cache = doc; 440 } 441 442 return ((XPathDocument)_cache).CreateNavigator(); 443 } 444 445 /// <summary> 446 /// Create a document containing a root node and a single text node child with "text" as its text value. 447 /// This method is thread-safe, and is always guaranteed to return the exact same document, no matter how many 448 /// threads have called it concurrently. 449 /// </summary> GetNavigator(string text, string baseUri, XmlNameTable nameTable)450 public XPathNavigator GetNavigator(string text, string baseUri, XmlNameTable nameTable) 451 { 452 if (_cache == null) 453 { 454 // Create XPathDocument 455 XPathDocument doc = new XPathDocument(nameTable); 456 XmlRawWriter writer = doc.LoadFromWriter(XPathDocument.LoadFlags.AtomizeNames, baseUri); 457 writer.WriteString(text); 458 writer.Close(); 459 460 _cache = doc; 461 } 462 463 return ((XPathDocument)_cache).CreateNavigator(); 464 } 465 } 466 } 467