File: System\Xml\Xsl\XsltOld\SortAction.cs
Project: ndp\fx\src\XmlUtils\System.Data.SqlXml.csproj (System.Data.SqlXml)
//------------------------------------------------------------------------------
// <copyright file="SortAction.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
 
namespace System.Xml.Xsl.XsltOld {
    using Res = System.Xml.Utils.Res;
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Xml;
    using System.Xml.XPath;
 
    internal 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.
            if (! XmlComplianceUtil.IsValidLanguageID(value.ToCharArray(), 0, value.Length)
                && (value.Length == 0 || CultureInfo.GetCultureInfo(value) == null)
            ) {
                if (this.forwardCompatibility) {
                    return null;
                }
                throw XsltException.Create(Res.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, localname;
            PrefixQName.ParseQualifiedName(value, out prefix, out localname);
			manager.ResolveXmlNamespace(prefix);
            if (prefix.Length == 0 && ! this.forwardCompatibility) {
                throw XsltException.Create(Res.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 (this.forwardCompatibility) {
                return XmlSortOrder.Ascending;
            }
            throw XsltException.Create(Res.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 (this.forwardCompatibility) {
                return XmlCaseOrder.None; 
            }
            throw XsltException.Create(Res.Xslt_InvalidAttrValue, "case-order", value);        
        }
        
        internal override void Compile(Compiler compiler) {
            CompileAttributes(compiler);
            CheckEmpty(compiler);
            if (selectKey == Compiler.InvalidQueryKey) {
                selectKey = compiler.AddQuery(".");
            }
 
            this.forwardCompatibility = compiler.ForwardCompatibility;
            this.manager = compiler.CloneScopeManager();
 
            this.lang      = ParseLang(     PrecalculateAvt(ref this.langAvt     ));
            this.dataType  = ParseDataType( PrecalculateAvt(ref this.dataTypeAvt ), manager);
            this.order     = ParseOrder(    PrecalculateAvt(ref this.orderAvt    ));
            this.caseOrder = ParseCaseOrder(PrecalculateAvt(ref this.caseOrderAvt));
 
            if(this.langAvt == null && this.dataTypeAvt == null && this.orderAvt == null && this.caseOrderAvt == null) {
                this.sort = new Sort(this.selectKey, this.lang, this.dataType, this.order, this.caseOrder);
            }
        }
 
        internal override bool CompileAttribute(Compiler compiler) {
            string name   = compiler.Input.LocalName;
            string value  = compiler.Input.Value;
 
            if (Ref.Equal(name, compiler.Atoms.Select)) {
                this.selectKey = compiler.AddQuery(value);
            }
            else if (Ref.Equal(name, compiler.Atoms.Lang)) {
                this.langAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Ref.Equal(name, compiler.Atoms.DataType)) {
                this.dataTypeAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Ref.Equal(name, compiler.Atoms.Order)) {
                this.orderAvt = Avt.CompileAvt(compiler, value);
            }    
            else if (Ref.Equal(name, compiler.Atoms.CaseOrder)) {
                this.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(this.sort != null ? 
                this.sort : 
                new Sort(
                    this.selectKey,
                    this.langAvt      == null ? this.lang      : ParseLang(     this.langAvt     .Evaluate(processor, frame)),
                    this.dataTypeAvt  == null ? this.dataType  : ParseDataType( this.dataTypeAvt .Evaluate(processor, frame), manager),
                    this.orderAvt     == null ? this.order     : ParseOrder(    this.orderAvt    .Evaluate(processor, frame)),
                    this.caseOrderAvt == null ? this.caseOrder : ParseCaseOrder(this.caseOrderAvt.Evaluate(processor, frame))
                )
            );
            frame.Finished();
        }
    }
}