//------------------------------------------------------------------------------
// <copyright file="ContainerAction.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------

namespace System.Xml.Xsl.XsltOld {
    using Res = System.Xml.Utils.Res;
    using System;
    using System.Diagnostics;
    using System.Text;
    using System.Globalization;
    using System.Xml;
    using System.Xml.XPath;
    using System.Xml.Xsl.Runtime;
    using MS.Internal.Xml.XPath;
    using System.Collections;
    using System.Runtime.Versioning;

    internal class NamespaceInfo {
        internal String prefix;
        internal String nameSpace;
        internal int stylesheetId;

        internal NamespaceInfo(String prefix, String nameSpace, int stylesheetId) {
            this.prefix = prefix;
            this.nameSpace = nameSpace;
            this.stylesheetId = stylesheetId;
        }
    }

    internal class ContainerAction : CompiledAction {
        internal ArrayList      containedActions;
        internal CopyCodeAction lastCopyCodeAction; // non null if last action is CopyCodeAction;

        private  int  maxid = 0;

        // Local execution states
        protected const int ProcessingChildren = 1;

        internal override void Compile(Compiler compiler) {
            throw new NotImplementedException();
        }

        internal void CompileStylesheetAttributes(Compiler compiler) {
            NavigatorInput input        = compiler.Input;
            string         element      = input.LocalName;
            string         badAttribute = null;
            string         version      = null;

            if (input.MoveToFirstAttribute()) {
                do {
                    string nspace = input.NamespaceURI;
                    string name   = input.LocalName;

                    if (nspace.Length != 0) continue;

                    if (Ref.Equal(name, input.Atoms.Version)) {
                        version = input.Value;
                        if (1 <= XmlConvert.ToXPathDouble(version)) {
                            compiler.ForwardCompatibility = (version != "1.0");
                        }
                        else {
                            // XmlConvert.ToXPathDouble(version) an be NaN!
                            if (! compiler.ForwardCompatibility) {
                                throw XsltException.Create(Res.Xslt_InvalidAttrValue, "version", version);
                            }
                        }
                    }
                    else if (Ref.Equal(name, input.Atoms.ExtensionElementPrefixes)) {
                        compiler.InsertExtensionNamespace(input.Value);
                    }
                    else if (Ref.Equal(name, input.Atoms.ExcludeResultPrefixes)) {
                        compiler.InsertExcludedNamespace(input.Value);
                    }
                    else if (Ref.Equal(name, input.Atoms.Id)) {
                        // Do nothing here.
                    }
                    else {
                        // We can have version atribute later. For now remember this attribute and continue
                        badAttribute = name;
                    }
                }
                while( input.MoveToNextAttribute());
                input.ToParent();
            }

            if (version == null) {
                throw XsltException.Create(Res.Xslt_MissingAttribute, "version");
            }

            if (badAttribute != null && ! compiler.ForwardCompatibility) {
                throw XsltException.Create(Res.Xslt_InvalidAttribute, badAttribute, element);
            }
        }

        internal void CompileSingleTemplate(Compiler compiler) {
            NavigatorInput input = compiler.Input;

            //
            // find mandatory version attribute and launch compilation of single template
            //

            string version = null;

            if (input.MoveToFirstAttribute()) {
                do {
                    string nspace = input.NamespaceURI;
                    string name   = input.LocalName;

                    if (Ref.Equal(nspace, input.Atoms.UriXsl) &&
                        Ref.Equal(name,   input.Atoms.Version)) {
                        version = input.Value;
                    }
                }
                while(input.MoveToNextAttribute());
                input.ToParent();
            }

            if (version == null) {
                if (Ref.Equal(input.LocalName, input.Atoms.Stylesheet) &&
                    input.NamespaceURI == XmlReservedNs.NsWdXsl) {
                    throw XsltException.Create(Res.Xslt_WdXslNamespace);
                }
                throw XsltException.Create(Res.Xslt_WrongStylesheetElement);
            }

            compiler.AddTemplate(compiler.CreateSingleTemplateAction());
        }

        /*
         * CompileTopLevelElements
         */
        protected void CompileDocument(Compiler compiler, bool inInclude) {
            NavigatorInput input = compiler.Input;

            // SkipToElement :
            while (input.NodeType != XPathNodeType.Element) {
                if (! compiler.Advance()) {
                    throw XsltException.Create(Res.Xslt_WrongStylesheetElement);
                }
            }

            Debug.Assert(compiler.Input.NodeType == XPathNodeType.Element);
            if (Ref.Equal(input.NamespaceURI, input.Atoms.UriXsl)) {
                if (
                    ! Ref.Equal(input.LocalName, input.Atoms.Stylesheet) &&
                    ! Ref.Equal(input.LocalName, input.Atoms.Transform)
                ) {
                    throw XsltException.Create(Res.Xslt_WrongStylesheetElement);
                }
                compiler.PushNamespaceScope();
                CompileStylesheetAttributes(compiler);
                CompileTopLevelElements(compiler);
                if (! inInclude) {
                    CompileImports(compiler);
                }
            }
            else {
                // single template
                compiler.PushLiteralScope();
                CompileSingleTemplate(compiler);
            }

            compiler.PopScope();
        }

        internal Stylesheet CompileImport(Compiler compiler, Uri uri, int id) {
            NavigatorInput input = compiler.ResolveDocument(uri);
            compiler.PushInputDocument(input);

            try {
                compiler.PushStylesheet(new Stylesheet());
                compiler.Stylesheetid = id;
                CompileDocument(compiler, /*inInclude*/ false);
            }
            catch (XsltCompileException) {
                throw;
            }
            catch (Exception e) {
                throw new XsltCompileException(e, input.BaseURI, input.LineNumber, input.LinePosition);
            }
            finally {
                compiler.PopInputDocument();
            }
            return compiler.PopStylesheet();
        }

        private void CompileImports(Compiler compiler) {
            ArrayList imports = compiler.CompiledStylesheet.Imports;
            // We can't reverce imports order. Template lookup relyes on it after compilation
            int saveStylesheetId = compiler.Stylesheetid;
            for (int i = imports.Count - 1; 0 <= i; i --) {   // Imports should be compiled in reverse order
                Uri uri = imports[i] as Uri;
                Debug.Assert(uri != null);
                imports[i] = CompileImport(compiler, uri, ++ this.maxid);
            }
            compiler.Stylesheetid = saveStylesheetId;
        }

        // SxS: This method does not take any resource name and does not expose any resources to the caller.
        // It's OK to suppress the SxS warning.
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.None)]
        void CompileInclude(Compiler compiler) {
            Uri uri = compiler.ResolveUri(compiler.GetSingleAttribute(compiler.Input.Atoms.Href));
            string resolved = uri.ToString();
            if (compiler.IsCircularReference(resolved)) {
                throw XsltException.Create(Res.Xslt_CircularInclude, resolved);
            }

            NavigatorInput input = compiler.ResolveDocument(uri);
            compiler.PushInputDocument(input);

            try {
                CompileDocument(compiler, /*inInclude*/ true);
            }
            catch (XsltCompileException) {
                throw;
            }
            catch (Exception e) {
                throw new XsltCompileException(e, input.BaseURI, input.LineNumber, input.LinePosition);
            }
            finally {
                compiler.PopInputDocument();
            }
            CheckEmpty(compiler);
        }

        internal void CompileNamespaceAlias(Compiler compiler) {
            NavigatorInput input   = compiler.Input;
            string         element = input.LocalName;
            string namespace1 = null, namespace2 = null;
            string prefix1 = null   , prefix2 = null;
            if (input.MoveToFirstAttribute()) {
                do {
                    string nspace = input.NamespaceURI;
                    string name   = input.LocalName;

                    if (nspace.Length != 0) continue;

                    if (Ref.Equal(name,input.Atoms.StylesheetPrefix)) {
                        prefix1    = input.Value;
                        namespace1 = compiler.GetNsAlias(ref prefix1);
                    }
                    else if (Ref.Equal(name,input.Atoms.ResultPrefix)){
                        prefix2    = input.Value;
                        namespace2 = compiler.GetNsAlias(ref prefix2);
                    }
                    else {
                        if (! compiler.ForwardCompatibility) {
                            throw XsltException.Create(Res.Xslt_InvalidAttribute, name, element);
                        }
                    }
                }
                while(input.MoveToNextAttribute());
                input.ToParent();
            }

            CheckRequiredAttribute(compiler, namespace1, "stylesheet-prefix");
            CheckRequiredAttribute(compiler, namespace2, "result-prefix"    );
            CheckEmpty(compiler);

            //String[] resultarray = { prefix2, namespace2 };
            compiler.AddNamespaceAlias( namespace1, new NamespaceInfo(prefix2, namespace2, compiler.Stylesheetid));
        }

        internal void CompileKey(Compiler compiler){
            NavigatorInput input    = compiler.Input;
            string         element  = input.LocalName;
            int            MatchKey = Compiler.InvalidQueryKey;
            int            UseKey   = Compiler.InvalidQueryKey;

            XmlQualifiedName Name = null;
            if (input.MoveToFirstAttribute()) {
                do {
                    string nspace = input.NamespaceURI;
                    string name   = input.LocalName;
                    string value  = input.Value;

                    if (nspace.Length != 0) continue;

					if (Ref.Equal(name, input.Atoms.Name)) {
                        Name = compiler.CreateXPathQName(value);
                    }
                    else if (Ref.Equal(name, input.Atoms.Match)) {
                        MatchKey = compiler.AddQuery(value, /*allowVars:*/false, /*allowKey*/false, /*pattern*/true);
                    }
                    else if (Ref.Equal(name, input.Atoms.Use)) {
                        UseKey = compiler.AddQuery(value, /*allowVars:*/false, /*allowKey*/false, /*pattern*/false);
                    }
                    else {
                        if (! compiler.ForwardCompatibility) {
                            throw XsltException.Create(Res.Xslt_InvalidAttribute, name, element);
                        }
                    }
                }
                while(input.MoveToNextAttribute());
                input.ToParent();
            }

            CheckRequiredAttribute(compiler, MatchKey != Compiler.InvalidQueryKey, "match");
            CheckRequiredAttribute(compiler, UseKey   != Compiler.InvalidQueryKey, "use"  );
            CheckRequiredAttribute(compiler, Name     != null                    , "name" );
            // It is a breaking change to check for emptiness, SQLBUDT 324364
            //CheckEmpty(compiler);

            compiler.InsertKey(Name, MatchKey, UseKey);
        }

        protected void CompileDecimalFormat(Compiler compiler){
            NumberFormatInfo info   = new NumberFormatInfo();
            DecimalFormat    format = new DecimalFormat(info, '#', '0', ';');
            XmlQualifiedName  Name  = null;
            NavigatorInput   input  = compiler.Input;
            if (input.MoveToFirstAttribute()) {
                do {
                    if (input.Prefix.Length != 0) continue;

                    string name   = input.LocalName;
                    string value  = input.Value;

                    if (Ref.Equal(name, input.Atoms.Name)) {
                        Name = compiler.CreateXPathQName(value);
                    }
                    else if (Ref.Equal(name, input.Atoms.DecimalSeparator)) {
                        info.NumberDecimalSeparator = value;
                    }
                    else if (Ref.Equal(name, input.Atoms.GroupingSeparator)) {
                        info.NumberGroupSeparator = value;
                    }
                    else if (Ref.Equal(name, input.Atoms.Infinity)) {
                        info.PositiveInfinitySymbol = value;
                    }
                    else if (Ref.Equal(name, input.Atoms.MinusSign)) {
                        info.NegativeSign = value;
                    }
                    else if (Ref.Equal(name, input.Atoms.NaN)) {
                        info.NaNSymbol = value;
                    }
                    else if (Ref.Equal(name, input.Atoms.Percent)) {
                        info.PercentSymbol = value;
                    }
                    else if (Ref.Equal(name, input.Atoms.PerMille)) {
                        info.PerMilleSymbol = value;
                    }
                    else if (Ref.Equal(name, input.Atoms.Digit)) {
                        if (CheckAttribute(value.Length == 1, compiler)) {
                            format.digit = value[0];
                        }
                    }
                    else if (Ref.Equal(name, input.Atoms.ZeroDigit)) {
                        if (CheckAttribute(value.Length == 1, compiler)) {
                            format.zeroDigit = value[0];
                        }
                    }
                    else if (Ref.Equal(name, input.Atoms.PatternSeparator)) {
                        if (CheckAttribute(value.Length == 1, compiler)) {
                            format.patternSeparator = value[0];
                        }
                    }
                }
                while(input.MoveToNextAttribute());
                input.ToParent();
            }
            info.NegativeInfinitySymbol = String.Concat(info.NegativeSign, info.PositiveInfinitySymbol);
            if (Name == null) {
                Name = new XmlQualifiedName();
            }
            compiler.AddDecimalFormat(Name, format);
            CheckEmpty(compiler);
        }

        internal bool CheckAttribute(bool valid, Compiler compiler) {
            if (! valid) {
                if (! compiler.ForwardCompatibility) {
                    throw XsltException.Create(Res.Xslt_InvalidAttrValue, compiler.Input.LocalName, compiler.Input.Value);
                }
                return false;
            }
            return true;
        }

        protected void CompileSpace(Compiler compiler, bool preserve){
            String value = compiler.GetSingleAttribute(compiler.Input.Atoms.Elements);
            String[] elements = XmlConvert.SplitString(value);
            for (int i = 0; i < elements.Length; i++){
                double defaultPriority = NameTest(elements[i]);
                compiler.CompiledStylesheet.AddSpace(compiler, elements[i], defaultPriority, preserve);
            }
            CheckEmpty(compiler);
        }

        double NameTest(String name) {
            if (name == "*") {
                return -0.5;
            }
            int idx = name.Length - 2;
            if (0 <= idx && name[idx] == ':' && name[idx + 1] == '*') {
                if (! PrefixQName.ValidatePrefix(name.Substring(0, idx))) {
                    throw XsltException.Create(Res.Xslt_InvalidAttrValue, "elements", name);
                }
                return -0.25;
            }
            else {
                string prefix, localname;
                PrefixQName.ParseQualifiedName(name, out prefix, out localname);
                return 0;
            }
        }

        // SxS: This method does not take any resource name and does not expose any resources to the caller.
        // It's OK to suppress the SxS warning.
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.None)]
        protected void CompileTopLevelElements(Compiler compiler) {
            // Navigator positioned at parent root, need to move to child and then back
            if (compiler.Recurse() == false) {
                return;
            }

            NavigatorInput input    = compiler.Input;
            bool notFirstElement    = false;
            do {
                switch (input.NodeType) {
                case XPathNodeType.Element:
                    string name   = input.LocalName;
                    string nspace = input.NamespaceURI;

                    if (Ref.Equal(nspace, input.Atoms.UriXsl)) {
                        if (Ref.Equal(name, input.Atoms.Import)) {
                            if (notFirstElement) {
                                throw XsltException.Create(Res.Xslt_NotFirstImport);
                            }
                            // We should compile imports in reverse order after all toplevel elements.
                            // remember it now and return to it in CompileImpoorts();
                            Uri uri = compiler.ResolveUri(compiler.GetSingleAttribute(compiler.Input.Atoms.Href));
                            string resolved = uri.ToString();
                            if (compiler.IsCircularReference(resolved)) {
                                throw XsltException.Create(Res.Xslt_CircularInclude, resolved);
                            }
                            compiler.CompiledStylesheet.Imports.Add(uri);
                            CheckEmpty(compiler);
                        }
                        else if (Ref.Equal(name, input.Atoms.Include)) {
                            notFirstElement = true;
                            CompileInclude(compiler);
                        }
                        else {
                            notFirstElement = true;
                            compiler.PushNamespaceScope();
                            if (Ref.Equal(name, input.Atoms.StripSpace)) {
                                CompileSpace(compiler, false);
                            }
                            else if (Ref.Equal(name, input.Atoms.PreserveSpace)) {
                                CompileSpace(compiler, true);
                            }
                            else if (Ref.Equal(name, input.Atoms.Output)) {
                                CompileOutput(compiler);
                            }
                            else if (Ref.Equal(name, input.Atoms.Key)) {
                                CompileKey(compiler);
                            }
                            else if (Ref.Equal(name, input.Atoms.DecimalFormat)) {
                                CompileDecimalFormat(compiler);
                            }
                            else if (Ref.Equal(name, input.Atoms.NamespaceAlias)) {
                                CompileNamespaceAlias(compiler);
                            }
                            else if (Ref.Equal(name, input.Atoms.AttributeSet)) {
                                compiler.AddAttributeSet(compiler.CreateAttributeSetAction());
                            }
                            else if (Ref.Equal(name, input.Atoms.Variable)) {
                                VariableAction action = compiler.CreateVariableAction(VariableType.GlobalVariable);
                                if (action != null) {
                                    AddAction(action);
                                }
                            }
                            else if (Ref.Equal(name, input.Atoms.Param)) {
                                VariableAction action = compiler.CreateVariableAction(VariableType.GlobalParameter);
                                if (action != null) {
                                    AddAction(action);
                                }
                            }
                            else if (Ref.Equal(name, input.Atoms.Template)) {
                                compiler.AddTemplate(compiler.CreateTemplateAction());
                            }
                            else {
                                if (!compiler.ForwardCompatibility) {
                                    throw compiler.UnexpectedKeyword();
                                }
                            }
                            compiler.PopScope();
                        }
                    }
#if !DISABLE_XSLT_SCRIPT
                    else if (nspace == input.Atoms.UrnMsxsl && name == input.Atoms.Script) {
                        AddScript(compiler);
                    }
#endif
                    else {
                        if (nspace.Length == 0) {
                            throw XsltException.Create(Res.Xslt_NullNsAtTopLevel, input.Name);
                        }
                        // Ignoring non-recognized namespace per XSLT spec 2.2
                    }
                    break;

                case XPathNodeType.ProcessingInstruction:
                case XPathNodeType.Comment:
                case XPathNodeType.Whitespace:
                case XPathNodeType.SignificantWhitespace:
                    break;

                default:
                    throw XsltException.Create(Res.Xslt_InvalidContents, "stylesheet");
                }
            }
            while (compiler.Advance());

            compiler.ToParent();
        }

        protected void CompileTemplate(Compiler compiler) {
            do {
                CompileOnceTemplate(compiler);
            }
            while (compiler.Advance());
        }

        protected void CompileOnceTemplate(Compiler compiler) {
            NavigatorInput input = compiler.Input;

            if (input.NodeType == XPathNodeType.Element) {
                string nspace = input.NamespaceURI;

                if (Ref.Equal(nspace, input.Atoms.UriXsl)) {
                    compiler.PushNamespaceScope();
                    CompileInstruction(compiler);
                    compiler.PopScope();
                }
                else {
                    compiler.PushLiteralScope();
                    compiler.InsertExtensionNamespace();
                    if (compiler.IsExtensionNamespace(nspace)) {
                        AddAction(compiler.CreateNewInstructionAction());
                    }
                    else {
                        CompileLiteral(compiler);
                    }
                    compiler.PopScope();
                }
            }
            else {
                CompileLiteral(compiler);
            }
        }

        void CompileInstruction(Compiler compiler) {
            NavigatorInput input  = compiler.Input;
            CompiledAction action = null;

            Debug.Assert(Ref.Equal(input.NamespaceURI, input.Atoms.UriXsl));

            string name = input.LocalName;

            if (Ref.Equal(name, input.Atoms.ApplyImports)) {
                action = compiler.CreateApplyImportsAction();
            }
            else if (Ref.Equal(name, input.Atoms.ApplyTemplates)) {
                action = compiler.CreateApplyTemplatesAction();
            }
            else if (Ref.Equal(name, input.Atoms.Attribute)) {
                action = compiler.CreateAttributeAction();
            }
            else if (Ref.Equal(name, input.Atoms.CallTemplate)) {
                action = compiler.CreateCallTemplateAction();
            }
            else if (Ref.Equal(name, input.Atoms.Choose)) {
                action = compiler.CreateChooseAction();
            }
            else if (Ref.Equal(name, input.Atoms.Comment)) {
                action = compiler.CreateCommentAction();
            }
            else if (Ref.Equal(name, input.Atoms.Copy)) {
                action = compiler.CreateCopyAction();
            }
            else if (Ref.Equal(name, input.Atoms.CopyOf)) {
                action = compiler.CreateCopyOfAction();
            }
            else if (Ref.Equal(name, input.Atoms.Element)) {
                action = compiler.CreateElementAction();
            }
            else if (Ref.Equal(name, input.Atoms.Fallback)) {
                return;
            }
            else if (Ref.Equal(name, input.Atoms.ForEach)) {
                action = compiler.CreateForEachAction();
            }
            else if (Ref.Equal(name, input.Atoms.If)) {
                action = compiler.CreateIfAction(IfAction.ConditionType.ConditionIf);
            }
            else if (Ref.Equal(name, input.Atoms.Message)) {
                action = compiler.CreateMessageAction();
            }
            else if (Ref.Equal(name, input.Atoms.Number)) {
                action = compiler.CreateNumberAction();
            }
            else if (Ref.Equal(name, input.Atoms.ProcessingInstruction)) {
                action = compiler.CreateProcessingInstructionAction();
            }
            else if (Ref.Equal(name, input.Atoms.Text)) {
                action = compiler.CreateTextAction();
            }
            else if (Ref.Equal(name, input.Atoms.ValueOf)) {
                action = compiler.CreateValueOfAction();
            }
            else if (Ref.Equal(name, input.Atoms.Variable)) {
                action = compiler.CreateVariableAction(VariableType.LocalVariable);
            }
            else {
                if (compiler.ForwardCompatibility)
                    action = compiler.CreateNewInstructionAction();
                else
                    throw compiler.UnexpectedKeyword();
            }

            Debug.Assert(action != null);

            AddAction(action);
        }

        void CompileLiteral(Compiler compiler) {
            NavigatorInput input = compiler.Input;

            switch (input.NodeType) {
            case XPathNodeType.Element:
                this.AddEvent(compiler.CreateBeginEvent());
                CompileLiteralAttributesAndNamespaces(compiler);

                if (compiler.Recurse()) {
                    CompileTemplate(compiler);
                    compiler.ToParent();
                }

                this.AddEvent(new EndEvent(XPathNodeType.Element));
                break;

            case XPathNodeType.Text:
            case XPathNodeType.SignificantWhitespace:
                this.AddEvent(compiler.CreateTextEvent());
                break;
            case XPathNodeType.Whitespace:
            case XPathNodeType.ProcessingInstruction:
            case XPathNodeType.Comment:
                break;

            default:
                Debug.Assert(false, "Unexpected node type.");
                break;
            }
        }

        void CompileLiteralAttributesAndNamespaces(Compiler compiler) {
            NavigatorInput input = compiler.Input;

            if (input.Navigator.MoveToAttribute("use-attribute-sets", input.Atoms.UriXsl)) {
                AddAction(compiler.CreateUseAttributeSetsAction());
                input.Navigator.MoveToParent();
            }
            compiler.InsertExcludedNamespace();

            if (input.MoveToFirstNamespace()) {
                do {
                    string uri = input.Value;

                    if (uri == XmlReservedNs.NsXslt) {
                        continue;
                    }
                    if (
                        compiler.IsExcludedNamespace(uri) ||
                        compiler.IsExtensionNamespace(uri) ||
                        compiler.IsNamespaceAlias(uri)
                    ) {
                            continue;
                    }
                    this.AddEvent(new NamespaceEvent(input));
                }
                while (input.MoveToNextNamespace());
                input.ToParent();
            }

            if (input.MoveToFirstAttribute()) {
                do {

                    // Skip everything from Xslt namespace
                    if (Ref.Equal(input.NamespaceURI, input.Atoms.UriXsl)) {
                        continue;
                    }

                    // Add attribute events
                    this.AddEvent (compiler.CreateBeginEvent());
                    this.AddEvents(compiler.CompileAvt(input.Value));
                    this.AddEvent (new EndEvent(XPathNodeType.Attribute));
                }
                while (input.MoveToNextAttribute());
                input.ToParent();
            }
        }

        void CompileOutput(Compiler compiler) {
            Debug.Assert((object) this == (object) compiler.RootAction);
            compiler.RootAction.Output.Compile(compiler);
        }

        internal void AddAction(Action action) {
            if (this.containedActions == null) {
                this.containedActions = new ArrayList();
            }
            this.containedActions.Add(action);
            lastCopyCodeAction = null;
        }

        private void EnsureCopyCodeAction() {
            if(lastCopyCodeAction == null) {
                CopyCodeAction copyCode = new CopyCodeAction();
                AddAction(copyCode);
                lastCopyCodeAction = copyCode;
            }
        }

        protected void AddEvent(Event copyEvent) {
            EnsureCopyCodeAction();
            lastCopyCodeAction.AddEvent(copyEvent);
        }

        protected void AddEvents(ArrayList copyEvents) {
            EnsureCopyCodeAction();
            lastCopyCodeAction.AddEvents(copyEvents);
        }

#if !DISABLE_XSLT_SCRIPT
        private void AddScript(Compiler compiler) {
            NavigatorInput input = compiler.Input;

            ScriptingLanguage lang = ScriptingLanguage.JScript;
            string implementsNamespace = null;
            if (input.MoveToFirstAttribute()) {
                do {
                    if (input.LocalName == input.Atoms.Language) {
                        string langName = input.Value;
                        if (
                            String.Compare(langName, "jscript"   , StringComparison.OrdinalIgnoreCase) == 0 ||
                            String.Compare(langName, "javascript", StringComparison.OrdinalIgnoreCase) == 0
                        ) {
                            lang = ScriptingLanguage.JScript;
                        } else if (
                            String.Compare(langName, "c#"    , StringComparison.OrdinalIgnoreCase) == 0 ||
                            String.Compare(langName, "csharp", StringComparison.OrdinalIgnoreCase) == 0
                        ) {
                            lang = ScriptingLanguage.CSharp;
                        }
#if !FEATURE_PAL // visualbasic
                        else if (
                            String.Compare(langName, "vb"         , StringComparison.OrdinalIgnoreCase) == 0 ||
                            String.Compare(langName, "visualbasic", StringComparison.OrdinalIgnoreCase) == 0
                        ) {
                            lang = ScriptingLanguage.VisualBasic;
                        }
#endif // !FEATURE_PAL
                        else {
                            throw XsltException.Create(Res.Xslt_ScriptInvalidLanguage, langName);
                        }
                    }
                    else if (input.LocalName == input.Atoms.ImplementsPrefix) {
                        if(! PrefixQName.ValidatePrefix(input.Value))  {
                            throw XsltException.Create(Res.Xslt_InvalidAttrValue, input.LocalName, input.Value);
                        }
                        implementsNamespace = compiler.ResolveXmlNamespace(input.Value);
                    }
                }
                while (input.MoveToNextAttribute());
                input.ToParent();
            }
            if (implementsNamespace == null) {
                throw XsltException.Create(Res.Xslt_MissingAttribute, input.Atoms.ImplementsPrefix);
            }
            if (!input.Recurse() || input.NodeType != XPathNodeType.Text) {
                throw XsltException.Create(Res.Xslt_ScriptEmpty);
            }
            compiler.AddScript(input.Value, lang, implementsNamespace, input.BaseURI, input.LineNumber);
            input.ToParent();
        }
#endif

        internal override void Execute(Processor processor, ActionFrame frame) {
            Debug.Assert(processor != null && frame != null);

            switch (frame.State) {
            case Initialized:
                if (this.containedActions != null && this.containedActions.Count > 0) {
                    processor.PushActionFrame(frame);
                    frame.State = ProcessingChildren;
                }
                else {
                    frame.Finished();
                }
                break;                              // Allow children to run

            case ProcessingChildren:
                frame.Finished();
                break;

            default:
                Debug.Fail("Invalid Container action execution state");
                break;
            }
        }

        internal Action GetAction(int actionIndex) {
            Debug.Assert(actionIndex == 0 || this.containedActions != null);

            if (this.containedActions != null && actionIndex < this.containedActions.Count) {
                return (Action) this.containedActions[actionIndex];
            }
            else {
                return null;
            }
        }

        internal void CheckDuplicateParams(XmlQualifiedName name) {
            if (this.containedActions != null) {
                foreach(CompiledAction action in this.containedActions) {
                    WithParamAction param = action as WithParamAction;
                    if (param != null && param.Name == name) {
                        throw XsltException.Create(Res.Xslt_DuplicateWithParam, name.ToString());
                    }
                }
            }
        }

        internal override void ReplaceNamespaceAlias(Compiler compiler){
            if (this.containedActions == null) {
                return;
            }
            int count = this.containedActions.Count;
            for(int i= 0; i < this.containedActions.Count; i++) {
                ((Action)this.containedActions[i]).ReplaceNamespaceAlias(compiler);
            }
        }
    }
}
