|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Dispatcher
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime;
using System.Threading;
using System.Xml;
using System.Xml.XPath;
/// <summary>
/// A navigator is a cursor over the nodes in a DOM, where each node is assigned a unique position.
/// A node is a (navigator, position) pair.
/// </summary>
internal struct QueryNode
{
SeekableXPathNavigator node;
long nodePosition;
/// <summary>
/// Create a query node from the given navigator and its current position
/// </summary>
/// <param name="node"></param>
internal QueryNode(SeekableXPathNavigator node)
{
this.node = node;
this.nodePosition = node.CurrentPosition;
}
/// <summary>
/// Initialize using the given (node, position) pair
/// </summary>
#if NO
internal QueryNode(SeekableXPathNavigator node, long nodePosition)
{
this.node = node;
this.nodePosition = nodePosition;
}
#endif
internal string LocalName
{
get
{
return this.node.GetLocalName(this.nodePosition);
}
}
/// <summary>
/// Return the node's name
/// </summary>
internal string Name
{
get
{
return this.node.GetName(this.nodePosition);
}
}
/// <summary>
/// Return the node's namespace
/// </summary>
internal string Namespace
{
get
{
return this.node.GetNamespace(this.nodePosition);
}
}
/// <summary>
/// Return this query node's underlying Node
/// </summary>
internal SeekableXPathNavigator Node
{
get
{
return this.node;
}
}
/// <summary>
///
/// </summary>
internal long Position
{
get
{
return this.nodePosition;
}
}
#if NO
/// <summary>
/// This node's type
/// </summary>
internal QueryNodeType Type
{
get
{
return QueryDataModel.GetNodeType(this.node.GetNodeType(this.nodePosition));
}
}
#endif
/// <summary>
/// This node's string value
/// </summary>
internal string Value
{
get
{
return this.node.GetValue(this.nodePosition);
}
}
#if NO
/// <summary>
/// Raw xpath node type
/// </summary>
internal XPathNodeType XPathNodeType
{
get
{
return this.node.GetNodeType(this.nodePosition);
}
}
#endif
/// <summary>
/// Move this node's navigator to its position
/// </summary>
/// <returns></returns>
internal SeekableXPathNavigator MoveTo()
{
this.node.CurrentPosition = this.nodePosition;
return this.node;
}
}
internal enum NodeSequenceItemFlags : byte
{
None = 0x00,
NodesetLast = 0x01,
}
// PERF, Microsoft, Remove when generic sort works
// Used to sort in document order
#if NO
internal class NodeSequenceItemObjectComparer : IComparer
{
internal NodeSequenceItemObjectComparer()
{
}
public int Compare(object obj1, object obj2)
{
NodeSequenceItem item1 = (NodeSequenceItem)obj1;
NodeSequenceItem item2 = (NodeSequenceItem)obj2;
XmlNodeOrder order = item1.Node.Node.ComparePosition(item1.Node.Position, item2.Node.Position);
int ret;
switch(order)
{
case XmlNodeOrder.Before:
ret = -1;
break;
case XmlNodeOrder.Same:
ret = 0;
break;
case XmlNodeOrder.After:
ret = 1;
break;
case XmlNodeOrder.Unknown:
default:
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XPathException(SR.GetString(SR.QueryNotSortable)), TraceEventType.Critical);
}
return ret;
}
}
// Used to sort in document order
internal class NodeSequenceItemComparer : IComparer<NodeSequenceItem>
{
internal NodeSequenceItemComparer()
{
}
public int Compare(NodeSequenceItem item1, NodeSequenceItem item2)
{
XmlNodeOrder order = item1.Node.Node.ComparePosition(item1.Node.Position, item2.Node.Position);
int ret;
switch(order)
{
case XmlNodeOrder.Before:
ret = -1;
break;
case XmlNodeOrder.Same:
ret = 0;
break;
case XmlNodeOrder.After:
ret = 1;
break;
case XmlNodeOrder.Unknown:
default:
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XPathException(SR.GetString(SR.QueryNotSortable)), TraceEventType.Critical);
}
return ret;
}
public bool Equals(NodeSequenceItem item1, NodeSequenceItem item2)
{
return Compare(item1, item2) == 0;
}
public int GetHashCode(NodeSequenceItem item)
{
return item.GetHashCode();
}
}
#endif
// Used to sort in document order
internal class QueryNodeComparer : IComparer<QueryNode>
{
public QueryNodeComparer()
{
}
public int Compare(QueryNode item1, QueryNode item2)
{
XmlNodeOrder order = item1.Node.ComparePosition(item1.Position, item2.Position);
int ret;
switch (order)
{
case XmlNodeOrder.Before:
ret = -1;
break;
case XmlNodeOrder.Same:
ret = 0;
break;
case XmlNodeOrder.After:
ret = 1;
break;
case XmlNodeOrder.Unknown:
default:
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new XPathException(SR.GetString(SR.QueryNotSortable)));
}
return ret;
}
public bool Equals(QueryNode item1, QueryNode item2)
{
return Compare(item1, item2) == 0;
}
public int GetHashCode(QueryNode item)
{
return item.GetHashCode();
}
}
internal struct NodeSequenceItem
{
NodeSequenceItemFlags flags;
QueryNode node;
int position;
int size;
internal NodeSequenceItemFlags Flags
{
get
{
return this.flags;
}
set
{
this.flags = value;
}
}
internal bool Last
{
get
{
return (0 != (NodeSequenceItemFlags.NodesetLast & this.flags));
}
set
{
if (value)
{
this.flags |= NodeSequenceItemFlags.NodesetLast;
}
else
{
this.flags &= ~(NodeSequenceItemFlags.NodesetLast);
}
}
}
internal string LocalName
{
get
{
return this.node.LocalName;
}
}
internal string Name
{
get
{
return this.node.Name;
}
}
internal string Namespace
{
get
{
return this.node.Namespace;
}
}
internal QueryNode Node
{
get
{
return this.node;
}
#if NO
set
{
this.node = value;
}
#endif
}
internal int Position
{
get
{
return this.position;
}
#if NO
set
{
this.position = value;
}
#endif
}
internal int Size
{
get
{
return this.size;
}
set
{
this.size = value;
}
}
internal bool Compare(double dblVal, RelationOperator op)
{
return QueryValueModel.Compare(this.NumberValue(), dblVal, op);
}
internal bool Compare(string strVal, RelationOperator op)
{
return QueryValueModel.Compare(this.StringValue(), strVal, op);
}
internal bool Compare(ref NodeSequenceItem item, RelationOperator op)
{
return QueryValueModel.Compare(this.StringValue(), item.StringValue(), op);
}
internal bool Equals(string literal)
{
return QueryValueModel.Equals(this.StringValue(), literal);
}
internal bool Equals(double literal)
{
return (this.NumberValue() == literal);
}
internal SeekableXPathNavigator GetNavigator()
{
return this.node.MoveTo();
}
internal long GetNavigatorPosition()
{
return this.node.Position;
}
internal double NumberValue()
{
return QueryValueModel.Double(this.StringValue());
}
internal void Set(SeekableXPathNavigator node, int position, int size)
{
Fx.Assert(position > 0, "");
Fx.Assert(null != node, "");
this.node = new QueryNode(node);
this.position = position;
this.size = size;
this.flags = NodeSequenceItemFlags.None;
}
internal void Set(QueryNode node, int position, int size)
{
Fx.Assert(position > 0, "");
this.node = node;
this.position = position;
this.size = size;
this.flags = NodeSequenceItemFlags.None;
}
internal void Set(ref NodeSequenceItem item, int position, int size)
{
Fx.Assert(position > 0, "");
this.node = item.node;
this.position = position;
this.size = size;
this.flags = item.flags;
}
internal void SetPositionAndSize(int position, int size)
{
this.position = position;
this.size = size;
this.flags &= ~NodeSequenceItemFlags.NodesetLast;
}
internal void SetSizeAndLast()
{
this.size = 1;
this.flags |= NodeSequenceItemFlags.NodesetLast;
}
// This is not optimized right now
// We may want to CACHE string values once they are computed
internal string StringValue()
{
return this.node.Value;
}
}
internal class NodeSequence
{
#if DEBUG
// debugging aid. Because C# references do not have displayble numeric values, hard to deduce the
// graph structure to see what opcode is connected to what
static long nextUniqueId = 0;
internal long uniqueID;
#endif
int count;
internal static NodeSequence Empty = new NodeSequence(0);
NodeSequenceItem[] items;
NodeSequence next;
ProcessingContext ownerContext;
int position;
internal int refCount;
int sizePosition;
static readonly QueryNodeComparer staticQueryNodeComparerInstance = new QueryNodeComparer();
internal NodeSequence()
: this(8, null)
{
}
internal NodeSequence(int capacity)
: this(capacity, null)
{
}
internal NodeSequence(int capacity, ProcessingContext ownerContext)
{
this.items = new NodeSequenceItem[capacity];
this.ownerContext = ownerContext;
#if DEBUG
this.uniqueID = Interlocked.Increment(ref NodeSequence.nextUniqueId);
#endif
}
#if NO
internal NodeSequence(int capacity, ProcessingContext ownerContext, XPathNodeIterator iter)
: this(capacity, ownerContext)
{
while(iter.MoveNext())
{
SeekableXPathNavigator nav = iter.Current as SeekableXPathNavigator;
if(nav != null)
{
Add(nav);
}
else
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryProcessingException(QueryProcessingError.Unexpected, SR.GetString(SR.QueryMustBeSeekable)), TraceEventType.Critical);
}
}
}
#endif
internal int Count
{
get
{
return this.count;
}
#if NO
set
{
Fx.Assert(value >= 0 && value <= this.count, "");
this.count = value;
}
#endif
}
internal NodeSequenceItem this[int index]
{
get
{
return this.items[index];
}
}
internal NodeSequenceItem[] Items
{
get
{
return this.items;
}
}
internal bool IsNotEmpty
{
get
{
return (this.count > 0);
}
}
internal string LocalName
{
get
{
if (this.count > 0)
{
return this.items[0].LocalName;
}
return string.Empty;
}
}
internal string Name
{
get
{
if (this.count > 0)
{
return this.items[0].Name;
}
return string.Empty;
}
}
internal string Namespace
{
get
{
if (this.count > 0)
{
return this.items[0].Namespace;
}
return string.Empty;
}
}
internal NodeSequence Next
{
get
{
return this.next;
}
set
{
this.next = value;
}
}
internal ProcessingContext OwnerContext
{
get
{
return this.ownerContext;
}
set
{
this.ownerContext = value;
}
}
#if NO
internal int NodesetStartAt
{
get
{
return -this.sizePosition;
}
}
#endif
internal void Add(XPathNodeIterator iter)
{
while (iter.MoveNext())
{
SeekableXPathNavigator nav = iter.Current as SeekableXPathNavigator;
if (nav != null)
{
this.Add(nav);
}
else
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected, SR.GetString(SR.QueryMustBeSeekable)));
}
}
}
internal void Add(SeekableXPathNavigator node)
{
Fx.Assert(this.items.Length > 0, "");
if (this.count == this.items.Length)
{
this.Grow(this.items.Length * 2);
}
this.position++;
this.items[this.count++].Set(node, this.position, this.sizePosition);
}
internal void Add(QueryNode node)
{
Fx.Assert(this.items.Length > 0, "");
if (this.count == this.items.Length)
{
this.Grow(this.items.Length * 2);
}
this.position++;
this.items[this.count++].Set(node, this.position, this.sizePosition);
}
internal void Add(ref NodeSequenceItem item)
{
Fx.Assert(this.items.Length > 0, "");
if (this.count == this.items.Length)
{
this.Grow(this.items.Length * 2);
}
this.position++;
this.items[this.count++].Set(ref item, this.position, this.sizePosition);
}
internal void AddCopy(ref NodeSequenceItem item, int size)
{
Fx.Assert(this.items.Length > 0, "");
if (this.count == this.items.Length)
{
this.Grow(this.items.Length * 2);
}
this.items[this.count] = item;
this.items[this.count++].Size = size;
}
internal void AddCopy(ref NodeSequenceItem item)
{
Fx.Assert(this.items.Length > 0, "");
if (this.count == this.items.Length)
{
this.Grow(this.items.Length * 2);
}
this.items[this.count++] = item;
}
#if NO
internal void Add(NodeSequence seq)
{
int newCount = this.count + seq.count;
if (newCount > this.items.Length)
{
// We are going to need room. Grow the array
int growTo = this.items.Length * 2;
this.Grow(newCount > growTo ? newCount : growTo);
}
Array.Copy(seq.items, 0, this.items, this.count, seq.count);
this.count += seq.count;
}
#endif
internal bool CanReuse(ProcessingContext context)
{
return (this.count == 1 && this.ownerContext == context && this.refCount == 1);
}
internal void Clear()
{
this.count = 0;
}
internal void Reset(NodeSequence nextSeq)
{
this.count = 0;
this.refCount = 0;
this.next = nextSeq;
}
internal bool Compare(double val, RelationOperator op)
{
for (int i = 0; i < this.count; ++i)
{
if (this.items[i].Compare(val, op))
{
return true;
}
}
return false;
}
internal bool Compare(string val, RelationOperator op)
{
Fx.Assert(null != val, "");
for (int i = 0; i < this.count; ++i)
{
if (this.items[i].Compare(val, op))
{
return true;
}
}
return false;
}
internal bool Compare(ref NodeSequenceItem item, RelationOperator op)
{
for (int i = 0; i < this.count; ++i)
{
if (this.items[i].Compare(ref item, op))
{
return true;
}
}
return false;
}
internal bool Compare(NodeSequence sequence, RelationOperator op)
{
Fx.Assert(null != sequence, "");
for (int i = 0; i < sequence.count; ++i)
{
if (this.Compare(ref sequence.items[i], op))
{
return true;
}
}
return false;
}
#if NO
void EnsureCapacity()
{
if (this.count == this.items.Length)
{
this.Grow(this.items.Length * 2);
}
}
void EnsureCapacity(int capacity)
{
if (capacity > this.items.Length)
{
int newSize = this.items.Length * 2;
this.Grow(newSize > capacity ? newSize : capacity);
}
}
#endif
internal bool Equals(string val)
{
Fx.Assert(null != val, "");
for (int i = 0; i < this.count; ++i)
{
if (this.items[i].Equals(val))
{
return true;
}
}
return false;
}
internal bool Equals(double val)
{
for (int i = 0; i < this.count; ++i)
{
if (this.items[i].Equals(val))
{
return true;
}
}
return false;
}
internal static int GetContextSize(NodeSequence sequence, int itemIndex)
{
Fx.Assert(null != sequence, "");
int size = sequence.items[itemIndex].Size;
if (size <= 0)
{
return sequence.items[-size].Size;
}
return size;
}
void Grow(int newSize)
{
NodeSequenceItem[] newItems = new NodeSequenceItem[newSize];
if (this.items != null)
{
Array.Copy(this.items, newItems, this.items.Length);
}
this.items = newItems;
}
/// <summary>
/// Merge all nodesets in this sequence... turning it into a sequence with a single nodeset
/// This is done by simply renumbering all positions.. and clearing the nodeset flag
/// </summary>
internal void Merge()
{
Merge(true);
}
internal void Merge(bool renumber)
{
if (this.count == 0)
{
return;
}
if (renumber)
{
RenumberItems();
}
}
#if NO
// Assumes list is flat and sorted
internal void RemoveDuplicates()
{
if(this.count < 2)
{
return;
}
int last = 0;
for(int next = 1; next < this.count; ++next)
{
if(Comparer.Compare(this.items[last], this.items[next]) != 0)
{
++last;
if(last != next)
{
this.items[last] = this.items[next];
}
}
}
this.count = last + 1;
RenumberItems();
}
#endif
void RenumberItems()
{
if (this.count > 0)
{
for (int i = 0; i < this.count; ++i)
{
this.items[i].SetPositionAndSize(i + 1, this.count);
}
this.items[this.count - 1].Flags |= NodeSequenceItemFlags.NodesetLast;
}
}
#if NO
internal void SortNodes()
{
this.Merge(false);
// PERF, Microsoft, make this work
//Array.Sort<NodeSequenceItem>(this.items, 0, this.count, NodeSequence.Comparer);
Array.Sort(this.items, 0, this.count, NodeSequence.ObjectComparer);
RenumberItems();
}
#endif
internal void StartNodeset()
{
this.position = 0;
this.sizePosition = -this.count;
}
internal void StopNodeset()
{
switch (this.position)
{
default:
int sizePos = -this.sizePosition;
this.items[sizePos].Size = this.position;
this.items[sizePos + this.position - 1].Last = true;
break;
case 0:
break;
case 1:
this.items[-this.sizePosition].SetSizeAndLast();
break;
}
}
internal string StringValue()
{
if (this.count > 0)
{
return this.items[0].StringValue();
}
return string.Empty;
}
/// <summary>
/// Union algorithm:
/// 1. Add both sequences of items to a newly created sequence
/// 2. Sort the items based on document position
/// 3. Renumber positions in this new unionized sequence
/// </summary>
internal NodeSequence Union(ProcessingContext context, NodeSequence otherSeq)
{
NodeSequence seq = context.CreateSequence();
SortedBuffer<QueryNode, QueryNodeComparer> buff = new SortedBuffer<QueryNode, QueryNodeComparer>(staticQueryNodeComparerInstance);
for (int i = 0; i < this.count; ++i)
buff.Add(this.items[i].Node);
for (int i = 0; i < otherSeq.count; ++i)
buff.Add(otherSeq.items[i].Node);
for (int i = 0; i < buff.Count; ++i)
seq.Add(buff[i]);
seq.RenumberItems();
return seq;
/*
// PERF, Microsoft, I think we can do the merge ourselves and avoid the sort.
// Need to verify that the sequences are always in document order.
for(int i = 0; i < this.count; ++i)
{
seq.AddCopy(ref this.items[i]);
}
for(int i = 0; i < otherSeq.count; ++i)
{
seq.AddCopy(ref otherSeq.items[i]);
}
seq.SortNodes();
seq.RemoveDuplicates();
return seq;
*/
}
#region IQueryBufferPool Members
#if NO
public void Reset()
{
this.count = 0;
this.Trim();
}
public void Trim()
{
if (this.count == 0)
{
this.items = null;
}
else if (this.count < this.items.Length)
{
NodeSequenceItem[] newItems = new NodeSequenceItem[this.count];
Array.Copy(this.items, newItems, this.count);
this.items = newItems;
}
}
#endif
#endregion
}
internal class NodeSequenceIterator : XPathNodeIterator
{
// Shared
NodeSequence seq;
// Instance
NodeSequenceIterator data;
int index;
SeekableXPathNavigator nav; // the navigator that will be used by this iterator
internal NodeSequenceIterator(NodeSequence seq)
: base()
{
this.data = this;
this.seq = seq;
}
internal NodeSequenceIterator(NodeSequenceIterator iter)
{
this.data = iter.data;
this.index = iter.index;
}
public override int Count
{
get
{
return this.data.seq.Count;
}
}
public override XPathNavigator Current
{
get
{
if (this.index == 0)
{
#pragma warning suppress 56503 // Microsoft, postponing the public change
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryProcessingException(QueryProcessingError.Unexpected, SR.GetString(SR.QueryContextNotSupportedInSequences)));
}
if (this.index > this.data.seq.Count)
{
#pragma warning suppress 56503 // Microsoft, postponing the public change
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.QueryAfterNodes)));
}
//
// From MSDN - the public contract of .Current
// You can use the properties of the XPathNavigator to return information on the current node.
// However, the XPathNavigator cannot be used to move away from the selected node set.
// Doing so could invalidate the state of the navigator. Alternatively, you can clone the XPathNavigator.
// The cloned XPathNavigator can then be moved away from the selected node set. This is an application level decision.
// Providing this functionality may effect the performance of the XPath query.
//
// Return the navigator as is - where it is positioned. If the user moved the navigator, then the user is
// hosed. We will make no guarantees - and are not required to. Doing so would force cloning, which is expensive.
//
// NOTE: .Current can get called repeatedly, so its activity should be relative CHEAP.
// No cloning, copying etc. All that work should be done in MoveNext()
return this.nav;
}
}
public override int CurrentPosition
{
get
{
return this.index;
}
}
internal void Clear()
{
this.data.seq = null;
this.nav = null;
}
public override XPathNodeIterator Clone()
{
return new NodeSequenceIterator(this);
}
public override IEnumerator GetEnumerator()
{
return new NodeSequenceEnumerator(this);
}
public override bool MoveNext()
{
if (null == this.data.seq)
{
// User is trying to use an iterator that is out of scope.
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.QueryIteratorOutOfScope)));
}
if (this.index < this.data.seq.Count)
{
if (null == this.nav)
{
// We haven't aquired the navigator we will use for this iterator yet.
this.nav = (SeekableXPathNavigator)this.data.seq[this.index].GetNavigator().Clone();
}
else
{
this.nav.CurrentPosition = this.data.seq[this.index].GetNavigatorPosition();
}
this.index++;
return true;
}
this.index++;
this.nav = null;
return false;
}
public void Reset()
{
this.nav = null;
this.index = 0;
}
}
internal class NodeSequenceEnumerator : IEnumerator
{
NodeSequenceIterator iter;
internal NodeSequenceEnumerator(NodeSequenceIterator iter)
{
this.iter = new NodeSequenceIterator(iter);
Reset();
}
public object Current
{
get
{
if (this.iter.CurrentPosition == 0)
{
#pragma warning suppress 56503 // Microsoft, postponing the public change
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.QueryBeforeNodes)));
}
if (this.iter.CurrentPosition > this.iter.Count)
{
#pragma warning suppress 56503 // Microsoft, postponing the public change
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.QueryAfterNodes)));
}
return this.iter.Current;
}
}
public bool MoveNext()
{
return iter.MoveNext();
}
public void Reset()
{
this.iter.Reset();
}
}
internal class SafeNodeSequenceIterator : NodeSequenceIterator, IDisposable
{
ProcessingContext context;
int disposed;
NodeSequence seq;
public SafeNodeSequenceIterator(NodeSequence seq, ProcessingContext context)
: base(seq)
{
this.context = context;
this.seq = seq;
Interlocked.Increment(ref this.seq.refCount);
this.context.Processor.AddRef();
}
public override XPathNodeIterator Clone()
{
return new SafeNodeSequenceIterator(this.seq, this.context);
}
public void Dispose()
{
if (Interlocked.CompareExchange(ref this.disposed, 1, 0) == 0)
{
QueryProcessor processor = this.context.Processor;
this.context.ReleaseSequence(this.seq);
this.context.Processor.Matcher.ReleaseProcessor(processor);
}
}
}
internal struct NodesetIterator
{
int index;
int indexStart;
NodeSequence sequence;
NodeSequenceItem[] items;
internal NodesetIterator(NodeSequence sequence)
{
Fx.Assert(null != sequence, "");
this.sequence = sequence;
this.items = sequence.Items;
this.index = -1;
this.indexStart = -1;
}
internal int Index
{
get
{
return this.index;
}
}
internal bool NextItem()
{
if (-1 == this.index)
{
this.index = this.indexStart;
return true;
}
if (this.items[this.index].Last)
{
return false;
}
this.index++;
return true;
}
internal bool NextNodeset()
{
this.indexStart = this.index + 1;
this.index = -1;
return (this.indexStart < this.sequence.Count);
}
}
internal struct NodeSequenceBuilder
{
ProcessingContext context;
NodeSequence sequence;
internal NodeSequenceBuilder(ProcessingContext context, NodeSequence sequence)
{
this.context = context;
this.sequence = sequence;
}
internal NodeSequenceBuilder(ProcessingContext context)
: this(context, null)
{
}
#if NO
internal NodeSequenceBuilder(NodeSequence sequence)
: this(sequence.OwnerContext, sequence)
{
}
#endif
internal NodeSequence Sequence
{
get
{
return (null != this.sequence) ? this.sequence : NodeSequence.Empty;
}
set
{
this.sequence = value;
}
}
internal void Add(ref NodeSequenceItem item)
{
if (null == this.sequence)
{
this.sequence = this.context.CreateSequence();
this.sequence.StartNodeset();
}
this.sequence.Add(ref item);
}
internal void EndNodeset()
{
if (null != this.sequence)
{
this.sequence.StopNodeset();
}
}
internal void StartNodeset()
{
if (null != this.sequence)
{
this.sequence.StartNodeset();
}
}
}
}
|