615 lines
25 KiB
C#
615 lines
25 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="asttree.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Xml.Schema {
|
||
|
using System.Xml.XPath;
|
||
|
using System.Diagnostics;
|
||
|
using System.Globalization;
|
||
|
using System.IO;
|
||
|
using System.Collections;
|
||
|
using System.Xml.Schema;
|
||
|
using MS.Internal.Xml.XPath;
|
||
|
|
||
|
/*--------------------------------------------------------------------------------------------- *
|
||
|
* Dynamic Part Below... *
|
||
|
* -------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
// stack element class
|
||
|
// this one needn't change, even the parameter in methods
|
||
|
internal class AxisElement {
|
||
|
internal DoubleLinkAxis curNode; // current under-checking node during navigating
|
||
|
internal int rootDepth; // root depth -- contextDepth + 1 if ! isDss; context + {1...} if isDss
|
||
|
internal int curDepth; // current depth
|
||
|
internal bool isMatch; // current is already matching or waiting for matching
|
||
|
|
||
|
internal DoubleLinkAxis CurNode {
|
||
|
get { return this.curNode; }
|
||
|
}
|
||
|
|
||
|
// constructor
|
||
|
internal AxisElement (DoubleLinkAxis node, int depth) {
|
||
|
this.curNode = node;
|
||
|
this.rootDepth = this.curDepth = depth;
|
||
|
this.isMatch = false;
|
||
|
}
|
||
|
|
||
|
internal void SetDepth (int depth) {
|
||
|
this.rootDepth = this.curDepth = depth;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// "a/b/c" pointer from b move to a
|
||
|
// needn't change even tree structure changes
|
||
|
internal void MoveToParent(int depth, ForwardAxis parent) {
|
||
|
// "a/b/c", trying to match b (current node), but meet the end of a, so move pointer to a
|
||
|
if ( depth == this.curDepth - 1 ) {
|
||
|
// really need to move the current node pointer to parent
|
||
|
// what i did here is for seperating the case of IsDss or only IsChild
|
||
|
// bcoz in the first case i need to expect "a" from random depth
|
||
|
// -1 means it doesn't expect some specific depth (referecing the dealing to -1 in movetochild method
|
||
|
// while in the second case i can't change the root depth which is 1.
|
||
|
if ((this.curNode.Input == parent.RootNode ) && (parent.IsDss)) {
|
||
|
this.curNode = parent.RootNode;
|
||
|
this.rootDepth = this.curDepth = -1;
|
||
|
return;
|
||
|
}
|
||
|
else if (this.curNode.Input != null) { // else cur-depth --, cur-node change
|
||
|
this.curNode = (DoubleLinkAxis) (this.curNode.Input);
|
||
|
this.curDepth --;
|
||
|
return;
|
||
|
}
|
||
|
else return;
|
||
|
}
|
||
|
// "a/b/c", trying to match b (current node), but meet the end of x (another child of a)
|
||
|
// or maybe i matched, now move out the current node
|
||
|
// or move out after failing to match attribute
|
||
|
// the node i m next expecting is still the current node
|
||
|
else if (depth == this.curDepth) { // after matched or [2] failed in matching attribute
|
||
|
if (this.isMatch) {
|
||
|
this.isMatch = false;
|
||
|
}
|
||
|
}
|
||
|
return; // this node is still what i am expecting
|
||
|
// ignore
|
||
|
}
|
||
|
|
||
|
// equal & ! attribute then move
|
||
|
// "a/b/c" pointer from a move to b
|
||
|
// return true if reach c and c is an element and c is the axis
|
||
|
internal bool MoveToChild(string name, string URN, int depth, ForwardAxis parent) {
|
||
|
// an attribute can never be the same as an element
|
||
|
if (Asttree.IsAttribute(this.curNode)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// either moveToParent or moveToChild status will have to be changed into unmatch...
|
||
|
if (this.isMatch) {
|
||
|
this.isMatch = false;
|
||
|
}
|
||
|
if (! AxisStack.Equal (this.curNode.Name, this.curNode.Urn, name, URN)) {
|
||
|
return false;
|
||
|
}
|
||
|
if (this.curDepth == -1) {
|
||
|
SetDepth (depth);
|
||
|
}
|
||
|
else if (depth > this.curDepth) {
|
||
|
return false;
|
||
|
}
|
||
|
// matched ...
|
||
|
if (this.curNode == parent.TopNode) {
|
||
|
this.isMatch = true;
|
||
|
return true;
|
||
|
}
|
||
|
// move down this.curNode
|
||
|
DoubleLinkAxis nowNode = (DoubleLinkAxis) (this.curNode.Next);
|
||
|
if (Asttree.IsAttribute (nowNode)) {
|
||
|
this.isMatch = true; // for attribute
|
||
|
return false;
|
||
|
}
|
||
|
this.curNode = nowNode;
|
||
|
this.curDepth ++;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
internal class AxisStack {
|
||
|
// property
|
||
|
private ArrayList stack; // of AxisElement
|
||
|
private ForwardAxis subtree; // reference to the corresponding subtree
|
||
|
private ActiveAxis parent;
|
||
|
|
||
|
internal ForwardAxis Subtree {
|
||
|
get { return this.subtree; }
|
||
|
}
|
||
|
|
||
|
internal int Length { // stack length
|
||
|
get {return this.stack.Count; }
|
||
|
}
|
||
|
|
||
|
// instructor
|
||
|
public AxisStack (ForwardAxis faxis, ActiveAxis parent) {
|
||
|
this.subtree = faxis;
|
||
|
this.stack = new ArrayList();
|
||
|
this.parent = parent; // need to use its contextdepth each time....
|
||
|
|
||
|
// improvement:
|
||
|
// if ! isDss, there has nothing to do with Push/Pop, only one copy each time will be kept
|
||
|
// if isDss, push and pop each time....
|
||
|
if (! faxis.IsDss) { // keep an instance
|
||
|
this.Push (1); // context depth + 1
|
||
|
}
|
||
|
// else just keep stack empty
|
||
|
}
|
||
|
|
||
|
// method
|
||
|
internal void Push (int depth) {
|
||
|
AxisElement eaxis = new AxisElement (this.subtree.RootNode, depth);
|
||
|
this.stack.Add (eaxis);
|
||
|
}
|
||
|
|
||
|
internal void Pop () {
|
||
|
this.stack.RemoveAt (Length - 1);
|
||
|
}
|
||
|
|
||
|
// used in the beginning of .// and MoveToChild
|
||
|
// didn't consider Self, only consider name
|
||
|
internal static bool Equal (string thisname, string thisURN, string name, string URN) {
|
||
|
// which means "b" in xpath, no namespace should be specified
|
||
|
if (thisURN == null) {
|
||
|
if ( !((URN == null) || (URN.Length == 0))) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
// != "*"
|
||
|
else if ((thisURN.Length != 0) && (thisURN != URN)) {
|
||
|
return false;
|
||
|
}
|
||
|
// != "a:*" || "*"
|
||
|
if ((thisname.Length != 0) && (thisname != name)) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// "a/b/c" pointer from b move to a
|
||
|
// needn't change even tree structure changes
|
||
|
internal void MoveToParent(string name, string URN, int depth) {
|
||
|
if (this.subtree.IsSelfAxis) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < this.stack.Count; ++i) {
|
||
|
((AxisElement)stack[i]).MoveToParent (depth, this.subtree);
|
||
|
}
|
||
|
|
||
|
// in ".//"'s case, since each time you push one new element while match, why not pop one too while match?
|
||
|
if ( this.subtree.IsDss && Equal (this.subtree.RootNode.Name, this.subtree.RootNode.Urn, name, URN)) {
|
||
|
Pop();
|
||
|
} // only the last one
|
||
|
}
|
||
|
|
||
|
// "a/b/c" pointer from a move to b
|
||
|
// return true if reach c
|
||
|
internal bool MoveToChild(string name, string URN, int depth) {
|
||
|
bool result = false;
|
||
|
// push first
|
||
|
if ( this.subtree.IsDss && Equal (this.subtree.RootNode.Name, this.subtree.RootNode.Urn, name, URN)) {
|
||
|
Push(-1);
|
||
|
}
|
||
|
for (int i = 0; i < this.stack.Count; ++i) {
|
||
|
if (((AxisElement)stack[i]).MoveToChild(name, URN, depth, this.subtree)) {
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// attribute can only at the topaxis part
|
||
|
// dealing with attribute only here, didn't go into stack element at all
|
||
|
// stack element only deal with moving the pointer around elements
|
||
|
internal bool MoveToAttribute(string name, string URN, int depth) {
|
||
|
if (! this.subtree.IsAttribute) {
|
||
|
return false;
|
||
|
}
|
||
|
if (! Equal (this.subtree.TopNode.Name, this.subtree.TopNode.Urn, name, URN) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool result = false;
|
||
|
|
||
|
// no stack element for single attribute, so dealing with it seperately
|
||
|
if (this.subtree.TopNode.Input == null) {
|
||
|
return (this.subtree.IsDss || (depth == 1));
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < this.stack.Count; ++i) {
|
||
|
AxisElement eaxis = (AxisElement)this.stack[i];
|
||
|
if ((eaxis.isMatch) && (eaxis.CurNode == this.subtree.TopNode.Input)) {
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// whenever an element is under identity-constraint, an instance of this class will be called
|
||
|
// only care about property at this time
|
||
|
internal class ActiveAxis {
|
||
|
// consider about reactivating.... the stack should be clear right??
|
||
|
// just reset contextDepth & isActive....
|
||
|
private int currentDepth; // current depth, trace the depth by myself... movetochild, movetoparent, movetoattribute
|
||
|
private bool isActive; // not active any more after moving out context node
|
||
|
private Asttree axisTree; // reference to the whole tree
|
||
|
// for each subtree i need to keep a stack...
|
||
|
private ArrayList axisStack; // of AxisStack
|
||
|
|
||
|
public int CurrentDepth {
|
||
|
get { return this.currentDepth; }
|
||
|
}
|
||
|
|
||
|
// if an instance is !IsActive, then it can be reactive and reuse
|
||
|
// still need thinking.....
|
||
|
internal void Reactivate () {
|
||
|
this.isActive = true;
|
||
|
this.currentDepth = -1;
|
||
|
}
|
||
|
|
||
|
internal ActiveAxis (Asttree axisTree) {
|
||
|
this.axisTree = axisTree; // only a pointer. do i need it?
|
||
|
this.currentDepth = -1; // context depth is 0 -- enforce moveToChild for the context node
|
||
|
// otherwise can't deal with "." node
|
||
|
this.axisStack = new ArrayList(axisTree.SubtreeArray.Count); // defined length
|
||
|
// new one stack element for each one
|
||
|
for (int i = 0; i < axisTree.SubtreeArray.Count; ++i) {
|
||
|
AxisStack stack = new AxisStack ((ForwardAxis)axisTree.SubtreeArray[i], this);
|
||
|
axisStack.Add (stack);
|
||
|
}
|
||
|
this.isActive = true;
|
||
|
}
|
||
|
|
||
|
public bool MoveToStartElement (string localname, string URN) {
|
||
|
if (!isActive) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// for each:
|
||
|
this.currentDepth ++;
|
||
|
bool result = false;
|
||
|
for (int i = 0; i < this.axisStack.Count; ++i) {
|
||
|
AxisStack stack = (AxisStack)this.axisStack[i];
|
||
|
// special case for self tree "." | ".//."
|
||
|
if (stack.Subtree.IsSelfAxis) {
|
||
|
if ( stack.Subtree.IsDss || (this.CurrentDepth == 0))
|
||
|
result = true;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// otherwise if it's context node then return false
|
||
|
if (this.CurrentDepth == 0) continue;
|
||
|
|
||
|
if (stack.MoveToChild (localname, URN, this.currentDepth)) {
|
||
|
result = true;
|
||
|
// even already know the last result is true, still need to continue...
|
||
|
// run everyone once
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// return result doesn't have any meaning until in SelectorActiveAxis
|
||
|
public virtual bool EndElement (string localname, string URN) {
|
||
|
// need to think if the early quitting will affect reactivating....
|
||
|
if (this.currentDepth == 0) { // leave context node
|
||
|
this.isActive = false;
|
||
|
this.currentDepth --;
|
||
|
}
|
||
|
if (! this.isActive) {
|
||
|
return false;
|
||
|
}
|
||
|
for (int i = 0; i < this.axisStack.Count; ++i) {
|
||
|
((AxisStack)axisStack[i]).MoveToParent (localname, URN, this.currentDepth);
|
||
|
}
|
||
|
this.currentDepth -- ;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Secondly field interface
|
||
|
public bool MoveToAttribute (string localname, string URN) {
|
||
|
if (! this.isActive) {
|
||
|
return false;
|
||
|
}
|
||
|
bool result = false;
|
||
|
for (int i = 0; i < this.axisStack.Count; ++i) {
|
||
|
if (((AxisStack)axisStack[i]).MoveToAttribute(localname, URN, this.currentDepth + 1)) { // don't change depth for attribute, but depth is add 1
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- *
|
||
|
* Static Part Below... *
|
||
|
* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
// each node in the xpath tree
|
||
|
internal class DoubleLinkAxis : Axis {
|
||
|
internal Axis next;
|
||
|
|
||
|
internal Axis Next {
|
||
|
get { return this.next; }
|
||
|
set { this.next = value; }
|
||
|
}
|
||
|
|
||
|
//constructor
|
||
|
internal DoubleLinkAxis(Axis axis, DoubleLinkAxis inputaxis)
|
||
|
: base(axis.TypeOfAxis, inputaxis, axis.Prefix, axis.Name, axis.NodeType) {
|
||
|
this.next = null;
|
||
|
this.Urn = axis.Urn;
|
||
|
this.abbrAxis = axis.AbbrAxis;
|
||
|
if (inputaxis != null) {
|
||
|
inputaxis.Next = this;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// recursive here
|
||
|
internal static DoubleLinkAxis ConvertTree (Axis axis) {
|
||
|
if (axis == null) {
|
||
|
return null;
|
||
|
}
|
||
|
return ( new DoubleLinkAxis (axis, ConvertTree ((Axis) (axis.Input))));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// only keep axis, rootNode, isAttribute, isDss inside
|
||
|
// act as an element tree for the Asttree
|
||
|
internal class ForwardAxis {
|
||
|
// Axis tree
|
||
|
private DoubleLinkAxis topNode;
|
||
|
private DoubleLinkAxis rootNode; // the root for reverse Axis
|
||
|
|
||
|
// Axis tree property
|
||
|
private bool isAttribute; // element or attribute? "@"?
|
||
|
private bool isDss; // has ".//" in front of it?
|
||
|
private bool isSelfAxis; // only one node in the tree, and it's "." (self) node
|
||
|
|
||
|
internal DoubleLinkAxis RootNode {
|
||
|
get { return this.rootNode; }
|
||
|
}
|
||
|
|
||
|
internal DoubleLinkAxis TopNode {
|
||
|
get { return this.topNode; }
|
||
|
}
|
||
|
|
||
|
internal bool IsAttribute {
|
||
|
get { return this.isAttribute; }
|
||
|
}
|
||
|
|
||
|
// has ".//" in front of it?
|
||
|
internal bool IsDss {
|
||
|
get { return this.isDss; }
|
||
|
}
|
||
|
|
||
|
internal bool IsSelfAxis {
|
||
|
get { return this.isSelfAxis; }
|
||
|
}
|
||
|
|
||
|
public ForwardAxis (DoubleLinkAxis axis, bool isdesorself) {
|
||
|
this.isDss = isdesorself;
|
||
|
this.isAttribute = Asttree.IsAttribute (axis);
|
||
|
this.topNode = axis;
|
||
|
this.rootNode = axis;
|
||
|
while ( this.rootNode.Input != null ) {
|
||
|
this.rootNode = (DoubleLinkAxis)(this.rootNode.Input);
|
||
|
}
|
||
|
// better to calculate it out, since it's used so often, and if the top is self then the whole tree is self
|
||
|
this.isSelfAxis = Asttree.IsSelf (this.topNode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// static, including an array of ForwardAxis (this is the whole picture)
|
||
|
internal class Asttree {
|
||
|
// set private then give out only get access, to keep it intact all along
|
||
|
private ArrayList fAxisArray;
|
||
|
private string xpathexpr;
|
||
|
private bool isField; // field or selector
|
||
|
private XmlNamespaceManager nsmgr;
|
||
|
|
||
|
internal ArrayList SubtreeArray {
|
||
|
get { return fAxisArray; }
|
||
|
}
|
||
|
|
||
|
// when making a new instance for Asttree, we do the compiling, and create the static tree instance
|
||
|
public Asttree (string xPath, bool isField, XmlNamespaceManager nsmgr) {
|
||
|
this.xpathexpr = xPath;
|
||
|
this.isField = isField;
|
||
|
this.nsmgr = nsmgr;
|
||
|
// checking grammar... and build fAxisArray
|
||
|
this.CompileXPath (xPath, isField, nsmgr); // might throw exception in the middle
|
||
|
}
|
||
|
|
||
|
// only for debug
|
||
|
#if DEBUG
|
||
|
public void PrintTree (StreamWriter msw) {
|
||
|
for (int i = 0; i < fAxisArray.Count; ++i) {
|
||
|
ForwardAxis axis = (ForwardAxis)fAxisArray[i];
|
||
|
msw.WriteLine("<Tree IsDss=\"{0}\" IsAttribute=\"{1}\">", axis.IsDss, axis.IsAttribute);
|
||
|
DoubleLinkAxis printaxis = axis.TopNode;
|
||
|
while ( printaxis != null ) {
|
||
|
msw.WriteLine (" <node>");
|
||
|
msw.WriteLine (" <URN> {0} </URN>", printaxis.Urn);
|
||
|
msw.WriteLine (" <Prefix> {0} </Prefix>", printaxis.Prefix);
|
||
|
msw.WriteLine (" <Name> {0} </Name>", printaxis.Name);
|
||
|
msw.WriteLine (" <NodeType> {0} </NodeType>", printaxis.NodeType);
|
||
|
msw.WriteLine (" <AxisType> {0} </AxisType>", printaxis.TypeOfAxis);
|
||
|
msw.WriteLine (" </node>");
|
||
|
printaxis = (DoubleLinkAxis) (printaxis.Input);
|
||
|
}
|
||
|
msw.WriteLine ("</Tree>");
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// this part is for parsing restricted xpath from grammar
|
||
|
private static bool IsNameTest(Axis ast) {
|
||
|
// Type = Element, abbrAxis = false
|
||
|
// all are the same, has child:: or not
|
||
|
return ((ast.TypeOfAxis == Axis.AxisType.Child) && (ast.NodeType == XPathNodeType.Element));
|
||
|
}
|
||
|
|
||
|
internal static bool IsAttribute(Axis ast) {
|
||
|
return ((ast.TypeOfAxis == Axis.AxisType.Attribute) && (ast.NodeType == XPathNodeType.Attribute));
|
||
|
}
|
||
|
|
||
|
private static bool IsDescendantOrSelf(Axis ast) {
|
||
|
return ((ast.TypeOfAxis == Axis.AxisType.DescendantOrSelf) && (ast.NodeType == XPathNodeType.All) && (ast.AbbrAxis));
|
||
|
}
|
||
|
|
||
|
internal static bool IsSelf(Axis ast) {
|
||
|
return ((ast.TypeOfAxis == Axis.AxisType.Self) && (ast.NodeType == XPathNodeType.All) && (ast.AbbrAxis));
|
||
|
}
|
||
|
|
||
|
// don't return true or false, if it's invalid path, just throw exception during the process
|
||
|
// for whitespace thing, i will directly trim the tree built here...
|
||
|
public void CompileXPath (string xPath, bool isField, XmlNamespaceManager nsmgr) {
|
||
|
if ((xPath == null) || (xPath.Length == 0)) {
|
||
|
throw new XmlSchemaException(Res.Sch_EmptyXPath, string.Empty);
|
||
|
}
|
||
|
|
||
|
// firstly i still need to have an ArrayList to store tree only...
|
||
|
// can't new ForwardAxis right away
|
||
|
string[] xpath = xPath.Split('|');
|
||
|
ArrayList AstArray = new ArrayList(xpath.Length);
|
||
|
this.fAxisArray = new ArrayList(xpath.Length);
|
||
|
|
||
|
// throw compile exceptions
|
||
|
// can i only new one builder here then run compile several times??
|
||
|
try {
|
||
|
for (int i = 0; i < xpath.Length; ++i) {
|
||
|
// default ! isdesorself (no .//)
|
||
|
Axis ast = (Axis) (XPathParser.ParseXPathExpresion(xpath[i]));
|
||
|
AstArray.Add (ast);
|
||
|
}
|
||
|
}
|
||
|
catch {
|
||
|
throw new XmlSchemaException(Res.Sch_ICXpathError, xPath);
|
||
|
}
|
||
|
|
||
|
Axis stepAst;
|
||
|
for (int i = 0; i < AstArray.Count; ++i) {
|
||
|
Axis ast = (Axis) AstArray[i];
|
||
|
// Restricted form
|
||
|
// field can have an attribute:
|
||
|
|
||
|
// throw exceptions during casting
|
||
|
if ((stepAst = ast) == null) {
|
||
|
throw new XmlSchemaException(Res.Sch_ICXpathError, xPath);
|
||
|
}
|
||
|
|
||
|
Axis top = stepAst;
|
||
|
|
||
|
// attribute will have namespace too
|
||
|
// field can have top attribute
|
||
|
if (IsAttribute (stepAst)) {
|
||
|
if (! isField) {
|
||
|
throw new XmlSchemaException(Res.Sch_SelectorAttr, xPath);
|
||
|
}
|
||
|
else {
|
||
|
SetURN (stepAst, nsmgr);
|
||
|
try {
|
||
|
stepAst = (Axis) (stepAst.Input);
|
||
|
}
|
||
|
catch {
|
||
|
throw new XmlSchemaException(Res.Sch_ICXpathError, xPath);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// field or selector
|
||
|
while ((stepAst != null) && (IsNameTest (stepAst) || IsSelf (stepAst))) {
|
||
|
// trim tree "." node, if it's not the top one
|
||
|
if (IsSelf (stepAst) && (ast != stepAst)) {
|
||
|
top.Input = stepAst.Input;
|
||
|
}
|
||
|
else {
|
||
|
top = stepAst;
|
||
|
// set the URN
|
||
|
if (IsNameTest(stepAst)) {
|
||
|
SetURN (stepAst, nsmgr);
|
||
|
}
|
||
|
}
|
||
|
try {
|
||
|
stepAst = (Axis) (stepAst.Input);
|
||
|
}
|
||
|
catch {
|
||
|
throw new XmlSchemaException(Res.Sch_ICXpathError, xPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// the rest part can only be .// or null
|
||
|
// trim the rest part, but need compile the rest part first
|
||
|
top.Input = null;
|
||
|
if (stepAst == null) { // top "." and has other element beneath, trim this "." node too
|
||
|
if (IsSelf(ast) && (ast.Input != null)) {
|
||
|
this.fAxisArray.Add ( new ForwardAxis ( DoubleLinkAxis.ConvertTree ((Axis) (ast.Input)), false));
|
||
|
}
|
||
|
else {
|
||
|
this.fAxisArray.Add ( new ForwardAxis ( DoubleLinkAxis.ConvertTree (ast), false));
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
if (! IsDescendantOrSelf (stepAst)) {
|
||
|
throw new XmlSchemaException(Res.Sch_ICXpathError, xPath);
|
||
|
}
|
||
|
try {
|
||
|
stepAst = (Axis) (stepAst.Input);
|
||
|
}
|
||
|
catch {
|
||
|
throw new XmlSchemaException(Res.Sch_ICXpathError, xPath);
|
||
|
}
|
||
|
if ((stepAst == null) || (! IsSelf (stepAst)) || (stepAst.Input != null)) {
|
||
|
throw new XmlSchemaException(Res.Sch_ICXpathError, xPath);
|
||
|
}
|
||
|
|
||
|
// trim top "." if it's not the only node
|
||
|
if (IsSelf(ast) && (ast.Input != null)) {
|
||
|
this.fAxisArray.Add ( new ForwardAxis ( DoubleLinkAxis.ConvertTree ((Axis) (ast.Input)), true));
|
||
|
}
|
||
|
else {
|
||
|
this.fAxisArray.Add ( new ForwardAxis ( DoubleLinkAxis.ConvertTree (ast), true));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// depending on axis.Name & axis.Prefix, i will set the axis.URN;
|
||
|
// also, record urn from prefix during this
|
||
|
// 4 different types of element or attribute (with @ before it) combinations:
|
||
|
// (1) a:b (2) b (3) * (4) a:*
|
||
|
// i will check xpath to be strictly conformed from these forms
|
||
|
// for (1) & (4) i will have URN set properly
|
||
|
// for (2) the URN is null
|
||
|
// for (3) the URN is empty
|
||
|
private void SetURN (Axis axis, XmlNamespaceManager nsmgr) {
|
||
|
if (axis.Prefix.Length != 0) { // (1) (4)
|
||
|
axis.Urn = nsmgr.LookupNamespace(axis.Prefix);
|
||
|
|
||
|
if (axis.Urn == null) {
|
||
|
throw new XmlSchemaException(Res.Sch_UnresolvedPrefix, axis.Prefix);
|
||
|
}
|
||
|
} else if (axis.Name.Length != 0) { // (2)
|
||
|
axis.Urn = null;
|
||
|
} else { // (3)
|
||
|
axis.Urn = "";
|
||
|
}
|
||
|
}
|
||
|
}// Asttree
|
||
|
}
|