File: System\Xml\Xsl\Xslt\XsltLoader.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
//#define XSLT2
 
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml.XPath;
using System.Xml.Xsl.Qil;
using ContextInfo = System.Xml.Xsl.Xslt.XsltInput.ContextInfo;
using F = System.Xml.Xsl.Xslt.AstFactory;
using QName = System.Xml.Xsl.Xslt.XsltInput.DelayedQName;
using TypeFactory = System.Xml.Xsl.XmlQueryTypeFactory;
using XsltAttribute = System.Xml.Xsl.Xslt.XsltInput.XsltAttribute;
 
namespace System.Xml.Xsl.Xslt
{
    internal sealed class XsltLoader : IErrorHelper
    {
        private Compiler _compiler = null!;
        private XmlResolver _xmlResolver = null!;
        private QueryReaderSettings _readerSettings = null!;
        private KeywordsTable _atoms = null!; // XSLT keywords atomized with QueryReaderSettings.NameTabel
        private XsltInput _input = null!;     // Current input stream
        private Stylesheet? _curStylesheet;   // Current stylesheet
        private Template? _curTemplate;       // Current template
 
        internal static readonly QilName nullMode = F.QName(string.Empty);
 
        // Flags which control attribute versioning
        public const int V1Opt = 1;
        public const int V1Req = 2;
        public const int V2Opt = 4;
        public const int V2Req = 8;
 
        public void Load(Compiler compiler, object stylesheet, XmlResolver? xmlResolver, XmlResolver? origResolver)
        {
            Debug.Assert(compiler != null);
            _compiler = compiler;
            _xmlResolver = xmlResolver ?? XmlResolver.ThrowingResolver;
 
            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 the stylesheet has been provided as a string (URI, really), then we'll bounce
                    // through an XmlResolver to look up its contents. There's a complication here since
                    // the default resolver provided by our caller is likely a throwing resolver, and
                    // a throwing resolver would fail even when attempting to read this URL. We need
                    // to at minimum allow this URL to be read, because the user after all did explicitly
                    // ask us to do so.
                    //
                    // In this case, we'll rely on the 'origResolver' argument, which is the XmlResolver
                    // which was provided *to our caller* before any default substitution took place.
                    // If an explicit resolver was specified, we'll honor it. Otherwise we'll substitute
                    // an XmlUrlResolver for this one read operation. The stored resolver (which is used
                    // for reads beyond the initial stylesheet read) will use a throwing resolver as its
                    // default, as shown at the very top of this method.
 
                    origResolver ??= XmlReaderSettings.GetDefaultPermissiveResolver();
                    Uri resolvedUri = origResolver.ResolveUri(null, uri);
                    if (resolvedUri == null)
                    {
                        throw new XslLoadException(SR.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)
        {
            _atoms = new KeywordsTable(reader.NameTable);
            AtomizeAttributes();
            LoadStylesheet(reader, /*include:*/false);
        }
 
 
        private void AtomizeAttributes(XsltAttribute[] attributes)
        {
            for (int i = 0; i < attributes.Length; i++)
            {
                attributes[i].name = _atoms.NameTable.Add(attributes[i].name);
            }
        }
        private 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 readonly HybridDictionary _documentUriInUse = new HybridDictionary();
 
        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(SR.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(SR.Xslt_CannotLoadStylesheet, uri.ToString(), input == null ? "null" : input.GetType().ToString());
        }
 
        private Stylesheet LoadStylesheet(Uri uri, bool include)
        {
            using (XmlReader reader = CreateReader(uri, _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]*/SR.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]*/SR.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;
        }
 
        private readonly 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]*/SR.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);
                                }
                                else if (_input.IsKeyword(_atoms.Param))
                                {
                                    LoadGlobalVariableOrParameter(ctxInfo.nsList);
                                }
                                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]*/SR.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]*/SR.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]*/SR.Xslt_TextNodesNotAllowed, parentName);
                            break;
                    }
                } while (_input.MoveToNextSibling());
            }
        }
 
 
        private readonly 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.
        private void LoadImport()
        {
            _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]*/SR.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.
        private void LoadInclude()
        {
            _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]*/SR.Xslt_CircularInclude, _input.Value);
                }
                else
                {
                    LoadStylesheet(uri, /*include:*/ true);
                }
            }
            else
            {
                // The error was already reported. Ignore the instruction
            }
 
            CheckNoContent();
        }
 
        private readonly 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();
        }
 
        private readonly 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()
        {
            _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]*/SR.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]*/SR.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]*/SR.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]*/SR.Xslt_AttributeRedefinition, "encoding");
                        }
                        settings.Encoding = encoding;
                        output.Encoding = _input.Value;
                        output.EncodingPrec = currentPrec;
                    }
                    catch (ArgumentException)
                    {
                        if (!_input.ForwardCompatibility)
                        {
                            ReportWarning(/*[XT_004]*/SR.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]*/SR.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]*/SR.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]*/SR.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]*/SR.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]*/SR.Xslt_AttributeRedefinition, "version");
                    }
                    // BUGBUG: Check that version is a valid nmtoken
                    // ignore version since we support only one version for both xml (1.0) and html (4.0)
                    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!, SR.Xslt_CircularAttributeSet, attSet.Name!.QualifiedName);
                    break;
            }
        }
 
        private readonly 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");
            ParseCollationAttribute(3);
 
            _input.MoveToElement();
 
            List<XslNode>? content;
 
            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]*/SR.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);
            }
        }
 
        private readonly 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;
            Span<char> characters = stackalloc 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 attrs should have lineInfo. If both are default they can't conflict.");
                        ReportError(/*[XT1300]*/SR.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]*/SR.Xslt_DecimalFormatRedefined, "infinity", infinity);
                _input.MoveToXsltAttribute(2, "NaN");
                CheckError(nan != format.NanSymbol, /*[XT1290]*/SR.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]*/SR.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();
        }
 
        private readonly 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]*/SR.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]*/SR.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]*/SR.Xslt_DupNsAlias, stylesheetNsUri);
            }
        }
 
        private readonly 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]*/SR.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]*/SR.Xslt_TextNodesNotAllowed, parentName);
                            break;
                    }
                } while (_input.MoveToNextSibling());
            }
            set.AddContent(SetInfo(F.List(), LoadEndTag(content), ctxInfo));
        }
 
        private void LoadGlobalVariableOrParameter(NsDecl? stylesheetNsList)
        {
            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]*/SR.Xslt_DupGlobalVariable, var.Name!.QualifiedName);
        }
 
        //: http://www.w3.org/TR/xslt#section-Defining-Template-Rules
        private readonly 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]*/SR.Xslt_InvalidAttrValue, "priority", _input.Value);
                }
            }
            QilName mode = V1 ? ParseModeAttribute(3) : ParseModeListAttribute(3);
 
            if (match == null)
            {
                CheckError(!_input.AttributeExists(1, "name"), /*[XT_007]*/SR.Xslt_BothMatchNameAbsent);
                CheckError(_input.AttributeExists(3, "mode"), /*[XT_008]*/SR.Xslt_ModeWithoutMatch);
                mode = nullMode;
                if (_input.AttributeExists(2, "priority"))
                {
                    if (V1)
                    {
                        ReportWarning(/*[XT_008]*/SR.Xslt_PriorityWithoutMatch);
                    }
                    else
                    {
                        ReportError(/*[XT_008]*/SR.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]*/SR.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]*/SR.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]*/SR.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]*/SR.Xslt_SchemaDeclaration, input.ElementName);
            input.SkipNode();
        }
#endif
 
        private readonly 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]*/SR.Xslt_EmptyAttrValue, "implements-prefix", _input.Value);
                }
                else
                {
                    scriptNs = _input.LookupXmlNamespace(_input.Value);
                    if (scriptNs == XmlReservedNs.NsXslt)
                    {
                        ReportError(/*[XT_036]*/SR.Xslt_ScriptXsltNamespace);
                        scriptNs = null;
                    }
                }
            }
 
            scriptNs ??= _compiler.CreatePhantomNamespace();
            ParseStringAttribute(1, "language");
 
            if (!_compiler.Settings.EnableScript)
            {
                _compiler.Scripts.ScriptClasses[scriptNs] = null;
                _input.SkipNode();
                return;
            }
 
            throw new PlatformNotSupportedException(SR.CompilingScriptsNotSupported); // Not adding any scripts as script compilation is not available
        }
 
        private readonly XsltAttribute[] _assemblyAttributes = {
            new XsltAttribute("name", V1Opt | V2Opt),
            new XsltAttribute("href", V1Opt | V2Opt)
        };
 
        private readonly XsltAttribute[] _usingAttributes = {
            new XsltAttribute("namespace", V1Req | V2Req)
        };
 
        // ----------------- 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.
 
        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);
        }
 
        private const int MAX_LOADINSTRUCTIONS_DEPTH = 1024;
        private int _loadInstructionsDepth;
        private List<XslNode> LoadInstructions(List<XslNode> content, InstructionFlags flags)
        {
            if (++_loadInstructionsDepth > MAX_LOADINSTRUCTIONS_DEPTH)
            {
                if (LocalAppContextSwitches.LimitXPathComplexity)
                {
                    throw XslLoadException.Create(SR.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]*/SR.Xslt_UnexpectedElement :
                                        !atTop ? /*[XT_014]*/SR.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]*/SR.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]*/SR.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]*/SR.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]*/SR.Xslt_NotEmptyContents, _atoms.ApplyImports);
                    }
                    else
                    {
                        return SetInfo(F.Error(XslLoadException.CreateMessage(contentInfo, /*[XT0260]*/SR.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
        private readonly 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") ?? "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
        private readonly 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
        private readonly 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);
        }
 
        private readonly 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;
        }
 
        private readonly 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]*/SR.Xslt_WhenAfterOtherwise);
                                        _input.SkipNode();
                                        continue;
                                    }
                                    else
                                    {
                                        when = true;
                                        node = XslIf();
                                    }
                                }
                                else if (Ref.Equal(_input.LocalName, _atoms.Otherwise))
                                {
                                    if (otherwise)
                                    {
                                        ReportError(/*[XT_019]*/SR.Xslt_DupOtherwise);
                                        _input.SkipNode();
                                        continue;
                                    }
                                    else
                                    {
                                        otherwise = true;
                                        node = XslOtherwise();
                                    }
                                }
                            }
                            if (node == null)
                            {
                                ReportError(/*[XT_020]*/SR.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]*/SR.Xslt_TextNodesNotAllowed, parentName);
                            break;
                    }
                } while (_input.MoveToNextSibling());
            }
            CheckError(!when, /*[XT_021]*/SR.Xslt_NoWhen);
            return SetInfo(F.Choose(), content, ctxInfo);
        }
 
        private XslNode XslOtherwise()
        {
            ContextInfo ctxInfo = _input.GetAttributes();
            return SetInfo(F.Otherwise(), LoadInstructions(), ctxInfo);
        }
 
        private readonly 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
        private readonly 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
        private readonly 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]*/SR.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"
            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
        private readonly 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)
            {
                ReportNYI("xsl:value-of/@separator");
            }
 
            List<XslNode>? content;
 
            if (V1)
            {
                if (select == null)
                {
                    _input.SkipNode();
                    return SetInfo(F.Error(XslLoadException.CreateMessage(ctxInfo.lineInfo, SR.Xslt_MissingAttribute, "select")), null, ctxInfo);
                }
                CheckNoContent();
            }
            else
            {
                content = LoadContent(select != null);
                CheckError(select == null && content.Count == 0, /*[???]*/SR.Xslt_NoSelectNoContent, _input.ElementName);
                if (content.Count != 0)
                {
                    ReportNYI("xsl:value-of/*");
                }
            }
 
            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
        private readonly XsltAttribute[] _variableAttributes = {
            new XsltAttribute("name",     V1Req | V2Req),
            new XsltAttribute("select",   V1Opt | V2Opt),
            new XsltAttribute("as",       V2Opt),
            new XsltAttribute("required", 0),
            new XsltAttribute("tunnel",   0)
        };
        private readonly XsltAttribute[] _paramAttributes = {
            new XsltAttribute("name",     V1Req | V2Req),
            new XsltAttribute("select",   V1Opt | V2Opt),
            new XsltAttribute("as",       V2Opt),
            new XsltAttribute("required", V2Opt),
            new XsltAttribute("tunnel",   V2Opt)
        };
        private readonly 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);
            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 (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(/*[???]*/SR.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), /*[???]*/SR.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
        private readonly 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]*/SR.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
        private readonly 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
        private readonly 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]*/SR.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
        private readonly 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]*/SR.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
        private readonly 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]*/SR.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");
 
            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
        private readonly 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");
            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, SR.Xslt_SortStable);
            }
 
            List<XslNode>? content;
            if (V1)
            {
                CheckNoContent();
            }
            else
            {
                content = LoadContent(select != null);
                if (content.Count != 0)
                {
                    ReportNYI("xsl:sort/*");
                }
            }
 
            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" ) ?? "";
 
            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, /*[???]*/SR.Xslt_AnalyzeStringChildOrder);
                            CheckError(matching    != null, /*[???]*/SR.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, /*[???]*/SR.Xslt_AnalyzeStringDupChild, atoms.NonMatchingSubstring);
                            input.CanHaveApplyImports = false;
                            nonMatching = SetInfo(F.List(), LoadInstructions(), ctxInfoChld);
                        } else if (input.IsXsltKeyword(atoms.Fallback)) {
                            XslFallback();
                        } else {
                            ReportError(/*[XT_017]*/SR.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]*/SR.Xslt_TextNodesNotAllowed, parentName);
                        break;
                    }
                } while (input.MoveToNextSibling());
            }
            CheckError(matching == nonMatching, /*[XTSE1130]*/SR.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, /*[???]*/SR.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(SR.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]*/SR.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]*/SR.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]*/SR.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");
 
            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]*/SR.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) {
                // BUGBUG: Check that version is a valid nmtoken
                // ignore version since we support only one version for both xml (1.0) and html (4.0)
                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]*/SR.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]*/SR.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, SR.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(SR.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(SR.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(SR.Xslt_ModeListAll);
                            break;
                        }
                        else
                        {
                            mode = CreateXPathQName(qname);
                        }
                        bool dup = false;
                        foreach (QilName m in modes)
                        {
                            dup |= m.Equals(mode);
                        }
                        if (dup)
                        {
                            ReportError(SR.Xslt_ModeListDup, qname);
                        }
                        else
                        {
                            modes.Add(mode);
                        }
                    }
                }
 
                if (!_compiler.ExitForwardsCompatible(_input.ForwardCompatibility))
                {
                    modes.Clear();
                    modes.Add(nullMode);
                }
                if (1 < modes.Count)
                {
                    ReportNYI("Multiple 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 localName, namespaceName;
            ResolveQName(ignoreDefaultNs, qname, out localName, out namespaceName, out _);
            return new XmlQualifiedName(localName, namespaceName);
        }
 
        // Does not suppress errors
        private void ParseWhitespaceRules(string elements, bool preserveSpace)
        {
            if (!string.IsNullOrEmpty(elements))
            {
                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 (string.IsNullOrEmpty(prefix))
                    {
                        namespaceName = prefix;
                    }
                    else
                    {
                        namespaceName = _input.LookupXmlNamespace(prefix) ?? _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]*/SR.Xslt_InvalidAttrValue, nameof(method), attValue);
                        return null;
                }
            }
            else
            {
                if (!_input.ForwardCompatibility)
                {
                    ReportWarning(/*[XT1570]*/SR.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]*/SR.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]*/SR.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, /*[???]*/SR.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(/*[???]*/SR.Xslt_SchemaAttributeValue, attributeName, value);
                }
                else if (!_input.ForwardCompatibility)
                {
                    ReportError(/*[???]*/SR.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]*/SR.Xslt_InputTypeAnnotations);
                        }
                        break;
                    default:
                        if (!_input.ForwardCompatibility)
                        {
                            ReportError(/*[???]*/SR.Xslt_InvalidAttrValue, "input-type-annotations", value);
                        }
                        break;
                }
            }
        }
 
        private void CheckNoContent()
        {
            _input.MoveToElement();
            QName parentName = _input.ElementName;
            ISourceLineInfo? errorLineInfo = SkipEmptyContent();
 
            if (errorLineInfo != null)
            {
                _compiler.ReportError(errorLineInfo, /*[XT0260]*/SR.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 whitespace
            if (_input.MoveToFirstChild())
            {
                do
                {
                    // NOTE: XmlNodeType.SignificantWhitespace is not allowed here
                    if (_input.NodeType != XmlNodeType.Whitespace)
                    {
                        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 overridden 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 locally 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(SR.Xslt_NotYetImplemented, arg);
            }
        }
 
        public void CheckError(bool cond, string res, params string?[]? args)
        {
            if (cond)
            {
                _compiler.ReportError(_input.BuildNameLineInfo(), res, args);
            }
        }
    }
}