File: System\Xml\Xsl\XsltOld\Processor.cs
Project: ndp\fx\src\XmlUtils\System.Data.SqlXml.csproj (System.Data.SqlXml)
//------------------------------------------------------------------------------
// <copyright file="Processor.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
 
namespace System.Xml.Xsl.XsltOld {
    using Res = System.Xml.Utils.Res;
    using System.Globalization;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Xml.XPath;
    using MS.Internal.Xml.XPath;
    using System.Text;
    using System.Collections;
    using System.Collections.Generic;
    using System.Xml.Xsl.XsltOld.Debugger;
    using System.Reflection;
    using System.Security;
 
    internal sealed class Processor : IXsltProcessor {
        //
        // Static constants
        //
 
        const int StackIncrement = 10;
 
        //
        // Execution result
        //
 
        internal enum ExecResult {
            Continue,           // Continues next iteration immediately
            Interrupt,          // Returns to caller, was processed enough
            Done                // Execution finished
        }
 
        internal enum OutputResult {
            Continue,
            Interrupt,
            Overflow,
            Error,
            Ignore
        }
 
        private ExecResult     execResult;
 
        //
        // Compiled stylesheet
        //
 
        private Stylesheet      stylesheet;     // Root of import tree of template managers
        private RootAction      rootAction;
        private Key[]           keyList;
        private List<TheQuery>  queryStore;
        public  PermissionSet   permissions;   // used by XsltCompiledContext in document and extension functions
 
        //
        // Document Being transformed
        //
 
        private XPathNavigator    document;
 
        //
        // Execution action stack
        //
 
        private HWStack         actionStack;
        private HWStack         debuggerStack;
 
        //
        // Register for returning value from calling nested action
        //
 
        private StringBuilder   sharedStringBuilder;
 
        //
        // Output related member variables
        //
        int                     ignoreLevel;
        StateMachine            xsm;
        RecordBuilder           builder;
 
        XsltOutput              output;
 
        XmlNameTable            nameTable      = new NameTable();
 
        XmlResolver             resolver;
 
#pragma warning disable 618
        XsltArgumentList        args;
#pragma warning restore 618
        Hashtable               scriptExtensions;
 
        ArrayList               numberList;
        //
        // Template lookup action
        //
 
        TemplateLookupAction    templateLookup = new TemplateLookupAction();
 
        private IXsltDebugger   debugger;
        Query[]                 queryList;
 
        private ArrayList sortArray;
 
        private Hashtable documentCache;
 
        // NOTE: ValueOf() can call Matches() through XsltCompileContext.PreserveWhitespace(),
        // that's why we use two different contexts here, valueOfContext and matchesContext
        private XsltCompileContext  valueOfContext;
        private XsltCompileContext  matchesContext;
 
        internal XPathNavigator Current {
            get {
                ActionFrame frame = (ActionFrame) this.actionStack.Peek();
                return frame != null ? frame.Node : null;
            }
        }
 
        internal ExecResult ExecutionResult {
            get { return this.execResult; }
 
            set {
                Debug.Assert(this.execResult == ExecResult.Continue);
                this.execResult = value;
            }
        }
 
        internal Stylesheet Stylesheet {
            get { return this.stylesheet; }
        }
 
        internal XmlResolver Resolver {
            get {
                Debug.Assert(this.resolver != null, "Constructor should create it if null passed");
                return this.resolver;
            }
        }
 
        internal ArrayList SortArray {
            get {
                Debug.Assert(this.sortArray != null, "InitSortArray() wasn't called");
                return this.sortArray;
            }
        }
 
        internal Key[] KeyList {
            get { return this.keyList; }
        }
 
        [SuppressMessage("Microsoft.Security.Xml", "CA3068:TextReaderImplNeedsSettingsAndResolver", Justification="XmlResolver and DtdProcessing is set on XmlTextReaderImpl after it is constructed or default to null resolver / prohibit DTD")]
        internal XPathNavigator GetNavigator(Uri ruri) {
            XPathNavigator result = null;
            if (documentCache != null) {
                result = documentCache[ruri] as XPathNavigator;
                if (result != null) {
                    return result.Clone();
                }
            }
            else {
                documentCache = new Hashtable();
            }
 
            Object input = resolver.GetEntity(ruri, null, null);
            if (input is Stream) {
                XmlTextReaderImpl tr  = new XmlTextReaderImpl(ruri.ToString(), (Stream) input); {
                    tr.XmlResolver = this.resolver;
                }
                // reader is closed by Compiler.LoadDocument()
                result = ((IXPathNavigable)Compiler.LoadDocument(tr)).CreateNavigator();
            }
            else if (input is XPathNavigator){
                result = (XPathNavigator) input;
            }
            else {
                throw XsltException.Create(Res.Xslt_CantResolve, ruri.ToString());
            }
            documentCache[ruri] = result.Clone();
            return result;
        }
 
        internal void AddSort(Sort sortinfo) {
            Debug.Assert(this.sortArray != null, "InitSortArray() wasn't called");
            this.sortArray.Add(sortinfo);
        }
 
        internal void InitSortArray() {
            if (this.sortArray == null) {
                this.sortArray = new ArrayList();
            }
            else {
                this.sortArray.Clear();
            }
        }
 
        internal object GetGlobalParameter(XmlQualifiedName qname) {
            object parameter = args.GetParam(qname.Name, qname.Namespace);
            if (parameter == null) {
                return null;
            }
            // 
            if (
                parameter is XPathNodeIterator ||
                parameter is XPathNavigator ||
                parameter is Boolean ||
                parameter is Double ||
                parameter is String
            ) {
                // doing nothing
            } else if (
                parameter is Int16 || parameter is UInt16 ||
                parameter is Int32 || parameter is UInt32 ||
                parameter is Int64 || parameter is UInt64 ||
                parameter is Single || parameter is Decimal
            ) {
                parameter = XmlConvert.ToXPathDouble(parameter);
            } else {
                parameter = parameter.ToString();
            }
            return parameter;
        }
 
        internal object GetExtensionObject(string nsUri) {
            return args.GetExtensionObject(nsUri);
        }
 
        internal object GetScriptObject(string nsUri) {
            return scriptExtensions[nsUri];
        }
 
        internal RootAction RootAction {
            get { return this.rootAction; }
        }
 
        internal XPathNavigator Document {
            get { return this.document; }
        }
 
#if DEBUG
        private bool stringBuilderLocked = false;
#endif
 
        internal StringBuilder GetSharedStringBuilder() {
#if DEBUG
            Debug.Assert(! stringBuilderLocked);
#endif
            if (sharedStringBuilder == null) {
                sharedStringBuilder = new StringBuilder();
            }
            else {
                sharedStringBuilder.Length = 0;
            }
#if DEBUG
            stringBuilderLocked = true;
#endif
            return sharedStringBuilder;
        }
 
        internal void ReleaseSharedStringBuilder() {
            // don't clean stringBuilderLocked here. ToString() will happen after this call
#if DEBUG
            stringBuilderLocked = false;
#endif
        }
 
        internal ArrayList NumberList {
            get {
                if (this.numberList == null) {
                    this.numberList = new ArrayList();
                }
                return this.numberList;
            }
        }
 
        internal IXsltDebugger Debugger {
            get { return this.debugger; }
        }
 
        internal HWStack ActionStack {
            get { return this.actionStack; }
        }
 
        internal RecordBuilder Builder {
            get { return this.builder; }
        }
 
        internal XsltOutput Output {
            get { return this.output; }
        }
 
        //
        // Construction
        //
        public Processor(
            XPathNavigator doc, XsltArgumentList args, XmlResolver resolver,
            Stylesheet stylesheet, List<TheQuery> queryStore, RootAction rootAction,
            IXsltDebugger debugger
        ) {
            this.stylesheet = stylesheet;
            this.queryStore = queryStore;
            this.rootAction = rootAction;
            this.queryList  = new Query[queryStore.Count]; {
                for(int i = 0; i < queryStore.Count; i ++) {
                    queryList[i] = Query.Clone(queryStore[i].CompiledQuery.QueryTree);
                }
            }
 
            this.xsm                 = new StateMachine();
            this.document            = doc;
            this.builder             = null;
            this.actionStack         = new HWStack(StackIncrement);
            this.output              = this.rootAction.Output;
            this.permissions         = this.rootAction.permissions;
            this.resolver            = resolver ?? XmlNullResolver.Singleton;
            this.args                = args     ?? new XsltArgumentList();
            this.debugger            = debugger;
            if (this.debugger != null) {
                this.debuggerStack = new HWStack(StackIncrement, /*limit:*/1000);
                templateLookup     = new TemplateLookupActionDbg();
            }
 
            // Clone the compile-time KeyList
            if (this.rootAction.KeyList != null) {
                this.keyList = new Key[this.rootAction.KeyList.Count];
                for (int i = 0; i < this.keyList.Length; i ++) {
                    this.keyList[i] = this.rootAction.KeyList[i].Clone();
                }
            }
 
            this.scriptExtensions = new Hashtable(this.stylesheet.ScriptObjectTypes.Count); {
                foreach(DictionaryEntry entry in this.stylesheet.ScriptObjectTypes) {
                    string namespaceUri = (string)entry.Key;
                    if (GetExtensionObject(namespaceUri) != null) {
                        throw XsltException.Create(Res.Xslt_ScriptDub, namespaceUri);
                    }
                    scriptExtensions.Add(namespaceUri, Activator.CreateInstance((Type)entry.Value,
                        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, null, null, null));
                }
            }
 
            this.PushActionFrame(this.rootAction, /*nodeSet:*/null);
        }
 
        public ReaderOutput StartReader() {
            ReaderOutput output = new ReaderOutput(this);
            this.builder = new RecordBuilder(output, this.nameTable);
            return output;
        }
 
        public void Execute(Stream stream) {
            RecordOutput recOutput = null;
 
            switch (this.output.Method) {
            case XsltOutput.OutputMethod.Text:
                recOutput = new TextOnlyOutput(this, stream);
                break;
            case XsltOutput.OutputMethod.Xml:
            case XsltOutput.OutputMethod.Html:
            case XsltOutput.OutputMethod.Other:
            case XsltOutput.OutputMethod.Unknown:
                recOutput = new TextOutput(this, stream);
                break;
            }
            this.builder = new RecordBuilder(recOutput, this.nameTable);
            Execute();
        }
 
        public void Execute(TextWriter writer) {
            RecordOutput recOutput = null;
 
            switch (this.output.Method) {
            case XsltOutput.OutputMethod.Text:
                recOutput = new TextOnlyOutput(this, writer);
                break;
            case XsltOutput.OutputMethod.Xml:
            case XsltOutput.OutputMethod.Html:
            case XsltOutput.OutputMethod.Other:
            case XsltOutput.OutputMethod.Unknown:
                recOutput = new TextOutput(this, writer);
                break;
            }
            this.builder = new RecordBuilder(recOutput, this.nameTable);
            Execute();
        }
 
        public void Execute(XmlWriter writer) {
            this.builder = new RecordBuilder(new WriterOutput(this, writer), this.nameTable);
            Execute();
        }
 
        //
        //  Execution part of processor
        //
        internal void Execute() {
            Debug.Assert(this.actionStack != null);
 
            while (this.execResult == ExecResult.Continue) {
                ActionFrame frame = (ActionFrame) this.actionStack.Peek();
 
                if (frame == null) {
                    Debug.Assert(this.builder != null);
                    this.builder.TheEnd();
                    ExecutionResult = ExecResult.Done;
                    break;
                }
 
                // Execute the action which was on the top of the stack
                if (frame.Execute(this)) {
                    this.actionStack.Pop();
                }
            }
 
            if (this.execResult == ExecResult.Interrupt) {
                this.execResult = ExecResult.Continue;
            }
        }
 
        //
        // Action frame support
        //
 
        internal ActionFrame PushNewFrame() {
            ActionFrame prent = (ActionFrame) this.actionStack.Peek();
            ActionFrame frame = (ActionFrame) this.actionStack.Push();
            if (frame == null) {
                frame = new ActionFrame();
                this.actionStack.AddToTop(frame);
            }
            Debug.Assert(frame != null);
 
            if (prent != null) {
                frame.Inherit(prent);
            }
 
            return frame;
        }
 
        internal void PushActionFrame(Action action, XPathNodeIterator nodeSet) {
            ActionFrame frame = PushNewFrame();
            frame.Init(action, nodeSet);
        }
 
        internal void PushActionFrame(ActionFrame container) {
            this.PushActionFrame(container, container.NodeSet);
        }
 
        internal void PushActionFrame(ActionFrame container, XPathNodeIterator nodeSet) {
            ActionFrame frame = PushNewFrame();
            frame.Init(container, nodeSet);
        }
 
        internal void PushTemplateLookup(XPathNodeIterator nodeSet, XmlQualifiedName mode, Stylesheet importsOf) {
            Debug.Assert(this.templateLookup != null);
            this.templateLookup.Initialize(mode, importsOf);
            PushActionFrame(this.templateLookup, nodeSet);
        }
 
        internal string GetQueryExpression(int key) {
            Debug.Assert(key != Compiler.InvalidQueryKey);
            return this.queryStore[key].CompiledQuery.Expression;
        }
 
        internal Query GetCompiledQuery(int key) {
            Debug.Assert(key != Compiler.InvalidQueryKey);
            TheQuery theQuery = this.queryStore[key];
            theQuery.CompiledQuery.CheckErrors();
            Query expr = Query.Clone(this.queryList[key]);
            expr.SetXsltContext(new XsltCompileContext(theQuery._ScopeManager, this));
            return expr;
        }
 
        internal Query GetValueQuery(int key) {
            return GetValueQuery(key, null);
        }
 
        internal Query GetValueQuery(int key, XsltCompileContext context) {
            Debug.Assert(key != Compiler.InvalidQueryKey);
            TheQuery theQuery = this.queryStore[key];
            theQuery.CompiledQuery.CheckErrors();
            Query expr = this.queryList[key];
 
            if (context == null) {
                context = new XsltCompileContext(theQuery._ScopeManager, this);
            } else {
                context.Reinitialize(theQuery._ScopeManager, this);
            }
 
            expr.SetXsltContext(context);
            return expr;
        }
 
        private XsltCompileContext GetValueOfContext() {
            if (this.valueOfContext == null) {
                this.valueOfContext = new XsltCompileContext();
            }
            return this.valueOfContext;
        }
 
        [Conditional("DEBUG")]
        private void RecycleValueOfContext() {
            if (this.valueOfContext != null) {
                this.valueOfContext.Recycle();
            }
        }
 
        private XsltCompileContext GetMatchesContext() {
            if (this.matchesContext == null) {
                this.matchesContext = new XsltCompileContext();
            }
            return this.matchesContext;
        }
 
        [Conditional("DEBUG")]
        private void RecycleMatchesContext() {
            if (this.matchesContext != null) {
                this.matchesContext.Recycle();
            }
        }
 
        internal String ValueOf(ActionFrame context, int key) {
            string result;
 
            Query query = this.GetValueQuery(key, GetValueOfContext());
            object value = query.Evaluate(context.NodeSet);
            if (value is XPathNodeIterator) {
                XPathNavigator n = query.Advance();
                result = n != null ? ValueOf(n) : string.Empty;
            } else {
                result = XmlConvert.ToXPathString(value);
            }
 
            RecycleValueOfContext();
            return result;
        }
 
        internal String ValueOf(XPathNavigator n) {
            if (this.stylesheet.Whitespace && n.NodeType == XPathNodeType.Element) {
                StringBuilder builder = this.GetSharedStringBuilder();
                ElementValueWithoutWS(n, builder);
                this.ReleaseSharedStringBuilder();
                return builder.ToString();
            }
            return n.Value;
        }
 
        private void ElementValueWithoutWS(XPathNavigator nav, StringBuilder builder) {
            Debug.Assert(nav.NodeType == XPathNodeType.Element);
            bool preserve = this.Stylesheet.PreserveWhiteSpace(this, nav);
            if (nav.MoveToFirstChild()) {
                do {
                    switch (nav.NodeType) {
                    case XPathNodeType.Text :
                    case XPathNodeType.SignificantWhitespace :
                        builder.Append(nav.Value);
                        break;
                    case XPathNodeType.Whitespace :
                        if (preserve) {
                            builder.Append(nav.Value);
                        }
                        break;
                    case XPathNodeType.Element :
                        ElementValueWithoutWS(nav, builder);
                        break;
                    }
                }while (nav.MoveToNext());
                nav.MoveToParent();
            }
        }
 
        internal XPathNodeIterator StartQuery(XPathNodeIterator context, int key) {
            Query query = GetCompiledQuery(key);
            object result = query.Evaluate(context);
            if (result is XPathNodeIterator) {
                // ToDo: We create XPathSelectionIterator to count positions, but it's better create special query in this case at compile time.
                return new XPathSelectionIterator(context.Current, query);
            }
            throw XsltException.Create(Res.XPath_NodeSetExpected);
        }
 
        internal object Evaluate(ActionFrame context, int key) {
            return GetValueQuery(key).Evaluate(context.NodeSet);
        }
 
        internal object RunQuery(ActionFrame context, int key) {
            Query query = GetCompiledQuery(key);
            object value = query.Evaluate(context.NodeSet);
            XPathNodeIterator it = value as XPathNodeIterator;
            if (it != null) {
                return new XPathArrayIterator(it);
            }
 
            return value;
        }
 
        internal string EvaluateString(ActionFrame context, int key) {
            object objValue = Evaluate(context, key);
            string value = null;
            if (objValue != null)
                value = XmlConvert.ToXPathString(objValue);
            if (value == null)
                value = string.Empty;
            return value;
        }
 
        internal bool EvaluateBoolean(ActionFrame context, int key) {
            object objValue = Evaluate(context, key);
 
            if (objValue != null) {
                XPathNavigator nav = objValue as XPathNavigator;
                return nav != null ? Convert.ToBoolean(nav.Value, CultureInfo.InvariantCulture) : Convert.ToBoolean(objValue, CultureInfo.InvariantCulture);
            }
            else {
                return false;
            }
        }
 
        internal bool Matches(XPathNavigator context, int key) {
            // We don't use XPathNavigator.Matches() to avoid cloning of Query on each call
            Query query = this.GetValueQuery(key, GetMatchesContext());
 
            try {
                bool result = query.MatchNode(context) != null;
 
                RecycleMatchesContext();
                return result;
            } catch(XPathException) {
                throw XsltException.Create(Res.Xslt_InvalidPattern, this.GetQueryExpression(key));
            }
        }
 
        //
        // Outputting part of processor
        //
 
        internal XmlNameTable NameTable {
            get { return this.nameTable; }
        }
 
        internal bool CanContinue {
            get { return this.execResult == ExecResult.Continue; }
        }
 
        internal bool ExecutionDone {
            get { return this.execResult == ExecResult.Done; }
        }
 
        internal void ResetOutput() {
            Debug.Assert(this.builder != null);
            this.builder.Reset();
        }
        internal bool BeginEvent(XPathNodeType nodeType, string prefix, string name, string nspace, bool empty) {
            return BeginEvent(nodeType, prefix, name,  nspace,  empty, null, true);
        }
 
        internal bool BeginEvent(XPathNodeType nodeType, string prefix, string name, string nspace, bool empty, Object htmlProps, bool search) {
            Debug.Assert(this.xsm != null);
 
            int stateOutlook = this.xsm.BeginOutlook(nodeType);
 
            if (this.ignoreLevel > 0 || stateOutlook == StateMachine.Error) {
                this.ignoreLevel ++;
                return true;                        // We consumed the event, so pretend it was output.
            }
 
            switch (this.builder.BeginEvent(stateOutlook, nodeType, prefix, name, nspace, empty, htmlProps, search)) {
            case OutputResult.Continue:
                this.xsm.Begin(nodeType);
                Debug.Assert(StateMachine.StateOnly(stateOutlook) == this.xsm.State);
                Debug.Assert(ExecutionResult == ExecResult.Continue);
                return true;
            case OutputResult.Interrupt:
                this.xsm.Begin(nodeType);
                Debug.Assert(StateMachine.StateOnly(stateOutlook) == this.xsm.State);
                ExecutionResult = ExecResult.Interrupt;
                return true;
            case OutputResult.Overflow:
                ExecutionResult = ExecResult.Interrupt;
                return false;
            case OutputResult.Error:
                this.ignoreLevel ++;
                return true;
            case OutputResult.Ignore:
                return true;
            default:
                Debug.Fail("Unexpected result of RecordBuilder.BeginEvent()");
                return true;
            }
        }
 
        internal bool TextEvent(string text) {
            return this.TextEvent(text, false);
        }
 
        internal bool TextEvent(string text, bool disableOutputEscaping) {
            Debug.Assert(this.xsm != null);
 
            if (this.ignoreLevel > 0) {
                return true;
            }
 
            int stateOutlook = this.xsm.BeginOutlook(XPathNodeType.Text);
 
            switch (this.builder.TextEvent(stateOutlook, text, disableOutputEscaping)) {
                case OutputResult.Continue:
                this.xsm.Begin(XPathNodeType.Text);
                Debug.Assert(StateMachine.StateOnly(stateOutlook) == this.xsm.State);
                Debug.Assert(ExecutionResult == ExecResult.Continue);
                return true;
            case OutputResult.Interrupt:
                this.xsm.Begin(XPathNodeType.Text);
                Debug.Assert(StateMachine.StateOnly(stateOutlook) == this.xsm.State);
                ExecutionResult = ExecResult.Interrupt;
                return true;
            case OutputResult.Overflow:
                ExecutionResult = ExecResult.Interrupt;
                return false;
            case OutputResult.Error:
            case OutputResult.Ignore:
                return true;
            default:
                Debug.Fail("Unexpected result of RecordBuilder.TextEvent()");
                return true;
            }
        }
 
        internal bool EndEvent(XPathNodeType nodeType) {
            Debug.Assert(this.xsm != null);
 
            if (this.ignoreLevel > 0) {
                this.ignoreLevel --;
                return true;
            }
 
            int stateOutlook = this.xsm.EndOutlook(nodeType);
 
            switch (this.builder.EndEvent(stateOutlook, nodeType)) {
                case OutputResult.Continue:
                this.xsm.End(nodeType);
                Debug.Assert(StateMachine.StateOnly(stateOutlook) == this.xsm.State);
                return true;
            case OutputResult.Interrupt:
                this.xsm.End(nodeType);
                Debug.Assert(StateMachine.StateOnly(stateOutlook) == this.xsm.State,
                             "StateMachine.StateOnly(stateOutlook) == this.xsm.State");
                ExecutionResult = ExecResult.Interrupt;
                return true;
            case OutputResult.Overflow:
                ExecutionResult = ExecResult.Interrupt;
                return false;
            case OutputResult.Error:
            case OutputResult.Ignore:
            default:
                Debug.Fail("Unexpected result of RecordBuilder.TextEvent()");
                return true;
            }
        }
 
        internal bool CopyBeginEvent(XPathNavigator node, bool emptyflag) {
            switch (node.NodeType) {
            case XPathNodeType.Element:
            case XPathNodeType.Attribute:
            case XPathNodeType.ProcessingInstruction:
            case XPathNodeType.Comment:
                return BeginEvent(node.NodeType, node.Prefix, node.LocalName, node.NamespaceURI, emptyflag);
            case XPathNodeType.Namespace:
                // value instead of namespace here!
                return BeginEvent(XPathNodeType.Namespace, null, node.LocalName, node.Value, false);
            case XPathNodeType.Text:
                // Text will be copied in CopyContents();
                break;
 
            case XPathNodeType.Root:
            case XPathNodeType.Whitespace:
            case XPathNodeType.SignificantWhitespace:
            case XPathNodeType.All:
                break;
 
            default:
                Debug.Fail("Invalid XPathNodeType in CopyBeginEvent");
                break;
            }
 
            return true;
        }
 
        internal bool CopyTextEvent(XPathNavigator node) {
            switch (node.NodeType) {
            case XPathNodeType.Element:
            case XPathNodeType.Namespace:
                break;
 
            case XPathNodeType.Attribute:
            case XPathNodeType.ProcessingInstruction:
            case XPathNodeType.Comment:
            case XPathNodeType.Text:
            case XPathNodeType.Whitespace:
            case XPathNodeType.SignificantWhitespace:
                string text = node.Value;
                return TextEvent(text);
 
            case XPathNodeType.Root:
            case XPathNodeType.All:
                break;
 
            default:
                Debug.Fail("Invalid XPathNodeType in CopyTextEvent");
                break;
            }
 
            return true;
        }
 
        internal bool CopyEndEvent(XPathNavigator node) {
            switch (node.NodeType) {
            case XPathNodeType.Element:
            case XPathNodeType.Attribute:
            case XPathNodeType.ProcessingInstruction:
            case XPathNodeType.Comment:
            case XPathNodeType.Namespace:
                return EndEvent(node.NodeType);
 
            case XPathNodeType.Text:
                // Text was copied in CopyContents();
                break;
 
 
            case XPathNodeType.Root:
            case XPathNodeType.Whitespace:
            case XPathNodeType.SignificantWhitespace:
            case XPathNodeType.All:
                break;
 
            default:
                Debug.Fail("Invalid XPathNodeType in CopyEndEvent");
                break;
            }
 
            return true;
        }
 
        internal static bool IsRoot(XPathNavigator navigator) {
            Debug.Assert(navigator != null);
 
            if (navigator.NodeType == XPathNodeType.Root) {
                return true;
            }
            else if (navigator.NodeType == XPathNodeType.Element) {
                XPathNavigator clone = navigator.Clone();
                clone.MoveToRoot();
                return clone.IsSamePosition(navigator);
            }
            else {
                return false;
            }
        }
 
        //
        // Builder stack
        //
        internal void PushOutput(RecordOutput output) {
            Debug.Assert(output != null);
            this.builder.OutputState = this.xsm.State;
            RecordBuilder lastBuilder = this.builder;
            this.builder      = new RecordBuilder(output, this.nameTable);
            this.builder.Next = lastBuilder;
 
            this.xsm.Reset();
        }
 
        internal RecordOutput PopOutput() {
            Debug.Assert(this.builder != null);
 
            RecordBuilder topBuilder = this.builder;
            this.builder              = topBuilder.Next;
            this.xsm.State            = this.builder.OutputState;
 
            topBuilder.TheEnd();
 
            return topBuilder.Output;
        }
 
        internal bool SetDefaultOutput(XsltOutput.OutputMethod method) {
            if(Output.Method != method) {
                this.output = this.output.CreateDerivedOutput(method);
                return true;
            }
            return false;
        }
 
        internal object GetVariableValue(VariableAction variable) {
            int variablekey = variable.VarKey;
            if (variable.IsGlobal) {
                ActionFrame rootFrame = (ActionFrame) this.actionStack[0];
                object result = rootFrame.GetVariable(variablekey);
                if (result == VariableAction.BeingComputedMark) {
                    throw XsltException.Create(Res.Xslt_CircularReference, variable.NameStr);
                }
                if (result != null) {
                    return result;
                }
                // Variable wasn't evaluated yet
                int saveStackSize = this.actionStack.Length;
                ActionFrame varFrame = PushNewFrame();
                varFrame.Inherit(rootFrame);
                varFrame.Init(variable, rootFrame.NodeSet);
                do {
                    bool endOfFrame = ((ActionFrame) this.actionStack.Peek()).Execute(this);
                    if (endOfFrame) {
                        this.actionStack.Pop();
                    }
                } while (saveStackSize < this.actionStack.Length);
                Debug.Assert(saveStackSize == this.actionStack.Length);
                result = rootFrame.GetVariable(variablekey);
                Debug.Assert(result != null, "Variable was just calculated and result can't be null");
                return result;
            } else {
                return ((ActionFrame) this.actionStack.Peek()).GetVariable(variablekey);
            }
        }
 
        internal void SetParameter(XmlQualifiedName name, object value) {
            Debug.Assert(1 < actionStack.Length);
            ActionFrame parentFrame = (ActionFrame) this.actionStack[actionStack.Length - 2];
            parentFrame.SetParameter(name, value);
        }
 
        internal void ResetParams() {
            ActionFrame frame = (ActionFrame) this.actionStack[actionStack.Length - 1];
            frame.ResetParams();
        }
 
        internal object GetParameter(XmlQualifiedName name) {
            Debug.Assert(2 < actionStack.Length);
            ActionFrame parentFrame = (ActionFrame) this.actionStack[actionStack.Length - 3];
            return parentFrame.GetParameter(name);
        }
 
        // ---------------------- Debugger stack -----------------------
 
        internal class DebuggerFrame {
            internal ActionFrame        actionFrame;
            internal XmlQualifiedName   currentMode;
        }
 
        internal void PushDebuggerStack() {
            Debug.Assert(this.Debugger != null, "We don't generate calls this function if ! debugger");
            DebuggerFrame dbgFrame = (DebuggerFrame) this.debuggerStack.Push();
            if (dbgFrame == null) {
                dbgFrame = new DebuggerFrame();
                this.debuggerStack.AddToTop(dbgFrame);
            }
            dbgFrame.actionFrame = (ActionFrame) this.actionStack.Peek(); // In a case of next builtIn action.
        }
 
        internal void PopDebuggerStack() {
            Debug.Assert(this.Debugger != null, "We don't generate calls this function if ! debugger");
            this.debuggerStack.Pop();
        }
 
        internal void OnInstructionExecute() {
            Debug.Assert(this.Debugger != null, "We don't generate calls this function if ! debugger");
            DebuggerFrame dbgFrame = (DebuggerFrame) this.debuggerStack.Peek();
            Debug.Assert(dbgFrame != null, "PushDebuggerStack() wasn't ever called");
            dbgFrame.actionFrame = (ActionFrame) this.actionStack.Peek();
            this.Debugger.OnInstructionExecute((IXsltProcessor) this);
        }
 
        internal XmlQualifiedName GetPrevioseMode() {
            Debug.Assert(this.Debugger != null, "We don't generate calls this function if ! debugger");
            Debug.Assert(2 <= this.debuggerStack.Length);
            return ((DebuggerFrame) this.debuggerStack[this.debuggerStack.Length - 2]).currentMode;
        }
 
        internal void SetCurrentMode(XmlQualifiedName mode) {
            Debug.Assert(this.Debugger != null, "We don't generate calls this function if ! debugger");
            ((DebuggerFrame) this.debuggerStack[this.debuggerStack.Length - 1]).currentMode = mode;
        }
 
        // ----------------------- IXsltProcessor : --------------------
        int IXsltProcessor.StackDepth {
            get {return this.debuggerStack.Length;}
        }
 
        IStackFrame IXsltProcessor.GetStackFrame(int depth) {
            return ((DebuggerFrame) this.debuggerStack[depth]).actionFrame;
        }
    }
}