File: System\Xml\Xsl\XsltOld\SortAction.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.
 
using System;
using System.Diagnostics;
using System.Globalization;
using System.Xml;
using System.Xml.XPath;
 
namespace System.Xml.Xsl.XsltOld
{
    internal sealed class SortAction : CompiledAction
    {
        private int _selectKey = Compiler.InvalidQueryKey;
        private Avt? _langAvt;
        private Avt? _dataTypeAvt;
        private Avt? _orderAvt;
        private Avt? _caseOrderAvt;
        // Compile time precalculated AVTs
        private string? _lang;
        private XmlDataType _dataType = XmlDataType.Text;
        private XmlSortOrder _order = XmlSortOrder.Ascending;
        private XmlCaseOrder _caseOrder = XmlCaseOrder.None;
        private Sort? _sort; //When we not have AVTs at all we can do this. null otherwise.
        private bool _forwardCompatibility;
        private InputScopeManager? _manager;
 
        private string? ParseLang(string? value)
        {
            if (value == null)
            { // Avt is not constant, or attribute wasn't defined
                return null;
            }
            // XmlComplianceUtil.IsValidLanguageID uses the outdated RFC 1766. It would be
            // better to remove this method completely and not call it here, but that may
            // change exception types for some stylesheets.
            CultureInfo cultInfo = new CultureInfo(value);
            if (!XmlComplianceUtil.IsValidLanguageID(value)
                && (value.Length == 0 || cultInfo == null)
            )
            {
                if (_forwardCompatibility)
                {
                    return null;
                }
                throw XsltException.Create(SR.Xslt_InvalidAttrValue, "lang", value);
            }
            return value;
        }
 
        private XmlDataType ParseDataType(string? value, InputScopeManager manager)
        {
            if (value == null)
            { // Avt is not constant, or attribute wasn't defined
                return XmlDataType.Text;
            }
            if (value == "text")
            {
                return XmlDataType.Text;
            }
            if (value == "number")
            {
                return XmlDataType.Number;
            }
            string prefix;
            PrefixQName.ParseQualifiedName(value, out prefix, out _);
            manager.ResolveXmlNamespace(prefix);
            if (prefix.Length == 0 && !_forwardCompatibility)
            {
                throw XsltException.Create(SR.Xslt_InvalidAttrValue, "data-type", value);
            }
            return XmlDataType.Text;
        }
 
        private XmlSortOrder ParseOrder(string? value)
        {
            if (value == null)
            { // Avt is not constant, or attribute wasn't defined
                return XmlSortOrder.Ascending;
            }
            if (value == "ascending")
            {
                return XmlSortOrder.Ascending;
            }
            if (value == "descending")
            {
                return XmlSortOrder.Descending;
            }
            if (_forwardCompatibility)
            {
                return XmlSortOrder.Ascending;
            }
            throw XsltException.Create(SR.Xslt_InvalidAttrValue, "order", value);
        }
 
        private XmlCaseOrder ParseCaseOrder(string? value)
        {
            if (value == null)
            { // Avt is not constant, or attribute wasn't defined
                return XmlCaseOrder.None;
            }
            if (value == "upper-first")
            {
                return XmlCaseOrder.UpperFirst;
            }
            if (value == "lower-first")
            {
                return XmlCaseOrder.LowerFirst;
            }
            if (_forwardCompatibility)
            {
                return XmlCaseOrder.None;
            }
            throw XsltException.Create(SR.Xslt_InvalidAttrValue, "case-order", value);
        }
 
        internal override void Compile(Compiler compiler)
        {
            CompileAttributes(compiler);
            CheckEmpty(compiler);
            if (_selectKey == Compiler.InvalidQueryKey)
            {
                _selectKey = compiler.AddQuery(".");
            }
 
            _forwardCompatibility = compiler.ForwardCompatibility;
            _manager = compiler.CloneScopeManager();
 
            _lang = ParseLang(PrecalculateAvt(ref _langAvt));
            _dataType = ParseDataType(PrecalculateAvt(ref _dataTypeAvt), _manager);
            _order = ParseOrder(PrecalculateAvt(ref _orderAvt));
            _caseOrder = ParseCaseOrder(PrecalculateAvt(ref _caseOrderAvt));
 
            if (_langAvt == null && _dataTypeAvt == null && _orderAvt == null && _caseOrderAvt == null)
            {
                _sort = new Sort(_selectKey, _lang, _dataType, _order, _caseOrder);
            }
        }
 
        internal override bool CompileAttribute(Compiler compiler)
        {
            string name = compiler.Input.LocalName;
            string value = compiler.Input.Value;
 
            if (Ref.Equal(name, compiler.Atoms.Select))
            {
                _selectKey = compiler.AddQuery(value);
            }
            else if (Ref.Equal(name, compiler.Atoms.Lang))
            {
                _langAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Ref.Equal(name, compiler.Atoms.DataType))
            {
                _dataTypeAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Ref.Equal(name, compiler.Atoms.Order))
            {
                _orderAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Ref.Equal(name, compiler.Atoms.CaseOrder))
            {
                _caseOrderAvt = Avt.CompileAvt(compiler, value);
            }
            else
            {
                return false;
            }
            return true;
        }
 
        internal override void Execute(Processor processor, ActionFrame frame)
        {
            Debug.Assert(processor != null && frame != null);
            Debug.Assert(frame.State == Initialized);
 
            processor.AddSort(_sort ??
                new Sort(
                    _selectKey,
                    _langAvt == null ? _lang : ParseLang(_langAvt.Evaluate(processor, frame)),
                    _dataTypeAvt == null ? _dataType : ParseDataType(_dataTypeAvt.Evaluate(processor, frame), _manager!),
                    _orderAvt == null ? _order : ParseOrder(_orderAvt.Evaluate(processor, frame)),
                    _caseOrderAvt == null ? _caseOrder : ParseCaseOrder(_caseOrderAvt.Evaluate(processor, frame))
                )
            );
            frame.Finished();
        }
    }
}