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