File: System\Xml\Xsl\Xslt\XsltLoader.cs
Project: ndp\fx\src\XmlUtils\System.Data.SqlXml.csproj (System.Data.SqlXml)
//------------------------------------------------------------------------------
// <copyright file="XsltLoader.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
 
//#define XSLT2
 
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using System.IO;
using System.Xml.XPath;
using System.Xml.Xsl.Qil;
using System.Runtime.Versioning;
 
namespace System.Xml.Xsl.Xslt {
    using ContextInfo   = XsltInput.ContextInfo;
    using f             = AstFactory;
    using Res           = System.Xml.Utils.Res;
    using TypeFactory   = XmlQueryTypeFactory;
    using QName         = XsltInput.DelayedQName;
    using XsltAttribute = XsltInput.XsltAttribute;
 
    internal class XsltLoader : IErrorHelper {
        private Compiler                compiler;
        private XmlResolver             xmlResolver;
        private QueryReaderSettings     readerSettings;
        private KeywordsTable           atoms;          // XSLT keywords atomized with QueryReaderSettings.NameTabel
        private XsltInput               input;          // Current input stream
        private Stylesheet              curStylesheet;  // Current stylesheet
        private Template                curTemplate;    // Current template
        private object                  curFunction;    // Current function
 
        internal static QilName         nullMode        = f.QName(string.Empty);
 
        // Flags which control attribute versioning
        public static int V1Opt = 1;
        public static int V1Req = 2;
        public static int V2Opt = 4;
        public static int V2Req = 8;
 
        [ResourceConsumption(ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.Machine)]
        public void Load(Compiler compiler, object stylesheet, XmlResolver xmlResolver) {
            Debug.Assert(compiler != null);
            this.compiler = compiler;
            this.xmlResolver = xmlResolver ?? XmlNullResolver.Singleton;
 
            XmlReader reader = stylesheet as XmlReader;
            if (reader != null) {
                readerSettings = new QueryReaderSettings(reader);
                Load(reader);
            } else {
                // We should take DefaultReaderSettings from Compiler.Settings.DefaultReaderSettings.
 
                string uri = stylesheet as string;
                if (uri != null) {
                    // If xmlResolver == null, then the original uri will be resolved using XmlUrlResolver
                    XmlResolver origResolver = xmlResolver;
                    if (xmlResolver == null || xmlResolver == XmlNullResolver.Singleton)
                        origResolver = new XmlUrlResolver();
                    Uri resolvedUri = origResolver.ResolveUri(null, uri);
                    if (resolvedUri == null) {
                        throw new XslLoadException(Res.Xslt_CantResolve, uri);
                    }
 
                    readerSettings = new QueryReaderSettings(new NameTable());
                    using (reader = CreateReader(resolvedUri, origResolver)) {
                        Load(reader);
                    }
                } else {
                    IXPathNavigable navigable = stylesheet as IXPathNavigable;
                    if (navigable != null) {
                        reader = XPathNavigatorReader.Create(navigable.CreateNavigator());
                        readerSettings = new QueryReaderSettings(reader.NameTable);
                        Load(reader);
                    } else {
                        Debug.Fail("Should never get here");
                    }
                }
            }
            Debug.Assert(compiler.Root != null);
            compiler.StartApplyTemplates = f.ApplyTemplates(nullMode);
            ProcessOutputSettings();
            foreach (AttributeSet attSet in compiler.AttributeSets.Values) {
                CheckAttributeSetsDfs(attSet); // Check attribute sets for circular references using dfs marking method
            }
        }
 
        private void Load(XmlReader reader) {
            this.atoms = new KeywordsTable(reader.NameTable);
            AtomizeAttributes();
            LoadStylesheet(reader, /*include:*/false);
        }
 
 
        void AtomizeAttributes(XsltAttribute[] attributes) {
            for(int i = 0; i < attributes.Length; i ++) {
                attributes[i].name = atoms.NameTable.Add(attributes[i].name);
            }
        }
        void AtomizeAttributes() {
            AtomizeAttributes(stylesheetAttributes);
            AtomizeAttributes(importIncludeAttributes);
            AtomizeAttributes(loadStripSpaceAttributes);
            AtomizeAttributes(outputAttributes);
            AtomizeAttributes(keyAttributes);
            AtomizeAttributes(decimalFormatAttributes);
            AtomizeAttributes(namespaceAliasAttributes);
            AtomizeAttributes(attributeSetAttributes);
            AtomizeAttributes(templateAttributes);
            AtomizeAttributes(scriptAttributes);
            AtomizeAttributes(assemblyAttributes);
            AtomizeAttributes(usingAttributes);
            AtomizeAttributes(applyTemplatesAttributes);
            AtomizeAttributes(callTemplateAttributes);
            AtomizeAttributes(copyAttributes);
            AtomizeAttributes(copyOfAttributes);
            AtomizeAttributes(ifAttributes);
            AtomizeAttributes(forEachAttributes);
            AtomizeAttributes(messageAttributes);
            AtomizeAttributes(numberAttributes);
            AtomizeAttributes(valueOfAttributes);
            AtomizeAttributes(variableAttributes);
            AtomizeAttributes(paramAttributes);
            AtomizeAttributes(withParamAttributes);
            AtomizeAttributes(commentAttributes);
            AtomizeAttributes(processingInstructionAttributes);
            AtomizeAttributes(textAttributes);
            AtomizeAttributes(elementAttributes);
            AtomizeAttributes(attributeAttributes);
            AtomizeAttributes(sortAttributes);
#if XSLT2
            AtomizeAttributes(characterMapAttributes);
            AtomizeAttributes(outputCharacterAttributes);
            AtomizeAttributes(functionAttributes);
            AtomizeAttributes(importSchemaAttributes);
            AtomizeAttributes(documentAttributes);
            AtomizeAttributes(analyzeStringAttributes);
            AtomizeAttributes(namespaceAttributes);
            AtomizeAttributes(performSortAttributes);
            AtomizeAttributes(forEachGroupAttributes);
            AtomizeAttributes(sequenceAttributes);
            AtomizeAttributes(resultDocumentAttributes);
#endif
        }
 
        private bool V1 { get {
                Debug.Assert(compiler.Version != 0, "Version should be already decided at this point");
                return compiler.Version == 1;
        }}
#if XSLT2
        private bool V2 { get { return ! V1; } }
#endif
 
        // Import/Include XsltInput management
 
        private HybridDictionary documentUriInUse = new HybridDictionary();
 
        [ResourceConsumption(ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.Machine)]        
        private Uri ResolveUri(string relativeUri, string baseUri) {
            Uri resolvedBaseUri = (baseUri.Length != 0) ? xmlResolver.ResolveUri(null, baseUri) : null;
            Uri resolvedUri = xmlResolver.ResolveUri(resolvedBaseUri, relativeUri);
            if (resolvedUri == null) {
                throw new XslLoadException(Res.Xslt_CantResolve, relativeUri);
            }
            return resolvedUri;
        }
 
        private XmlReader CreateReader(Uri uri, XmlResolver xmlResolver) {
            object input = xmlResolver.GetEntity(uri, null, null);
 
            Stream stream = input as Stream;
            if (stream != null) {
                return readerSettings.CreateReader(stream, uri.ToString());
            }
 
            XmlReader reader = input as XmlReader;
            if (reader != null) {
                return reader;
            }
 
            IXPathNavigable navigable = input as IXPathNavigable;
            if (navigable != null) {
                return XPathNavigatorReader.Create(navigable.CreateNavigator());
            }
 
            throw new XslLoadException(Res.Xslt_CannotLoadStylesheet, uri.ToString(), input == null ? "null" : input.GetType().ToString());
        }
 
        private Stylesheet LoadStylesheet(Uri uri, bool include) {
            using (XmlReader reader = CreateReader(uri, this.xmlResolver)) {
                return LoadStylesheet(reader, include);
            }
        }
 
        private Stylesheet LoadStylesheet(XmlReader reader, bool include) {
            string baseUri = reader.BaseURI;
            Debug.Assert(!documentUriInUse.Contains(baseUri), "Circular references must be checked while processing xsl:include and xsl:import");
            documentUriInUse.Add(baseUri, null);
            compiler.AddModule(baseUri);
 
            Stylesheet  prevStylesheet  = curStylesheet;
            XsltInput   prevInput       = input;
            Stylesheet  thisStylesheet  = include ? curStylesheet : compiler.CreateStylesheet();
 
            input = new XsltInput(reader, compiler, atoms);
            curStylesheet = thisStylesheet;
 
            try {
                LoadDocument();
                if (!include) {
                    compiler.MergeWithStylesheet(curStylesheet);
 
                    List<Uri> importHrefs = curStylesheet.ImportHrefs;
                    curStylesheet.Imports = new Stylesheet[importHrefs.Count];
                    // Imports should be compiled in the reverse order. Template lookup logic relies on that.
                    for (int i = importHrefs.Count; 0 <= --i; ) {
                        curStylesheet.Imports[i] = LoadStylesheet(importHrefs[i], /*include:*/false);
                    }
                }
            }
            catch (XslLoadException) {
                throw;
            }
            catch (Exception e) {
                if (!XmlException.IsCatchableException(e)) {
                    throw;
                }
                // Note that XmlResolver or XmlReader may throw XmlException with SourceUri == null.
                // In that case we report current line information from XsltInput.
                XmlException ex = e as XmlException;
                ISourceLineInfo lineInfo = (ex != null && ex.SourceUri != null ?
                    new SourceLineInfo(ex.SourceUri, ex.LineNumber, ex.LinePosition, ex.LineNumber, ex.LinePosition) :
                    input.BuildReaderLineInfo()
                );
                throw new XslLoadException(e, lineInfo);
            }
            finally {
                documentUriInUse.Remove(baseUri);
                input         = prevInput;
                curStylesheet = prevStylesheet;
            }
            return thisStylesheet;
        }
 
        private void LoadDocument() {
            if (!input.FindStylesheetElement()) {
                ReportError(/*[XT_002]*/Res.Xslt_WrongStylesheetElement);
                return;
            }
            Debug.Assert(input.NodeType == XmlNodeType.Element);
            if (input.IsXsltNamespace()) {
                if (
                    input.IsKeyword(atoms.Stylesheet) ||
                    input.IsKeyword(atoms.Transform)
                ) {
                    LoadRealStylesheet();
                } else {
                    ReportError(/*[XT_002]*/Res.Xslt_WrongStylesheetElement);
                    input.SkipNode();
                }
            } else {
                LoadSimplifiedStylesheet();
            }
            input.Finish();
        }
 
        private void LoadSimplifiedStylesheet() {
            Debug.Assert(!input.IsXsltNamespace());
            Debug.Assert(curTemplate == null);
 
            // Prefix will be fixed later in LoadLiteralResultElement()
            curTemplate = f.Template(/*name:*/null, /*match:*/"/", /*mode:*/nullMode, /*priority:*/double.NaN, input.XslVersion);
 
            // This template has mode=null match="/" and no imports
            input.CanHaveApplyImports = true;
            XslNode lre = LoadLiteralResultElement(/*asStylesheet:*/true);
            if (lre != null) {
                SetLineInfo(curTemplate, lre.SourceLine);
 
                List<XslNode> content = new List<XslNode>();
                content.Add(lre);
                SetContent(curTemplate, content);
                if (!curStylesheet.AddTemplate(curTemplate)) {
                    Debug.Fail("AddTemplate() returned false for simplified stylesheet");
                }
            }
            curTemplate = null;
        }
 
        XsltAttribute[] stylesheetAttributes = {
            new XsltAttribute("version"               , V1Req | V2Req),
            new XsltAttribute("id"                    , V1Opt | V2Opt),
            new XsltAttribute("default-validation"    ,         V2Opt),
            new XsltAttribute("input-type-annotations",         V2Opt),
        };
        private void LoadRealStylesheet() {
            Debug.Assert(input.IsXsltNamespace() && (input.IsKeyword(atoms.Stylesheet) || input.IsKeyword(atoms.Transform)));
            ContextInfo ctxInfo = input.GetAttributes(stylesheetAttributes);
 
            ParseValidationAttribute(2, /*defVal:*/true);
            ParseInputTypeAnnotationsAttribute(3);
 
            QName parentName = input.ElementName;
            if (input.MoveToFirstChild()) {
                bool atTop = true;
                do {
                    bool isImport = false;
                    switch (input.NodeType) {
                    case XmlNodeType.Element:
                        if (input.IsXsltNamespace()) {
                            if (input.IsKeyword(atoms.Import)) {
                                if (!atTop) {
                                    ReportError(/*[XT0200]*/Res.Xslt_NotAtTop, input.QualifiedName, parentName);
                                    input.SkipNode();
                                } else {
                                    isImport = true;
                                    LoadImport();
                                }
                            } else if (input.IsKeyword(atoms.Include)) {
                                LoadInclude();
                            } else if (input.IsKeyword(atoms.StripSpace)) {
                                LoadStripSpace(ctxInfo.nsList);
                            } else if (input.IsKeyword(atoms.PreserveSpace)) {
                                LoadPreserveSpace(ctxInfo.nsList);
                            } else if (input.IsKeyword(atoms.Output)) {
                                LoadOutput();
                            } else if (input.IsKeyword(atoms.Key)) {
                                LoadKey(ctxInfo.nsList);
                            } else if (input.IsKeyword(atoms.DecimalFormat)) {
                                LoadDecimalFormat(ctxInfo.nsList);
                            } else if (input.IsKeyword(atoms.NamespaceAlias)) {
                                LoadNamespaceAlias(ctxInfo.nsList);
                            } else if (input.IsKeyword(atoms.AttributeSet)) {
                                LoadAttributeSet(ctxInfo.nsList);
                            } else if (input.IsKeyword(atoms.Variable)) {
                                LoadGlobalVariableOrParameter(ctxInfo.nsList, XslNodeType.Variable);
                            } else if (input.IsKeyword(atoms.Param)) {
                                LoadGlobalVariableOrParameter(ctxInfo.nsList, XslNodeType.Param);
                            } else if (input.IsKeyword(atoms.Template)) {
                                LoadTemplate(ctxInfo.nsList);
#if XSLT2
                            } else if (V2 && input.IsKeyword(atoms.CharacterMap)) {
                                LoadCharacterMap(ctxInfo.nsList);
                            } else if (V2 && input.IsKeyword(atoms.Function)) {
                                LoadFunction(ctxInfo.nsList);
                            } else if (V2 && input.IsKeyword(atoms.ImportSchema)) {
                                LoadImportSchema();
#endif
                            } else {
                                input.GetVersionAttribute();
                                if (!input.ForwardCompatibility) {
                                    ReportError(/*[XT_003]*/Res.Xslt_UnexpectedElement, input.QualifiedName, parentName);
                                }
                                input.SkipNode();
                            }
                        } else if (input.IsNs(atoms.UrnMsxsl) && input.IsKeyword(atoms.Script)) {
                            LoadMsScript(ctxInfo.nsList);
                        } else {
                            if (input.IsNullNamespace()) {
                                ReportError(/*[XT0130]*/Res.Xslt_NullNsAtTopLevel, input.LocalName);
                            }
                            // Ignoring non-recognized namespace per XSLT spec 2.2
                            input.SkipNode();
                        }
                        atTop = isImport;
                        break;
 
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                        break;
                    default:
                        Debug.Assert(input.NodeType == XmlNodeType.Text);
                        ReportError(/*[XT0120]*/Res.Xslt_TextNodesNotAllowed, parentName);
                        break;
                    }
                } while (input.MoveToNextSibling());
            }
        }
 
 
        XsltAttribute[] importIncludeAttributes = {new XsltAttribute("href" , V1Req | V2Req)};
        // SxS: This method reads resource names from source document 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)]
        private void LoadImport() {
            ContextInfo ctxInfo = input.GetAttributes(importIncludeAttributes);
 
            if (input.MoveToXsltAttribute(0, "href")) {
                // Resolve href right away using the current BaseUri (it might change later)
                Uri uri = ResolveUri(input.Value, input.BaseUri);
 
                // Check for circular references
                if (documentUriInUse.Contains(uri.ToString())) {
                    ReportError(/*[XT0210]*/Res.Xslt_CircularInclude, input.Value);
                } else {
                    curStylesheet.ImportHrefs.Add(uri);
                }
            } else {
                // The error was already reported. Ignore the instruction
            }
 
            CheckNoContent();
        }
 
        // SxS: This method reads resource names from source document 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)]
        private void LoadInclude() {
            ContextInfo ctxInfo = input.GetAttributes(importIncludeAttributes);
 
            if (input.MoveToXsltAttribute(0, "href")) {
 
                Uri uri = ResolveUri(input.Value, input.BaseUri);
 
                // Check for circular references
                if (documentUriInUse.Contains(uri.ToString())) {
                    ReportError(/*[XT0180]*/Res.Xslt_CircularInclude, input.Value);
                } else {
                    LoadStylesheet(uri, /*include:*/ true);
                }
            } else {
                // The error was already reported. Ignore the instruction
            }
 
            CheckNoContent();
        }
 
        XsltAttribute[] loadStripSpaceAttributes = {new XsltAttribute("elements" , V1Req | V2Req)};
        private void LoadStripSpace(NsDecl stylesheetNsList) {
            ContextInfo ctxInfo = input.GetAttributes(loadStripSpaceAttributes);
            ctxInfo.nsList = MergeNamespaces(ctxInfo.nsList, stylesheetNsList);
 
            if (input.MoveToXsltAttribute(0, atoms.Elements)) {
                ParseWhitespaceRules(input.Value, false);
            }
            CheckNoContent();
        }
 
        private void LoadPreserveSpace(NsDecl stylesheetNsList) {
            ContextInfo ctxInfo = input.GetAttributes(loadStripSpaceAttributes);
            ctxInfo.nsList = MergeNamespaces(ctxInfo.nsList, stylesheetNsList);
 
            if (input.MoveToXsltAttribute(0, atoms.Elements)) {
                ParseWhitespaceRules(input.Value, true);
            }
            CheckNoContent();
        }
 
        XsltAttribute[] outputAttributes = {
            new XsltAttribute("name"                  ,         V2Opt),
            new XsltAttribute("method"                , V1Opt | V2Opt),
            new XsltAttribute("byte-order-mark"       ,         V2Opt),
            new XsltAttribute("cdata-section-elements", V1Opt | V2Opt),
            new XsltAttribute("doctype-public"        , V1Opt | V2Opt),
            new XsltAttribute("doctype-system"        , V1Opt | V2Opt),
            new XsltAttribute("encoding"              , V1Opt | V2Opt),
            new XsltAttribute("escape-uri-attributes" ,         V2Opt),
            new XsltAttribute("include-content-type"  ,         V2Opt),
            new XsltAttribute("indent"                , V1Opt | V2Opt),
            new XsltAttribute("media-type"            , V1Opt | V2Opt),
            new XsltAttribute("normalization-form"    ,         V2Opt),
            new XsltAttribute("omit-xml-declaration"  , V1Opt | V2Opt),
            new XsltAttribute("standalone"            , V1Opt | V2Opt),
            new XsltAttribute("undeclare-prefixes"    ,         V2Opt),
            new XsltAttribute("use-character-maps"    ,         V2Opt),
            new XsltAttribute("version"               , V1Opt | V2Opt)
        };
        private void LoadOutput() {
            ContextInfo ctxInfo = input.GetAttributes(outputAttributes);
 
            Output output = compiler.Output;
            XmlWriterSettings settings = output.Settings;
            int currentPrec = compiler.CurrentPrecedence;
            TriState triState;
 
            QilName name = ParseQNameAttribute(0);
            if (name != null) ReportNYI("xsl:output/@name");
 
            if (input.MoveToXsltAttribute(1, "method")) {
                if (output.MethodPrec <= currentPrec) {
                    compiler.EnterForwardsCompatible();
                    XmlOutputMethod outputMethod;
                    XmlQualifiedName method = ParseOutputMethod(input.Value, out outputMethod);
                    if (compiler.ExitForwardsCompatible(input.ForwardCompatibility) && method != null) {
                        if (currentPrec == output.MethodPrec && !output.Method.Equals(method)) {
                            ReportWarning(/*[XT1560]*/Res.Xslt_AttributeRedefinition, "method");
                        }
                        settings.OutputMethod = outputMethod;
                        output.Method = method;
                        output.MethodPrec = currentPrec;
                    }
                }
            }
 
            TriState byteOrderMask = ParseYesNoAttribute(2, "byte-order-mark");
            if (byteOrderMask != TriState.Unknown) ReportNYI("xsl:output/@byte-order-mark");
 
            if (input.MoveToXsltAttribute(3, "cdata-section-elements")) {
                // Do not check the import precedence, the effective value is the union of all specified values
                compiler.EnterForwardsCompatible();
                string[] qnames = XmlConvert.SplitString(input.Value);
                List<XmlQualifiedName> list = new List<XmlQualifiedName>();
                for (int i = 0; i < qnames.Length; i++) {
                    list.Add(ResolveQName(/*ignoreDefaultNs:*/false, qnames[i]));
                }
                if (compiler.ExitForwardsCompatible(input.ForwardCompatibility)) {
                    settings.CDataSectionElements.AddRange(list);
                }
            }
 
            if (input.MoveToXsltAttribute(4, "doctype-public")) {
                if (output.DocTypePublicPrec <= currentPrec) {
                    if (currentPrec == output.DocTypePublicPrec && settings.DocTypePublic != input.Value) {
                        ReportWarning(/*[XT1560]*/Res.Xslt_AttributeRedefinition, "doctype-public");
                    }
                    settings.DocTypePublic = input.Value;
                    output.DocTypePublicPrec = currentPrec;
                }
            }
 
            if (input.MoveToXsltAttribute(5, "doctype-system")) {
                if (output.DocTypeSystemPrec <= currentPrec) {
                    if (currentPrec == output.DocTypeSystemPrec && settings.DocTypeSystem != input.Value) {
                        ReportWarning(/*[XT1560]*/Res.Xslt_AttributeRedefinition, "doctype-system");
                    }
                    settings.DocTypeSystem = input.Value;
                    output.DocTypeSystemPrec = currentPrec;
                }
            }
 
            if (input.MoveToXsltAttribute(6, "encoding")) {
                if (output.EncodingPrec <= currentPrec) {
                    try {
                        // Encoding.GetEncoding() should never throw NotSupportedException, only ArgumentException
                        Encoding encoding = Encoding.GetEncoding(input.Value);
                        if (currentPrec == output.EncodingPrec && output.Encoding != input.Value) {
                            ReportWarning(/*[XT1560]*/Res.Xslt_AttributeRedefinition, "encoding");
                        }
                        settings.Encoding = encoding;
                        output.Encoding = input.Value;
                        output.EncodingPrec = currentPrec;
                    } catch (ArgumentException) {
                        if (!input.ForwardCompatibility) {
                            ReportWarning(/*[XT_004]*/Res.Xslt_InvalidEncoding, input.Value);
                        }
                    }
                }
            }
 
            bool escapeUriAttributes = ParseYesNoAttribute(7, "escape-uri-attributes") != TriState.False;
            if (! escapeUriAttributes) ReportNYI("xsl:output/@escape-uri-attributes == flase()");
 
            bool includeContentType = ParseYesNoAttribute(8, "include-content-type") != TriState.False;
            if (!includeContentType) ReportNYI("xsl:output/@include-content-type == flase()");
 
            triState = ParseYesNoAttribute(9, "indent");
            if (triState != TriState.Unknown) {
                if (output.IndentPrec <= currentPrec) {
                    bool indent = (triState == TriState.True);
                    if (currentPrec == output.IndentPrec && settings.Indent != indent) {
                        ReportWarning(/*[XT1560]*/Res.Xslt_AttributeRedefinition, "indent");
                    }
                    settings.Indent = indent;
                    output.IndentPrec = currentPrec;
                }
            }
 
            if (input.MoveToXsltAttribute(10, "media-type")) {
                if (output.MediaTypePrec <= currentPrec) {
                    if (currentPrec == output.MediaTypePrec && settings.MediaType != input.Value) {
                        ReportWarning(/*[XT1560]*/Res.Xslt_AttributeRedefinition, "media-type");
                    }
                    settings.MediaType = input.Value;
                    output.MediaTypePrec = currentPrec;
                }
            }
 
            if (input.MoveToXsltAttribute(11, "normalization-form")) {
                ReportNYI("xsl:output/@normalization-form");
            }
 
            triState = ParseYesNoAttribute(12, "omit-xml-declaration");
            if (triState != TriState.Unknown) {
                if (output.OmitXmlDeclarationPrec <= currentPrec) {
                    bool omitXmlDeclaration = (triState == TriState.True);
                    if (currentPrec == output.OmitXmlDeclarationPrec && settings.OmitXmlDeclaration != omitXmlDeclaration) {
                        ReportWarning(/*[XT1560]*/Res.Xslt_AttributeRedefinition, "omit-xml-declaration");
                    }
                    settings.OmitXmlDeclaration = omitXmlDeclaration;
                    output.OmitXmlDeclarationPrec = currentPrec;
                }
            }
 
            triState = ParseYesNoAttribute(13, "standalone");
            if (triState != TriState.Unknown) {
                if (output.StandalonePrec <= currentPrec) {
                    XmlStandalone standalone = (triState == TriState.True) ? XmlStandalone.Yes : XmlStandalone.No;
                    if (currentPrec == output.StandalonePrec && settings.Standalone != standalone) {
                        ReportWarning(/*[XT1560]*/Res.Xslt_AttributeRedefinition, "standalone");
                    }
                    settings.Standalone = standalone;
                    output.StandalonePrec = currentPrec;
                }
            }
 
            bool undeclarePrefixes = ParseYesNoAttribute(14, "undeclare-prefixes") == TriState.True;
            if (undeclarePrefixes) ReportNYI("xsl:output/@undeclare-prefixes == true()");
 
            List<QilName> useCharacterMaps = ParseUseCharacterMaps(15);
            if (useCharacterMaps.Count != 0) ReportNYI("xsl:output/@use-character-maps");
 
            if (input.MoveToXsltAttribute(16, "version")) {
                if (output.VersionPrec <= currentPrec) {
                    if (currentPrec == output.VersionPrec && output.Version != input.Value) {
                        ReportWarning(/*[XT1560]*/Res.Xslt_AttributeRedefinition, "version");
                    }
                    // 
 
                    output.Version = input.Value;
                    output.VersionPrec = currentPrec;
                }
            }
 
            CheckNoContent();
        }
 
        /*
            Default values for method="xml" :   version="1.0"   indent="no"     media-type="text/xml"
            Default values for method="html":   version="4.0"   indent="yes"    media-type="text/html"
            Default values for method="text":                                   media-type="text/plain"
        */
        private void ProcessOutputSettings() {
            Output output = compiler.Output;
            XmlWriterSettings settings = output.Settings;
 
            // version is ignored, indent="no" by default
            if (settings.OutputMethod == XmlOutputMethod.Html && output.IndentPrec == Output.NeverDeclaredPrec) {
                settings.Indent = true;
            }
            if (output.MediaTypePrec == Output.NeverDeclaredPrec) {
                settings.MediaType =
                    settings.OutputMethod == XmlOutputMethod.Xml  ? "text/xml" :
                    settings.OutputMethod == XmlOutputMethod.Html ? "text/html" :
                    settings.OutputMethod == XmlOutputMethod.Text ? "text/plain" : null;
            }
        }
 
        private void CheckUseAttrubuteSetInList(IList<XslNode> list) {
            foreach (XslNode xslNode in list) {
                switch (xslNode.NodeType) {
                case XslNodeType.UseAttributeSet:
                    AttributeSet usedAttSet;
                    if (compiler.AttributeSets.TryGetValue(xslNode.Name, out usedAttSet)) {
                        CheckAttributeSetsDfs(usedAttSet);
                    } else {
                        // The error will be reported in QilGenerator while compiling this attribute set.
                    }
                    break;
                case XslNodeType.List:
                    CheckUseAttrubuteSetInList(xslNode.Content);
                    break;
                }
            }
        }
 
        private void CheckAttributeSetsDfs(AttributeSet attSet) {
            Debug.Assert(attSet != null);
            switch (attSet.CycleCheck) {
            case CycleCheck.NotStarted:
                attSet.CycleCheck = CycleCheck.Processing;
                CheckUseAttrubuteSetInList(attSet.Content);
                attSet.CycleCheck = CycleCheck.Completed;
                break;
            case CycleCheck.Completed:
                break;
            default:
                Debug.Assert(attSet.CycleCheck == CycleCheck.Processing);
                Debug.Assert(attSet.Content[0].SourceLine != null);
                compiler.ReportError(/*[XT0720]*/attSet.Content[0].SourceLine, Res.Xslt_CircularAttributeSet, attSet.Name.QualifiedName);
                break;
            }
        }
 
        XsltAttribute[] keyAttributes = {
            new XsltAttribute("name"     , V1Req | V2Req),
            new XsltAttribute("match"    , V1Req | V2Req),
            new XsltAttribute("use"      , V1Req | V2Opt),
            new XsltAttribute("collation",         V2Opt)
        };
        private void LoadKey(NsDecl stylesheetNsList) {
            ContextInfo ctxInfo = input.GetAttributes(keyAttributes);
            ctxInfo.nsList = MergeNamespaces(ctxInfo.nsList, stylesheetNsList);
 
            QilName keyName   = ParseQNameAttribute(    0);
            string  match     = ParseStringAttribute(   1, "match");
            string  use       = ParseStringAttribute(   2, "use");
            string  collation = ParseCollationAttribute(3);
 
            input.MoveToElement();
 
            List<XslNode> content = null;
 
            if (V1) {
                if (use == null) {
                    input.SkipNode();
                } else {
                    CheckNoContent();
                }
            } else {
                content = LoadInstructions();
                // Load the end tag only if the content is not empty
                if (content.Count != 0) {
                    content = LoadEndTag(content);
                }
                if ((use == null) == (content.Count == 0)) {
                    ReportError(/*[XTSE1205]*/Res.Xslt_KeyCntUse);
                } else {
                    if (use == null) ReportNYI("xsl:key[count(@use) = 0]");
                }
            }
 
            Key key = (Key)SetInfo(f.Key(keyName, match, use, input.XslVersion), null, ctxInfo);
 
            if (compiler.Keys.Contains(keyName)) {
                // Add to the list of previous definitions
                compiler.Keys[keyName].Add(key);
            } else {
                // First definition of key with that name
                List<Key> defList = new List<Key>();
                defList.Add(key);
                compiler.Keys.Add(defList);
            }
        }
 
        XsltAttribute[] decimalFormatAttributes = {
            new XsltAttribute("name"              , V1Opt | V2Opt),
            new XsltAttribute("infinity"          , V1Opt | V2Opt),
            new XsltAttribute("NaN"               , V1Opt | V2Opt),
            new XsltAttribute("decimal-separator" , V1Opt | V2Opt),
            new XsltAttribute("grouping-separator", V1Opt | V2Opt),
            new XsltAttribute("percent"           , V1Opt | V2Opt),
            new XsltAttribute("per-mille"         , V1Opt | V2Opt),
            new XsltAttribute("zero-digit"        , V1Opt | V2Opt),
            new XsltAttribute("digit"             , V1Opt | V2Opt),
            new XsltAttribute("pattern-separator" , V1Opt | V2Opt),
            new XsltAttribute("minus-sign"        , V1Opt | V2Opt)
        };
        private void LoadDecimalFormat(NsDecl stylesheetNsList) {
            const int NumCharAttrs = 8, NumSignAttrs = 7;
            ContextInfo ctxInfo = input.GetAttributes(decimalFormatAttributes);
            ctxInfo.nsList = MergeNamespaces(ctxInfo.nsList, stylesheetNsList);
 
            XmlQualifiedName name;
            if (input.MoveToXsltAttribute(0, "name")) {
                compiler.EnterForwardsCompatible();
                name = ResolveQName(/*ignoreDefaultNs:*/true, input.Value);
                if (!compiler.ExitForwardsCompatible(input.ForwardCompatibility)) {
                    name = new XmlQualifiedName();
                }
            } else {
                // Use name="" for the default decimal-format
                name = new XmlQualifiedName();
            }
 
            string infinity = DecimalFormatDecl.Default.InfinitySymbol;
            if (input.MoveToXsltAttribute(1, "infinity")) {
                infinity = input.Value;
            }
 
            string nan = DecimalFormatDecl.Default.NanSymbol;
            if (input.MoveToXsltAttribute(2, "NaN")) {
                nan = input.Value;
            }
 
            char[] DefaultValues = DecimalFormatDecl.Default.Characters;
            char[] characters = new char[NumCharAttrs];
            Debug.Assert(NumCharAttrs == DefaultValues.Length);
 
            for (int idx = 0; idx < NumCharAttrs; idx++) {
                characters[idx] = ParseCharAttribute(3 + idx, decimalFormatAttributes[3 + idx].name, DefaultValues[idx]);
            }
 
            // Check all NumSignAttrs signs are distinct
            for (int i = 0; i < NumSignAttrs; i++) {
                for (int j = i+1; j < NumSignAttrs; j++) {
                    if (characters[i] == characters[j]) {
                        // Try move to second attribute and if it is missing to first.
                        bool dummy = input.MoveToXsltAttribute(3 + j, decimalFormatAttributes[3 + j].name) || input.MoveToXsltAttribute(3 + i, decimalFormatAttributes[3 + i].name);
                        Debug.Assert(dummy, "One of the atts should have lineInfo. if both are defualt they can't conflict.");
                        ReportError(/*[XT1300]*/Res.Xslt_DecimalFormatSignsNotDistinct, decimalFormatAttributes[3 + i].name, decimalFormatAttributes[3 + j].name);
                        break;
                    }
                }
            }
 
            if (compiler.DecimalFormats.Contains(name)) {
                // Check all attributes have the same values
                DecimalFormatDecl format = compiler.DecimalFormats[name];
                input.MoveToXsltAttribute(1, "infinity");
                CheckError(infinity != format.InfinitySymbol, /*[XT1290]*/Res.Xslt_DecimalFormatRedefined, "infinity", infinity);
                input.MoveToXsltAttribute(2, "NaN");
                CheckError(nan != format.NanSymbol, /*[XT1290]*/Res.Xslt_DecimalFormatRedefined, "NaN", nan);
                for (int idx = 0; idx < NumCharAttrs; idx++) {
                    input.MoveToXsltAttribute(3 + idx, decimalFormatAttributes[3 + idx].name);
                    CheckError(characters[idx] != format.Characters[idx], /*[XT1290]*/Res.Xslt_DecimalFormatRedefined, decimalFormatAttributes[3 + idx].name, char.ToString(characters[idx]));
                }
                Debug.Assert(name.Equals(format.Name));
            } else {
                // Add format to the global collection
                DecimalFormatDecl format = new DecimalFormatDecl(name, infinity, nan, new string(characters));
                compiler.DecimalFormats.Add(format);
            }
            CheckNoContent();
        }
 
        XsltAttribute[] namespaceAliasAttributes = {
            new XsltAttribute("stylesheet-prefix", V1Req | V2Req),
            new XsltAttribute("result-prefix"    , V1Req | V2Req)
        };
        private void LoadNamespaceAlias(NsDecl stylesheetNsList) {
            ContextInfo ctxInfo = input.GetAttributes(namespaceAliasAttributes);
            ctxInfo.nsList = MergeNamespaces(ctxInfo.nsList, stylesheetNsList);
 
            string stylesheetNsUri  = null;
            string resultPrefix = null;
            string resultNsUri  = null;
 
            if (input.MoveToXsltAttribute(0, "stylesheet-prefix")) {
                if (input.Value.Length == 0) {
                    ReportError(/*[XT_005]*/Res.Xslt_EmptyNsAlias, "stylesheet-prefix");
                } else {
                    stylesheetNsUri = input.LookupXmlNamespace(input.Value == "#default" ? string.Empty : input.Value);
                }
            }
 
            if (input.MoveToXsltAttribute(1, "result-prefix")) {
                if (input.Value.Length == 0) {
                    ReportError(/*[XT_005]*/Res.Xslt_EmptyNsAlias, "result-prefix");
                } else {
                    resultPrefix = input.Value == "#default" ? string.Empty : input.Value;
                    resultNsUri = input.LookupXmlNamespace(resultPrefix);
                }
            }
 
            CheckNoContent();
 
            if (stylesheetNsUri == null || resultNsUri == null) {
                // At least one of attributes is missing or invalid
                return;
            }
            if (compiler.SetNsAlias(stylesheetNsUri, resultNsUri, resultPrefix, curStylesheet.ImportPrecedence)) {
                // Namespace alias redefinition
                input.MoveToElement();
                ReportWarning(/*[XT0810]*/Res.Xslt_DupNsAlias, stylesheetNsUri);
            }
        }
 
        XsltAttribute[] attributeSetAttributes = {
            new XsltAttribute("name"            , V1Req | V2Req),
            new XsltAttribute("use-attribute-sets", V1Opt | V2Opt)
        };
        private void LoadAttributeSet(NsDecl stylesheetNsList) {
            ContextInfo ctxInfo = input.GetAttributes(attributeSetAttributes);
            ctxInfo.nsList = MergeNamespaces(ctxInfo.nsList, stylesheetNsList);
 
            QilName setName = ParseQNameAttribute(0);
            Debug.Assert(setName != null, "Required attribute always != null");
 
            AttributeSet set;
            if (!curStylesheet.AttributeSets.TryGetValue(setName, out set)) {
                set = f.AttributeSet(setName);
                // First definition for setName within this stylesheet
                curStylesheet.AttributeSets[setName] = set;
                if (!compiler.AttributeSets.ContainsKey(setName)) {
                    // First definition for setName overall, adding it to the list here
                    // to ensure stable order of prototemplate functions in QilExpression
                    compiler.AllTemplates.Add(set);
                }
            }
 
            List<XslNode> content = new List<XslNode>();
            if (input.MoveToXsltAttribute(1, "use-attribute-sets")) {
                AddUseAttributeSets(content);
            }
 
            QName parentName = input.ElementName;
            if (input.MoveToFirstChild()) {
                do {
                    switch (input.NodeType) {
                    case XmlNodeType.Element:
                        // Only xsl:attribute's are allowed here
                        if (input.IsXsltKeyword(atoms.Attribute)) {
                            AddInstruction(content, XslAttribute());
                        } else {
                            ReportError(/*[XT_006]*/Res.Xslt_UnexpectedElement, input.QualifiedName, parentName);
                            input.SkipNode();
                        }
                        break;
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                        break;
                    default:
                        Debug.Assert(input.NodeType == XmlNodeType.Text);
                        ReportError(/*[XT_006]*/Res.Xslt_TextNodesNotAllowed, parentName);
                        break;
                    }
                } while (input.MoveToNextSibling());
            }
            set.AddContent(SetInfo(f.List(), LoadEndTag(content), ctxInfo));
        }
 
        private void LoadGlobalVariableOrParameter(NsDecl stylesheetNsList, XslNodeType nodeType) {
            Debug.Assert(curTemplate == null);
            Debug.Assert(input.CanHaveApplyImports == false);
            VarPar var = XslVarPar();
            // Preserving namespaces to parse content later
            var.Namespaces = MergeNamespaces(var.Namespaces, stylesheetNsList);
            CheckError(!curStylesheet.AddVarPar(var), /*[XT0630]*/Res.Xslt_DupGlobalVariable, var.Name.QualifiedName);
        }
 
        //: http://www.w3.org/TR/xslt#section-Defining-Template-Rules
        XsltAttribute[] templateAttributes = {
            new XsltAttribute("match"   , V1Opt | V2Opt),
            new XsltAttribute("name"    , V1Opt | V2Opt),
            new XsltAttribute("priority", V1Opt | V2Opt),
            new XsltAttribute("mode"    , V1Opt | V2Opt),
            new XsltAttribute("as"      ,         V2Opt)
        };
        private void LoadTemplate(NsDecl stylesheetNsList) {
            Debug.Assert(curTemplate == null);
            ContextInfo ctxInfo = input.GetAttributes(templateAttributes);
            ctxInfo.nsList = MergeNamespaces(ctxInfo.nsList, stylesheetNsList);
 
            string match = ParseStringAttribute(0, "match");
            QilName name = ParseQNameAttribute(1);
            double priority = double.NaN;
            if (input.MoveToXsltAttribute(2, "priority")) {
                priority = XPathConvert.StringToDouble(input.Value);
                if (double.IsNaN(priority) && !input.ForwardCompatibility) {
                    ReportError(/*[XT0530]*/Res.Xslt_InvalidAttrValue, "priority", input.Value);
                }
            }
            QilName mode = V1 ? ParseModeAttribute(3) : ParseModeListAttribute(3);
 
            if (match == null) {
                CheckError(! input.AttributeExists(1, "name"), /*[XT_007]*/Res.Xslt_BothMatchNameAbsent);
                CheckError(  input.AttributeExists(3, "mode"), /*[XT_008]*/Res.Xslt_ModeWithoutMatch   );
                mode = nullMode;
                if (input.AttributeExists(2, "priority")) {
                    if (V1) {
                        ReportWarning(/*[XT_008]*/Res.Xslt_PriorityWithoutMatch);
                    } else {
                        ReportError  (/*[XT_008]*/Res.Xslt_PriorityWithoutMatch);
                    }
                }
            }
 
            if (input.MoveToXsltAttribute(4, "as")) {
                ReportNYI("xsl:template/@as");
            }
 
            curTemplate = f.Template(name, match, mode, priority, input.XslVersion);
 
            // Template without match considered to not have mode and can't call xsl:apply-imports
            input.CanHaveApplyImports = (match != null);
 
            SetInfo(curTemplate,
                LoadEndTag(LoadInstructions(InstructionFlags.AllowParam)), ctxInfo
            );
 
            if (!curStylesheet.AddTemplate(curTemplate)) {
                ReportError(/*[XT0660]*/Res.Xslt_DupTemplateName, curTemplate.Name.QualifiedName);
            }
            curTemplate = null;
        }
 
#if XSLT2
        //: http://www.w3.org/TR/xslt20/#element-character-map
        XsltAttribute[] characterMapAttributes = {
            new XsltAttribute("name"            , V2Req),
            new XsltAttribute("use-character-maps", V2Opt)
        };
        XsltAttribute[] outputCharacterAttributes = {
            new XsltAttribute("character", V2Req),
            new XsltAttribute("string"   , V2Req)
        };
        private void LoadCharacterMap(NsDecl stylesheetNsList) {
            ContextInfo ctxInfo = input.GetAttributes(characterMapAttributes);
            ctxInfo.nsList = MergeNamespaces(ctxInfo.nsList, stylesheetNsList);
 
            QilName name = ParseQNameAttribute(0);
            List<QilName> useCharacterMaps = ParseUseCharacterMaps(1);
 
            ReportNYI("xsl:character-map");
 
            QName parentName = input.ElementName;
            if (input.MoveToFirstChild()) {
                do {
                    switch (input.NodeType) {
                    case XmlNodeType.Element:
                        // Only xsl:output-character are allowed here
                        if (input.IsXsltKeyword(atoms.OutputCharacter)) {
                            input.GetAttributes(outputCharacterAttributes);
                            ReportNYI("xsl:output-character");
                            char ch  = ParseCharAttribute(0, "character", /*defVal:*/(char)0);
                            string s = ParseStringAttribute(1, "string");
                            CheckNoContent();
                        } else {
                            ReportError(/*[XT_006]*/Res.Xslt_UnexpectedElement, input.QualifiedName, parentName);
                            input.SkipNode();
                        }
                        break;
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                        break;
                    default:
                        Debug.Assert(input.NodeType == XmlNodeType.Text);
                        ReportError(/*[XT_006]*/Res.Xslt_TextNodesNotAllowed, parentName);
                        break;
                    }
                } while (input.MoveToNextSibling());
            }
        }
 
        //: http://www.w3.org/TR/xslt20/#stylesheet-functions
        XsltAttribute[] functionAttributes = {
            new XsltAttribute("name"    , V2Req),
            new XsltAttribute("as"      , V2Opt),
            new XsltAttribute("override", V2Opt)
        };
        private void LoadFunction(NsDecl stylesheetNsList) {
            Debug.Assert(curTemplate == null);
            ContextInfo ctxInfo = input.GetAttributes(functionAttributes);
            ctxInfo.nsList = MergeNamespaces(ctxInfo.nsList, stylesheetNsList);
 
            QilName name  = ParseQNameAttribute(0);
            string asType = ParseStringAttribute(1, "as");
            bool over     = ParseYesNoAttribute(2, "override") == TriState.True;
 
            ReportNYI("xsl:function");
 
            Debug.Assert(input.CanHaveApplyImports == false);
 
            curFunction = new Object();
            LoadInstructions(InstructionFlags.AllowParam);
            curFunction = null;
        }
 
        //: http://www.w3.org/TR/xslt20/#element-import-schema
        XsltAttribute[] importSchemaAttributes = {
            new XsltAttribute("namespace"     , V2Opt),
            new XsltAttribute("schema-location", V2Opt)
        };
        private void LoadImportSchema() {
            ContextInfo ctxInfo = input.GetAttributes(importSchemaAttributes);
            ReportError(/*[XTSE1650]*/Res.Xslt_SchemaDeclaration, input.ElementName);
            input.SkipNode();
        }
#endif
 
        XsltAttribute[] scriptAttributes = {
            new XsltAttribute("implements-prefix", V1Req | V2Req),
            new XsltAttribute("language"         , V1Opt | V2Opt)
        };
        private void LoadMsScript(NsDecl stylesheetNsList) {
            ContextInfo ctxInfo = input.GetAttributes(scriptAttributes);
            ctxInfo.nsList = MergeNamespaces(ctxInfo.nsList, stylesheetNsList);
 
 
            string scriptNs = null;
            if (input.MoveToXsltAttribute(0, "implements-prefix")) {
                if (input.Value.Length == 0) {
                    ReportError(/*[XT_009]*/Res.Xslt_EmptyAttrValue, "implements-prefix", input.Value);
                } else {
                    scriptNs = input.LookupXmlNamespace(input.Value);
                    if (scriptNs == XmlReservedNs.NsXslt) {
                        ReportError(/*[XT_036]*/Res.Xslt_ScriptXsltNamespace);
                        scriptNs = null;
                    }
                }
            }
 
            if (scriptNs == null) {
                scriptNs = compiler.CreatePhantomNamespace();
            }
            string language = ParseStringAttribute(1, "language");
            if (language == null) {
                language = "jscript";
            }
 
            if (! compiler.Settings.EnableScript) {
                compiler.Scripts.ScriptClasses[scriptNs] = null;
                input.SkipNode();
                return;
            }
 
            ScriptClass     scriptClass;
            StringBuilder   scriptCode  = new StringBuilder();
            string          uriString   = input.Uri;
            int             lineNumber  = 0;
            int             lastEndLine = 0;
 
            scriptClass = compiler.Scripts.GetScriptClass(scriptNs, language, (IErrorHelper)this);
            if (scriptClass == null) {
                input.SkipNode();
                return;
            }
 
            QName parentName = input.ElementName;
            if (input.MoveToFirstChild()) {
                do {
                    switch (input.NodeType) {
                    case XmlNodeType.Text:
                        int startLine = input.Start.Line;
                        int endLine   = input.End.Line;
                        if (scriptCode.Length == 0) {
                            lineNumber = startLine;
                        } else if (lastEndLine < startLine) {
                            // A multiline comment, a PI, or an unrecognized element encountered within
                            // this script block. Insert missed '\n' characters here; otherwise line numbers
                            // in error messages and in the debugger will be ----ed up. This action may spoil
                            // the script if the current position is situated in the middle of some identifier
                            // or string literal; however we hope users will not put XML nodes there.
                            scriptCode.Append('\n', startLine - lastEndLine);
                        }
                        scriptCode.Append(input.Value);
                        lastEndLine = endLine;
                        break;
                    case XmlNodeType.Element:
                        if (input.IsNs(atoms.UrnMsxsl) && (input.IsKeyword(atoms.Assembly) || input.IsKeyword(atoms.Using))) {
                            if (scriptCode.Length != 0) {
                                ReportError(/*[XT_012]*/Res.Xslt_ScriptNotAtTop, input.QualifiedName);
                                input.SkipNode();
                            } else if (input.IsKeyword(atoms.Assembly)) {
                                LoadMsAssembly(scriptClass);
                            } else if (input.IsKeyword(atoms.Using)) {
                                LoadMsUsing(scriptClass);
                            }
                        } else {
                            ReportError(/*[XT_012]*/Res.Xslt_UnexpectedElement, input.QualifiedName, parentName);
                            input.SkipNode();
                        }
                        break;
                    default:
                        Debug.Assert(
                            input.NodeType == XmlNodeType.SignificantWhitespace ||
                            input.NodeType == XmlNodeType.Whitespace
                        );
                        // Skip leading whitespaces
                        if (scriptCode.Length != 0) {
                            goto case XmlNodeType.Text;
                        }
                        break;
                    }
                } while (input.MoveToNextSibling());
            }
 
            if (scriptCode.Length == 0) {
                lineNumber = input.Start.Line;
            }
            scriptClass.AddScriptBlock(scriptCode.ToString(), uriString, lineNumber, input.Start);
        }
 
        XsltAttribute[] assemblyAttributes = {
            new XsltAttribute("name", V1Opt | V2Opt),
            new XsltAttribute("href", V1Opt | V2Opt)
        };
        // SxS: This method reads resource names from source document 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)]
        private void LoadMsAssembly(ScriptClass scriptClass) {
            input.GetAttributes(assemblyAttributes);
 
            string name = ParseStringAttribute(0, "name");
            string href = ParseStringAttribute(1, "href");
 
            if ((name != null) == (href != null)) {
                ReportError(/*[XT_046]*/Res.Xslt_AssemblyNameHref);
            } else {
                string asmLocation = null;
                if (name != null) {
                    try {
                        asmLocation = Assembly.Load(name).Location;
                    }
                    catch {
                        AssemblyName asmName = new AssemblyName(name);
 
                        // If the assembly is simply named, let CodeDomProvider and Fusion resolve it
                        byte[] publicKeyToken = asmName.GetPublicKeyToken();
                        if ((publicKeyToken == null || publicKeyToken.Length == 0) && asmName.Version == null) {
                            asmLocation = asmName.Name + ".dll";
                        } else {
                            throw;
                        }
                    }
                } else {
                    Debug.Assert(href != null);
                    asmLocation = Assembly.LoadFrom(ResolveUri(href, input.BaseUri).ToString()).Location;
                    scriptClass.refAssembliesByHref = true;
                }
 
                if (asmLocation != null) {
                    scriptClass.refAssemblies.Add(asmLocation);
                }
            }
 
            CheckNoContent();
        }
 
        XsltAttribute[] usingAttributes = {
            new XsltAttribute("namespace", V1Req | V2Req)
        };
        private void LoadMsUsing(ScriptClass scriptClass) {
            input.GetAttributes(usingAttributes);
 
            if (input.MoveToXsltAttribute(0, "namespace")) {
                scriptClass.nsImports.Add(input.Value);
            }
            CheckNoContent();
        }
 
        // ----------------- Template level methods --------------------------
        // Each instruction in AST tree has nsdecl list attuched to it.
        // Load*() methods do this treek. Xsl*() methods rely on LoadOneInstruction() to do this.
        // ToDo: check how LoadUnknown*() follows this gideline!
 
        private enum InstructionFlags {
            None          = 0x00,
            AllowParam    = 0x01,
            AllowSort     = 0x02,
            AllowFallback = 0x04,
        }
 
        private List<XslNode> LoadInstructions() {
            return LoadInstructions(new List<XslNode>(), InstructionFlags.None);
        }
 
        private List<XslNode> LoadInstructions(InstructionFlags flags) {
            return LoadInstructions(new List<XslNode>(), flags);
        }
 
        private List<XslNode> LoadInstructions(List<XslNode> content) {
            return LoadInstructions(content, InstructionFlags.None);
        }
 
        const int MAX_LOADINSTRUCTIONS_DEPTH = 1024;
        private int loadInstructionsDepth = 0;
        private List<XslNode> LoadInstructions(List<XslNode> content, InstructionFlags flags) {
            if (++loadInstructionsDepth > MAX_LOADINSTRUCTIONS_DEPTH) {
                if (System.Xml.XmlConfiguration.XsltConfigSection.LimitXPathComplexity) {
                    throw XslLoadException.Create(Res.Xslt_InputTooComplex);
                }
            }
            QName parentName = input.ElementName;
            if (input.MoveToFirstChild()) {
                bool    atTop = true;
                int     sortNumber = 0;
                XslNode result;
 
                do {
                    switch (input.NodeType) {
                    case XmlNodeType.Element:
                        string nspace = input.NamespaceUri;
                        string name   = input.LocalName;
                        if (nspace == atoms.UriXsl) {
                            InstructionFlags instrFlag = (
                                Ref.Equal(name, atoms.Param) ? InstructionFlags.AllowParam :
                                Ref.Equal(name, atoms.Sort ) ? InstructionFlags.AllowSort  :
                                /*else */ InstructionFlags.None
                            );
                            if (instrFlag != InstructionFlags.None) {
                                string error = (
                                    (flags & instrFlag) == 0 ? /*[XT_013]*/Res.Xslt_UnexpectedElement :
                                    !atTop                   ? /*[XT_014]*/Res.Xslt_NotAtTop :
                                    /*else*/ null
                                );
                                if (error != null) {
                                    ReportError(error, input.QualifiedName, parentName);
                                    atTop = false;
                                    input.SkipNode();
                                    continue;
                                }
                            } else {
                                atTop = false;
                            }
                            result = (
                                Ref.Equal(name, atoms.ApplyImports         ) ? XslApplyImports() :
                                Ref.Equal(name, atoms.ApplyTemplates       ) ? XslApplyTemplates() :
                                Ref.Equal(name, atoms.CallTemplate         ) ? XslCallTemplate() :
                                Ref.Equal(name, atoms.Copy                 ) ? XslCopy() :
                                Ref.Equal(name, atoms.CopyOf               ) ? XslCopyOf() :
                                Ref.Equal(name, atoms.Fallback             ) ? XslFallback() :
                                Ref.Equal(name, atoms.If                   ) ? XslIf() :
                                Ref.Equal(name, atoms.Choose               ) ? XslChoose() :
                                Ref.Equal(name, atoms.ForEach              ) ? XslForEach() :
                                Ref.Equal(name, atoms.Message              ) ? XslMessage() :
                                Ref.Equal(name, atoms.Number               ) ? XslNumber() :
                                Ref.Equal(name, atoms.ValueOf              ) ? XslValueOf() :
                                Ref.Equal(name, atoms.Comment              ) ? XslComment() :
                                Ref.Equal(name, atoms.ProcessingInstruction) ? XslProcessingInstruction() :
                                Ref.Equal(name, atoms.Text                 ) ? XslText() :
                                Ref.Equal(name, atoms.Element              ) ? XslElement() :
                                Ref.Equal(name, atoms.Attribute            ) ? XslAttribute() :
                                Ref.Equal(name, atoms.Variable             ) ? XslVarPar() :
                                Ref.Equal(name, atoms.Param                ) ? XslVarPar() :
                                Ref.Equal(name, atoms.Sort                 ) ? XslSort(sortNumber ++) :
#if XSLT2
                                V2 && Ref.Equal(name, atoms.AnalyzeString  ) ? XslAnalyzeString() :
                                V2 && Ref.Equal(name, "namespace"      ) ? XslNamespace() :
                                V2 && Ref.Equal(name, atoms.PerformSort    ) ? XslPerformSort() :
                                V2 && Ref.Equal(name, atoms.Document       ) ? XslDocument() :
                                V2 && Ref.Equal(name, atoms.ForEachGroup   ) ? XslForEachGroup() :
                                V2 && Ref.Equal(name, atoms.NextMatch      ) ? XslNextMatch() :
                                V2 && Ref.Equal(name, atoms.Sequence       ) ? XslSequence() :
                                V2 && Ref.Equal(name, atoms.ResultDocument ) ? XslResultDocument() :
#endif
                                /*default:*/                                   LoadUnknownXsltInstruction(parentName)
                            );
                        } else {
                            atTop = false;
                            result = LoadLiteralResultElement(/*asStylesheet:*/false);
                        }
                        break;
                    case XmlNodeType.SignificantWhitespace:
                        result = SetLineInfo(f.Text(input.Value), input.BuildLineInfo());
                        break;
                    case XmlNodeType.Whitespace:
                        continue;
                    default:
                        Debug.Assert(input.NodeType == XmlNodeType.Text);
                        atTop = false;
                        goto case XmlNodeType.SignificantWhitespace;
                    }
                    AddInstruction(content, result);
                } while (input.MoveToNextSibling());
            }
            --loadInstructionsDepth;
            return content;
        }
 
        private List<XslNode> LoadWithParams(InstructionFlags flags) {
            QName parentName = input.ElementName;
            List<XslNode> content = new List<XslNode>();
            /* Process children */
            if (input.MoveToFirstChild()) {
                int sortNumber = 0;
                do {
                    switch (input.NodeType) {
                    case XmlNodeType.Element:
                        if (input.IsXsltKeyword(atoms.WithParam)) {
                            XslNode withParam = XslVarPar();
                            CheckWithParam(content, withParam);
                            AddInstruction(content, withParam);
                        } else if (flags == InstructionFlags.AllowSort && input.IsXsltKeyword(atoms.Sort)) {
                            AddInstruction(content, XslSort(sortNumber++));
                        } else if (flags == InstructionFlags.AllowFallback && input.IsXsltKeyword(atoms.Fallback)) {
                            XslFallback();
                        } else {
                            ReportError(/*[XT_016]*/Res.Xslt_UnexpectedElement, input.QualifiedName, parentName);
                            input.SkipNode();
                        }
                        break;
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                        break;
                    default:
                        Debug.Assert(input.NodeType == XmlNodeType.Text);
                        ReportError(/*[XT_016]*/Res.Xslt_TextNodesNotAllowed, parentName);
                        break;
                    }
                } while (input.MoveToNextSibling());
            }
            return content;
        }
 
        // http://www.w3.org/TR/xslt#apply-imports
        private XslNode XslApplyImports() {
            ContextInfo ctxInfo = input.GetAttributes();
            if (!input.CanHaveApplyImports) {
                ReportError(/*[XT_015]*/Res.Xslt_InvalidApplyImports);
                input.SkipNode();
                return null;
            }
 
            List<XslNode> content = LoadWithParams(InstructionFlags.None);
 
            ctxInfo.SaveExtendedLineInfo(input);
 
            if (V1) {
                if (content.Count != 0) {
                    ISourceLineInfo contentInfo = content[0].SourceLine;
                    if (!input.ForwardCompatibility) {
                        compiler.ReportError(contentInfo, /*[XT0260]*/Res.Xslt_NotEmptyContents, atoms.ApplyImports);
                    } else {
                        return SetInfo(f.Error(XslLoadException.CreateMessage(contentInfo, /*[XT0260]*/Res.Xslt_NotEmptyContents, atoms.ApplyImports)), null, ctxInfo);
                    }
                }
                content = null;
            } else {
                if (content.Count != 0) ReportNYI("xsl:apply-imports/xsl:with-param");
                content = null;
            }
 
            return SetInfo(f.ApplyImports(/*Mode:*/curTemplate.Mode, curStylesheet, input.XslVersion), content, ctxInfo);
        }
 
        // http://www.w3.org/TR/xslt#section-Applying-Template-Rules
        XsltAttribute[] applyTemplatesAttributes = {
            new XsltAttribute("select", V1Opt | V2Opt),
            new XsltAttribute("mode"  , V1Opt | V2Opt)
        };
        private XslNode XslApplyTemplates() {
            ContextInfo ctxInfo = input.GetAttributes(applyTemplatesAttributes);
 
            string select = ParseStringAttribute(0, "select");
            if (select == null) {
                select = "node()";
            }
            QilName mode = ParseModeAttribute(1);
 
            List<XslNode> content = LoadWithParams(InstructionFlags.AllowSort);
            ctxInfo.SaveExtendedLineInfo(input);
            return SetInfo(f.ApplyTemplates(mode, select, ctxInfo, input.XslVersion),
                content, ctxInfo
            );
        }
 
        // http://www.w3.org/TR/xslt#named-templates
        // http://www.w3.org/TR/xslt#element-call-template
        XsltAttribute[] callTemplateAttributes = {
            new XsltAttribute("name", V1Req | V2Req)
        };
        private XslNode XslCallTemplate() {
            ContextInfo ctxInfo = input.GetAttributes(callTemplateAttributes);
            QilName name = ParseQNameAttribute(0);
 
            List<XslNode> content = LoadWithParams(InstructionFlags.None);
            ctxInfo.SaveExtendedLineInfo(input);
            return SetInfo(f.CallTemplate(name, ctxInfo), content, ctxInfo);
        }
 
        // http://www.w3.org/TR/xslt#copying
        // http://www.w3.org/TR/xslt20/#element-copy
        XsltAttribute[] copyAttributes = {
            new XsltAttribute("copy-namespaces"   ,         V2Opt),
            new XsltAttribute("inherit-namespaces",         V2Opt),
            new XsltAttribute("use-attribute-sets", V1Opt | V2Opt),
            new XsltAttribute("type"              ,         V2Opt),
            new XsltAttribute("validation"        ,         V2Opt)
        };
        private XslNode XslCopy() {
            ContextInfo ctxInfo = input.GetAttributes(copyAttributes);
 
            bool copyNamespaces    = ParseYesNoAttribute(0, "copy-namespaces"   ) != TriState.False;
            bool inheritNamespaces = ParseYesNoAttribute(1, "inherit-namespaces") != TriState.False;
            if (! copyNamespaces   ) ReportNYI("xsl:copy[@copy-namespaces    = 'no']");
            if (! inheritNamespaces) ReportNYI("xsl:copy[@inherit-namespaces = 'no']");
 
            List<XslNode> content = new List<XslNode>();
            if (input.MoveToXsltAttribute(2, "use-attribute-sets")) {
                AddUseAttributeSets(content);
            }
 
            ParseTypeAttribute(3);
            ParseValidationAttribute(4, /*defVal:*/false);
 
            return SetInfo(f.Copy(), LoadEndTag(LoadInstructions(content)), ctxInfo);
        }
 
        XsltAttribute[] copyOfAttributes = {
            new XsltAttribute("select"         , V1Req | V2Req),
            new XsltAttribute("copy-namespaces",         V2Opt),
            new XsltAttribute("type"           ,         V2Opt),
            new XsltAttribute("validation"     ,         V2Opt)
        };
        private XslNode XslCopyOf() {
            ContextInfo ctxInfo = input.GetAttributes(copyOfAttributes);
            string select = ParseStringAttribute(0, "select");
            bool copyNamespaces = ParseYesNoAttribute(1, "copy-namespaces") != TriState.False;
            if (!copyNamespaces) ReportNYI("xsl:copy-of[@copy-namespaces    = 'no']");
 
            ParseTypeAttribute(2);
            ParseValidationAttribute(3, /*defVal:*/false);
 
            CheckNoContent();
            return SetInfo(f.CopyOf(select, input.XslVersion), null, ctxInfo);
        }
 
        // http://www.w3.org/TR/xslt#fallback
        // See LoadFallbacks() for real fallback implementation
        private XslNode XslFallback() {
            input.GetAttributes();
            input.SkipNode();
            return null;
        }
 
        XsltAttribute[] ifAttributes = {
            new XsltAttribute("test", V1Req | V2Req)
        };
        private XslNode XslIf() {
            ContextInfo ctxInfo = input.GetAttributes(ifAttributes);
            string test = ParseStringAttribute(0, "test");
 
            return SetInfo(f.If(test, input.XslVersion), LoadInstructions(), ctxInfo);
        }
 
        private XslNode XslChoose() {
            ContextInfo ctxInfo = input.GetAttributes();
 
            List<XslNode> content   = new List<XslNode>();
            bool        otherwise = false;
            bool        when      = false;
 
            QName parentName = input.ElementName;
            if (input.MoveToFirstChild()) {
                do {
                    switch (input.NodeType) {
                    case XmlNodeType.Element:
                        XslNode node = null;
                        if (Ref.Equal(input.NamespaceUri, atoms.UriXsl)) {
                            if (Ref.Equal(input.LocalName, atoms.When)) {
                                if (otherwise) {
                                    ReportError(/*[XT_018]*/Res.Xslt_WhenAfterOtherwise);
                                    input.SkipNode();
                                    continue;
                                } else {
                                    when = true;
                                    node = XslIf();
                                }
                            } else if (Ref.Equal(input.LocalName, atoms.Otherwise)) {
                                if (otherwise) {
                                    ReportError(/*[XT_019]*/Res.Xslt_DupOtherwise);
                                    input.SkipNode();
                                    continue;
                                } else {
                                    otherwise = true;
                                    node = XslOtherwise();
                                }
                            }
                        }
                        if (node == null) {
                            ReportError(/*[XT_020]*/Res.Xslt_UnexpectedElement, input.QualifiedName, parentName);
                            input.SkipNode();
                            continue;
                        }
                        AddInstruction(content, node);
                        break;
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                        break;
                    default:
                        Debug.Assert(input.NodeType == XmlNodeType.Text);
                        ReportError(/*[XT_020]*/Res.Xslt_TextNodesNotAllowed, parentName);
                        break;
                    }
                } while (input.MoveToNextSibling());
            }
            CheckError(!when, /*[XT_021]*/Res.Xslt_NoWhen);
            return SetInfo(f.Choose(), content, ctxInfo);
        }
 
        private XslNode XslOtherwise() {
            ContextInfo ctxInfo = input.GetAttributes();
            return SetInfo(f.Otherwise(), LoadInstructions(), ctxInfo);
        }
 
        XsltAttribute[] forEachAttributes = {
            new XsltAttribute("select", V1Req | V2Req)
        };
        private XslNode XslForEach() {
            ContextInfo ctxInfo = input.GetAttributes(forEachAttributes);
 
            string select = ParseStringAttribute(0, "select");
            // The current template rule becomes null, so we must not allow xsl:apply-import's within this element
            input.CanHaveApplyImports = false;
            List<XslNode> content = LoadInstructions(InstructionFlags.AllowSort);
            ctxInfo.SaveExtendedLineInfo(input);
 
            return SetInfo(f.ForEach(select, ctxInfo, input.XslVersion),
                content, ctxInfo
            );
        }
 
        // http://www.w3.org/TR/xslt#message
        // http://www.w3.org/TR/xslt20/#element-message
        XsltAttribute[] messageAttributes = {
            new XsltAttribute("select"   ,         V2Opt),
            new XsltAttribute("terminate", V1Opt | V2Opt)
        };
        private XslNode XslMessage() {
            ContextInfo ctxInfo = input.GetAttributes(messageAttributes);
 
            string select = ParseStringAttribute(0, "select");
            bool terminate = ParseYesNoAttribute(1, /*attName:*/"terminate") == TriState.True;
 
            List<XslNode> content = LoadInstructions();
            if (content.Count != 0) {
                content = LoadEndTag(content);
            }
            if (select != null) {
                content.Insert(0, f.CopyOf(select, input.XslVersion));
            }
 
            return SetInfo(f.Message(terminate), content, ctxInfo);
        }
 
        // http://www.w3.org/TR/xslt#number
        // http://www.w3.org/TR/xslt20/#element-number
        XsltAttribute[] numberAttributes = {
            new XsltAttribute("value"             , V1Opt | V2Opt),
            new XsltAttribute("select"            ,         V2Opt),
            new XsltAttribute("level"             , V1Opt | V2Opt),
            new XsltAttribute("count"             , V1Opt | V2Opt),
            new XsltAttribute("from"              , V1Opt | V2Opt),
            new XsltAttribute("format"            , V1Opt | V2Opt),
            new XsltAttribute("lang"              , V1Opt | V2Opt),
            new XsltAttribute("letter-value"      , V1Opt | V2Opt),
            new XsltAttribute("ordinal"           ,         V2Opt),
            new XsltAttribute("grouping-separator", V1Opt | V2Opt),
            new XsltAttribute("grouping-size"     , V1Opt | V2Opt)
        };
        private XslNode XslNumber() {
            ContextInfo ctxInfo = input.GetAttributes(numberAttributes);
 
            string value  = ParseStringAttribute(0, "value");
            string select = ParseStringAttribute(1, "select");
            if (select != null) ReportNYI("xsl:number/@select");
            NumberLevel level = NumberLevel.Single;
            if (input.MoveToXsltAttribute(2, "level")) {
                switch (input.Value) {
                case "single"  : level = NumberLevel.Single  ; break;
                case "multiple": level = NumberLevel.Multiple; break;
                case "any"     : level = NumberLevel.Any     ; break;
                default:
                    if (!input.ForwardCompatibility) {
                        ReportError(/*[XT_022]*/Res.Xslt_InvalidAttrValue, "level", input.Value);
                    }
                    break;
                }
            }
            string count             = ParseStringAttribute(3, "count" );
            string from              = ParseStringAttribute(4, "from"  );
            string format            = ParseStringAttribute(5, "format");
            string lang              = ParseStringAttribute(6, "lang"  );
            string letterValue       = ParseStringAttribute(7, "letter-value");
            string ordinal           = ParseStringAttribute(8, "ordinal");
            if (!string.IsNullOrEmpty(ordinal)) ReportNYI("xsl:number/@ordinal");
            string groupingSeparator = ParseStringAttribute(9, "grouping-separator");
            string groupingSize      = ParseStringAttribute(10, "grouping-size"  );
 
            // Default values for xsl:number :  level="single"  format="1"
            if (format == null) {
                format = "1";
            }
 
            CheckNoContent();
            return SetInfo(
                f.Number(level, count, from, value,
                    format, lang, letterValue, groupingSeparator, groupingSize,
                    input.XslVersion
                ),
                null, ctxInfo
            );
        }
 
        // http://www.w3.org/TR/xslt#value-of
        XsltAttribute[] valueOfAttributes = {
            new XsltAttribute("select"                 , V1Req | V2Opt),
            new XsltAttribute("separator"              ,         V2Opt),
            new XsltAttribute("disable-output-escaping", V1Opt | V2Opt)
        };
        private XslNode XslValueOf() {
            ContextInfo ctxInfo = input.GetAttributes(valueOfAttributes);
 
            string select    = ParseStringAttribute(0, "select");
            string separator = ParseStringAttribute(1, "separator");
            bool doe = ParseYesNoAttribute(2, /*attName:*/"disable-output-escaping") == TriState.True;
 
            if (separator == null) {
                if (!input.BackwardCompatibility) {
                    separator = select != null ? " " : string.Empty;
                }
            } else {
                ReportNYI("xsl:value-of/@separator");
            }
 
            List<XslNode> content = null;
 
            if (V1) {
                if (select == null) {
                    input.SkipNode();
                    return SetInfo(f.Error(XslLoadException.CreateMessage(ctxInfo.lineInfo, Res.Xslt_MissingAttribute, "select")), null, ctxInfo);
                }
                CheckNoContent();
            } else {
                content = LoadContent(select != null);
                CheckError(select == null && content.Count == 0, /*[???]*/Res.Xslt_NoSelectNoContent, input.ElementName);
                if (content.Count != 0) {
                    ReportNYI("xsl:value-of/*");
                    content = null;
                }
            }
 
            return SetInfo(f.XslNode(doe ? XslNodeType.ValueOfDoe : XslNodeType.ValueOf, null, select, input.XslVersion),
                null, ctxInfo
            );
        }
 
        //                    required tunnel select
        // variable              -        -      +
        // with-param            -        +      +
        // stylesheet/param      +        -      +
        // template/param        +        +      +
        // function/param        -        -      -
        // xsl:variable     http://www.w3.org/TR/xslt#local-variables
        // xsl:param        http://www.w3.org/TR/xslt#element-param
        // xsl:with-param   http://www.w3.org/TR/xslt#element-with-param
        XsltAttribute[] variableAttributes = {
            new XsltAttribute("name"    , V1Req | V2Req),
            new XsltAttribute("select"  , V1Opt | V2Opt),
            new XsltAttribute("as"      ,         V2Opt),
            new XsltAttribute("required",             0),
            new XsltAttribute("tunnel"  ,             0)
        };
        XsltAttribute[] paramAttributes = {
            new XsltAttribute("name"    , V1Req | V2Req),
            new XsltAttribute("select"  , V1Opt | V2Opt),
            new XsltAttribute("as"      ,         V2Opt),
            new XsltAttribute("required",         V2Opt),
            new XsltAttribute("tunnel"  ,         V2Opt)
        };
        XsltAttribute[] withParamAttributes = {
            new XsltAttribute("name"    , V1Req | V2Req),
            new XsltAttribute("select"  , V1Opt | V2Opt),
            new XsltAttribute("as"      ,         V2Opt),
            new XsltAttribute("required",             0),
            new XsltAttribute("tunnel"  ,         V2Opt)
        };
        private VarPar XslVarPar() {
            string localName = input.LocalName;
            XslNodeType nodeType = (
                Ref.Equal(localName, atoms.Variable ) ? XslNodeType.Variable  :
                Ref.Equal(localName, atoms.Param    ) ? XslNodeType.Param     :
                Ref.Equal(localName, atoms.WithParam) ? XslNodeType.WithParam :
                XslNodeType.Unknown
            );
            Debug.Assert(nodeType != XslNodeType.Unknown);
            bool isParam = Ref.Equal(localName, atoms.Param);
            ContextInfo ctxInfo = input.GetAttributes(
                nodeType == XslNodeType.Variable ? variableAttributes :
                nodeType == XslNodeType.Param    ? paramAttributes   :
                /*default:*/                       withParamAttributes
            );
 
            QilName name  = ParseQNameAttribute(0);
            string select = ParseStringAttribute(1, "select");
            string asType = ParseStringAttribute(2, "as");
            TriState required = ParseYesNoAttribute(3, "required");
            if (nodeType == XslNodeType.Param && curFunction != null) {
                if (!input.ForwardCompatibility) {
                    CheckError(required != TriState.Unknown, /*[???]*/Res.Xslt_RequiredOnFunction, name.ToString());
                }
                required = TriState.True;
            } else {
                if (required == TriState.True) ReportNYI("xsl:param/@required == true()");
            }
 
            if (asType != null) {
                ReportNYI("xsl:param/@as");
            }
 
            TriState tunnel = ParseYesNoAttribute(4, "tunnel");
            if (tunnel != TriState.Unknown) {
                if (nodeType == XslNodeType.Param && curTemplate == null) {
                    if (!input.ForwardCompatibility) {
                        ReportError(/*[???]*/Res.Xslt_NonTemplateTunnel, name.ToString());
                    }
                } else {
                    if (tunnel == TriState.True) ReportNYI("xsl:param/@tunnel == true()");
                }
            }
 
            List<XslNode> content = LoadContent(select != null);
            CheckError((required == TriState.True) && (select != null || content.Count != 0), /*[???]*/Res.Xslt_RequiredAndSelect, name.ToString());
 
            VarPar result = f.VarPar(nodeType, name, select, input.XslVersion);
            SetInfo(result, content, ctxInfo);
            return result;
        }
 
        // http://www.w3.org/TR/xslt#section-Creating-Comments
        // http://www.w3.org/TR/xslt20/#element-comment
        XsltAttribute[] commentAttributes = {
            new XsltAttribute("select", V2Opt)
        };
        private XslNode XslComment() {
            ContextInfo ctxInfo = input.GetAttributes(commentAttributes);
            string select = ParseStringAttribute(0, "select");
            if (select != null) ReportNYI("xsl:comment/@select");
 
            return SetInfo(f.Comment(), LoadContent(select != null), ctxInfo);
        }
 
        private List<XslNode> LoadContent(bool hasSelect) {
            QName parentName = input.ElementName;
            List<XslNode> content = LoadInstructions();
            CheckError(hasSelect && content.Count != 0, /*[XT0620]*/Res.Xslt_ElementCntSel, parentName);
            // Load the end tag only if the content is not empty
            if (content.Count != 0) {
                content = LoadEndTag(content);
            }
            return content;
        }
 
        // http://www.w3.org/TR/xslt#section-Creating-Processing-Instructions
        // http://www.w3.org/TR/xslt20/#element-processing-instruction
        XsltAttribute[] processingInstructionAttributes = {
            new XsltAttribute("name"  , V1Req | V2Req),
            new XsltAttribute("select",         V2Opt)
        };
        private XslNode XslProcessingInstruction() {
            ContextInfo ctxInfo = input.GetAttributes(processingInstructionAttributes);
            string name = ParseNCNameAttribute(0);
            string select = ParseStringAttribute(1, "select");
            if (select != null) ReportNYI("xsl:processing-instruction/@select");
 
            return SetInfo(f.PI(name, input.XslVersion), LoadContent(select != null), ctxInfo);
        }
 
        // http://www.w3.org/TR/xslt#section-Creating-Text
        XsltAttribute[] textAttributes = {
            new XsltAttribute("disable-output-escaping", V1Opt | V2Opt)
        };
        private XslNode XslText() {
            ContextInfo ctxInfo = input.GetAttributes(textAttributes);
 
            bool doe = ParseYesNoAttribute(0, /*attName:*/ "disable-output-escaping") == TriState.True;
            SerializationHints hints = doe ? SerializationHints.DisableOutputEscaping : SerializationHints.None;
 
            // We are not using StringBuilder here because in most cases there will be just one text node.
            List<XslNode> content = new List<XslNode>();
 
            QName parentName = input.ElementName;
            if (input.MoveToFirstChild()) {
                do {
                    switch (input.NodeType) {
                    case XmlNodeType.Text:
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                        // xsl:text may contain multiple child text nodes separated by comments and PIs, which are ignored by XsltInput
                        content.Add(f.Text(input.Value, hints));
                        break;
                    default:
                        Debug.Assert(input.NodeType == XmlNodeType.Element);
                        ReportError(/*[XT_023]*/Res.Xslt_UnexpectedElement, input.QualifiedName, parentName);
                        input.SkipNode();
                        break;
                    }
                } while (input.MoveToNextSibling());
            }
 
            // Empty xsl:text elements will be ignored
            return SetInfo(f.List(), content, ctxInfo);
        }
 
        // http://www.w3.org/TR/xslt#section-Creating-Elements-with-xsl:element
        // http://www.w3.org/TR/xslt20/#element-element
        XsltAttribute[] elementAttributes = {
            new XsltAttribute("name"             , V1Req | V2Req),
            new XsltAttribute("namespace"        , V1Opt | V2Opt),
            new XsltAttribute("inherit-namespaces",         V2Opt),
            new XsltAttribute("use-attribute-sets" , V1Opt | V2Opt),
            new XsltAttribute("type"             ,         V2Opt),
            new XsltAttribute("validation"       ,         V2Opt)
        };
        private XslNode XslElement() {
            ContextInfo ctxInfo = input.GetAttributes(elementAttributes);
 
            string name = ParseNCNameAttribute(0); ;
            string ns = ParseStringAttribute(1, "namespace");
            CheckError(ns == XmlReservedNs.NsXmlNs, /*[XT_024]*/Res.Xslt_ReservedNS, ns);
 
            bool inheritNamespaces = ParseYesNoAttribute(2, "inherit-namespaces") != TriState.False;
            if (!inheritNamespaces) ReportNYI("xsl:copy[@inherit-namespaces = 'no']");
 
            ParseTypeAttribute(4);
            ParseValidationAttribute(5, /*defVal:*/false);
 
            List<XslNode> content = new List<XslNode>();
            if (input.MoveToXsltAttribute(3, "use-attribute-sets")) {
                AddUseAttributeSets(content);
            }
            return SetInfo(f.Element(name, ns, input.XslVersion),
                LoadEndTag(LoadInstructions(content)), ctxInfo
            );
        }
 
        // http://www.w3.org/TR/xslt#creating-attributes
        // http://www.w3.org/TR/xslt20#creating-attributes
        XsltAttribute[] attributeAttributes = {
            new XsltAttribute("name"      , V1Req | V2Req),
            new XsltAttribute("namespace" , V1Opt | V2Opt),
            new XsltAttribute("select"    ,         V2Opt),
            new XsltAttribute("separator" ,         V2Opt),
            new XsltAttribute("type"      ,         V2Opt),
            new XsltAttribute("validation",         V2Opt)
        };
        private XslNode XslAttribute() {
            ContextInfo ctxInfo = input.GetAttributes(attributeAttributes);
 
            string name = ParseNCNameAttribute(0);
            string ns = ParseStringAttribute(1, "namespace");
            CheckError(ns == XmlReservedNs.NsXmlNs, /*[XT_024]*/Res.Xslt_ReservedNS, ns);
 
            string select = ParseStringAttribute(2, "select");
            if (select != null) ReportNYI("xsl:attribute/@select");
            string separator = ParseStringAttribute(3, "separator");
            if (separator != null) ReportNYI("xsl:attribute/@separator");
            separator = separator != null ? separator : (select != null ? " " : string.Empty);
 
            ParseTypeAttribute(4);
            ParseValidationAttribute(5, /*defVal:*/false);
 
            return SetInfo(f.Attribute(name, ns, input.XslVersion), LoadContent(select != null), ctxInfo);
        }
 
        // http://www.w3.org/TR/xslt#sorting
        // http://www.w3.org/TR/xslt20/#element-sort
        XsltAttribute[] sortAttributes = {
            new XsltAttribute("select"    , V1Opt | V2Opt),
            new XsltAttribute("lang"      , V1Opt | V2Opt),
            new XsltAttribute("order"     , V1Opt | V2Opt),
            new XsltAttribute("collation" , V1Opt | V2Opt),
            new XsltAttribute("stable"    , V1Opt | V2Opt),
            new XsltAttribute("case-order", V1Opt | V2Opt),
            new XsltAttribute("data-type" , V1Opt | V2Opt)
        };
        private XslNode XslSort(int sortNumber) {
            ContextInfo ctxInfo = input.GetAttributes(sortAttributes);
 
            string   select    = ParseStringAttribute(   0, "select"    );
            string   lang      = ParseStringAttribute(   1, "lang"      );
            string   order     = ParseStringAttribute(   2, "order"     );
            string   collation = ParseCollationAttribute(3);
            TriState stable    = ParseYesNoAttribute (   4, "stable"    );
            string   caseOrder = ParseStringAttribute(   5, "case-order");
            string   dataType  = ParseStringAttribute(   6, "data-type" );
 
            if (stable != TriState.Unknown) {
                CheckError(sortNumber != 0, Res.Xslt_SortStable);
            }
 
            List<XslNode> content = null;
            if (V1) {
                CheckNoContent();
            } else {
                content = LoadContent(select != null);
                if (content.Count != 0) {
                    ReportNYI("xsl:sort/*");
                    content = null;
                }
            }
 
            if (select == null /*&& content.Count == 0*/) {
                select = ".";
            }
 
            return SetInfo(f.Sort(select, lang, dataType, order, caseOrder, input.XslVersion),
                null, ctxInfo
            );
        }
 
#if XSLT2
        // http://www.w3.org/TR/xslt20/#element-document
        XsltAttribute[] documentAttributes = {
            new XsltAttribute("type"      , V2Opt),
            new XsltAttribute("validation", V2Opt)
        };
        private XslNode XslDocument() {
            ContextInfo ctxInfo = input.GetAttributes(documentAttributes);
 
            ParseTypeAttribute(0);
            ParseValidationAttribute(1, /*defVal:*/false);
 
            ReportNYI("xsl:document");
 
            List<XslNode> content = LoadEndTag(LoadInstructions());
 
            return null;
        }
 
        // http://www.w3.org/TR/xslt20/#element-analyze-string
        XsltAttribute[] analyzeStringAttributes = {
            new XsltAttribute("select", V2Req),
            new XsltAttribute("regex" , V2Req),
            new XsltAttribute("flags" , V2Opt)
        };
        private XslNode XslAnalyzeString() {
            ContextInfo ctxInfo = input.GetAttributes(analyzeStringAttributes);
 
            string select = ParseStringAttribute(0, "select");
            string regex  = ParseStringAttribute(1, "regex" );
            string flags  = ParseStringAttribute(2, "flags" );
            if (flags == null) {
                flags = "";
            }
 
            ReportNYI("xsl:analyze-string");
 
            XslNode matching = null;
            XslNode nonMatching = null;
            QName parentName = input.ElementName;
            if (input.MoveToFirstChild()) {
                do {
                    switch (input.NodeType) {
                    case XmlNodeType.Element:
                        if (input.IsXsltKeyword(atoms.MatchingSubstring)) {
                            ContextInfo ctxInfoChld = input.GetAttributes();
                            CheckError(nonMatching != null, /*[???]*/Res.Xslt_AnalyzeStringChildOrder);
                            CheckError(matching    != null, /*[???]*/Res.Xslt_AnalyzeStringDupChild, atoms.MatchingSubstring);
                            // The current template rule becomes null, so we must not allow xsl:apply-import's within this element
                            input.CanHaveApplyImports = false;
                            matching = SetInfo(f.List(), LoadInstructions(), ctxInfoChld);
                        } else if (input.IsXsltKeyword(atoms.NonMatchingSubstring)) {
                            ContextInfo ctxInfoChld = input.GetAttributes();
                            CheckError(nonMatching != null, /*[???]*/Res.Xslt_AnalyzeStringDupChild, atoms.NonMatchingSubstring);
                            input.CanHaveApplyImports = false;
                            nonMatching = SetInfo(f.List(), LoadInstructions(), ctxInfoChld);
                        } else if (input.IsXsltKeyword(atoms.Fallback)) {
                            XslFallback();
                        } else {
                            ReportError(/*[XT_017]*/Res.Xslt_UnexpectedElement, input.QualifiedName, parentName);
                            input.SkipNode();
                        }
                        break;
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                        break;
                    default:
                        Debug.Assert(input.NodeType == XmlNodeType.Text);
                        ReportError(/*[XT_017]*/Res.Xslt_TextNodesNotAllowed, parentName);
                        break;
                    }
                } while (input.MoveToNextSibling());
            }
            CheckError(matching == nonMatching, /*[XTSE1130]*/Res.Xslt_AnalyzeStringEmpty);
 
            ctxInfo.SaveExtendedLineInfo(input);
 
            return null;
        }
 
        // http://www.w3.org/TR/xslt20/#element-namespace
        XsltAttribute[] namespaceAttributes = {
            new XsltAttribute("name"  , V2Req),
            new XsltAttribute("select", V2Opt)
        };
        private XslNode XslNamespace() {
            ContextInfo ctxInfo = input.GetAttributes(namespaceAttributes);
            string name = ParseNCNameAttribute(0);
            string select= ParseStringAttribute(1, "select");
 
            List<XslNode> content = LoadContent(select != null);
            CheckError(select == null && content.Count == 0, /*[???]*/Res.Xslt_NoSelectNoContent, input.ElementName);
 
            ReportNYI("xsl:namespace");
 
            return null;
        }
 
        // http://www.w3.org/TR/xslt20/#element-perform-sort
        XsltAttribute[] performSortAttributes = {
            new XsltAttribute("select", V2Opt)
        };
        private XslNode XslPerformSort() {
            ContextInfo ctxInfo = input.GetAttributes(performSortAttributes);
            string select = ParseStringAttribute(0, "select");
 
            List<XslNode> content = LoadInstructions(InstructionFlags.AllowSort);
            ctxInfo.SaveExtendedLineInfo(input);
 
            if (select != null) {
                foreach (XslNode node in content) {
                    if (node.NodeType != XslNodeType.Sort) {
                        ReportError(Res.Xslt_PerformSortCntSel);
                        break;
                    }
                }
            }
 
            ReportNYI("xsl:perform-sort");
            return null;
        }
 
        // http://www.w3.org/TR/xslt20/#element-for-each-group
        XsltAttribute[] forEachGroupAttributes = {
            new XsltAttribute("select"             , V2Req),
            new XsltAttribute("group-by"           , V2Opt),
            new XsltAttribute("group-adjacent"     , V2Opt),
            new XsltAttribute("group-starting-with", V2Opt),
            new XsltAttribute("group-ending-with"  , V2Opt),
            new XsltAttribute("collation"          , V2Opt)
        };
        private XslNode XslForEachGroup() {
            ContextInfo ctxInfo = input.GetAttributes(forEachGroupAttributes);
 
            string select            = ParseStringAttribute(   0, "select"             );
            string groupBy           = ParseStringAttribute(   1, "group-by"           );
            string groupAdjacent     = ParseStringAttribute(   2, "group-adjacent"     );
            string groupStartingWith = ParseStringAttribute(   3, "group-starting-with");
            string groupEndingWith   = ParseStringAttribute(   4, "group-ending-with"  );
            string collation         = ParseCollationAttribute(5);
 
            ReportNYI("xsl:for-each-group");
 
            // The current template rule becomes null, so we must not allow xsl:apply-import's within this element
            input.CanHaveApplyImports = false;
            List<XslNode> content = LoadInstructions(InstructionFlags.AllowSort);
            ctxInfo.SaveExtendedLineInfo(input);
 
            return null;
        }
 
        // http://www.w3.org/TR/xslt20/#element-next-match
        private XslNode XslNextMatch() {
            ContextInfo ctxInfo = input.GetAttributes();
 
            // We need to do this dynamic any way:
            //if (!input.CanHaveApplyImports) {
            //    ReportError(/*[XT_015]*/Res.Xslt_InvalidApplyImports);
            //    input.SkipNode();
            //    return null;
            //}
 
            ReportNYI("xsl:next-match");
 
            List<XslNode> content = LoadWithParams(InstructionFlags.AllowFallback);
            ctxInfo.SaveExtendedLineInfo(input);
 
            return null;
        }
 
        // http://www.w3.org/TR/xslt20/#element-sequence
        XsltAttribute[] sequenceAttributes = {
            new XsltAttribute("select", V2Req)
        };
        private XslNode XslSequence() {
            ContextInfo ctxInfo = input.GetAttributes(sequenceAttributes);
            string select = ParseStringAttribute(0, "select");
            ReportNYI("xsl:sequence");
 
            QName parentName = input.ElementName;
            if (input.MoveToFirstChild()) {
                do {
                    switch (input.NodeType) {
                    case XmlNodeType.Element:
                        if (input.IsXsltKeyword(atoms.Fallback)) {
                            XslFallback();
                        } else {
                            ReportError(/*[XT_017]*/Res.Xslt_UnexpectedElement, input.QualifiedName, parentName);
                            input.SkipNode();
                        }
                        break;
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                        break;
                    default:
                        Debug.Assert(input.NodeType == XmlNodeType.Text);
                        ReportError(/*[XT_017]*/Res.Xslt_TextNodesNotAllowed, parentName);
                        break;
                    }
                } while (input.MoveToNextSibling());
            }
            return null;
        }
 
        // http://www.w3.org/TR/xslt20/#element-result-document
        XsltAttribute[] resultDocumentAttributes = {
            new XsltAttribute("format"                , V2Opt), // 0
            new XsltAttribute("href"                  , V2Opt), // 1
            new XsltAttribute("validation"            , V2Opt), // 2
            new XsltAttribute("type"                  , V2Opt), // 3
            new XsltAttribute("name"                  , V2Opt), // 4
            new XsltAttribute("method"                , V2Opt), // 5
            new XsltAttribute("byte-order-mark"       , V2Opt), // 6
            new XsltAttribute("cdata-section-elements", V2Opt), // 7
            new XsltAttribute("doctype-public"        , V2Opt), // 8
            new XsltAttribute("doctype-system"        , V2Opt), // 9
            new XsltAttribute("encoding"              , V2Opt), // 10
            new XsltAttribute("escape-uri-attributes" , V2Opt), // 11
            new XsltAttribute("include-content-type"  , V2Opt), // 12
            new XsltAttribute("indent"                , V2Opt), // 13
            new XsltAttribute("media-type"            , V2Opt), // 14
            new XsltAttribute("normalization-form"    , V2Opt), // 15
            new XsltAttribute("omit-xml-declaration"  , V2Opt), // 16
            new XsltAttribute("standalone"            , V2Opt), // 17
            new XsltAttribute("undeclare-prefixes"    , V2Opt), // 18
            new XsltAttribute("use-character-maps"    , V2Opt), // 19
            new XsltAttribute("output-version"        , V2Opt)  // 20
        };
        private XslNode XslResultDocument() {
            ContextInfo ctxInfo = input.GetAttributes(resultDocumentAttributes);
 
            string format                  = ParseStringAttribute(0 , "format");
            XmlWriterSettings settings = new XmlWriterSettings(); // we should use attFormat to determing settings
            string href                    = ParseStringAttribute(1 , "href");
            ParseValidationAttribute(2, /*defVal:*/false);
            ParseTypeAttribute(3);
            QilName  name                  = ParseQNameAttribute( 4);
            TriState byteOrderMask         = ParseYesNoAttribute( 6 , "byte-order-mark");
            string   docTypePublic         = ParseStringAttribute(8 , "doctype-public");
            string   docTypeSystem         = ParseStringAttribute(9 , "doctype-system");
            bool     escapeUriAttributes   = ParseYesNoAttribute( 11, "escape-uri-attributes") != TriState.False;
            bool     includeContentType    = ParseYesNoAttribute( 12, "include-content-type") != TriState.False;
            settings.Indent                = ParseYesNoAttribute( 13, "indent") == TriState.True;
            string   mediaType             = ParseStringAttribute(14, "media-type");
            string   normalizationForm     = ParseStringAttribute(15, "normalization-form");
            settings.OmitXmlDeclaration    = ParseYesNoAttribute( 16, "omit-xml-declaration") == TriState.True;
            settings.Standalone            = ParseYesNoAttribute( 17, "standalone"        ) == TriState.True ? XmlStandalone.Yes : XmlStandalone.No;
            bool undeclarePrefixes         = ParseYesNoAttribute( 18, "undeclare-prefixes") == TriState.True;
            List<QilName> useCharacterMaps = ParseUseCharacterMaps(19);
            string   outputVersion         = ParseStringAttribute(20, "output-version");
 
            ReportNYI("xsl:result-document");
 
            if (format != null) ReportNYI("xsl:result-document/@format");
 
            if (href == null) {
                href = string.Empty;
            }
            // attHref is a BaseUri of new output tree. It should be resolved relative to "base output URI"
 
 
            if (input.MoveToXsltAttribute(5, "method")) {
                compiler.EnterForwardsCompatible();
                XmlOutputMethod   outputMethod;
                ParseOutputMethod(input.Value, out outputMethod);
                if (compiler.ExitForwardsCompatible(input.ForwardCompatibility)) {
                    settings.OutputMethod = outputMethod;
                }
            }
            if (input.MoveToXsltAttribute(7, "cdata-section-elements")) {
                // Do not check the import precedence, the effective value is the union of all specified values
                compiler.EnterForwardsCompatible();
                string[] qnames = XmlConvert.SplitString(input.Value);
                List<XmlQualifiedName> list = new List<XmlQualifiedName>();
                for (int i = 0; i < qnames.Length; i++) {
                    list.Add(ResolveQName(/*ignoreDefaultNs:*/false, qnames[i]));
                }
                if (compiler.ExitForwardsCompatible(input.ForwardCompatibility)) {
                    foreach (XmlQualifiedName qname in list) {
                        settings.CDataSectionElements.Add(qname);
                    }
                }
            }
            if (input.MoveToXsltAttribute(10, "encoding")) {
                try {
                    // Encoding.GetEncoding() should never throw NotSupportedException, only ArgumentException
                    settings.Encoding = Encoding.GetEncoding(input.Value);
                } catch (ArgumentException) {
                    if (!input.ForwardCompatibility) {
                        ReportWarning(/*[XT_004]*/Res.Xslt_InvalidEncoding, input.Value);
                    }
                }
            }
 
            if (byteOrderMask != TriState.Unknown) ReportNYI("xsl:result-document/@byte-order-mark");
            if (!escapeUriAttributes) ReportNYI("xsl:result-document/@escape-uri-attributes == flase()");
            if (!includeContentType) ReportNYI("xsl:output/@include-content-type == flase()");
            if (normalizationForm != null) ReportNYI("xsl:result-document/@normalization-form");
            if (undeclarePrefixes) ReportNYI("xsl:result-document/@undeclare-prefixes == true()");
 
            if (docTypePublic != null) {
                settings.DocTypePublic = docTypePublic;
            }
 
            if (docTypeSystem != null) {
                settings.DocTypeSystem = docTypeSystem;
            }
            if (mediaType != null) {
                settings.MediaType = mediaType;
            }
 
            if (useCharacterMaps != null) ReportNYI("xsl:result-document/@use-character-maps");
 
            if (outputVersion != null) {
                // 
 
                ReportNYI("xsl:result-document/@output-version");
            }
 
            LoadInstructions();
            return null;
        }
#endif
 
        // http://www.w3.org/TR/xslt#literal-result-element
        private XslNode LoadLiteralResultElement(bool asStylesheet) {
            Debug.Assert(input.NodeType == XmlNodeType.Element);
            string prefix   = input.Prefix;
            string name     = input.LocalName;
            string nsUri    = input.NamespaceUri;
 
            ContextInfo ctxInfo = input.GetLiteralAttributes(asStylesheet);
 
            if (input.IsExtensionNamespace(nsUri)) {
                // This is not a literal result element, so drop all attributes we have collected
                return SetInfo(f.List(), LoadFallbacks(name), ctxInfo);
            }
 
            List<XslNode> content = new List<XslNode>();
 
            for (int i = 1; input.MoveToLiteralAttribute(i); i++) {
                if (input.IsXsltNamespace() && input.IsKeyword(atoms.UseAttributeSets)) {
                    AddUseAttributeSets(content);
                }
            }
 
            for (int i = 1; input.MoveToLiteralAttribute(i); i++) {
                if (! input.IsXsltNamespace()) {
                    XslNode att = f.LiteralAttribute(f.QName(input.LocalName, input.NamespaceUri, input.Prefix), input.Value, input.XslVersion);
                    // QilGenerator takes care of AVTs, and needs line info
                    AddInstruction(content, SetLineInfo(att, ctxInfo.lineInfo));
                } else {
                    // ignore all other xslt attributes. See XslInput.GetLiteralAttributes()
                }
            }
 
            content = LoadEndTag(LoadInstructions(content));
            return SetInfo(f.LiteralElement(f.QName(name, nsUri, prefix)), content, ctxInfo);
        }
 
        private void CheckWithParam(List<XslNode> content, XslNode withParam) {
            Debug.Assert(content != null && withParam != null);
            Debug.Assert(withParam.NodeType == XslNodeType.WithParam);
            foreach (XslNode node in content) {
                if (node.NodeType == XslNodeType.WithParam && node.Name.Equals(withParam.Name)) {
                    ReportError(/*[XT0670]*/Res.Xslt_DuplicateWithParam, withParam.Name.QualifiedName);
                    break;
                }
            }
        }
 
        private static void AddInstruction(List<XslNode> content, XslNode instruction) {
            Debug.Assert(content != null);
            if (instruction != null) {
                content.Add(instruction);
            }
        }
 
        private List<XslNode> LoadEndTag(List<XslNode> content) {
            Debug.Assert(content != null);
            if (compiler.IsDebug && !input.IsEmptyElement) {
                AddInstruction(content, SetLineInfo(f.Nop(), input.BuildLineInfo()));
            }
            return content;
        }
 
        private XslNode LoadUnknownXsltInstruction(string parentName) {
            input.GetVersionAttribute();
            if (!input.ForwardCompatibility) {
                ReportError(/*[XT_026]*/Res.Xslt_UnexpectedElement, input.QualifiedName, parentName);
                input.SkipNode();
                return null;
            } else {
                ContextInfo ctxInfo = input.GetAttributes();
                List<XslNode> fallbacks = LoadFallbacks(input.LocalName);
                return SetInfo(f.List(), fallbacks, ctxInfo);
            }
        }
 
        private List<XslNode> LoadFallbacks(string instrName) {
            input.MoveToElement();
            ISourceLineInfo extElmLineInfo = input.BuildNameLineInfo();
            List<XslNode> fallbacksArray = new List<XslNode>();
            // 
            /* Process children */
            if (input.MoveToFirstChild()) {
                do {
                    if (input.IsXsltKeyword(atoms.Fallback)) {
                        ContextInfo ctxInfo = input.GetAttributes();
                        fallbacksArray.Add(SetInfo(f.List(), LoadInstructions(), ctxInfo));
                    } else {
                        input.SkipNode();
                    }
                } while (input.MoveToNextSibling());
            }
 
            // Generate runtime error if there is no fallbacks
            if (fallbacksArray.Count == 0) {
                fallbacksArray.Add(
                    f.Error(XslLoadException.CreateMessage(extElmLineInfo, Res.Xslt_UnknownExtensionElement, instrName))
                );
            }
            return fallbacksArray;
        }
 
        // ------------------ little helper methods ---------------------
 
        // Suppresses errors if FCB is enabled
        private QilName ParseModeAttribute(int attNum) {
            //Debug.Assert(
            //    input.IsXsltKeyword(atoms.ApplyTemplates) ||
            //    input.IsXsltKeyword(atoms.Template) && V1
            //);
            if (! input.MoveToXsltAttribute(attNum, "mode")) {
                return nullMode;
            }
            // mode is always optional attribute
            compiler.EnterForwardsCompatible();
            string qname = input.Value;
            QilName mode;
            if (!V1 && qname == "#default") {
                mode = nullMode;
            } else if (!V1 && qname == "#current") {
                ReportNYI("xsl:apply-templates[@mode='#current']");
                mode = nullMode;
            } else if (!V1 && qname == "#all") {
                ReportError(Res.Xslt_ModeListAll);
                mode = nullMode;
            } else {
                mode = CreateXPathQName(qname);
            }
            if (!compiler.ExitForwardsCompatible(input.ForwardCompatibility)) {
                mode = nullMode;
            }
            return mode;
        }
 
        // Parse mode when it is list. V1: -, V2: xsl:template
        // Suppresses errors if FCB is enabled
        private QilName ParseModeListAttribute(int attNum) {
            //Debug.Assert(input.IsXsltKeyword(atoms.Template) && !V1);
            if (! input.MoveToXsltAttribute(attNum, "mode")) {
                return nullMode;
            }
 
            string modeList = input.Value;
            if (modeList == "#all") {
                ReportNYI("xsl:template[@mode='#all']");
                return nullMode;
            } else {
                string[] list = XmlConvert.SplitString(modeList);
                List<QilName> modes = new List<QilName>(list.Length);
 
                compiler.EnterForwardsCompatible();  // mode is always optional attribute
 
                if (list.Length == 0) {
                    ReportError(Res.Xslt_ModeListEmpty);
                } else {
                    foreach (string qname in list) {
                        QilName mode;
                        if (qname == "#default") {
                            mode = nullMode;
                        } else if (qname == "#current") {
                            ReportNYI("xsl:apply-templates[@mode='#current']");
                            break;
                        } else if (qname == "#all") {
                            ReportError(Res.Xslt_ModeListAll);
                            break;
                        } else {
                            mode = CreateXPathQName(qname);
                        }
                        bool dup = false;
                        foreach (QilName m in modes) {
                            dup |= m.Equals(mode);
                        }
                        if (dup) {
                            ReportError(Res.Xslt_ModeListDup, qname);
                        } else {
                            modes.Add(mode);
                        }
                    }
                }
 
                if (!compiler.ExitForwardsCompatible(input.ForwardCompatibility)) {
                    modes.Clear();
                    modes.Add(nullMode);
                }
                if (1 < modes.Count) {
                    ReportNYI("Multipe modes");
                    return nullMode;
                }
                if (modes.Count == 0) {
                    return nullMode;
                }
                return modes[0];
            }
        }
 
        private string ParseCollationAttribute(int attNum) {
            if (input.MoveToXsltAttribute(attNum, "collation")) {
                ReportNYI("@collation");
            }
            return null;
        }
 
        // Does not suppress errors
        private bool ResolveQName(bool ignoreDefaultNs, string qname, out string localName, out string namespaceName, out string prefix) {
            if (qname == null) {
                // That means stylesheet is incorrect
                prefix = compiler.PhantomNCName;
                localName = compiler.PhantomNCName;
                namespaceName = compiler.CreatePhantomNamespace();
                return false;
            }
            if (!compiler.ParseQName(qname, out prefix, out localName, (IErrorHelper)this)) {
                namespaceName = compiler.CreatePhantomNamespace();
                return false;
            }
            if (ignoreDefaultNs && prefix.Length == 0) {
                namespaceName = string.Empty;
            } else {
                namespaceName = input.LookupXmlNamespace(prefix);
                if (namespaceName == null) {
                    namespaceName = compiler.CreatePhantomNamespace();
                    return false;
                }
            }
            return true;
        }
 
        // Does not suppress errors
        private QilName ParseQNameAttribute(int attNum) {
            bool required = input.IsRequiredAttribute(attNum);
            QilName result = null;
            if (!required) {
                compiler.EnterForwardsCompatible();
            }
            if (input.MoveToXsltAttribute(attNum, "name")) {
                string prefix, localName, namespaceName;
                if (ResolveQName(/*ignoreDefaultNs:*/true, input.Value, out localName, out namespaceName, out prefix)) {
                    result = f.QName(localName, namespaceName, prefix);
                }
            }
            if (!required) {
                compiler.ExitForwardsCompatible(input.ForwardCompatibility);
            }
            if (result == null && required) {
                result = f.QName(compiler.PhantomNCName, compiler.CreatePhantomNamespace(), compiler.PhantomNCName);
            }
            return result;
        }
 
        private string ParseNCNameAttribute(int attNum) {
            Debug.Assert(input.IsRequiredAttribute(attNum), "It happened that @name as NCName is always required attribute");
            if (input.MoveToXsltAttribute(attNum, "name")) {
                return input.Value;
            }
            return compiler.PhantomNCName;
        }
 
        // Does not suppress errors
        private QilName CreateXPathQName(string qname) {
            string prefix, localName, namespaceName;
            ResolveQName(/*ignoreDefaultNs:*/true, qname, out localName, out namespaceName, out prefix);
            return f.QName(localName, namespaceName, prefix);
        }
 
        // Does not suppress errors
        private XmlQualifiedName ResolveQName(bool ignoreDefaultNs, string qname) {
            string prefix, localName, namespaceName;
            ResolveQName(ignoreDefaultNs, qname, out localName, out namespaceName, out prefix);
            return new XmlQualifiedName(localName, namespaceName);
        }
 
        // Does not suppress errors
        private void ParseWhitespaceRules(string elements, bool preserveSpace) {
            if (elements != null && elements.Length != 0) {
                string[] tokens = XmlConvert.SplitString(elements);
                for (int i = 0; i < tokens.Length; i++) {
                    string prefix, localName, namespaceName;
                    if (!compiler.ParseNameTest(tokens[i], out prefix, out localName, (IErrorHelper)this)) {
                        namespaceName = compiler.CreatePhantomNamespace();
                    } else if (prefix == null || prefix.Length == 0) {
                        namespaceName = prefix;
                    } else {
                        namespaceName = input.LookupXmlNamespace(prefix);
                        if (namespaceName == null) {
                            namespaceName = compiler.CreatePhantomNamespace();
                        }
                    }
                    int index = (
                        (localName     == null ? 1 : 0) +
                        (namespaceName == null ? 1 : 0)
                    );
                    curStylesheet.AddWhitespaceRule(index, new WhitespaceRule(localName, namespaceName, preserveSpace));
                }
            }
        }
 
        // Does not suppress errors.  In case of error, null is returned.
        private XmlQualifiedName ParseOutputMethod(string attValue, out XmlOutputMethod method) {
            string prefix, localName, namespaceName;
            ResolveQName(/*ignoreDefaultNs:*/true, attValue, out localName, out namespaceName, out prefix);
            method = XmlOutputMethod.AutoDetect;
 
            if (compiler.IsPhantomNamespace(namespaceName)) {
                return null;
            } else if (prefix.Length == 0) {
                switch (localName) {
                case "xml"  : method = XmlOutputMethod.Xml;  break;
                case "html" : method = XmlOutputMethod.Html; break;
                case "text" : method = XmlOutputMethod.Text; break;
                default:
                    ReportError(/*[XT1570]*/Res.Xslt_InvalidAttrValue, "method", attValue);
                    return null;
                }
            } else {
                if (!input.ForwardCompatibility) {
                    ReportWarning(/*[XT1570]*/Res.Xslt_InvalidMethod, attValue);
                }
            }
            return new XmlQualifiedName(localName, namespaceName);
        }
 
        // Suppresses errors if FCB is enabled
        private void AddUseAttributeSets(List<XslNode> list) {
            Debug.Assert(input.LocalName == "use-attribute-sets", "we are positioned on this attribute");
            Debug.Assert(list != null && list.Count == 0, "It happened that we always add use-attribute-sets first. Otherwise we can't call list.Clear()");
 
            compiler.EnterForwardsCompatible();
            foreach (string qname in XmlConvert.SplitString(input.Value)) {
                AddInstruction(list, SetLineInfo(f.UseAttributeSet(CreateXPathQName(qname)), input.BuildLineInfo()));
            }
            if (!compiler.ExitForwardsCompatible(input.ForwardCompatibility)) {
                // There were errors in the list, ignore the whole list
                list.Clear();
            }
        }
 
        private List<QilName> ParseUseCharacterMaps(int attNum) {
            List<QilName> useCharacterMaps = new List<QilName>();
            if (input.MoveToXsltAttribute(attNum, "use-character-maps")) {
                compiler.EnterForwardsCompatible();
                foreach (string qname in XmlConvert.SplitString(input.Value)) {
                    useCharacterMaps.Add(CreateXPathQName(qname));
                }
                if (!compiler.ExitForwardsCompatible(input.ForwardCompatibility)) {
                    useCharacterMaps.Clear(); // There were errors in the list, ignore the whole list
                }
            }
            return useCharacterMaps;
        }
 
        private string ParseStringAttribute(int attNum, string attName) {
            if (input.MoveToXsltAttribute(attNum, attName)) {
                return input.Value;
            }
            return null;
        }
 
        private char ParseCharAttribute(int attNum, string attName, char defVal) {
            if (input.MoveToXsltAttribute(attNum, attName)) {
                if (input.Value.Length == 1) {
                    return input.Value[0];
                } else {
                    if (input.IsRequiredAttribute(attNum) || !input.ForwardCompatibility) {
                        ReportError(/*[XT_029]*/Res.Xslt_CharAttribute, attName);
                    }
                }
            }
            return defVal;
        }
 
        // Suppresses errors if FCB is enabled
        private TriState ParseYesNoAttribute(int attNum, string attName) {
            Debug.Assert(!input.IsRequiredAttribute(attNum), "All Yes/No attributes are optional.");
            if (input.MoveToXsltAttribute(attNum, attName)) {
                switch (input.Value) {
                case "yes" : return TriState.True;
                case "no"  : return TriState.False;
                default:
                    if (!input.ForwardCompatibility) {
                        ReportError(/*[XT_028]*/Res.Xslt_BistateAttribute, attName, "yes", "no");
                    }
                    break;
                }
            }
            return TriState.Unknown;
        }
 
        private void ParseTypeAttribute(int attNum) {
            Debug.Assert(!input.IsRequiredAttribute(attNum), "All 'type' attributes are optional.");
            if (input.MoveToXsltAttribute(attNum, "type")) {
                CheckError(true, /*[???]*/Res.Xslt_SchemaAttribute, "type");
            }
        }
 
        private void ParseValidationAttribute(int attNum, bool defVal) {
            Debug.Assert(!input.IsRequiredAttribute(attNum), "All 'validation' attributes are optional.");
            string attributeName = defVal ? atoms.DefaultValidation : "validation";
            if (input.MoveToXsltAttribute(attNum, attributeName)) {
                string value = input.Value;
                if (value == "strip") {
                    // no error
                } else if (
                    value == "preserve" ||
                    value == "strict" && !defVal ||
                    value == "lax" && !defVal
                ) {
                    ReportError(/*[???]*/Res.Xslt_SchemaAttributeValue, attributeName, value);
                } else if (!input.ForwardCompatibility) {
                    ReportError(/*[???]*/Res.Xslt_InvalidAttrValue, attributeName, value);
                }
            }
        }
 
        private void ParseInputTypeAnnotationsAttribute(int attNum) {
            Debug.Assert(!input.IsRequiredAttribute(attNum), "All 'input-type-validation' attributes are optional.");
            if (input.MoveToXsltAttribute(attNum, "input-type-annotations")) {
                string value = input.Value;
                switch (value) {
                case "unspecified":
                    break;
                case "strip":
                case "preserve":
                    if (compiler.inputTypeAnnotations == null) {
                        compiler.inputTypeAnnotations = value;
                    } else {
                        CheckError(compiler.inputTypeAnnotations != value, /*[XTSE0265]*/Res.Xslt_InputTypeAnnotations);
                    }
                    break;
                default:
                    if (!input.ForwardCompatibility) {
                        ReportError(/*[???]*/Res.Xslt_InvalidAttrValue, "input-type-annotations", value);
                    }
                    break;
                }
            }
        }
 
 
        // ToDo: We don't need separation on SkipEmptyContent() and CheckNoContent(). Merge them back when we are done with parsing.
        private void CheckNoContent() {
            input.MoveToElement();
            QName parentName = input.ElementName;
            ISourceLineInfo errorLineInfo = SkipEmptyContent();
 
            if (errorLineInfo != null) {
                compiler.ReportError(errorLineInfo, /*[XT0260]*/Res.Xslt_NotEmptyContents, parentName);
            }
        }
 
        // Returns ISourceLineInfo of the first violating (non-whitespace) node, or null otherwise
        private ISourceLineInfo SkipEmptyContent() {
            ISourceLineInfo result = null;
 
            // Really EMPTY means no content at all, but for the sake of compatibility with MSXML we allow whitespaces
            if (input.MoveToFirstChild()) {
                do {
                    // NOTE: XmlNodeType.SignificantWhitespace are not allowed here
                    if (input.NodeType != XmlNodeType.Whitespace) {
                        if (result == null) {
                            result = input.BuildNameLineInfo();
                        }
                        input.SkipNode();
                    }
                } while (input.MoveToNextSibling());
            }
            return result;
        }
 
        private static XslNode SetLineInfo(XslNode node, ISourceLineInfo lineInfo) {
            Debug.Assert(node != null);
            node.SourceLine = lineInfo;
            return node;
        }
 
        private static void SetContent(XslNode node, List<XslNode> content) {
            Debug.Assert(node != null);
            if (content != null && content.Count == 0) {
                content = null; // Actualy we can reuse this ArrayList.
            }
            node.SetContent(content);
        }
 
        internal static XslNode SetInfo(XslNode to, List<XslNode> content, ContextInfo info) {
            Debug.Assert(to != null);
            to.Namespaces = info.nsList;
            SetContent(to, content);
            SetLineInfo(to, info.lineInfo);
            return to;
        }
 
        // NOTE! We inverting namespace order that is irelevant for namespace of the same node, but
        // for included styleseets we don't keep stylesheet as a node and adding it's namespaces to
        // each toplevel element by MergeNamespaces().
        // Namespaces of stylesheet can be overriden in template and to make this works correclety we
        // should attache them after NsDec of top level elements.
        // Toplevel element almost never contais NsDecl and in practice node duplication will not happened, but if they have
        // we should copy NsDecls of stylesheet localy in toplevel elements.
        private static NsDecl MergeNamespaces(NsDecl thisList, NsDecl parentList) {
            if (parentList == null) {
                return thisList;
            }
            if (thisList == null) {
                return parentList;
            }
            // Clone all nodes and attache them to nodes of thisList;
            while (parentList != null) {
                bool duplicate = false;
                for (NsDecl tmp = thisList; tmp != null; tmp = tmp.Prev) {
                    if (Ref.Equal(tmp.Prefix, parentList.Prefix) && (
                        tmp.Prefix != null ||           // Namespace declaration
                        tmp.NsUri == parentList.NsUri   // Extension or excluded namespace
                    )) {
                        duplicate = true;
                        break;
                    }
                }
                if (!duplicate) {
                    thisList = new NsDecl(thisList, parentList.Prefix, parentList.NsUri);
                }
                parentList = parentList.Prev;
            }
            return thisList;
        }
 
        // -------------------------------- IErrorHelper --------------------------------
 
        public void ReportError(string res, params string[] args) {
            compiler.ReportError(input.BuildNameLineInfo(), res, args);
        }
 
        public void ReportWarning(string res, params string[] args) {
            compiler.ReportWarning(input.BuildNameLineInfo(), res, args);
        }
 
        private void ReportNYI(string arg) {
            if (! input.ForwardCompatibility) {
                ReportError(Res.Xslt_NotYetImplemented, arg);
            }
        }
 
        public void CheckError(bool cond, string res, params string[] args) {
            if (cond) {
                compiler.ReportError(input.BuildNameLineInfo(), res, args);
            }
        }
    }
}