Imported Upstream version 4.0.0~alpha1

Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
Jo Shields
2015-04-07 09:35:12 +01:00
parent 283343f570
commit 3c1f479b9d
22469 changed files with 2931443 additions and 869343 deletions

View File

@@ -0,0 +1,424 @@
//------------------------------------------------------------------------------
// <copyright file="ContentIterators.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Schema;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// Iterate over all child content nodes (this is different from the QIL Content operator, which iterates over content + attributes).
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct ContentIterator {
private XPathNavigator navCurrent;
private bool needFirst;
/// <summary>
/// Initialize the ContentIterator.
/// </summary>
public void Create(XPathNavigator context) {
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
this.needFirst = true;
}
/// <summary>
/// Position the iterator on the next child content node. Return true if such a child exists and
/// set Current property. Otherwise, return false (Current property is undefined).
/// </summary>
public bool MoveNext() {
if (this.needFirst) {
this.needFirst = !this.navCurrent.MoveToFirstChild();
return !this.needFirst;
}
return this.navCurrent.MoveToNext();
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all child elements with a matching name.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct ElementContentIterator {
private string localName, ns;
private XPathNavigator navCurrent;
private bool needFirst;
/// <summary>
/// Initialize the ElementContentIterator.
/// </summary>
public void Create(XPathNavigator context, string localName, string ns) {
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
this.localName = localName;
this.ns = ns;
this.needFirst = true;
}
/// <summary>
/// Position the iterator on the next child element with a matching name. Return true if such a child exists and
/// set Current property. Otherwise, return false (Current property is undefined).
/// </summary>
public bool MoveNext() {
if (this.needFirst) {
this.needFirst = !this.navCurrent.MoveToChild(this.localName, this.ns);
return !this.needFirst;
}
return this.navCurrent.MoveToNext(this.localName, this.ns);
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all child content nodes with a matching node kind.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct NodeKindContentIterator {
private XPathNodeType nodeType;
private XPathNavigator navCurrent;
private bool needFirst;
/// <summary>
/// Initialize the NodeKindContentIterator.
/// </summary>
public void Create(XPathNavigator context, XPathNodeType nodeType) {
Debug.Assert(nodeType != XPathNodeType.Attribute && nodeType != XPathNodeType.Namespace);
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
this.nodeType = nodeType;
this.needFirst = true;
}
/// <summary>
/// Position the iterator on the next child content node with a matching node kind. Return true if such a child
/// exists and set Current property. Otherwise, return false (Current property is undefined).
/// </summary>
public bool MoveNext() {
if (this.needFirst) {
this.needFirst = !this.navCurrent.MoveToChild(this.nodeType);
return !this.needFirst;
}
return this.navCurrent.MoveToNext(this.nodeType);
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all attributes.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct AttributeIterator {
private XPathNavigator navCurrent;
private bool needFirst;
/// <summary>
/// Initialize the AttributeIterator.
/// </summary>
public void Create(XPathNavigator context) {
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
this.needFirst = true;
}
/// <summary>
/// Position the iterator on the attribute. Return true if such a child exists and set Current
/// property. Otherwise, return false (Current property is undefined).
/// </summary>
public bool MoveNext() {
if (this.needFirst) {
this.needFirst = !this.navCurrent.MoveToFirstAttribute();
return !this.needFirst;
}
return this.navCurrent.MoveToNextAttribute();
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all namespace nodes.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct NamespaceIterator {
private XPathNavigator navCurrent;
private XmlNavigatorStack navStack;
/// <summary>
/// Initialize the NamespaceIterator.
/// </summary>
public void Create(XPathNavigator context) {
// Push all of context's in-scope namespaces onto a stack in order to return them in document order
// (MoveToXXXNamespace methods return namespaces in reverse document order)
this.navStack.Reset();
if (context.MoveToFirstNamespace(XPathNamespaceScope.All)) {
do {
// Don't return the default namespace undeclaration
if (context.LocalName.Length != 0 || context.Value.Length != 0)
this.navStack.Push(context.Clone());
}
while (context.MoveToNextNamespace(XPathNamespaceScope.All));
context.MoveToParent();
}
}
/// <summary>
/// Pop the top namespace from the stack and save it as navCurrent. If there are no more namespaces, return false.
/// </summary>
public bool MoveNext() {
if (this.navStack.IsEmpty)
return false;
this.navCurrent = this.navStack.Pop();
return true;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all attribute and child content nodes.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct AttributeContentIterator {
private XPathNavigator navCurrent;
private bool needFirst;
/// <summary>
/// Initialize the AttributeContentIterator.
/// </summary>
public void Create(XPathNavigator context) {
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
this.needFirst = true;
}
/// <summary>
/// Position the iterator on the next child content node with a matching node kind. Return true if such a child
/// exists and set Current property. Otherwise, return false (Current property is undefined).
/// </summary>
public bool MoveNext() {
if (this.needFirst) {
this.needFirst = !XmlNavNeverFilter.MoveToFirstAttributeContent(this.navCurrent);
return !this.needFirst;
}
return XmlNavNeverFilter.MoveToNextAttributeContent(this.navCurrent);
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over child content nodes or following-sibling nodes. Maintain document order by using a stack. Input
/// nodes are assumed to be in document order, but can contain one another (ContentIterator doesn't allow this).
/// </summary>
/// <remarks>
/// 1. Assume that the list I of input nodes is in document order, with no duplicates. There are N nodes in list I.
/// 2. For each node in list I, derive a list of nodes consisting of matching children or following-sibling nodes.
/// Call these lists S(1)...S(N).
/// 3. Let F be the first node in any list S(X), where X >= 1 and X < N
/// 4. There exists exactly one contiguous sequence of lists S(Y)...S(Z), where Y > X and Z <= N, such that the lists
/// S(X+1)...S(N) can be partitioned into these three groups:
/// a. 1st group (S(X+1)...S(Y-1)) -- All nodes in these lists precede F in document order
/// b. 2nd group (S(Y)...S(Z)) -- All nodes in these lists are duplicates of nodes in list S(X)
/// c. 3rd group (> S(Z)) -- All nodes in these lists succeed F in document order
/// 5. Given #4, node F can be returned once all nodes in the 1st group have been returned. Lists S(Y)...S(Z) can be
/// discarded. And only a single node in the 3rd group need be generated in order to guarantee that all nodes in
/// the 1st and 2nd groups have already been generated.
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct ContentMergeIterator {
private XmlNavigatorFilter filter;
private XPathNavigator navCurrent, navNext;
private XmlNavigatorStack navStack;
private IteratorState state;
private enum IteratorState {
NeedCurrent = 0,
HaveCurrentNeedNext,
HaveCurrentNoNext,
HaveCurrentHaveNext,
};
/// <summary>
/// Initialize the ContentMergeIterator (merge multiple sets of content nodes in document order and remove duplicates).
/// </summary>
public void Create(XmlNavigatorFilter filter) {
this.filter = filter;
this.navStack.Reset();
this.state = IteratorState.NeedCurrent;
}
/// <summary>
/// Position this iterator to the next content or sibling node. Return IteratorResult.NoMoreNodes if there are
/// no more content or sibling nodes. Return IteratorResult.NeedInputNode if the next input node needs to be
/// fetched first. Return IteratorResult.HaveCurrent if the Current property is set to the next node in the
/// iteration.
/// </summary>
public IteratorResult MoveNext(XPathNavigator input) {
return MoveNext(input, true);
}
/// <summary>
/// Position this iterator to the next content or sibling node. Return IteratorResult.NoMoreNodes if there are
/// no more content or sibling nodes. Return IteratorResult.NeedInputNode if the next input node needs to be
/// fetched first. Return IteratorResult.HaveCurrent if the Current property is set to the next node in the
/// iteration.
/// </summary>
internal IteratorResult MoveNext(XPathNavigator input, bool isContent) {
switch (this.state) {
case IteratorState.NeedCurrent:
// If there are no more input nodes, then iteration is complete
if (input == null)
return IteratorResult.NoMoreNodes;
// Save the input node as the current node
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, input);
// If matching child or sibling is found, then we have a current node
if (isContent ? this.filter.MoveToContent(this.navCurrent) :
this.filter.MoveToFollowingSibling(this.navCurrent))
this.state = IteratorState.HaveCurrentNeedNext;
return IteratorResult.NeedInputNode;
case IteratorState.HaveCurrentNeedNext:
if (input == null) {
// There are no more input nodes, so enter HaveCurrentNoNext state and return Current
this.state = IteratorState.HaveCurrentNoNext;
return IteratorResult.HaveCurrentNode;
}
// Save the input node as the next node
this.navNext = XmlQueryRuntime.SyncToNavigator(this.navNext, input);
// If matching child or sibling is found,
if (isContent ? this.filter.MoveToContent(this.navNext) :
this.filter.MoveToFollowingSibling(this.navNext)) {
// Then compare position of current and next nodes
this.state = IteratorState.HaveCurrentHaveNext;
return DocOrderMerge();
}
// Input node does not result in matching child or sibling, so get next input node
return IteratorResult.NeedInputNode;
case IteratorState.HaveCurrentNoNext:
case IteratorState.HaveCurrentHaveNext:
// If the current node has no more matching siblings,
if (isContent ? !this.filter.MoveToNextContent(this.navCurrent) :
!this.filter.MoveToFollowingSibling(this.navCurrent)) {
if (this.navStack.IsEmpty) {
if (this.state == IteratorState.HaveCurrentNoNext) {
// No more input nodes, so iteration is complete
return IteratorResult.NoMoreNodes;
}
// Make navNext the new current node and fetch a new navNext
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, this.navNext);
this.state = IteratorState.HaveCurrentNeedNext;
return IteratorResult.NeedInputNode;
}
// Pop new current node from the stack
this.navCurrent = this.navStack.Pop();
}
// If there is no next node, then no need to call DocOrderMerge; just return the current node
if (this.state == IteratorState.HaveCurrentNoNext)
return IteratorResult.HaveCurrentNode;
// Compare positions of current and next nodes
return DocOrderMerge();
}
Debug.Assert(false, "Invalid IteratorState " + this.state);
return IteratorResult.NoMoreNodes;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned IteratorResult.HaveCurrentNode.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
/// <summary>
/// If the context node-set returns a node that is contained in the subtree of the previous node,
/// then returning children of each node in "natural" order may not correspond to document order.
/// Therefore, in order to guarantee document order, keep a stack in order to push the sibling of
/// ancestor nodes. These siblings will not be returned until all of the descendants' children are
/// returned first.
/// </summary>
private IteratorResult DocOrderMerge() {
XmlNodeOrder cmp;
Debug.Assert(this.state == IteratorState.HaveCurrentHaveNext);
// Compare location of navCurrent with navNext
cmp = this.navCurrent.ComparePosition(this.navNext);
// If navCurrent is before navNext in document order,
// If cmp = XmlNodeOrder.Unknown, then navCurrent is before navNext (since input is is doc order)
if (cmp == XmlNodeOrder.Before || cmp == XmlNodeOrder.Unknown) {
// Then navCurrent can be returned (it is guaranteed to be first in document order)
return IteratorResult.HaveCurrentNode;
}
// If navCurrent is after navNext in document order, then delay returning navCurrent
// Otherwise, discard navNext since it is positioned to the same node as navCurrent
if (cmp == XmlNodeOrder.After) {
this.navStack.Push(this.navCurrent);
this.navCurrent = this.navNext;
this.navNext = null;
}
// Need next input node
this.state = IteratorState.HaveCurrentNeedNext;
return IteratorResult.NeedInputNode;
}
}
}

View File

@@ -0,0 +1,240 @@
//------------------------------------------------------------------------------
// <copyright file="DecimalFormatter.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System.Diagnostics;
using System.Globalization;
using System.Text;
namespace System.Xml.Xsl.Runtime {
using Res = System.Xml.Utils.Res;
internal class DecimalFormat {
public NumberFormatInfo info;
public char digit;
public char zeroDigit;
public char patternSeparator;
internal DecimalFormat(NumberFormatInfo info, char digit, char zeroDigit, char patternSeparator) {
this.info = info;
this.digit = digit;
this.zeroDigit = zeroDigit;
this.patternSeparator = patternSeparator;
}
}
internal class DecimalFormatter {
private NumberFormatInfo posFormatInfo;
private NumberFormatInfo negFormatInfo;
private string posFormat;
private string negFormat;
private char zeroDigit;
// These characters have special meaning for CLR and must be escaped
// <spec>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcustomnumericformatstrings.asp</spec>
private const string ClrSpecialChars = "0#.,%\u2030Ee\\'\";";
// This character is used to escape literal (passive) digits '0'..'9'
private const char EscChar = '\a';
public DecimalFormatter(string formatPicture, DecimalFormat decimalFormat) {
Debug.Assert(formatPicture != null && decimalFormat != null);
if (formatPicture.Length == 0) {
throw XsltException.Create(Res.Xslt_InvalidFormat);
}
zeroDigit = decimalFormat.zeroDigit;
posFormatInfo = (NumberFormatInfo)decimalFormat.info.Clone();
StringBuilder temp = new StringBuilder();
bool integer = true;
bool sawPattern = false, sawZeroDigit = false, sawDigit = false, sawDecimalSeparator = false;
bool digitOrZeroDigit = false;
char decimalSeparator = posFormatInfo.NumberDecimalSeparator[0];
char groupSeparator = posFormatInfo.NumberGroupSeparator[0];
char percentSymbol = posFormatInfo.PercentSymbol[0];
char perMilleSymbol = posFormatInfo.PerMilleSymbol[0];
int commaIndex = 0;
int groupingSize = 0;
int decimalIndex = -1;
int lastDigitIndex = -1;
for (int i = 0; i < formatPicture.Length; i++) {
char ch = formatPicture[i];
if (ch == decimalFormat.digit) {
if (sawZeroDigit && integer) {
throw XsltException.Create(Res.Xslt_InvalidFormat1, formatPicture);
}
lastDigitIndex = temp.Length;
sawDigit = digitOrZeroDigit = true;
temp.Append('#');
continue;
}
if (ch == decimalFormat.zeroDigit) {
if (sawDigit && !integer) {
throw XsltException.Create(Res.Xslt_InvalidFormat2, formatPicture);
}
lastDigitIndex = temp.Length;
sawZeroDigit = digitOrZeroDigit = true;
temp.Append('0');
continue;
}
if (ch == decimalFormat.patternSeparator) {
if (!digitOrZeroDigit) {
throw XsltException.Create(Res.Xslt_InvalidFormat8);
}
if (sawPattern) {
throw XsltException.Create(Res.Xslt_InvalidFormat3, formatPicture);
}
sawPattern = true;
if (decimalIndex < 0) {
decimalIndex = lastDigitIndex + 1;
}
groupingSize = RemoveTrailingComma(temp, commaIndex, decimalIndex);
if (groupingSize > 9) {
groupingSize = 0;
}
posFormatInfo.NumberGroupSizes = new int[] { groupingSize };
if (!sawDecimalSeparator) {
posFormatInfo.NumberDecimalDigits = 0;
}
posFormat = temp.ToString();
temp.Length = 0;
decimalIndex = -1;
lastDigitIndex = -1;
commaIndex = 0;
sawDigit = sawZeroDigit = digitOrZeroDigit = false;
sawDecimalSeparator = false;
integer = true;
negFormatInfo = (NumberFormatInfo)decimalFormat.info.Clone();
negFormatInfo.NegativeSign = string.Empty;
continue;
}
if (ch == decimalSeparator) {
if (sawDecimalSeparator) {
throw XsltException.Create(Res.Xslt_InvalidFormat5, formatPicture);
}
decimalIndex = temp.Length;
sawDecimalSeparator = true;
sawDigit = sawZeroDigit = integer = false;
temp.Append('.');
continue;
}
if (ch == groupSeparator) {
commaIndex = temp.Length;
lastDigitIndex = commaIndex;
temp.Append(',');
continue;
}
if (ch == percentSymbol) {
temp.Append('%');
continue;
}
if (ch == perMilleSymbol) {
temp.Append('\u2030');
continue;
}
if (ch == '\'') {
int pos = formatPicture.IndexOf('\'', i + 1);
if (pos < 0) {
pos = formatPicture.Length - 1;
}
temp.Append(formatPicture, i, pos - i + 1);
i = pos;
continue;
}
// Escape literal digits with EscChar, double literal EscChar
if ('0' <= ch && ch <= '9' || ch == EscChar) {
if (decimalFormat.zeroDigit != '0') {
temp.Append(EscChar);
}
}
// Escape characters having special meaning for CLR
if (ClrSpecialChars.IndexOf(ch) >= 0) {
temp.Append('\\');
}
temp.Append(ch);
}
if (!digitOrZeroDigit) {
throw XsltException.Create(Res.Xslt_InvalidFormat8);
}
NumberFormatInfo formatInfo = sawPattern ? negFormatInfo : posFormatInfo;
if (decimalIndex < 0) {
decimalIndex = lastDigitIndex + 1;
}
groupingSize = RemoveTrailingComma(temp, commaIndex, decimalIndex);
if (groupingSize > 9) {
groupingSize = 0;
}
formatInfo.NumberGroupSizes = new int[] { groupingSize };
if (!sawDecimalSeparator) {
formatInfo.NumberDecimalDigits = 0;
}
if (sawPattern) {
negFormat = temp.ToString();
} else {
posFormat = temp.ToString();
}
}
private static int RemoveTrailingComma(StringBuilder builder, int commaIndex, int decimalIndex) {
if (commaIndex > 0 && commaIndex == (decimalIndex - 1)) {
builder.Remove(decimalIndex - 1, 1);
} else if (decimalIndex > commaIndex) {
return decimalIndex - commaIndex - 1;
}
return 0;
}
public string Format(double value) {
NumberFormatInfo formatInfo;
string subPicture;
if (value < 0 && negFormatInfo != null) {
formatInfo = this.negFormatInfo;
subPicture = this.negFormat;
} else {
formatInfo = this.posFormatInfo;
subPicture = this.posFormat;
}
string result = value.ToString(subPicture, formatInfo);
if (this.zeroDigit != '0') {
StringBuilder builder = new StringBuilder(result.Length);
int shift = this.zeroDigit - '0';
for (int i = 0; i < result.Length; i++) {
char ch = result[i];
if ((uint)(ch - '0') <= 9) {
ch += (char)shift;
} else if (ch == EscChar) {
// This is an escaped literal digit or EscChar, thus unescape it. We make use
// of the fact that no extra EscChar could be inserted by value.ToString().
Debug.Assert(i+1 < result.Length);
ch = result[++i];
Debug.Assert('0' <= ch && ch <= '9' || ch == EscChar);
}
builder.Append(ch);
}
result = builder.ToString();
}
return result;
}
public static string Format(double value, string formatPicture, DecimalFormat decimalFormat) {
return new DecimalFormatter(formatPicture, decimalFormat).Format(value);
}
}
}

View File

@@ -0,0 +1,76 @@
//------------------------------------------------------------------------------
// <copyright file="DocumentOrderComparer.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Xml.XPath;
using System.Diagnostics;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// IComparer implementation that orders navigators based on ComparePosition. When ComparePosition returns
/// XmlNodeOrder.Unknown, a stable order between documents is maintained by an ordered list mapping each root node
/// to an ordering index.
/// </summary>
internal class DocumentOrderComparer : IComparer<XPathNavigator> {
private List<XPathNavigator> roots;
/// <summary>
/// Return:
/// -1 if navThis is positioned before navThat
/// 0 if navThis has the same position as navThat
/// 1 if navThis is positioned after navThat
/// </summary>
public int Compare(XPathNavigator navThis, XPathNavigator navThat) {
switch (navThis.ComparePosition(navThat)) {
case XmlNodeOrder.Before: return -1;
case XmlNodeOrder.Same: return 0;
case XmlNodeOrder.After: return 1;
}
// Use this.roots to impose stable ordering
if (this.roots == null)
this.roots = new List<XPathNavigator>();
Debug.Assert(GetDocumentIndex(navThis) != GetDocumentIndex(navThat));
return GetDocumentIndex(navThis) < GetDocumentIndex(navThat) ? -1 : 1;
}
/// <summary>
/// Map navigator's document to a unique index.
/// When consecutive calls are made to GetIndexOfNavigator for navThis and navThat, it is not possible
/// for them to return the same index. navThis compared to navThat is always XmlNodeOrder.Unknown.
/// Therefore, no matter where navThis is inserted in the list, navThat will never be inserted just
/// before navThis, and therefore will never have the same index.
/// </summary>
public int GetDocumentIndex(XPathNavigator nav) {
XPathNavigator navRoot;
// Use this.roots to impose stable ordering
if (this.roots == null)
this.roots = new List<XPathNavigator>();
// Position navigator to root
navRoot = nav.Clone();
navRoot.MoveToRoot();
for (int idx = 0; idx < this.roots.Count; idx++) {
if (navRoot.IsSamePosition(this.roots[idx])) {
// navigator's document was previously mapped to a unique index
return idx;
}
}
// Add navigator to this.roots mapping
this.roots.Add(navRoot);
return this.roots.Count - 1;
}
}
}

View File

@@ -0,0 +1,136 @@
//------------------------------------------------------------------------------
// <copyright file="DodSequenceMerge.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Xml.XPath;
using System.Diagnostics;
using System.Globalization;
using System.ComponentModel;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// Merges several doc-order-distinct sequences into a single doc-order-distinct sequence.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct DodSequenceMerge {
private IList<XPathNavigator> firstSequence;
private List<IEnumerator<XPathNavigator>> sequencesToMerge;
private int nodeCount;
private XmlQueryRuntime runtime;
/// <summary>
/// Initialize this instance of DodSequenceMerge.
/// </summary>
public void Create(XmlQueryRuntime runtime) {
this.firstSequence = null;
this.sequencesToMerge = null;
this.nodeCount = 0;
this.runtime = runtime;
}
/// <summary>
/// Add a new sequence to the list of sequences to merge.
/// </summary>
public void AddSequence(IList<XPathNavigator> sequence) {
// Ignore empty sequences
if (sequence.Count == 0)
return;
if (this.firstSequence == null) {
this.firstSequence = sequence;
}
else {
if (this.sequencesToMerge == null) {
this.sequencesToMerge = new List<IEnumerator<XPathNavigator>>();
MoveAndInsertSequence(this.firstSequence.GetEnumerator());
this.nodeCount = this.firstSequence.Count;
}
MoveAndInsertSequence(sequence.GetEnumerator());
this.nodeCount += sequence.Count;
}
}
/// <summary>
/// Return the fully merged sequence.
/// </summary>
public IList<XPathNavigator> MergeSequences() {
XmlQueryNodeSequence newSequence;
// Zero sequences to merge
if (this.firstSequence == null)
return XmlQueryNodeSequence.Empty;
// One sequence to merge
if (this.sequencesToMerge == null || this.sequencesToMerge.Count <= 1)
return this.firstSequence;
// Two or more sequences to merge
newSequence = new XmlQueryNodeSequence(this.nodeCount);
while (this.sequencesToMerge.Count != 1) {
// Save last item in list in temp variable, and remove it from list
IEnumerator<XPathNavigator> sequence = this.sequencesToMerge[this.sequencesToMerge.Count - 1];
this.sequencesToMerge.RemoveAt(this.sequencesToMerge.Count - 1);
// Add current node to merged sequence
newSequence.Add(sequence.Current);
// Now move to the next node, and re-insert it into the list in reverse document order
MoveAndInsertSequence(sequence);
}
// Add nodes in remaining sequence to end of list
Debug.Assert(this.sequencesToMerge.Count == 1, "While loop should terminate when count == 1");
do {
newSequence.Add(this.sequencesToMerge[0].Current);
}
while (this.sequencesToMerge[0].MoveNext());
return newSequence;
}
/// <summary>
/// Move to the next item in the sequence. If there is no next item, then do not
/// insert the sequence. Otherwise, call InsertSequence.
/// </summary>
private void MoveAndInsertSequence(IEnumerator<XPathNavigator> sequence) {
if (sequence.MoveNext())
InsertSequence(sequence);
}
/// <summary>
/// Insert the specified sequence into the list of sequences to be merged.
/// Insert it in reverse document order with respect to the current nodes in other sequences.
/// </summary>
private void InsertSequence(IEnumerator<XPathNavigator> sequence) {
for (int i = this.sequencesToMerge.Count - 1; i >= 0; i--) {
int cmp = this.runtime.ComparePosition(sequence.Current, this.sequencesToMerge[i].Current);
if (cmp == -1) {
// Insert after current item
this.sequencesToMerge.Insert(i + 1, sequence);
return;
}
else if (cmp == 0) {
// Found duplicate, so skip the duplicate
if (!sequence.MoveNext()) {
// No more nodes, so don't insert anything
return;
}
// Next node must be after current node in document order, so don't need to reset loop
}
}
// Insert at beginning of list
this.sequencesToMerge.Insert(0, sequence);
}
}
}

View File

@@ -0,0 +1,64 @@
//------------------------------------------------------------------------------
// <copyright file="EarlyBoundInfo.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="false">[....]</owner>
//------------------------------------------------------------------------------
using System.Diagnostics;
using System.Reflection;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// This class contains information about early bound function objects.
/// </summary>
internal sealed class EarlyBoundInfo {
private string namespaceUri; // Namespace Uri mapped to these early bound functions
private ConstructorInfo constrInfo; // Constructor for the early bound function object
public EarlyBoundInfo(string namespaceUri, Type ebType) {
Debug.Assert(namespaceUri != null && ebType != null);
// Get the default constructor
this.namespaceUri = namespaceUri;
this.constrInfo = ebType.GetConstructor(Type.EmptyTypes);
Debug.Assert(this.constrInfo != null, "The early bound object type " + ebType.FullName + " must have a public default constructor");
}
/// <summary>
/// Get the Namespace Uri mapped to these early bound functions.
/// </summary>
public string NamespaceUri { get { return this.namespaceUri; } }
/// <summary>
/// Return the Clr Type of the early bound object.
/// </summary>
public Type EarlyBoundType { get { return this.constrInfo.DeclaringType; } }
/// <summary>
/// Create an instance of the early bound object.
/// </summary>
public object CreateObject() { return this.constrInfo.Invoke(new object[] {}); }
/// <summary>
/// Override Equals method so that EarlyBoundInfo to implement value comparison.
/// </summary>
public override bool Equals(object obj) {
EarlyBoundInfo info = obj as EarlyBoundInfo;
if (info == null)
return false;
return this.namespaceUri == info.namespaceUri && this.constrInfo == info.constrInfo;
}
/// <summary>
/// Override GetHashCode since Equals is overriden.
/// </summary>
public override int GetHashCode() {
return this.namespaceUri.GetHashCode();
}
}
}

View File

@@ -0,0 +1,177 @@
//------------------------------------------------------------------------------
// <copyright file="NumberFormatter.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System.Diagnostics;
using System.Text;
namespace System.Xml.Xsl.Runtime {
using Res = System.Xml.Utils.Res;
internal static class CharUtil {
// Checks whether a given character is alphanumeric. Alphanumeric means any character that has
// a Unicode category of Nd (8), Nl (9), No (10), Lu (0), Ll (1), Lt (2), Lm (3) or Lo (4)
// <spec>http://www.w3.org/TR/xslt.html#convert</spec>
public static bool IsAlphaNumeric(char ch) {
int category = (int)char.GetUnicodeCategory(ch);
return category <= 4 || (category <= 10 && category >= 8);
}
// Checks whether a given character has decimal digit value of 1. The decimal digits are characters
// having the Unicode category of Nd (8). NOTE: We do not support Tamil and Ethiopic numbering systems
// having no zeros.
public static bool IsDecimalDigitOne(char ch) {
int category = (int)char.GetUnicodeCategory(--ch);
return category == 8 && char.GetNumericValue(ch) == 0;
}
}
internal enum NumberingSequence {
Nil = -1,
FirstDecimal,
Arabic = FirstDecimal, // 0x0031 -- 1, 2, 3, 4, ...
DArabic, // 0xff11 -- Combines DbChar w/ Arabic
Hindi3, // 0x0967 -- Hindi numbers
Thai2, // 0x0e51 -- Thai numbers
FEDecimal, // 0x4e00 -- FE numbering style (decimal numbers)
KorDbNum1, // 0xc77c -- Korea (decimal)
LastNum = KorDbNum1,
// Alphabetic numbering sequences (do not change order unless you also change _rgnfcToLab's order)
FirstAlpha,
UCLetter = FirstAlpha, // 0x0041 -- A, B, C, D, ...
LCLetter, // 0x0061 -- a, b, c, d, ...
UCRus, // 0x0410 -- Upper case Russian alphabet
LCRus, // 0x0430 -- Lower case Russian alphabet
Thai1, // 0x0e01 -- Thai letters
Hindi1, // 0x0915 -- Hindi vowels
Hindi2, // 0x0905 -- Hindi consonants
Aiueo, // 0xff71 -- Japan numbering style (SbChar)
DAiueo, // 0x30a2 -- Japan - Combines DbChar w/ Aiueo
Iroha, // 0xff72 -- Japan numbering style (SbChar)
DIroha, // 0x30a4 -- Japan - Combines DbChar w/ Iroha// New defines for 97...
DChosung, // 0x3131 -- Korea Chosung (DbChar)
Ganada, // 0xac00 -- Korea
ArabicScript, // 0x0623 -- BIDI AraAlpha for Arabic/Persian/Urdu
LastAlpha = ArabicScript,
// Special numbering sequences (includes peculiar alphabetic and numeric sequences)
FirstSpecial,
UCRoman = FirstSpecial, // 0x0049 -- I, II, III, IV, ...
LCRoman, // 0x0069 -- i, ii, iii, iv, ...
Hebrew, // 0x05d0 -- BIDI Heb1 for Hebrew
DbNum3, // 0x58f1 -- FE numbering style (similar to China2, some different characters)
ChnCmplx, // 0x58f9 -- China (complex, traditional chinese, spell out numbers)
KorDbNum3, // 0xd558 -- Korea (1-99)
Zodiac1, // 0x7532 -- CJK-heavenly-stem (10 numbers)
Zodiac2, // 0x5b50 -- CJK-earthly-branch (12 numbers)
Zodiac3, // 0x7532 -- (Zodiac1 + Zodiac2 Combination)
LastSpecial = Zodiac3,
}
internal class NumberFormatterBase {
protected const int MaxAlphabeticValue = int.MaxValue; // Maximum value that can be represented
private const int MaxAlphabeticLength = 7; // Number of letters needed to represent the maximum value
public static void ConvertToAlphabetic(StringBuilder sb, double val, char firstChar, int totalChars) {
Debug.Assert(1 <= val && val <= MaxAlphabeticValue);
Debug.Assert(Math.Pow(totalChars, MaxAlphabeticLength) >= MaxAlphabeticValue);
char[] letters = new char[MaxAlphabeticLength];
int idx = MaxAlphabeticLength;
int number = (int)val;
while (number > totalChars) {
int quot = --number / totalChars;
letters[--idx] = (char)(firstChar + (number - quot * totalChars));
number = quot;
}
letters[--idx] = (char)(firstChar + --number);
sb.Append(letters, idx, MaxAlphabeticLength - idx);
}
protected const int MaxRomanValue = 32767;
private const string RomanDigitsUC = "IIVIXXLXCCDCM";
private const string RomanDigitsLC = "iivixxlxccdcm";
// RomanDigit = { I IV V IX X XL L XC C CD D CM M }
private static readonly int[] RomanDigitValue = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
public static void ConvertToRoman(StringBuilder sb, double val, bool upperCase) {
Debug.Assert(1 <= val && val <= MaxRomanValue);
int number = (int)val;
string digits = upperCase ? RomanDigitsUC : RomanDigitsLC;
for (int idx = RomanDigitValue.Length; idx-- != 0; ) {
while (number >= RomanDigitValue[idx]) {
number -= RomanDigitValue[idx];
sb.Append(digits, idx, 1 + (idx & 1));
}
}
}
// Most of tables here were taken from MSXML sources and compared with the last
// CSS3 proposal available at http://www.w3.org/TR/2002/WD-css3-lists-20021107/
// MSXML-, CSS3+
// CSS3 inserts two new characters U+3090, U+3091 before U+3092
private const string hiraganaAiueo =
"\u3042\u3044\u3046\u3048\u304a\u304b\u304d\u304f\u3051\u3053" +
"\u3055\u3057\u3059\u305b\u305d\u305f\u3061\u3064\u3066\u3068" +
"\u306a\u306b\u306c\u306d\u306e\u306f\u3072\u3075\u3078\u307b" +
"\u307e\u307f\u3080\u3081\u3082\u3084\u3086\u3088\u3089\u308a" +
"\u308b\u308c\u308d\u308f\u3092\u3093";
// MSXML-, CSS3+
private const string hiraganaIroha =
"\u3044\u308d\u306f\u306b\u307b\u3078\u3068\u3061\u308a\u306c" +
"\u308b\u3092\u308f\u304b\u3088\u305f\u308c\u305d\u3064\u306d" +
"\u306a\u3089\u3080\u3046\u3090\u306e\u304a\u304f\u3084\u307e" +
"\u3051\u3075\u3053\u3048\u3066\u3042\u3055\u304d\u3086\u3081" +
"\u307f\u3057\u3091\u3072\u3082\u305b\u3059";
// MSXML+, CSS3+
// CSS3 inserts two new characters U+30F0, U+30F1 before U+30F2
private const string katakanaAiueo =
"\u30a2\u30a4\u30a6\u30a8\u30aa\u30ab\u30ad\u30af\u30b1\u30b3" +
"\u30b5\u30b7\u30b9\u30bb\u30bd\u30bf\u30c1\u30c4\u30c6\u30c8" +
"\u30ca\u30cb\u30cc\u30cd\u30ce\u30cf\u30d2\u30d5\u30d8\u30db" +
"\u30de\u30df\u30e0\u30e1\u30e2\u30e4\u30e6\u30e8\u30e9\u30ea" +
"\u30eb\u30ec\u30ed\u30ef\u30f2\u30f3";
// MSXML+, CSS3+
// CSS3 removes last U+30F3 character
private const string katakanaIroha =
"\u30a4\u30ed\u30cf\u30cb\u30db\u30d8\u30c8\u30c1\u30ea\u30cc" +
"\u30eb\u30f2\u30ef\u30ab\u30e8\u30bf\u30ec\u30bd\u30c4\u30cd" +
"\u30ca\u30e9\u30e0\u30a6\u30f0\u30ce\u30aa\u30af\u30e4\u30de" +
"\u30b1\u30d5\u30b3\u30a8\u30c6\u30a2\u30b5\u30ad\u30e6\u30e1" +
"\u30df\u30b7\u30f1\u30d2\u30e2\u30bb\u30b9\u30f3";
// MSXML+, CSS3-
private const string katakanaAiueoHw =
"\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78\uff79\uff7a" +
"\uff7b\uff7c\uff7d\uff7e\uff7f\uff80\uff81\uff82\uff83\uff84" +
"\uff85\uff86\uff87\uff88\uff89\uff8a\uff8b\uff8c\uff8d\uff8e" +
"\uff8f\uff90\uff91\uff92\uff93\uff94\uff95\uff96\uff97\uff98" +
"\uff99\uff9a\uff9b\uff9c\uff66\uff9d";
// MSXML+, CSS3-
private const string katakanaIrohaHw =
"\uff72\uff9b\uff8a\uff86\uff8e\uff8d\uff84\uff81\uff98\uff87" +
"\uff99\uff66\uff9c\uff76\uff96\uff80\uff9a\uff7f\uff82\uff88" +
"\uff85\uff97\uff91\uff73\u30f0\uff89\uff75\uff78\uff94\uff8f" +
"\uff79\uff8c\uff7a\uff74\uff83\uff71\uff7b\uff77\uff95\uff92" +
"\uff90\uff7c\u30f1\uff8b\uff93\uff7e\uff7d\uff9d";
// MSXML+, CSS3-
// Unicode 4.0.0 spec: When used to represent numbers in decimal notation, zero
// is represented by U+3007. Otherwise, zero is represented by U+96F6.
private const string cjkIdeographic =
"\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d";
}
}

View File

@@ -0,0 +1,426 @@
//------------------------------------------------------------------------------
// <copyright file="RtfNavigator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
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 {
/// <summary>
/// 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.
/// </summary>
internal abstract class RtfNavigator : XPathNavigator {
//-----------------------------------------------
// RtfNavigator
//-----------------------------------------------
/// <summary>
/// Preserve serialization hints when deep copying.
/// </summary>
public abstract void CopyToWriter(XmlWriter writer);
/// <summary>
/// Discard serialization hints and return a navigator that actually allows navigation.
/// </summary>
public abstract XPathNavigator ToNavigator();
//-----------------------------------------------
// XPathNavigator
//-----------------------------------------------
/// <summary>
/// Get the XPath node type of the current node.
/// </summary>
public override XPathNodeType NodeType {
get { return XPathNodeType.Root; }
}
/// <summary>
/// Get the local name portion of the current node's name.
/// </summary>
public override string LocalName {
get { return string.Empty; }
}
/// <summary>
/// Get the namespace portion of the current node's name.
/// </summary>
public override string NamespaceURI {
get { return string.Empty; }
}
/// <summary>
/// Get the name of the current node.
/// </summary>
public override string Name {
get { return string.Empty; }
}
/// <summary>
/// Get the prefix portion of the current node's name.
/// </summary>
public override string Prefix {
get { return string.Empty; }
}
/// <summary>
/// Return true if this is an element which used a shortcut tag in its Xml 1.0 serialized form.
/// </summary>
public override bool IsEmptyElement {
get { return false; }
}
/// <summary>
/// Return the xml name table which was used to atomize all prefixes, local-names, and
/// namespace uris in the document.
/// </summary>
public override XmlNameTable NameTable {
get { throw new NotSupportedException(); }
}
/// <summary>
/// Position the navigator on the first attribute of the current node and return true. If no attributes
/// can be found, return false.
/// </summary>
public override bool MoveToFirstAttribute() {
throw new NotSupportedException();
}
/// <summary>
/// If positioned on an attribute, move to its next sibling attribute. If no attributes can be found,
/// return false.
/// </summary>
public override bool MoveToNextAttribute() {
throw new NotSupportedException();
}
/// <summary>
/// Position the navigator on the namespace within the specified scope. If no matching namespace
/// can be found, return false.
/// </summary>
public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) {
throw new NotSupportedException();
}
/// <summary>
/// Position the navigator on the next namespace within the specified scope. If no matching namespace
/// can be found, return false.
/// </summary>
public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) {
throw new NotSupportedException();
}
/// <summary>
/// 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.
/// </summary>
public override bool MoveToNext() {
throw new NotSupportedException();
}
/// <summary>
/// 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.
/// </summary>
public override bool MoveToPrevious() {
throw new NotSupportedException();
}
/// <summary>
/// Move to the first content-typed child of the current node. Return false if the current
/// node has no content children.
/// </summary>
public override bool MoveToFirstChild() {
throw new NotSupportedException();
}
/// <summary>
/// Position the navigator on the parent of the current node. If the current node has no parent,
/// return false.
/// </summary>
public override bool MoveToParent() {
throw new NotSupportedException();
}
/// <summary>
/// Position to the navigator to the element whose id is equal to the specified "id" string.
/// </summary>
public override bool MoveToId(string id) {
throw new NotSupportedException();
}
/// <summary>
/// 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.
/// </summary>
public override bool IsSamePosition(XPathNavigator other) {
throw new NotSupportedException();
}
}
/// <summary>
/// This navigator is a cursor over a cache that stores Xslt disable-output-escaping flags.
/// </summary>
internal sealed class RtfTreeNavigator : RtfNavigator {
private XmlEventCache events;
private NavigatorConstructor constr;
private XmlNameTable nameTable;
//-----------------------------------------------
// Constructors
//-----------------------------------------------
/// <summary>
/// Create a new navigator over the specified cache of Xml events.
/// </summary>
public RtfTreeNavigator(XmlEventCache events, XmlNameTable nameTable) {
this.events = events;
this.constr = new NavigatorConstructor();
this.nameTable = nameTable;
}
/// <summary>
/// Copy constructor.
/// </summary>
public RtfTreeNavigator(RtfTreeNavigator that) {
this.events = that.events;
this.constr = that.constr;
this.nameTable = that.nameTable;
}
//-----------------------------------------------
// RtfNavigator
//-----------------------------------------------
/// <summary>
/// Preserve serialization hints when deep copying.
/// </summary>
public override void CopyToWriter(XmlWriter writer) {
this.events.EventsToWriter(writer);
}
/// <summary>
/// Discard serialization hints and return a navigator that actually allows navigation.
/// </summary>
public override XPathNavigator ToNavigator() {
return this.constr.GetNavigator(this.events, this.nameTable);
}
//-----------------------------------------------
// XPathItem
//-----------------------------------------------
/// <summary>
/// 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.
/// </summary>
public override string Value {
get { return this.events.EventsToString(); }
}
//-----------------------------------------------
// XPathNavigator
//-----------------------------------------------
/// <summary>
/// Get the base URI of the Rtf.
/// </summary>
public override string BaseURI {
get { return this.events.BaseUri; }
}
/// <summary>
/// Create a copy of this navigator, positioned to the same node in the tree.
/// </summary>
public override XPathNavigator Clone() {
return new RtfTreeNavigator(this);
}
/// <summary>
/// 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.
/// </summary>
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;
}
}
/// <summary>
/// This RtfNavigator specializes the case of a root node having a single text node child. This is a very common
/// case, such as in <xsl:variable name="foo">bar</xsl:variable>.
/// </summary>
internal sealed class RtfTextNavigator : RtfNavigator {
private string text, baseUri;
private NavigatorConstructor constr;
//-----------------------------------------------
// Constructors
//-----------------------------------------------
/// <summary>
/// Create a new navigator having a text node with value = "text" string.
/// </summary>
public RtfTextNavigator(string text, string baseUri) {
this.text = text;
this.baseUri = baseUri;
this.constr = new NavigatorConstructor();
}
/// <summary>
/// Copy constructor.
/// </summary>
public RtfTextNavigator(RtfTextNavigator that) {
this.text = that.text;
this.baseUri = that.baseUri;
this.constr = that.constr;
}
//-----------------------------------------------
// RtfNavigator
//-----------------------------------------------
/// <summary>
/// Preserve serialization hints when deep copying.
/// </summary>
public override void CopyToWriter(XmlWriter writer) {
writer.WriteString(Value);
}
/// <summary>
/// Discard serialization hints and return a navigator that actually allows navigation.
/// </summary>
public override XPathNavigator ToNavigator() {
return this.constr.GetNavigator(this.text, this.baseUri, new NameTable());
}
//-----------------------------------------------
// XPathItem
//-----------------------------------------------
/// <summary>
/// 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.
/// </summary>
public override string Value {
get { return this.text; }
}
//-----------------------------------------------
// XPathNavigator
//-----------------------------------------------
/// <summary>
/// Get the base URI of the Rtf.
/// </summary>
public override string BaseURI {
get { return this.baseUri; }
}
/// <summary>
/// Create a copy of this navigator, positioned to the same node in the tree.
/// </summary>
public override XPathNavigator Clone() {
return new RtfTextNavigator(this);
}
/// <summary>
/// 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.
/// </summary>
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;
}
}
/// <summary>
/// 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).
/// </summary>
internal sealed class NavigatorConstructor {
object cache;
/// <summary>
/// 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.
/// </summary>
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();
}
/// <summary>
/// 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.
/// </summary>
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();
}
}
}

View File

@@ -0,0 +1,328 @@
//------------------------------------------------------------------------------
// <copyright file="SetIterators.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Schema;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// Set iterators (Union, Intersection, Difference) that use containment to control two nested iterators return
/// one of the following values from MoveNext().
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public enum SetIteratorResult {
NoMoreNodes, // Iteration is complete; there are no more nodes
InitRightIterator, // Initialize right nested iterator
NeedLeftNode, // The next node needs to be fetched from the left nested iterator
NeedRightNode, // The next node needs to be fetched from the right nested iterator
HaveCurrentNode, // This iterator's Current property is set to the next node in the iteration
};
/// <summary>
/// This iterator manages two sets of nodes that are already in document order with no duplicates.
/// Using a merge sort, this operator returns the union of these sets in document order with no duplicates.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct UnionIterator {
private XmlQueryRuntime runtime;
private XPathNavigator navCurr, navOther;
private IteratorState state;
private enum IteratorState {
InitLeft = 0,
NeedLeft,
NeedRight,
LeftIsCurrent,
RightIsCurrent,
};
/// <summary>
/// Create SetIterator.
/// </summary>
public void Create(XmlQueryRuntime runtime) {
this.runtime = runtime;
this.state = IteratorState.InitLeft;
}
/// <summary>
/// Position this iterator to the next node in the union.
/// </summary>
public SetIteratorResult MoveNext(XPathNavigator nestedNavigator) {
switch (this.state) {
case IteratorState.InitLeft:
// Fetched node from left iterator, now get initial node from right iterator
this.navOther = nestedNavigator;
this.state = IteratorState.NeedRight;
return SetIteratorResult.InitRightIterator;
case IteratorState.NeedLeft:
this.navCurr = nestedNavigator;
this.state = IteratorState.LeftIsCurrent;
break;
case IteratorState.NeedRight:
this.navCurr = nestedNavigator;
this.state = IteratorState.RightIsCurrent;
break;
case IteratorState.LeftIsCurrent:
// Just returned left node as current, so get new left
this.state = IteratorState.NeedLeft;
return SetIteratorResult.NeedLeftNode;
case IteratorState.RightIsCurrent:
// Just returned right node as current, so get new right
this.state = IteratorState.NeedRight;
return SetIteratorResult.NeedRightNode;
}
// Merge left and right nodes
if (this.navCurr == null) {
// If both navCurr and navOther are null, then iteration is complete
if (this.navOther == null)
return SetIteratorResult.NoMoreNodes;
Swap();
}
else if (this.navOther != null) {
int order = this.runtime.ComparePosition(this.navOther, this.navCurr);
// If navCurr is positioned to same node as navOther,
if (order == 0) {
// Skip navCurr, since it is a duplicate
if (this.state == IteratorState.LeftIsCurrent) {
this.state = IteratorState.NeedLeft;
return SetIteratorResult.NeedLeftNode;
}
this.state = IteratorState.NeedRight;
return SetIteratorResult.NeedRightNode;
}
// If navOther is before navCurr in document order, then swap navCurr with navOther
if (order < 0)
Swap();
}
// Return navCurr
return SetIteratorResult.HaveCurrentNode;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned -1.
/// </summary>
public XPathNavigator Current {
get { return this.navCurr; }
}
/// <summary>
/// Swap navCurr with navOther and invert state to reflect the change.
/// </summary>
private void Swap() {
XPathNavigator navTemp = this.navCurr;
this.navCurr = this.navOther;
this.navOther = navTemp;
if (this.state == IteratorState.LeftIsCurrent)
this.state = IteratorState.RightIsCurrent;
else
this.state = IteratorState.LeftIsCurrent;
}
}
/// <summary>
/// This iterator manages two sets of nodes that are already in document order with no duplicates.
/// This iterator returns the intersection of these sets in document order with no duplicates.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct IntersectIterator {
private XmlQueryRuntime runtime;
private XPathNavigator navLeft, navRight;
private IteratorState state;
private enum IteratorState {
InitLeft = 0,
NeedLeft,
NeedRight,
NeedLeftAndRight,
HaveCurrent,
};
/// <summary>
/// Create IntersectIterator.
/// </summary>
public void Create(XmlQueryRuntime runtime) {
this.runtime = runtime;
this.state = IteratorState.InitLeft;
}
/// <summary>
/// Position this iterator to the next node in the union.
/// </summary>
public SetIteratorResult MoveNext(XPathNavigator nestedNavigator) {
int order;
switch (this.state) {
case IteratorState.InitLeft:
// Fetched node from left iterator, now get initial node from right iterator
this.navLeft = nestedNavigator;
this.state = IteratorState.NeedRight;
return SetIteratorResult.InitRightIterator;
case IteratorState.NeedLeft:
this.navLeft = nestedNavigator;
break;
case IteratorState.NeedRight:
this.navRight = nestedNavigator;
break;
case IteratorState.NeedLeftAndRight:
// After fetching left node, still need right node
this.navLeft = nestedNavigator;
this.state = IteratorState.NeedRight;
return SetIteratorResult.NeedRightNode;
case IteratorState.HaveCurrent:
// Just returned left node as current, so fetch new left and right nodes
Debug.Assert(nestedNavigator == null, "null is passed to MoveNext after IteratorState.HaveCurrent has been returned.");
this.state = IteratorState.NeedLeftAndRight;
return SetIteratorResult.NeedLeftNode;
}
if (this.navLeft == null || this.navRight == null) {
// No more nodes from either left or right iterator (or both), so iteration is complete
return SetIteratorResult.NoMoreNodes;
}
// Intersect left and right sets
order = this.runtime.ComparePosition(this.navLeft, this.navRight);
if (order < 0) {
// If navLeft is positioned to a node that is before navRight, skip left node
this.state = IteratorState.NeedLeft;
return SetIteratorResult.NeedLeftNode;
}
else if (order > 0) {
// If navLeft is positioned to a node that is after navRight, so skip right node
this.state = IteratorState.NeedRight;
return SetIteratorResult.NeedRightNode;
}
// Otherwise, navLeft is positioned to the same node as navRight, so found one item in the intersection
this.state = IteratorState.HaveCurrent;
return SetIteratorResult.HaveCurrentNode;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned -1.
/// </summary>
public XPathNavigator Current {
get { return this.navLeft; }
}
}
/// <summary>
/// This iterator manages two sets of nodes that are already in document order with no duplicates.
/// This iterator returns the difference of these sets (Left - Right) in document order with no duplicates.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct DifferenceIterator {
private XmlQueryRuntime runtime;
private XPathNavigator navLeft, navRight;
private IteratorState state;
private enum IteratorState {
InitLeft = 0,
NeedLeft,
NeedRight,
NeedLeftAndRight,
HaveCurrent,
};
/// <summary>
/// Create DifferenceIterator.
/// </summary>
public void Create(XmlQueryRuntime runtime) {
this.runtime = runtime;
this.state = IteratorState.InitLeft;
}
/// <summary>
/// Position this iterator to the next node in the union.
/// </summary>
public SetIteratorResult MoveNext(XPathNavigator nestedNavigator) {
switch (this.state) {
case IteratorState.InitLeft:
// Fetched node from left iterator, now get initial node from right iterator
this.navLeft = nestedNavigator;
this.state = IteratorState.NeedRight;
return SetIteratorResult.InitRightIterator;
case IteratorState.NeedLeft:
this.navLeft = nestedNavigator;
break;
case IteratorState.NeedRight:
this.navRight = nestedNavigator;
break;
case IteratorState.NeedLeftAndRight:
// After fetching left node, still need right node
this.navLeft = nestedNavigator;
this.state = IteratorState.NeedRight;
return SetIteratorResult.NeedRightNode;
case IteratorState.HaveCurrent:
// Just returned left node as current, so fetch new left node
Debug.Assert(nestedNavigator == null, "null is passed to MoveNext after IteratorState.HaveCurrent has been returned.");
this.state = IteratorState.NeedLeft;
return SetIteratorResult.NeedLeftNode;
}
if (this.navLeft == null) {
// If navLeft is null, then difference operation is complete
return SetIteratorResult.NoMoreNodes;
}
else if (this.navRight != null) {
int order = this.runtime.ComparePosition(this.navLeft, this.navRight);
// If navLeft is positioned to same node as navRight,
if (order == 0) {
// Skip navLeft and navRight
this.state = IteratorState.NeedLeftAndRight;
return SetIteratorResult.NeedLeftNode;
}
// If navLeft is after navRight in document order, then skip navRight
if (order > 0) {
this.state = IteratorState.NeedRight;
return SetIteratorResult.NeedRightNode;
}
}
// Return navLeft
this.state = IteratorState.HaveCurrent;
return SetIteratorResult.HaveCurrentNode;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned -1.
/// </summary>
public XPathNavigator Current {
get { return this.navLeft; }
}
}
}

View File

@@ -0,0 +1,184 @@
//------------------------------------------------------------------------------
// <copyright file="SiblingIterators.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Schema;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// Iterate over all following-sibling content nodes.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct FollowingSiblingIterator {
private XmlNavigatorFilter filter;
private XPathNavigator navCurrent;
/// <summary>
/// Initialize the FollowingSiblingIterator.
/// </summary>
public void Create(XPathNavigator context, XmlNavigatorFilter filter) {
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
this.filter = filter;
}
/// <summary>
/// Position the iterator on the next following-sibling node. Return true if such a node exists and
/// set Current property. Otherwise, return false (Current property is undefined).
/// </summary>
public bool MoveNext() {
return this.filter.MoveToFollowingSibling(this.navCurrent);
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over child following-sibling nodes. This is a simple variation on the ContentMergeIterator, so use containment
/// to reuse its code (can't use inheritance with structures).
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct FollowingSiblingMergeIterator {
private ContentMergeIterator wrapped;
/// <summary>
/// Initialize the FollowingSiblingMergeIterator.
/// </summary>
public void Create(XmlNavigatorFilter filter) {
this.wrapped.Create(filter);
}
/// <summary>
/// Position this iterator to the next content or sibling node. Return IteratorResult.NoMoreNodes if there are
/// no more content or sibling nodes. Return IteratorResult.NeedInputNode if the next input node needs to be
/// fetched first. Return IteratorResult.HaveCurrent if the Current property is set to the next node in the
/// iteration.
/// </summary>
public IteratorResult MoveNext(XPathNavigator navigator) {
return this.wrapped.MoveNext(navigator, false);
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned IteratorResult.HaveCurrent.
/// </summary>
public XPathNavigator Current {
get { return this.wrapped.Current; }
}
}
/// <summary>
/// Iterate over all preceding nodes according to XPath preceding axis rules, returning nodes in reverse
/// document order.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct PrecedingSiblingIterator {
private XmlNavigatorFilter filter;
private XPathNavigator navCurrent;
/// <summary>
/// Initialize the PrecedingSiblingIterator.
/// </summary>
public void Create(XPathNavigator context, XmlNavigatorFilter filter) {
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
this.filter = filter;
}
/// <summary>
/// Return true if the Current property is set to the next Preceding node in reverse document order.
/// </summary>
public bool MoveNext() {
return this.filter.MoveToPreviousSibling(this.navCurrent);
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
/// <summary>
/// Iterate over all preceding-sibling content nodes in document order.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct PrecedingSiblingDocOrderIterator {
private XmlNavigatorFilter filter;
private XPathNavigator navCurrent, navEnd;
private bool needFirst, useCompPos;
/// <summary>
/// Initialize the PrecedingSiblingDocOrderIterator.
/// </summary>
public void Create(XPathNavigator context, XmlNavigatorFilter filter) {
this.filter = filter;
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
this.navEnd = XmlQueryRuntime.SyncToNavigator(this.navEnd, context);
this.needFirst = true;
// If the context node will be filtered out, then use ComparePosition to
// determine when the context node has been passed by. Otherwise, IsSamePosition
// is sufficient to determine when the context node has been reached.
this.useCompPos = this.filter.IsFiltered(context);
}
/// <summary>
/// Position the iterator on the next preceding-sibling node. Return true if such a node exists and
/// set Current property. Otherwise, return false (Current property is undefined).
/// </summary>
public bool MoveNext() {
if (this.needFirst) {
// Get first matching preceding-sibling node
if (!this.navCurrent.MoveToParent())
return false;
if (!this.filter.MoveToContent(this.navCurrent))
return false;
this.needFirst = false;
}
else {
// Get next matching preceding-sibling node
if (!this.filter.MoveToFollowingSibling(this.navCurrent))
return false;
}
// Accept matching sibling only if it precedes navEnd in document order
if (this.useCompPos)
return (this.navCurrent.ComparePosition(this.navEnd) == XmlNodeOrder.Before);
if (this.navCurrent.IsSamePosition(this.navEnd)) {
// Found the original context node, so iteration is complete. If MoveNext
// is called again, use ComparePosition so that false will continue to be
// returned.
this.useCompPos = true;
return false;
}
return true;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
}

View File

@@ -0,0 +1,103 @@
//------------------------------------------------------------------------------
// <copyright file="StringConcat.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ComponentModel;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// Efficiently concatenates strings when the number of string is not known beforehand, and
/// yet it is common for only one string to be concatenated. StringBuilder is not good for
/// this purpose, since it *always* allocates objects, even if only one string is appended.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct StringConcat {
private string s1, s2, s3, s4;
private string delimiter;
private List<string> strList;
int idxStr;
/// <summary>
/// Clear the result string.
/// </summary>
public void Clear() {
this.idxStr = 0;
this.delimiter = null;
}
/// <summary>
/// Gets or sets the string that delimits concatenated strings.
/// </summary>
public string Delimiter {
get { return this.delimiter; }
set { this.delimiter = value; }
}
/// <summary>
/// Return the number of concatenated strings, including delimiters.
/// </summary>
internal int Count {
get { return this.idxStr; }
}
/// <summary>
/// Concatenate a new string to the result.
/// </summary>
public void Concat(string value) {
Debug.Assert(value != null);
if (this.delimiter != null && this.idxStr != 0) {
// Add delimiter
ConcatNoDelimiter(this.delimiter);
}
ConcatNoDelimiter(value);
}
/// <summary>
/// Get the result string.
/// </summary>
public string GetResult() {
switch (this.idxStr) {
case 0: return string.Empty;
case 1: return this.s1;
case 2: return string.Concat(this.s1, this.s2);
case 3: return string.Concat(this.s1, this.s2, this.s3);
case 4: return string.Concat(this.s1, this.s2, this.s3, this.s4);
}
return string.Concat(this.strList.ToArray());
}
/// <summary>
/// Concatenate a new string to the result without adding a delimiter.
/// </summary>
internal void ConcatNoDelimiter(string s) {
switch (this.idxStr) {
case 0: this.s1 = s; break;
case 1: this.s2 = s; break;
case 2: this.s3 = s; break;
case 3: this.s4 = s; break;
case 4:
// Calling Clear() is expensive, allocate a new List instead
int capacity = (this.strList == null) ? 8 : this.strList.Count;
List<string> strList = this.strList = new List<string>(capacity);
strList.Add(this.s1);
strList.Add(this.s2);
strList.Add(this.s3);
strList.Add(this.s4);
goto default;
default:
this.strList.Add(s);
break;
}
this.idxStr++;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,166 @@
//------------------------------------------------------------------------------
// <copyright file="WhitespaceRuleLookup.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Xml;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using MS.Internal.Xml;
using System.Xml.Xsl.Qil;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// This class keeps a list of whitespace rules in order to determine whether whitespace children of particular
/// elements should be stripped.
/// </summary>
internal class WhitespaceRuleLookup {
private Hashtable qnames;
private ArrayList wildcards;
private InternalWhitespaceRule ruleTemp;
private XmlNameTable nameTable;
public WhitespaceRuleLookup() {
this.qnames = new Hashtable();
this.wildcards = new ArrayList();
}
/// <summary>
/// Create a new lookup internal class from the specified WhitespaceRules.
/// </summary>
public WhitespaceRuleLookup(IList<WhitespaceRule> rules) : this() {
WhitespaceRule rule;
InternalWhitespaceRule ruleInternal;
Debug.Assert(rules != null);
for (int i = rules.Count - 1; i >= 0; i--) {
// Make a copy of each rule
rule = rules[i];
ruleInternal = new InternalWhitespaceRule(rule.LocalName, rule.NamespaceName, rule.PreserveSpace, -i);
if (rule.LocalName == null || rule.NamespaceName == null) {
// Wildcard, so add to wildcards array
this.wildcards.Add(ruleInternal);
}
else {
// Exact name, so add to hashtable
this.qnames[ruleInternal] = ruleInternal;
}
}
// Create a temporary (not thread-safe) InternalWhitespaceRule used for lookups
this.ruleTemp = new InternalWhitespaceRule();
}
/// <summary>
/// Atomize all names contained within the whitespace rules with respect to "nameTable".
/// </summary>
public void Atomize(XmlNameTable nameTable) {
// If names are already atomized with respect to "nameTable", no need to do it again
if (nameTable != this.nameTable) {
this.nameTable = nameTable;
foreach (InternalWhitespaceRule rule in this.qnames.Values)
rule.Atomize(nameTable);
foreach (InternalWhitespaceRule rule in this.wildcards)
rule.Atomize(nameTable);
}
}
/// <summary>
/// Return true if elements of the specified name should have whitespace children stripped.
/// NOTE: This method is not thread-safe. Different threads should create their own copy of the
/// WhitespaceRuleLookup object. This allows all names to be atomized according to a private NameTable.
/// </summary>
public bool ShouldStripSpace(string localName, string namespaceName) {
InternalWhitespaceRule qnameRule, wildcardRule;
Debug.Assert(this.nameTable != null && this.ruleTemp != null);
Debug.Assert(localName != null && (object) this.nameTable.Get(localName) == (object) localName);
Debug.Assert(namespaceName != null && (object) this.nameTable.Get(namespaceName) == (object) namespaceName);
this.ruleTemp.Init(localName, namespaceName, false, 0);
// Lookup name in qnames table
// If found, the name will be stripped unless there is a preserve wildcard with higher priority
qnameRule = this.qnames[this.ruleTemp] as InternalWhitespaceRule;
for (int pos = this.wildcards.Count; pos-- != 0;) {
wildcardRule = this.wildcards[pos] as InternalWhitespaceRule;
if (qnameRule != null) {
// If qname priority is greater than any subsequent wildcard's priority, then we're done
if (qnameRule.Priority > wildcardRule.Priority)
return !qnameRule.PreserveSpace;
// Don't bother to consider wildcards with the same PreserveSpace flag
if (qnameRule.PreserveSpace == wildcardRule.PreserveSpace)
continue;
}
if (wildcardRule.LocalName == null || (object) wildcardRule.LocalName == (object) localName) {
if (wildcardRule.NamespaceName == null || (object) wildcardRule.NamespaceName == (object) namespaceName) {
// Found wildcard match, so we're done (since wildcards are in priority order)
return !wildcardRule.PreserveSpace;
}
}
}
return (qnameRule != null && !qnameRule.PreserveSpace);
}
private class InternalWhitespaceRule : WhitespaceRule {
private int priority; // Relative priority of this test
private int hashCode; // Cached hashcode
public InternalWhitespaceRule() {
}
public InternalWhitespaceRule(string localName, string namespaceName, bool preserveSpace, int priority) {
Init(localName, namespaceName, preserveSpace, priority);
}
public void Init(string localName, string namespaceName, bool preserveSpace, int priority) {
base.Init(localName, namespaceName, preserveSpace);
this.priority = priority;
if (localName != null && namespaceName != null) {
this.hashCode = localName.GetHashCode();
}
}
public void Atomize(XmlNameTable nameTable) {
if (LocalName != null)
LocalName = nameTable.Add(LocalName);
if (NamespaceName != null)
NamespaceName = nameTable.Add(NamespaceName);
}
public int Priority {
get { return this.priority; }
}
public override int GetHashCode() {
return this.hashCode;
}
public override bool Equals(object obj) {
Debug.Assert(obj is InternalWhitespaceRule);
InternalWhitespaceRule that = obj as InternalWhitespaceRule;
Debug.Assert(LocalName != null && that.LocalName != null);
Debug.Assert(NamespaceName != null && that.NamespaceName != null);
// string == operator compares object references first and if they are not the same compares contents
// of the compared strings. As a result we do not have to cast strings to objects to force reference
// comparison for atomized LocalNames and NamespaceNames.
return LocalName == that.LocalName && NamespaceName == that.NamespaceName;
}
}
}
}

View File

@@ -0,0 +1,145 @@
//------------------------------------------------------------------------------
// <copyright file="WhitespaceRuleReader.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Diagnostics;
using MS.Internal.Xml;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// </summary>
internal class WhitespaceRuleReader : XmlWrappingReader {
private WhitespaceRuleLookup wsRules;
private BitStack stkStrip;
private bool shouldStrip, preserveAdjacent;
private string val;
private XmlCharType xmlCharType = XmlCharType.Instance;
static public XmlReader CreateReader(XmlReader baseReader, WhitespaceRuleLookup wsRules) {
if (wsRules == null) {
return baseReader; // There is no rules to process
}
XmlReaderSettings readerSettings = baseReader.Settings;
if (readerSettings != null) {
if (readerSettings.IgnoreWhitespace) {
return baseReader; // V2 XmlReader that strips all WS
}
} else {
XmlTextReader txtReader = baseReader as XmlTextReader;
if (txtReader != null && txtReader.WhitespaceHandling == WhitespaceHandling.None) {
return baseReader; // V1 XmlTextReader that strips all WS
}
XmlTextReaderImpl txtReaderImpl = baseReader as XmlTextReaderImpl;
if (txtReaderImpl != null && txtReaderImpl.WhitespaceHandling == WhitespaceHandling.None) {
return baseReader; // XmlTextReaderImpl that strips all WS
}
}
return new WhitespaceRuleReader(baseReader, wsRules);
}
private WhitespaceRuleReader(XmlReader baseReader, WhitespaceRuleLookup wsRules) : base(baseReader) {
Debug.Assert(wsRules != null);
this.val = null;
this.stkStrip = new BitStack();
this.shouldStrip = false;
this.preserveAdjacent = false;
this.wsRules = wsRules;
this.wsRules.Atomize(baseReader.NameTable);
}
/// <summary>
/// Override Value in order to possibly prepend extra whitespace.
/// </summary>
public override string Value {
get { return (this.val == null) ? base.Value : this.val; }
}
/// <summary>
/// Override Read in order to search for strippable whitespace, to concatenate adjacent text nodes, and to
/// resolve entities.
/// </summary>
public override bool Read() {
XmlCharType xmlCharType = XmlCharType.Instance;
string ws = null;
// Clear text value
this.val = null;
while (base.Read()) {
switch (base.NodeType) {
case XmlNodeType.Element:
// Push boolean indicating whether whitespace children of this element should be stripped
if (!base.IsEmptyElement) {
this.stkStrip.PushBit(this.shouldStrip);
// Strip if rules say we should and we're not within the scope of xml:space="preserve"
this.shouldStrip = wsRules.ShouldStripSpace(base.LocalName, base.NamespaceURI) && (base.XmlSpace != XmlSpace.Preserve);
}
break;
case XmlNodeType.EndElement:
// Restore parent shouldStrip setting
this.shouldStrip = this.stkStrip.PopBit();
break;
case XmlNodeType.Text:
case XmlNodeType.CDATA:
// If preserving adjacent text, don't perform any further checks
if (this.preserveAdjacent)
return true;
if (this.shouldStrip) {
// Reader may report whitespace as Text or CDATA
if (xmlCharType.IsOnlyWhitespace(base.Value))
goto case XmlNodeType.Whitespace;
// If whitespace was cached, then prepend it to text or CDATA value
if (ws != null)
this.val = string.Concat(ws, base.Value);
// Preserve adjacent whitespace
this.preserveAdjacent = true;
return true;
}
break;
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
// If preserving adjacent text, don't perform any further checks
if (this.preserveAdjacent)
return true;
if (this.shouldStrip) {
// Save whitespace until it can be determined whether it will be stripped
if (ws == null)
ws = base.Value;
else
ws = string.Concat(ws, base.Value);
// Read next event
continue;
}
break;
case XmlNodeType.EndEntity:
// Read next event
continue;
}
// No longer preserve adjacent space
this.preserveAdjacent = false;
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,228 @@
//------------------------------------------------------------------------------
// <copyright file="XmlAggregates.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Xml;
using System.Diagnostics;
using System.ComponentModel;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// Computes aggregates over a sequence of Int32 values.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct Int32Aggregator {
private int result;
private int cnt;
public void Create() {
this.cnt = 0;
}
public void Sum(int value) {
if (this.cnt == 0) {
this.result = value;
this.cnt = 1;
}
else {
this.result += value;
}
}
public void Average(int value) {
if (this.cnt == 0)
this.result = value;
else
this.result += value;
this.cnt++;
}
public void Minimum(int value) {
if (this.cnt == 0 || value < this.result)
this.result = value;
this.cnt = 1;
}
public void Maximum(int value) {
if (this.cnt == 0 || value > this.result)
this.result = value;
this.cnt = 1;
}
public int SumResult { get { return this.result; } }
public int AverageResult { get { return this.result / this.cnt; } }
public int MinimumResult { get { return this.result; } }
public int MaximumResult { get { return this.result; } }
public bool IsEmpty { get { return this.cnt == 0; } }
}
/// <summary>
/// Computes aggregates over a sequence of Int64 values.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct Int64Aggregator {
private long result;
private int cnt;
public void Create() {
this.cnt = 0;
}
public void Sum(long value) {
if (this.cnt == 0) {
this.result = value;
this.cnt = 1;
}
else {
this.result += value;
}
}
public void Average(long value) {
if (this.cnt == 0)
this.result = value;
else
this.result += value;
this.cnt++;
}
public void Minimum(long value) {
if (this.cnt == 0 || value < this.result)
this.result = value;
this.cnt = 1;
}
public void Maximum(long value) {
if (this.cnt == 0 || value > this.result)
this.result = value;
this.cnt = 1;
}
public long SumResult { get { return this.result; } }
public long AverageResult { get { return this.result / (long) this.cnt; } }
public long MinimumResult { get { return this.result; } }
public long MaximumResult { get { return this.result; } }
public bool IsEmpty { get { return this.cnt == 0; } }
}
/// <summary>
/// Computes aggregates over a sequence of Decimal values.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct DecimalAggregator {
private decimal result;
private int cnt;
public void Create() {
this.cnt = 0;
}
public void Sum(decimal value) {
if (this.cnt == 0) {
this.result = value;
this.cnt = 1;
}
else {
this.result += value;
}
}
public void Average(decimal value) {
if (this.cnt == 0)
this.result = value;
else
this.result += value;
this.cnt++;
}
public void Minimum(decimal value) {
if (this.cnt == 0 || value < this.result)
this.result = value;
this.cnt = 1;
}
public void Maximum(decimal value) {
if (this.cnt == 0 || value > this.result)
this.result = value;
this.cnt = 1;
}
public decimal SumResult { get { return this.result; } }
public decimal AverageResult { get { return this.result / (decimal) this.cnt; } }
public decimal MinimumResult { get { return this.result; } }
public decimal MaximumResult { get { return this.result; } }
public bool IsEmpty { get { return this.cnt == 0; } }
}
/// <summary>
/// Computes aggregates over a sequence of Double values.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct DoubleAggregator {
private double result;
private int cnt;
public void Create() {
this.cnt = 0;
}
public void Sum(double value) {
if (this.cnt == 0) {
this.result = value;
this.cnt = 1;
}
else {
this.result += value;
}
}
public void Average(double value) {
if (this.cnt == 0)
this.result = value;
else
this.result += value;
this.cnt++;
}
public void Minimum(double value) {
if (this.cnt == 0 || value < this.result || double.IsNaN(value))
this.result = value;
this.cnt = 1;
}
public void Maximum(double value) {
if (this.cnt == 0 || value > this.result || double.IsNaN(value))
this.result = value;
this.cnt = 1;
}
public double SumResult { get { return this.result; } }
public double AverageResult { get { return this.result / (double) this.cnt; } }
public double MinimumResult { get { return this.result; } }
public double MaximumResult { get { return this.result; } }
public bool IsEmpty { get { return this.cnt == 0; } }
}
}

View File

@@ -0,0 +1,334 @@
//------------------------------------------------------------------------------
// <copyright file="XmlAttributeCache.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
namespace System.Xml.Xsl.Runtime {
using System;
using System.Diagnostics;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Schema;
/// <summary>
/// This writer supports only writer methods which write attributes. Attributes are stored in a
/// data structure until StartElementContent() is called, at which time the attributes are flushed
/// to the wrapped writer. In the case of duplicate attributes, the last attribute's value is used.
/// </summary>
internal sealed class XmlAttributeCache : XmlRawWriter, IRemovableWriter {
private XmlRawWriter wrapped;
private OnRemoveWriter onRemove; // Event handler that is called when cached attributes are flushed to wrapped writer
private AttrNameVal[] arrAttrs; // List of cached attribute names and value parts
private int numEntries; // Number of attributes in the cache
private int idxLastName; // The entry containing the name of the last attribute to be cached
private int hashCodeUnion; // Set of hash bits that can quickly guarantee a name is not a duplicate
/// <summary>
/// Initialize the cache. Use this method instead of a constructor in order to reuse the cache.
/// </summary>
public void Init(XmlRawWriter wrapped) {
SetWrappedWriter(wrapped);
// Clear attribute list
this.numEntries = 0;
this.idxLastName = 0;
this.hashCodeUnion = 0;
}
/// <summary>
/// Return the number of cached attributes.
/// </summary>
public int Count {
get { return this.numEntries; }
}
//-----------------------------------------------
// IRemovableWriter interface
//-----------------------------------------------
/// <summary>
/// This writer will raise this event once cached attributes have been flushed in order to signal that the cache
/// no longer needs to be part of the pipeline.
/// </summary>
public OnRemoveWriter OnRemoveWriterEvent {
get { return this.onRemove; }
set { this.onRemove = value; }
}
/// <summary>
/// The wrapped writer will callback on this method if it wishes to remove itself from the pipeline.
/// </summary>
private void SetWrappedWriter(XmlRawWriter writer) {
// If new writer might remove itself from pipeline, have it callback on this method when its ready to go
IRemovableWriter removable = writer as IRemovableWriter;
if (removable != null)
removable.OnRemoveWriterEvent = SetWrappedWriter;
this.wrapped = writer;
}
//-----------------------------------------------
// XmlWriter interface
//-----------------------------------------------
/// <summary>
/// Add an attribute to the cache. If an attribute if the same name already exists, replace it.
/// </summary>
public override void WriteStartAttribute(string prefix, string localName, string ns) {
int hashCode;
int idx = 0;
Debug.Assert(localName != null && localName.Length != 0 && prefix != null && ns != null);
// Compute hashcode based on first letter of the localName
hashCode = (1 << ((int) localName[0] & 31));
// If the hashcode is not in the union, then name will not be found by a scan
if ((this.hashCodeUnion & hashCode) != 0) {
// The name may or may not be present, so scan for it
Debug.Assert(this.numEntries != 0);
do {
if (this.arrAttrs[idx].IsDuplicate(localName, ns, hashCode))
break;
// Next attribute name
idx = this.arrAttrs[idx].NextNameIndex;
}
while (idx != 0);
}
else {
// Insert hashcode into union
this.hashCodeUnion |= hashCode;
}
// Insert new attribute; link attribute names together in a list
EnsureAttributeCache();
if (this.numEntries != 0)
this.arrAttrs[this.idxLastName].NextNameIndex = this.numEntries;
this.idxLastName = this.numEntries++;
this.arrAttrs[this.idxLastName].Init(prefix, localName, ns, hashCode);
}
/// <summary>
/// No-op.
/// </summary>
public override void WriteEndAttribute() {
}
/// <summary>
/// Pass through namespaces to underlying writer. If any attributes have been cached, flush them.
/// </summary>
internal override void WriteNamespaceDeclaration(string prefix, string ns) {
FlushAttributes();
this.wrapped.WriteNamespaceDeclaration(prefix, ns);
}
/// <summary>
/// Add a block of text to the cache. This text block makes up some or all of the untyped string
/// value of the current attribute.
/// </summary>
public override void WriteString(string text) {
Debug.Assert(text != null);
Debug.Assert(this.arrAttrs != null && this.numEntries != 0);
EnsureAttributeCache();
this.arrAttrs[this.numEntries++].Init(text);
}
/// <summary>
/// All other WriteValue methods are implemented by XmlWriter to delegate to WriteValue(object) or WriteValue(string), so
/// only these two methods need to be implemented.
/// </summary>
public override void WriteValue(object value) {
Debug.Assert(value is XmlAtomicValue, "value should always be an XmlAtomicValue, as XmlAttributeCache is only used by XmlQueryOutput");
Debug.Assert(this.arrAttrs != null && this.numEntries != 0);
EnsureAttributeCache();
this.arrAttrs[this.numEntries++].Init((XmlAtomicValue) value);
}
public override void WriteValue(string value) {
WriteValue(value);
}
/// <summary>
/// Send cached, non-overriden attributes to the specified writer. Calling this method has
/// the side effect of clearing the attribute cache.
/// </summary>
internal override void StartElementContent() {
FlushAttributes();
// Call StartElementContent on wrapped writer
this.wrapped.StartElementContent();
}
public override void WriteStartElement(string prefix, string localName, string ns) {
Debug.Assert(false, "Should never be called on XmlAttributeCache.");
}
internal override void WriteEndElement(string prefix, string localName, string ns) {
Debug.Assert(false, "Should never be called on XmlAttributeCache.");
}
public override void WriteComment(string text) {
Debug.Assert(false, "Should never be called on XmlAttributeCache.");
}
public override void WriteProcessingInstruction(string name, string text) {
Debug.Assert(false, "Should never be called on XmlAttributeCache.");
}
public override void WriteEntityRef(string name) {
Debug.Assert(false, "Should never be called on XmlAttributeCache.");
}
/// <summary>
/// Forward call to wrapped writer.
/// </summary>
public override void Close() {
this.wrapped.Close();
}
/// <summary>
/// Forward call to wrapped writer.
/// </summary>
public override void Flush() {
this.wrapped.Flush();
}
//-----------------------------------------------
// Helper methods
//-----------------------------------------------
private void FlushAttributes() {
int idx = 0, idxNext;
string localName;
while (idx != this.numEntries) {
// Get index of next attribute's name (0 if this is the last attribute)
idxNext = this.arrAttrs[idx].NextNameIndex;
if (idxNext == 0)
idxNext = this.numEntries;
// If localName is null, then this is a duplicate attribute that has been marked as "deleted"
localName = this.arrAttrs[idx].LocalName;
if (localName != null) {
string prefix = this.arrAttrs[idx].Prefix;
string ns = this.arrAttrs[idx].Namespace;
this.wrapped.WriteStartAttribute(prefix, localName, ns);
// Output all of this attribute's text or typed values
while (++idx != idxNext) {
string text = this.arrAttrs[idx].Text;
if (text != null)
this.wrapped.WriteString(text);
else
this.wrapped.WriteValue(this.arrAttrs[idx].Value);
}
this.wrapped.WriteEndAttribute();
}
else {
// Skip over duplicate attributes
idx = idxNext;
}
}
// Notify event listener that attributes have been flushed
if (this.onRemove != null)
this.onRemove(this.wrapped);
}
private struct AttrNameVal {
private string localName;
private string prefix;
private string namespaceName;
private string text;
private XmlAtomicValue value;
private int hashCode;
private int nextNameIndex;
public string LocalName { get { return this.localName; } }
public string Prefix { get { return this.prefix; } }
public string Namespace { get { return this.namespaceName; } }
public string Text { get { return this.text; } }
public XmlAtomicValue Value { get { return this.value; } }
public int NextNameIndex { get { return this.nextNameIndex; } set { this.nextNameIndex = value; } }
/// <summary>
/// Cache an attribute's name and type.
/// </summary>
public void Init(string prefix, string localName, string ns, int hashCode) {
this.localName = localName;
this.prefix = prefix;
this.namespaceName = ns;
this.hashCode = hashCode;
this.nextNameIndex = 0;
}
/// <summary>
/// Cache all or part of the attribute's string value.
/// </summary>
public void Init(string text) {
this.text = text;
this.value = null;
}
/// <summary>
/// Cache all or part of the attribute's typed value.
/// </summary>
public void Init(XmlAtomicValue value) {
this.text = null;
this.value = value;
}
/// <summary>
/// Returns true if this attribute has the specified name (and thus is a duplicate).
/// </summary>
public bool IsDuplicate(string localName, string ns, int hashCode) {
// If attribute is not marked as deleted
if (this.localName != null) {
// And if hash codes match,
if (this.hashCode == hashCode) {
// And if local names match,
if (this.localName.Equals(localName)) {
// And if namespaces match,
if (this.namespaceName.Equals(ns)) {
// Then found duplicate attribute, so mark the attribute as deleted
this.localName = null;
return true;
}
}
}
}
return false;
}
}
#if DEBUG
private const int DefaultCacheSize = 2;
#else
private const int DefaultCacheSize = 32;
#endif
/// <summary>
/// Ensure that attribute array has been created and is large enough for at least one
/// additional entry.
/// </summary>
private void EnsureAttributeCache() {
if (this.arrAttrs == null) {
// Create caching array
this.arrAttrs = new AttrNameVal[DefaultCacheSize];
}
else if (this.numEntries >= this.arrAttrs.Length) {
// Resize caching array
Debug.Assert(this.numEntries == this.arrAttrs.Length);
AttrNameVal[] arrNew = new AttrNameVal[this.numEntries * 2];
Array.Copy(this.arrAttrs, arrNew, this.numEntries);
this.arrAttrs = arrNew;
}
}
}
}

View File

@@ -0,0 +1,498 @@
//------------------------------------------------------------------------------
// <copyright file="XmlCollation.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.IO;
namespace System.Xml.Xsl.Runtime {
using Res = System.Xml.Utils.Res;
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class XmlCollation {
// lgid support for sort
private const int deDE = 0x0407;
private const int huHU = 0x040E;
private const int jaJP = 0x0411;
private const int kaGE = 0x0437;
private const int koKR = 0x0412;
private const int zhTW = 0x0404;
private const int zhCN = 0x0804;
private const int zhHK = 0x0C04;
private const int zhSG = 0x1004;
private const int zhMO = 0x1404;
private const int zhTWbopo = 0x030404;
private const int deDEphon = 0x010407;
private const int huHUtech = 0x01040e;
private const int kaGEmode = 0x010437;
// Invariant: compops == (options & Options.mask)
private CultureInfo cultInfo;
private Options options;
private CompareOptions compops;
/// <summary>
/// Extends System.Globalization.CompareOptions with additional flags.
/// </summary>
private struct Options {
public const int FlagUpperFirst = 0x1000;
public const int FlagEmptyGreatest = 0x2000;
public const int FlagDescendingOrder = 0x4000;
private const int Mask = FlagUpperFirst | FlagEmptyGreatest | FlagDescendingOrder;
private int value;
public Options(int value) {
this.value = value;
}
public bool GetFlag(int flag) {
return (this.value & flag) != 0;
}
public void SetFlag(int flag, bool value) {
if (value)
this.value |= flag;
else
this.value &= ~flag;
}
public bool UpperFirst {
get { return GetFlag(FlagUpperFirst); }
set { SetFlag(FlagUpperFirst, value); }
}
public bool EmptyGreatest {
get { return GetFlag(FlagEmptyGreatest); }
}
public bool DescendingOrder {
get { return GetFlag(FlagDescendingOrder); }
}
public bool IgnoreCase {
get { return GetFlag((int)CompareOptions.IgnoreCase); }
}
public bool Ordinal {
get { return GetFlag((int)CompareOptions.Ordinal); }
}
public CompareOptions CompareOptions {
get {
return (CompareOptions)(value & ~Mask);
}
set {
Debug.Assert(((int)value & Mask) == 0);
this.value = (this.value & Mask) | (int)value;
}
}
public static implicit operator int(Options options) {
return options.value;
}
}
//-----------------------------------------------
// Constructors
//-----------------------------------------------
/// <summary>
/// Construct a collation that uses the specified culture and compare options.
/// </summary>
private XmlCollation(CultureInfo cultureInfo, Options options) {
this.cultInfo = cultureInfo;
this.options = options;
this.compops = options.CompareOptions;
}
//-----------------------------------------------
// Create
//-----------------------------------------------
/// <summary>
/// Singleton collation that sorts according to Unicode code points.
/// </summary>
private static XmlCollation cp = new XmlCollation(CultureInfo.InvariantCulture, new Options((int)CompareOptions.Ordinal));
internal static XmlCollation CodePointCollation {
get { return cp; }
}
internal static XmlCollation Create(string collationLiteral) {
return Create(collationLiteral, /*throw:*/true);
}
// This function is used in both parser and F&O library, so just strictly map valid literals to XmlCollation.
// Set compare options one by one:
// 0, false: no effect; 1, true: yes
// Disregard unrecognized options.
internal static XmlCollation Create(string collationLiteral, bool throwOnError) {
Debug.Assert(collationLiteral != null, "collation literal should not be null");
if (collationLiteral == XmlReservedNs.NsCollCodePoint) {
return CodePointCollation;
}
Uri collationUri;
CultureInfo cultInfo = null;
Options options = new Options();
if (throwOnError) {
collationUri = new Uri(collationLiteral);
} else {
if (!Uri.TryCreate(collationLiteral, UriKind.Absolute, out collationUri)) {
return null;
}
}
string authority = collationUri.GetLeftPart(UriPartial.Authority);
if (authority == XmlReservedNs.NsCollationBase) {
// Language
// at least a '/' will be returned for Uri.LocalPath
string lang = collationUri.LocalPath.Substring(1);
if (lang.Length == 0) {
// Use default culture of current thread (cultinfo = null)
} else {
// Create culture from RFC 1766 string
try {
cultInfo = new CultureInfo(lang);
}
catch (ArgumentException) {
if (!throwOnError) return null;
throw new XslTransformException(Res.Coll_UnsupportedLanguage, lang);
}
}
} else if (collationUri.IsBaseOf(new Uri(XmlReservedNs.NsCollCodePoint))) {
// language with codepoint collation is not allowed
options.CompareOptions = CompareOptions.Ordinal;
} else {
// Unrecognized collation
if (!throwOnError) return null;
throw new XslTransformException(Res.Coll_Unsupported, collationLiteral);
}
// Sort & Compare option
// at least a '?' will be returned for Uri.Query if not empty
string query = collationUri.Query;
string sort = null;
if (query.Length != 0) {
foreach (string option in query.Substring(1).Split('&')) {
string[] pair = option.Split('=');
if (pair.Length != 2) {
if (!throwOnError) return null;
throw new XslTransformException(Res.Coll_BadOptFormat, option);
}
string optionName = pair[0].ToUpper(CultureInfo.InvariantCulture);
string optionValue = pair[1].ToUpper(CultureInfo.InvariantCulture);
if (optionName == "SORT") {
sort = optionValue;
}
else {
int flag;
switch (optionName) {
case "IGNORECASE": flag = (int)CompareOptions.IgnoreCase; break;
case "IGNORENONSPACE": flag = (int)CompareOptions.IgnoreNonSpace; break;
case "IGNORESYMBOLS": flag = (int)CompareOptions.IgnoreSymbols; break;
case "IGNOREKANATYPE": flag = (int)CompareOptions.IgnoreKanaType; break;
case "IGNOREWIDTH": flag = (int)CompareOptions.IgnoreWidth; break;
case "UPPERFIRST": flag = Options.FlagUpperFirst; break;
case "EMPTYGREATEST": flag = Options.FlagEmptyGreatest; break;
case "DESCENDINGORDER": flag = Options.FlagDescendingOrder; break;
default:
if (!throwOnError) return null;
throw new XslTransformException(Res.Coll_UnsupportedOpt, pair[0]);
}
switch (optionValue) {
case "0": case "FALSE": options.SetFlag(flag, false); break;
case "1": case "TRUE" : options.SetFlag(flag, true ); break;
default:
if (!throwOnError) return null;
throw new XslTransformException(Res.Coll_UnsupportedOptVal, pair[0], pair[1]);
}
}
}
}
// upperfirst option is only meaningful when not ignore case
if (options.UpperFirst && options.IgnoreCase)
options.UpperFirst = false;
// other CompareOptions are only meaningful if Ordinal comparison is not being used
if (options.Ordinal) {
options.CompareOptions = CompareOptions.Ordinal;
options.UpperFirst = false;
}
// new cultureinfo based on alternate sorting option
if (sort != null && cultInfo != null) {
int lgid = GetLangID(cultInfo.LCID);
switch (sort) {
case "bopo":
if (lgid == zhTW) {
cultInfo = new CultureInfo(zhTWbopo);
}
break;
case "strk":
if (lgid == zhCN || lgid == zhHK || lgid == zhSG || lgid == zhMO) {
cultInfo = new CultureInfo(MakeLCID(cultInfo.LCID, /*Stroke*/ 0x02));
}
break;
case "uni":
if (lgid == jaJP || lgid == koKR) {
cultInfo = new CultureInfo(MakeLCID(cultInfo.LCID, /*Unicode*/ 0x01));
}
break;
case "phn":
if (lgid == deDE) {
cultInfo = new CultureInfo(deDEphon);
}
break;
case "tech":
if (lgid == huHU) {
cultInfo = new CultureInfo(huHUtech);
}
break;
case "mod":
// ka-GE(Georgian - Georgia) Modern Sort: 0x00010437
if (lgid == kaGE) {
cultInfo = new CultureInfo(kaGEmode);
}
break;
case "pron": case "dict": case "trad":
// es-ES(Spanish - Spain) Traditional: 0x0000040A
// They are removing 0x040a (Spanish Traditional sort) in NLS+.
// So if you create 0x040a, it's just like 0x0c0a (Spanish International sort).
// Thus I don't handle it differently.
break;
default:
if (!throwOnError) return null;
throw new XslTransformException(Res.Coll_UnsupportedSortOpt, sort);
}
}
return new XmlCollation(cultInfo, options);
}
//-----------------------------------------------
// Collection Support
//-----------------------------------------------
// Redefine Equals and GetHashCode methods, they are needed for UniqueList<XmlCollation>
public override bool Equals(object obj) {
if (this == obj) {
return true;
}
XmlCollation that = obj as XmlCollation;
return that != null &&
this.options == that.options &&
object.Equals(this.cultInfo, that.cultInfo);
}
public override int GetHashCode() {
int hashCode = this.options;
if (this.cultInfo != null) {
hashCode ^= this.cultInfo.GetHashCode();
}
return hashCode;
}
//-----------------------------------------------
// Serialization Support
//-----------------------------------------------
// Denotes the current thread locale
private const int LOCALE_CURRENT = -1;
internal void GetObjectData(BinaryWriter writer) {
// NOTE: For CultureInfo we serialize only LCID. It seems to suffice for our purposes.
Debug.Assert(this.cultInfo == null || this.cultInfo.Equals(new CultureInfo(this.cultInfo.LCID)),
"Cannot serialize CultureInfo correctly");
writer.Write(this.cultInfo != null ? this.cultInfo.LCID : LOCALE_CURRENT);
writer.Write(this.options);
}
internal XmlCollation(BinaryReader reader) {
int lcid = reader.ReadInt32();
this.cultInfo = (lcid != LOCALE_CURRENT) ? new CultureInfo(lcid) : null;
this.options = new Options(reader.ReadInt32());
this.compops = options.CompareOptions;
}
//-----------------------------------------------
// Compare Properties
//-----------------------------------------------
internal bool UpperFirst {
get { return this.options.UpperFirst; }
}
internal bool EmptyGreatest {
get { return this.options.EmptyGreatest; }
}
internal bool DescendingOrder {
get { return this.options.DescendingOrder; }
}
internal CultureInfo Culture {
get {
// Use default thread culture if this.cultinfo = null
if (this.cultInfo == null)
return CultureInfo.CurrentCulture;
return this.cultInfo;
}
}
//-----------------------------------------------
//
//-----------------------------------------------
/// <summary>
/// Create a sort key that can be compared quickly with other keys.
/// </summary>
internal XmlSortKey CreateSortKey(string s) {
SortKey sortKey;
byte[] bytesKey;
int idx;
//
sortKey = Culture.CompareInfo.GetSortKey(s, this.compops);
// Create an XmlStringSortKey using the SortKey if possible
#if DEBUG
// In debug-only code, test other code path more frequently
if (!UpperFirst && DescendingOrder)
return new XmlStringSortKey(sortKey, DescendingOrder);
#else
if (!UpperFirst)
return new XmlStringSortKey(sortKey, DescendingOrder);
#endif
// Get byte buffer from SortKey and modify it
bytesKey = sortKey.KeyData;
if (UpperFirst && bytesKey.Length != 0) {
// By default lower-case is always sorted first for any locale (verified by empirical testing).
// In order to place upper-case first, invert the case weights in the generated sort key.
// Skip to case weight section (3rd weight section)
idx = 0;
while (bytesKey[idx] != 1)
idx++;
do {
idx++;
}
while (bytesKey[idx] != 1);
// Invert all case weights (including terminating 0x1)
do {
idx++;
bytesKey[idx] ^= 0xff;
}
while (bytesKey[idx] != 0xfe);
}
return new XmlStringSortKey(bytesKey, DescendingOrder);
}
#if not_used
/// <summary>
/// Compare two strings with each other. Return <0 if str1 sorts before str2, 0 if they're equal, and >0
/// if str1 sorts after str2.
/// </summary>
internal int Compare(string str1, string str2) {
CultureInfo cultinfo = Culture;
int result;
if (this.options.Ordinal) {
result = string.CompareOrdinal(str1, str2);
if (result < 0) result = -1;
else if (result > 0) result = 1;
}
else if (UpperFirst) {
// First compare case-insensitive, then break ties by considering case
result = cultinfo.CompareInfo.Compare(str1, str2, this.compops | CompareOptions.IgnoreCase);
if (result == 0)
result = -cultinfo.CompareInfo.Compare(str1, str2, this.compops);
}
else {
result = cultinfo.CompareInfo.Compare(str1, str2, this.compops);
}
if (DescendingOrder)
result = -result;
return result;
}
/// <summary>
/// Return the index of str1 in str2, or -1 if str1 is not a substring of str2.
/// </summary>
internal int IndexOf(string str1, string str2) {
return Culture.CompareInfo.IndexOf(str1, str2, this.compops);
}
/// <summary>
/// Return true if str1 ends with str2.
/// </summary>
internal bool IsSuffix(string str1, string str2) {
if (this.options.Ordinal){
if (str1.Length < str2.Length) {
return false;
} else {
return String.CompareOrdinal(str1, str1.Length - str2.Length, str2, 0, str2.Length) == 0;
}
}
return Culture.CompareInfo.IsSuffix (str1, str2, this.compops);
}
/// <summary>
/// Return true if str1 starts with str2.
/// </summary>
internal bool IsPrefix(string str1, string str2) {
if (this.options.Ordinal) {
if (str1.Length < str2.Length) {
return false;
} else {
return String.CompareOrdinal(str1, 0, str2, 0, str2.Length) == 0;
}
}
return Culture.CompareInfo.IsPrefix (str1, str2, this.compops);
}
#endif
//-----------------------------------------------
// Helper Functions
//-----------------------------------------------
private static int MakeLCID(int langid, int sortid) {
return (langid & 0xffff) | ((sortid & 0xf) << 16);
}
private static int GetLangID(int lcid) {
return (lcid & 0xffff);
}
}
}

View File

@@ -0,0 +1,309 @@
//------------------------------------------------------------------------------
// <copyright file="XmlExtensionFunction.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Reflection;
using System.Globalization;
using System.Diagnostics;
namespace System.Xml.Xsl.Runtime {
using Res = System.Xml.Utils.Res;
/// <summary>
/// Table of bound extension functions. Once an extension function is bound and entered into the table, future bindings
/// will be very fast. This table is not thread-safe.
/// </summary>
internal class XmlExtensionFunctionTable {
private Dictionary<XmlExtensionFunction, XmlExtensionFunction> table;
private XmlExtensionFunction funcCached;
public XmlExtensionFunctionTable() {
this.table = new Dictionary<XmlExtensionFunction, XmlExtensionFunction>();
}
public XmlExtensionFunction Bind(string name, string namespaceUri, int numArgs, Type objectType, BindingFlags flags) {
XmlExtensionFunction func;
if (this.funcCached == null)
this.funcCached = new XmlExtensionFunction();
// If the extension function already exists in the table, then binding has already been performed
this.funcCached.Init(name, namespaceUri, numArgs, objectType, flags);
if (!this.table.TryGetValue(this.funcCached, out func)) {
// Function doesn't exist, so bind it and enter it into the table
func = this.funcCached;
this.funcCached = null;
func.Bind();
this.table.Add(func, func);
}
return func;
}
}
/// <summary>
/// This internal class contains methods that allow binding to extension functions and invoking them.
/// </summary>
internal class XmlExtensionFunction {
private string namespaceUri; // Extension object identifier
private string name; // Name of this method
private int numArgs; // Argument count
private Type objectType; // Type of the object which will be searched for matching methods
private BindingFlags flags; // Modifiers that were used to search for a matching signature
private int hashCode; // Pre-computed hashcode
private MethodInfo meth; // MethodInfo for extension function
private Type[] argClrTypes; // Type array for extension function arguments
private Type retClrType; // Type for extension function return value
private XmlQueryType[] argXmlTypes; // XmlQueryType array for extension function arguments
private XmlQueryType retXmlType; // XmlQueryType for extension function return value
/// <summary>
/// Constructor.
/// </summary>
public XmlExtensionFunction() {
}
/// <summary>
/// Constructor (directly binds to passed MethodInfo).
/// </summary>
public XmlExtensionFunction(string name, string namespaceUri, MethodInfo meth) {
this.name = name;
this.namespaceUri = namespaceUri;
Bind(meth);
}
/// <summary>
/// Constructor.
/// </summary>
public XmlExtensionFunction(string name, string namespaceUri, int numArgs, Type objectType, BindingFlags flags) {
Init(name, namespaceUri, numArgs, objectType, flags);
}
/// <summary>
/// Initialize, but do not bind.
/// </summary>
public void Init(string name, string namespaceUri, int numArgs, Type objectType, BindingFlags flags) {
this.name = name;
this.namespaceUri = namespaceUri;
this.numArgs = numArgs;
this.objectType = objectType;
this.flags = flags;
this.meth = null;
this.argClrTypes = null;
this.retClrType = null;
this.argXmlTypes = null;
this.retXmlType = null;
// Compute hash code so that it is not recomputed each time GetHashCode() is called
this.hashCode = namespaceUri.GetHashCode() ^ name.GetHashCode() ^ ((int) flags << 16) ^ (int) numArgs;
}
/// <summary>
/// Once Bind has been successfully called, Method will be non-null.
/// </summary>
public MethodInfo Method {
get { return this.meth; }
}
/// <summary>
/// Once Bind has been successfully called, the Clr type of each argument can be accessed.
/// Note that this may be different than Method.GetParameterInfo().ParameterType.
/// </summary>
public Type GetClrArgumentType(int index) {
return this.argClrTypes[index];
}
/// <summary>
/// Once Bind has been successfully called, the Clr type of the return value can be accessed.
/// Note that this may be different than Method.GetParameterInfo().ReturnType.
/// </summary>
public Type ClrReturnType {
get { return this.retClrType; }
}
/// <summary>
/// Once Bind has been successfully called, the inferred Xml types of the arguments can be accessed.
/// </summary>
public XmlQueryType GetXmlArgumentType(int index) {
return this.argXmlTypes[index];
}
/// <summary>
/// Once Bind has been successfully called, the inferred Xml type of the return value can be accessed.
/// </summary>
public XmlQueryType XmlReturnType {
get { return this.retXmlType; }
}
/// <summary>
/// Return true if the CLR type specified in the Init() call has a matching method.
/// </summary>
public bool CanBind() {
MethodInfo[] methods = this.objectType.GetMethods(this.flags);
bool ignoreCase = (this.flags & BindingFlags.IgnoreCase) != 0;
StringComparison comparison = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
// Find method in object type
foreach (MethodInfo methSearch in methods) {
if (methSearch.Name.Equals(this.name, comparison) && (this.numArgs == -1 || methSearch.GetParameters().Length == this.numArgs)) {
// Binding to generic methods will never succeed
if (!methSearch.IsGenericMethodDefinition)
return true;
}
}
return false;
}
/// <summary>
/// Bind to the CLR type specified in the Init() call. If a matching method cannot be found, throw an exception.
/// </summary>
public void Bind() {
MethodInfo[] methods = this.objectType.GetMethods(this.flags);
MethodInfo methMatch = null;
bool ignoreCase = (this.flags & BindingFlags.IgnoreCase) != 0;
StringComparison comparison = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
// Find method in object type
foreach (MethodInfo methSearch in methods) {
if (methSearch.Name.Equals(this.name, comparison) && (this.numArgs == -1 || methSearch.GetParameters().Length == this.numArgs)) {
if (methMatch != null)
throw new XslTransformException(/*[XT_037]*/Res.XmlIl_AmbiguousExtensionMethod, this.namespaceUri, this.name, this.numArgs.ToString(CultureInfo.InvariantCulture));
methMatch = methSearch;
}
}
if (methMatch == null) {
methods = this.objectType.GetMethods(this.flags | BindingFlags.NonPublic);
foreach (MethodInfo methSearch in methods) {
if (methSearch.Name.Equals(this.name, comparison) && methSearch.GetParameters().Length == this.numArgs)
throw new XslTransformException(/*[XT_038]*/Res.XmlIl_NonPublicExtensionMethod, this.namespaceUri, this.name);
}
throw new XslTransformException(/*[XT_039]*/Res.XmlIl_NoExtensionMethod, this.namespaceUri, this.name, this.numArgs.ToString(CultureInfo.InvariantCulture));
}
if (methMatch.IsGenericMethodDefinition)
throw new XslTransformException(/*[XT_040]*/Res.XmlIl_GenericExtensionMethod, this.namespaceUri, this.name);
Debug.Assert(methMatch.ContainsGenericParameters == false);
Bind(methMatch);
}
/// <summary>
/// Bind to the specified MethodInfo.
/// </summary>
private void Bind(MethodInfo meth) {
ParameterInfo[] paramInfo = meth.GetParameters();
int i;
// Save the MethodInfo
this.meth = meth;
// Get the Clr type of each parameter
this.argClrTypes = new Type[paramInfo.Length];
for (i = 0; i < paramInfo.Length; i++)
this.argClrTypes[i] = GetClrType(paramInfo[i].ParameterType);
// Get the Clr type of the return value
this.retClrType = GetClrType(this.meth.ReturnType);
// Infer an Xml type for each Clr type
this.argXmlTypes = new XmlQueryType[paramInfo.Length];
for (i = 0; i < paramInfo.Length; i++) {
this.argXmlTypes[i] = InferXmlType(this.argClrTypes[i]);
//
if (this.namespaceUri.Length == 0) {
if ((object) this.argXmlTypes[i] == (object) XmlQueryTypeFactory.NodeNotRtf)
this.argXmlTypes[i] = XmlQueryTypeFactory.Node;
else if ((object) this.argXmlTypes[i] == (object) XmlQueryTypeFactory.NodeSDod)
this.argXmlTypes[i] = XmlQueryTypeFactory.NodeS;
}
else {
if ((object) this.argXmlTypes[i] == (object) XmlQueryTypeFactory.NodeSDod)
this.argXmlTypes[i] = XmlQueryTypeFactory.NodeNotRtfS;
}
}
// Infer an Xml type for the return Clr type
this.retXmlType = InferXmlType(this.retClrType);
}
/// <summary>
/// Convert the incoming arguments to an array of CLR objects, and then invoke the external function on the "extObj" object instance.
/// </summary>
public object Invoke(object extObj, object[] args) {
Debug.Assert(this.meth != null, "Must call Bind() before calling Invoke.");
Debug.Assert(args.Length == this.argClrTypes.Length, "Mismatched number of actual and formal arguments.");
try {
return this.meth.Invoke(extObj, this.flags, null, args, CultureInfo.InvariantCulture);
}
catch (TargetInvocationException e) {
throw new XslTransformException(e.InnerException, Res.XmlIl_ExtensionError, this.name);
}
catch (Exception e) {
if (!XmlException.IsCatchableException(e)) {
throw;
}
throw new XslTransformException(e, Res.XmlIl_ExtensionError, this.name);
}
}
/// <summary>
/// Return true if this XmlExtensionFunction has the same values as another XmlExtensionFunction.
/// </summary>
public override bool Equals(object other) {
XmlExtensionFunction that = other as XmlExtensionFunction;
Debug.Assert(that != null);
// Compare name, argument count, object type, and binding flags
return (this.hashCode == that.hashCode && this.name == that.name && this.namespaceUri == that.namespaceUri &&
this.numArgs == that.numArgs && this.objectType == that.objectType && this.flags == that.flags);
}
/// <summary>
/// Return this object's hash code, previously computed for performance.
/// </summary>
public override int GetHashCode() {
return this.hashCode;
}
/// <summary>
/// 1. Map enumerations to the underlying integral type.
/// 2. Throw an exception if the type is ByRef
/// </summary>
private Type GetClrType(Type clrType) {
if (clrType.IsEnum)
return Enum.GetUnderlyingType(clrType);
if (clrType.IsByRef)
throw new XslTransformException(/*[XT_050]*/Res.XmlIl_ByRefType, this.namespaceUri, this.name);
return clrType;
}
/// <summary>
/// Infer an Xml type from a Clr type using Xslt infererence rules
/// </summary>
private XmlQueryType InferXmlType(Type clrType) {
return XsltConvert.InferXsltType(clrType);
}
}
}

View File

@@ -0,0 +1,64 @@
//------------------------------------------------------------------------------
// <copyright file="XmlILIndex.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Xml.XPath;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// This class manages nodes from one input document, indexed by key value(s).
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class XmlILIndex {
private Dictionary<string, XmlQueryNodeSequence> table;
/// <summary>
/// This constructor is internal so that external users cannot construct it (and therefore we do not have to test it separately).
/// </summary>
internal XmlILIndex() {
this.table = new Dictionary<string, XmlQueryNodeSequence>();
}
/// <summary>
/// Add a node indexed by the specified key value.
/// </summary>
public void Add(string key, XPathNavigator navigator) {
XmlQueryNodeSequence seq;
if (!this.table.TryGetValue(key, out seq)) {
// Create a new sequence and add it to the index
seq = new XmlQueryNodeSequence();
seq.AddClone(navigator);
this.table.Add(key, seq);
}
else {
// The nodes are guaranteed to be added in document order with possible duplicates.
// Add node to the existing sequence if it differs from the last one.
Debug.Assert(navigator.ComparePosition(seq[seq.Count - 1]) >= 0, "Index nodes must be added in document order");
if (!navigator.IsSamePosition(seq[seq.Count - 1])) {
seq.AddClone(navigator);
}
}
}
/// <summary>
/// Lookup a sequence of nodes that are indexed by the specified key value.
/// Return a non-null empty sequence, if there are no nodes associated with the key.
/// </summary>
public XmlQueryNodeSequence Lookup(string key) {
XmlQueryNodeSequence seq;
if (!this.table.TryGetValue(key, out seq))
seq = new XmlQueryNodeSequence();
return seq;
}
}
}

View File

@@ -0,0 +1,100 @@
//------------------------------------------------------------------------------
// <copyright file="XmlILStorageConverter.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Schema;
using System.Xml.Xsl;
using System.ComponentModel;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// This is a simple convenience wrapper internal class that contains static helper methods that get a value
/// converter from XmlQueryRuntime and use it convert among several physical Clr representations for
/// the same logical Xml type. For example, an external function might have an argument typed as
/// xs:integer, with Clr type Decimal. Since ILGen stores xs:integer as Clr type Int64 instead of
/// Decimal, a conversion to the desired storage type must take place.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class XmlILStorageConverter {
//-----------------------------------------------
// ToAtomicValue
//-----------------------------------------------
public static XmlAtomicValue StringToAtomicValue(string value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static XmlAtomicValue DecimalToAtomicValue(decimal value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static XmlAtomicValue Int64ToAtomicValue(long value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static XmlAtomicValue Int32ToAtomicValue(int value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static XmlAtomicValue BooleanToAtomicValue(bool value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static XmlAtomicValue DoubleToAtomicValue(double value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static XmlAtomicValue SingleToAtomicValue(float value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static XmlAtomicValue DateTimeToAtomicValue(DateTime value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static XmlAtomicValue XmlQualifiedNameToAtomicValue(XmlQualifiedName value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static XmlAtomicValue TimeSpanToAtomicValue(TimeSpan value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static XmlAtomicValue BytesToAtomicValue(byte[] value, int index, XmlQueryRuntime runtime) {
return new XmlAtomicValue(runtime.GetXmlType(index).SchemaType, value);
}
public static IList<XPathItem> NavigatorsToItems(IList<XPathNavigator> listNavigators) {
// Check to see if the navigator cache implements IList<XPathItem>
IList<XPathItem> listItems = listNavigators as IList<XPathItem>;
if (listItems != null)
return listItems;
// Create XmlQueryNodeSequence, which does implement IList<XPathItem>
return new XmlQueryNodeSequence(listNavigators);
}
public static IList<XPathNavigator> ItemsToNavigators(IList<XPathItem> listItems) {
// Check to see if the navigator cache implements IList<XPathNavigator>
IList<XPathNavigator> listNavs = listItems as IList<XPathNavigator>;
if (listNavs != null)
return listNavs;
// Create XmlQueryNodeSequence, which does implement IList<XPathNavigator>
XmlQueryNodeSequence seq = new XmlQueryNodeSequence(listItems.Count);
for (int i = 0; i < listItems.Count; i++)
seq.Add((XPathNavigator) listItems[i]);
return seq;
}
}
}

View File

@@ -0,0 +1,58 @@
//------------------------------------------------------------------------------
// <copyright file="XmlIterators.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Xml;
using System.Xml.XPath;
using System.ComponentModel;
namespace System.Xml.Xsl.Runtime {
/// <summary>
/// Iterators that use containment to control a nested iterator return one of the following values from MoveNext().
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public enum IteratorResult {
NoMoreNodes, // Iteration is complete; there are no more nodes
NeedInputNode, // The next node needs to be fetched from the contained iterator before iteration can continue
HaveCurrentNode, // This iterator's Current property is set to the next node in the iteration
};
/// <summary>
/// Tokenize a string containing IDREF values and deref the values in order to get a list of ID elements.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public struct IdIterator {
private XPathNavigator navCurrent;
private string[] idrefs;
private int idx;
public void Create(XPathNavigator context, string value) {
this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
this.idrefs = XmlConvert.SplitString(value);
this.idx = -1;
}
public bool MoveNext() {
do {
this.idx++;
if (this.idx >= idrefs.Length)
return false;
}
while (!this.navCurrent.MoveToId(this.idrefs[this.idx]));
return true;
}
/// <summary>
/// Return the current result navigator. This is only defined after MoveNext() has returned true.
/// </summary>
public XPathNavigator Current {
get { return this.navCurrent; }
}
}
}

Some files were not shown because too many files have changed in this diff Show More