NumberAction.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / XmlUtils / System / Xml / Xsl / XsltOld / NumberAction.cs / 1 / NumberAction.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace System.Xml.Xsl.XsltOld { 
    using Res = System.Xml.Utils.Res;
    using System.Diagnostics; 
    using System.Text;
    using System.Globalization;
    using System.Collections;
    using System.Collections.Generic; 
    using System.Xml.XPath;
    using System.Xml.Xsl.Runtime; 
 
    internal class NumberAction : ContainerAction {
        const long msofnfcNil  =            0x00000000;     // no flags 
        const long msofnfcTraditional =     0x00000001;     // use traditional numbering
        const long msofnfcAlwaysFormat =    0x00000002;     // if requested format is not supported, use Arabic (Western) style

        const int cchMaxFormat        = 63 ;     // max size of formatted result 
        const int cchMaxFormatDecimal = 11  ;    // max size of formatted decimal result (doesn't handle the case of a very large pwszSeparator or minlen)
 
        internal class FormatInfo { 
            public bool               isSeparator;      // False for alphanumeric strings of chars
            public NumberingSequence  numSequence;      // Specifies numbering sequence 
            public int                length;           // Minimum length of decimal numbers (if necessary, pad to left with zeros)
            public string             formatString;     // Format string for separator token

            public FormatInfo(bool isSeparator, string formatString) { 
                this.isSeparator  = isSeparator;
                this.formatString = formatString; 
            } 

            public FormatInfo() {} 
        }

        static FormatInfo DefaultFormat    = new FormatInfo(false, "0");
        static FormatInfo DefaultSeparator = new FormatInfo(true , "."); 

        class NumberingFormat : NumberFormatterBase { 
            NumberingSequence seq; 
            int     cMinLen;
            string  separator; 
            int     sizeGroup;

            internal NumberingFormat() {}
 
            internal void setNumberingType(NumberingSequence seq) { this.seq = seq; }
            //void setLangID(LID langid) {_langid = langid;} 
            //internal void setTraditional(bool fTraditional) {_grfnfc = fTraditional ? msofnfcTraditional : 0;} 
            internal void setMinLen(int cMinLen) { this.cMinLen = cMinLen; }
            internal void setGroupingSeparator(string separator) { this.separator = separator; } 

            internal void setGroupingSize(int sizeGroup) {
                if (0 <= sizeGroup && sizeGroup <= 9) {
                    this.sizeGroup = sizeGroup; 
                }
            } 
 
            internal String FormatItem(object value) {
                double dblVal; 

                if (value is int) {
                    dblVal = (int)value;
                } else { 
                    dblVal = XmlConvert.ToXPathDouble(value);
 
                    if (0.5 <= dblVal && !double.IsPositiveInfinity(dblVal)) { 
                        dblVal = XmlConvert.XPathRound(dblVal);
                    } else { 
                        // It is an error if the number is NaN, infinite or less than 0.5; an XSLT processor may signal the error;
                        // if it does not signal the error, it must recover by converting the number to a string as if by a call
                        // to the string function and inserting the resulting string into the result tree.
                        return XmlConvert.ToXPathString(value); 
                    }
                } 
 
                Debug.Assert(dblVal >= 1);
 
                switch (seq) {
                case NumberingSequence.Arabic :
                    break;
                case NumberingSequence.UCLetter : 
                case NumberingSequence.LCLetter :
                    if (dblVal <= MaxAlphabeticValue) { 
                        StringBuilder sb = new StringBuilder(); 
                        ConvertToAlphabetic(sb, dblVal, seq == NumberingSequence.UCLetter ? 'A' : 'a', 26);
                        return sb.ToString(); 
                    }
                    break;
                case NumberingSequence.UCRoman :
                case NumberingSequence.LCRoman: 
                    if (dblVal <= MaxRomanValue) {
                        StringBuilder sb = new StringBuilder(); 
                        ConvertToRoman(sb, dblVal, seq == NumberingSequence.UCRoman); 
                        return sb.ToString();
                    } 
                    break;
                }

                return ConvertToArabic(dblVal, cMinLen, sizeGroup, separator); 
            }
 
            static string ConvertToArabic(double val, int minLength, int groupSize, string groupSeparator) { 
                String str;
 
                if (groupSize != 0 && groupSeparator != null ) {
                    NumberFormatInfo NumberFormat = new NumberFormatInfo();
                    NumberFormat.NumberGroupSizes = new int[] { groupSize };
                    NumberFormat.NumberGroupSeparator = groupSeparator; 
                    if (Math.Floor(val) == val) {
                        NumberFormat.NumberDecimalDigits = 0; 
                    } 
                    str = val.ToString("N", NumberFormat);
                } 
                else {
                    str = Convert.ToString(val, CultureInfo.InvariantCulture);
                }
 
                if (str.Length >= minLength) {
                    return str; 
                } else { 
                    StringBuilder sb = new StringBuilder(minLength);
                    sb.Append('0', minLength - str.Length); 
                    sb.Append(str);
                    return sb.ToString();
                }
            } 
        }
 
    // States: 
        private const int OutputNumber = 2;
 
        private String    level;
        private String    countPattern;
        private int       countKey = Compiler.InvalidQueryKey;
        private String    from; 
        private int       fromKey = Compiler.InvalidQueryKey;
        private String    value; 
        private int       valueKey = Compiler.InvalidQueryKey; 
        private Avt       formatAvt;
        private Avt       langAvt; 
        private Avt       letterAvt;
        private Avt       groupingSepAvt;
        private Avt       groupingSizeAvt;
        // Compile time precalculated AVTs 
        private List formatTokens;
        private String    lang; 
        private String    letter; 
        private String    groupingSep;
        private String    groupingSize; 
        private bool      forwardCompatibility;

        internal override bool CompileAttribute(Compiler compiler) {
            string name   = compiler.Input.LocalName; 
            string value  = compiler.Input.Value;
            if (Keywords.Equals(name, compiler.Atoms.Level)) { 
                if (value != "any" && value != "multiple" && value != "single") { 
                    throw XsltException.Create(Res.Xslt_InvalidAttrValue, Keywords.s_Level, value);
                } 
                this.level = value;
            }
            else if (Keywords.Equals(name, compiler.Atoms.Count)) {
                this.countPattern = value; 
                this.countKey = compiler.AddQuery(value, /*allowVars:*/true, /*allowKey:*/true, /*pattern*/true);
            } 
            else if (Keywords.Equals(name, compiler.Atoms.From)) { 
                this.from = value;
                this.fromKey = compiler.AddQuery(value, /*allowVars:*/true, /*allowKey:*/true, /*pattern*/true); 
            }
            else if (Keywords.Equals(name, compiler.Atoms.Value)) {
                this.value = value;
                this.valueKey = compiler.AddQuery(value); 
            }
            else if (Keywords.Equals(name, compiler.Atoms.Format)) { 
                this.formatAvt = Avt.CompileAvt(compiler, value); 
            }
            else if (Keywords.Equals(name, compiler.Atoms.Lang)) { 
                this.langAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Keywords.Equals(name, compiler.Atoms.LetterValue)) {
                this.letterAvt = Avt.CompileAvt(compiler, value); 
            }
            else if (Keywords.Equals(name, compiler.Atoms.GroupingSeparator)) { 
                this.groupingSepAvt = Avt.CompileAvt(compiler, value); 
            }
            else if (Keywords.Equals(name, compiler.Atoms.GroupingSize)) { 
                this.groupingSizeAvt = Avt.CompileAvt(compiler, value);
            }
            else {
               return false; 
            }
            return true; 
        } 

        internal override void Compile(Compiler compiler) { 
            CompileAttributes(compiler);
            CheckEmpty(compiler);

            this.forwardCompatibility = compiler.ForwardCompatibility; 
            this.formatTokens  = ParseFormat(PrecalculateAvt(ref this.formatAvt));
            this.letter        = ParseLetter(PrecalculateAvt(ref this.letterAvt)); 
            this.lang          = PrecalculateAvt(ref this.langAvt); 
            this.groupingSep   = PrecalculateAvt(ref this.groupingSepAvt);
            if (this.groupingSep != null && this.groupingSep.Length > 1) { 
                throw XsltException.Create(Res.Xslt_CharAttribute, Keywords.s_GroupingSeparator);
            }
            this.groupingSize  = PrecalculateAvt(ref this.groupingSizeAvt);
        } 

        private int numberAny(Processor processor, ActionFrame frame) { 
            int result = 0; 
            // Our current point will be our end point in this search
            XPathNavigator endNode = frame.Node; 
            if(endNode.NodeType == XPathNodeType.Attribute || endNode.NodeType == XPathNodeType.Namespace) {
                endNode = endNode.Clone();
                endNode.MoveToParent();
            } 
            XPathNavigator startNode = endNode.Clone();
 
            if(this.fromKey != Compiler.InvalidQueryKey) { 
                bool hitFrom = false;
                // First try to find start by traversing up. This gives the best candidate or we hit root 
                do{
                    if(processor.Matches(startNode, this.fromKey)) {
                        hitFrom = true;
                        break; 
                    }
                }while(startNode.MoveToParent()); 
 
                Debug.Assert(
                    processor.Matches(startNode, this.fromKey) ||   // we hit 'from' or 
                    startNode.NodeType == XPathNodeType.Root        // we are at root
                );

                // from this point (matched parent | root) create descendent quiery: 
                // we have to reset 'result' on each 'from' node, because this point can' be not last from point;
                XPathNodeIterator  sel = startNode.SelectDescendants(XPathNodeType.All, /*matchSelf:*/ true); 
                while (sel.MoveNext()) { 
                    if(processor.Matches(sel.Current, this.fromKey)) {
                        hitFrom = true; 
                        result = 0;
                    }
                    else if(MatchCountKey(processor, frame.Node, sel.Current)) {
                        result ++; 
                    }
                    if(sel.Current.IsSamePosition(endNode)) { 
                        break; 
                    }
                } 
                if(! hitFrom) {
                    result = 0;
                }
            } 
            else {
                // without 'from' we startting from the root 
                startNode.MoveToRoot(); 
                XPathNodeIterator  sel = startNode.SelectDescendants(XPathNodeType.All, /*matchSelf:*/ true);
                // and count root node by itself 
                while (sel.MoveNext()) {
                    if (MatchCountKey(processor, frame.Node, sel.Current)) {
                        result ++;
                    } 
                    if (sel.Current.IsSamePosition(endNode)) {
                        break; 
                    } 
                }
            } 
            return result;
        }

        // check 'from' condition: 
        // if 'from' exist it has to be ancestor-or-self for the nav
        private bool checkFrom(Processor processor, XPathNavigator nav) { 
            if(this.fromKey == Compiler.InvalidQueryKey) { 
                return true;
            } 
            do {
                if (processor.Matches(nav, this.fromKey)) {
                    return true;
                } 
            }while (nav.MoveToParent());
            return false; 
        } 

        private bool moveToCount(XPathNavigator nav, Processor processor, XPathNavigator contextNode) { 
            do {
                if (this.fromKey != Compiler.InvalidQueryKey && processor.Matches(nav, this.fromKey)) {
                    return false;
                } 
                if (MatchCountKey(processor, contextNode, nav)) {
                    return true; 
                } 
            }while (nav.MoveToParent());
            return false; 
        }

        private int numberCount(XPathNavigator nav, Processor processor, XPathNavigator contextNode) {
            Debug.Assert(nav.NodeType != XPathNodeType.Attribute && nav.NodeType != XPathNodeType.Namespace); 
            Debug.Assert(MatchCountKey(processor, contextNode, nav));
            XPathNavigator runner = nav.Clone(); 
            int number = 1; 
            if (runner.MoveToParent()) {
                runner.MoveToFirstChild(); 
                while (! runner.IsSamePosition(nav)) {
                    if (MatchCountKey(processor, contextNode, runner)) {
                        number++;
                    } 
                    if (! runner.MoveToNext()) {
                        Debug.Fail("We implementing preceding-sibling::node() and some how miss context node 'nav'"); 
                        break; 
                    }
                } 
            }
            return number;
        }
 
        private static object SimplifyValue(object value) {
            // If result of xsl:number is not in correct range it should be returned as is. 
            // so we need intermidiate string value. 
            // If it's already a double we would like to keep it as double.
            // So this function converts to string only if if result is nodeset or RTF 
            Debug.Assert(!(value is int));
            if (Type.GetTypeCode(value.GetType()) == TypeCode.Object) {
                XPathNodeIterator nodeset = value as XPathNodeIterator;
                if (nodeset != null) { 
                    if (nodeset.MoveNext()) {
                        return nodeset.Current.Value; 
                    } 
                    return string.Empty;
                } 
                XPathNavigator nav = value as XPathNavigator;
                if (nav != null) {
                    return nav.Value;
                } 
            }
            return value; 
        } 

        internal override void Execute(Processor processor, ActionFrame frame) { 
            Debug.Assert(processor != null && frame != null);
            ArrayList list = processor.NumberList;
            switch (frame.State) {
            case Initialized: 
                Debug.Assert(frame != null);
                Debug.Assert(frame.NodeSet != null); 
                list.Clear(); 
                if (this.valueKey != Compiler.InvalidQueryKey) {
                    list.Add(SimplifyValue(processor.Evaluate(frame, this.valueKey))); 
                }
                else if (this.level == "any") {
                    int number = numberAny(processor, frame);
                    if (number != 0) { 
                        list.Add(number);
                    } 
                } 
                else {
                    bool multiple = (this.level == "multiple"); 
                    XPathNavigator contextNode = frame.Node;         // context of xsl:number element. We using this node in MatchCountKey()
                    XPathNavigator countNode   = frame.Node.Clone(); // node we count for
                    if (countNode.NodeType == XPathNodeType.Attribute || countNode.NodeType == XPathNodeType.Namespace) {
                        countNode.MoveToParent(); 
                    }
                    while (moveToCount(countNode, processor, contextNode)) { 
                        list.Insert(0, numberCount(countNode, processor, contextNode)); 
                        if(! multiple || ! countNode.MoveToParent()) {
                            break; 
                        }
                    }
                    if(! checkFrom(processor, countNode)) {
                        list.Clear(); 
                    }
                } 
 
                /*CalculatingFormat:*/
                frame.StoredOutput = Format(list, 
                    this.formatAvt       == null ? this.formatTokens : ParseFormat(this.formatAvt.Evaluate(processor, frame)),
                    this.langAvt         == null ? this.lang         : this.langAvt        .Evaluate(processor, frame),
                    this.letterAvt       == null ? this.letter       : ParseLetter(this.letterAvt.Evaluate(processor, frame)),
                    this.groupingSepAvt  == null ? this.groupingSep  : this.groupingSepAvt .Evaluate(processor, frame), 
                    this.groupingSizeAvt == null ? this.groupingSize : this.groupingSizeAvt.Evaluate(processor, frame)
                ); 
                goto case OutputNumber; 
            case OutputNumber :
                Debug.Assert(frame.StoredOutput != null); 
                if (! processor.TextEvent(frame.StoredOutput)) {
                    frame.State        = OutputNumber;
                    break;
                } 
                frame.Finished();
                break; 
            default: 
                Debug.Fail("Invalid Number Action execution state");
                break; 
            }
        }

        private bool MatchCountKey(Processor processor, XPathNavigator contextNode, XPathNavigator nav){ 
            if (this.countKey != Compiler.InvalidQueryKey) {
                return processor.Matches(nav, this.countKey); 
            } 
            if (contextNode.Name == nav.Name && BasicNodeType(contextNode.NodeType) == BasicNodeType(nav.NodeType)) {
                return true; 
            }
            return false;
        }
 
        private XPathNodeType BasicNodeType(XPathNodeType type) {
            if(type == XPathNodeType.SignificantWhitespace || type == XPathNodeType.Whitespace) { 
                return XPathNodeType.Text; 
            }
            else { 
                return type;
            }
        }
 
        // [....]: perf.
        // for each call to xsl:number Format() will build new NumberingFormat object. 
        // in case of no AVTs we can build this object at compile time and reuse it on execution time. 
        // even partial step in this d---- will be usefull (when cFormats == 0)
 
        private static string Format(ArrayList numberlist, List tokens, string lang, string letter, string groupingSep, string groupingSize) {
            StringBuilder result = new StringBuilder();
            int cFormats = 0;
            if (tokens != null) { 
                cFormats = tokens.Count;
            } 
 
            NumberingFormat numberingFormat = new NumberingFormat();
            if (groupingSize != null) { 
                try {
                    numberingFormat.setGroupingSize(Convert.ToInt32(groupingSize, CultureInfo.InvariantCulture));
                }
                catch (System.FormatException) {} 
                catch (System.OverflowException) {}
            } 
            if (groupingSep != null) { 
                if (groupingSep.Length > 1) {
                    // It is a breaking change to throw an exception, SQLBUDT 324367 
                    //throw XsltException.Create(Res.Xslt_CharAttribute, Keywords.s_GroupingSeparator);
                }
                numberingFormat.setGroupingSeparator(groupingSep);
            } 
            if (0 < cFormats) {
                FormatInfo prefix = tokens[0]; 
                Debug.Assert(prefix == null || prefix.isSeparator); 
                FormatInfo sufix = null;
                if (cFormats % 2 == 1) { 
                    sufix = tokens[cFormats - 1];
                    cFormats --;
                }
                FormatInfo periodicSeparator = 2 < cFormats ? tokens[cFormats - 2] : DefaultSeparator; 
                FormatInfo periodicFormat    = 0 < cFormats ? tokens[cFormats - 1] : DefaultFormat   ;
                if (prefix != null) { 
                    result.Append(prefix.formatString); 
                }
                int numberlistCount = numberlist.Count; 
                for(int i = 0; i < numberlistCount; i++ ) {
                    int formatIndex   = i * 2;
                    bool haveFormat = formatIndex < cFormats;
                    if (0 < i) { 
                        FormatInfo thisSeparator = haveFormat ? tokens[formatIndex + 0] : periodicSeparator;
                        Debug.Assert(thisSeparator.isSeparator); 
                        result.Append(thisSeparator.formatString); 
                    }
 
                    FormatInfo thisFormat = haveFormat ? tokens[formatIndex + 1] : periodicFormat;
                    Debug.Assert(!thisFormat.isSeparator);

                    //numberingFormat.setletter(this.letter); 
                    //numberingFormat.setLang(this.lang);
 
                    numberingFormat.setNumberingType(thisFormat.numSequence); 
                    numberingFormat.setMinLen(thisFormat.length);
                    result.Append(numberingFormat.FormatItem(numberlist[i])); 
                }

                if (sufix != null) {
                    result.Append(sufix.formatString); 
                }
            } 
            else { 
                numberingFormat.setNumberingType(NumberingSequence.Arabic);
                for (int i = 0; i < numberlist.Count; i++) { 
                    if (i != 0) {
                        result.Append(".");
                    }
                    result.Append(numberingFormat.FormatItem(numberlist[i])); 
                }
            } 
            return result.ToString(); 
        }
 
        /*
        ---------------------------------------------------------------------------
            mapFormatToken()
 
            Maps a token of alphanumeric characters to a numbering format ID and a
            minimum length bound.  Tokens specify the character(s) that begins a 
            Unicode 
            numbering sequence.  For example, "i" specifies lower case roman numeral
            numbering.  Leading "zeros" specify a minimum length to be maintained by 
            padding, if necessary.
        ---------------------------------------------------------------------------
        */
        private static void mapFormatToken(String wsToken, int startLen, int tokLen, out NumberingSequence seq, out int pminlen) { 
            char wch = wsToken[startLen];
            bool UseArabic = false; 
            pminlen = 1; 
            seq = NumberingSequence.Nil;
 
            switch ((int)wch) {
            case 0x0030:    // Digit zero
            case 0x0966:    // Hindi digit zero
            case 0x0e50:    // Thai digit zero 
            case 0xc77b:    // Korean digit zero
            case 0xff10:    // Digit zero (double-byte) 
                do { 
                    // Leading zeros request padding.  Track how much.
                    pminlen++; 
                } while ((--tokLen > 0) && (wch == wsToken[++startLen]));

                if (wsToken[startLen] != (char)(wch + 1)) {
                    // If next character isn't "one", then use Arabic 
                    UseArabic = true;
                } 
                break; 
            }
 
            if (!UseArabic) {
                // Map characters of token to number format ID
                switch ((int)wsToken[startLen]) {
                case 0x0031: seq = NumberingSequence.Arabic; break; 
                case 0x0041: seq = NumberingSequence.UCLetter; break;
                case 0x0049: seq = NumberingSequence.UCRoman; break; 
                case 0x0061: seq = NumberingSequence.LCLetter; break; 
                case 0x0069: seq = NumberingSequence.LCRoman; break;
                case 0x0410: seq = NumberingSequence.UCRus; break; 
                case 0x0430: seq = NumberingSequence.LCRus; break;
                case 0x05d0: seq = NumberingSequence.Hebrew; break;
                case 0x0623: seq = NumberingSequence.ArabicScript; break;
                case 0x0905: seq = NumberingSequence.Hindi2; break; 
                case 0x0915: seq = NumberingSequence.Hindi1; break;
                case 0x0967: seq = NumberingSequence.Hindi3; break; 
                case 0x0e01: seq = NumberingSequence.Thai1; break; 
                case 0x0e51: seq = NumberingSequence.Thai2; break;
                case 0x30a2: seq = NumberingSequence.DAiueo; break; 
                case 0x30a4: seq = NumberingSequence.DIroha; break;
                case 0x3131: seq = NumberingSequence.DChosung; break;
                case 0x4e00: seq = NumberingSequence.FEDecimal; break;
                case 0x58f1: seq = NumberingSequence.DbNum3; break; 
                case 0x58f9: seq = NumberingSequence.ChnCmplx; break;
                case 0x5b50: seq = NumberingSequence.Zodiac2; break; 
                case 0xac00: seq = NumberingSequence.Ganada; break; 
                case 0xc77c: seq = NumberingSequence.KorDbNum1; break;
                case 0xd558: seq = NumberingSequence.KorDbNum3; break; 
                case 0xff11: seq = NumberingSequence.DArabic; break;
                case 0xff71: seq = NumberingSequence.Aiueo; break;
                case 0xff72: seq = NumberingSequence.Iroha; break;
 
                case 0x7532:
                    if (tokLen > 1 && wsToken[startLen + 1] == 0x5b50) { 
                        // 60-based Zodiak numbering begins with two characters 
                        seq = NumberingSequence.Zodiac3;
                        tokLen--; 
                        startLen++;
                    }
                    else {
                        // 10-based Zodiak numbering begins with one character 
                        seq = NumberingSequence.Zodiac1;
                    } 
                    break; 
                default:
                    seq = NumberingSequence.Arabic; 
                    break;
                }
            }
 
            //if (tokLen != 1 || UseArabic) {
            if (UseArabic) { 
                // If remaining token length is not 1, then don't recognize 
                // sequence and default to Arabic with no zero padding.
                seq = NumberingSequence.Arabic; 
                pminlen = 0;
            }
        }
 

        /* 
        ---------------------------------------------------------------------------- 
            parseFormat()
 
            Parse format string into format tokens (alphanumeric) and separators
            (non-alphanumeric).

        */ 
        private static List ParseFormat(string formatString) {
            if (formatString == null || formatString.Length == 0) { 
                return null; 
            }
            int length = 0; 
            bool lastAlphaNumeric = CharUtil.IsAlphaNumeric(formatString[length]);
            List tokens = new List();
            int count = 0;
 
            if (lastAlphaNumeric) {
                // If the first one is alpha num add empty separator as a prefix. 
                tokens.Add(null); 
            }
 
            while (length <= formatString.Length) {
                // Loop until a switch from format token to separator is detected (or vice-versa)
                bool currentchar = length < formatString.Length ? CharUtil.IsAlphaNumeric(formatString[length]) : !lastAlphaNumeric;
                if (lastAlphaNumeric != currentchar) { 
                    FormatInfo formatInfo = new FormatInfo();
                    if (lastAlphaNumeric) { 
                        // We just finished a format token.  Map it to a numbering format ID and a min-length bound. 
                        mapFormatToken(formatString, count, length - count, out formatInfo.numSequence, out formatInfo.length);
                    } 
                    else {
                        formatInfo.isSeparator = true;
                        // We just finished a separator.  Save its length and a pointer to it.
                        formatInfo.formatString = formatString.Substring(count, length - count); 
                    }
                    count = length; 
                    length++; 
                    // Begin parsing the next format token or separator
 
                    tokens.Add(formatInfo);
                    // Flip flag from format token to separator (or vice-versa)
                    lastAlphaNumeric = currentchar;
                } 
                else {
                    length++; 
                } 
            }
 
            return tokens;
        }

        private string ParseLetter(string letter) { 
            if (letter == null || letter == "traditional" || letter == "alphabetic") {
                return letter; 
            } 
            if (! this.forwardCompatibility) {
                throw XsltException.Create(Res.Xslt_InvalidAttrValue, Keywords.s_LetterValue, letter); 
            }
            return null;
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace System.Xml.Xsl.XsltOld { 
    using Res = System.Xml.Utils.Res;
    using System.Diagnostics; 
    using System.Text;
    using System.Globalization;
    using System.Collections;
    using System.Collections.Generic; 
    using System.Xml.XPath;
    using System.Xml.Xsl.Runtime; 
 
    internal class NumberAction : ContainerAction {
        const long msofnfcNil  =            0x00000000;     // no flags 
        const long msofnfcTraditional =     0x00000001;     // use traditional numbering
        const long msofnfcAlwaysFormat =    0x00000002;     // if requested format is not supported, use Arabic (Western) style

        const int cchMaxFormat        = 63 ;     // max size of formatted result 
        const int cchMaxFormatDecimal = 11  ;    // max size of formatted decimal result (doesn't handle the case of a very large pwszSeparator or minlen)
 
        internal class FormatInfo { 
            public bool               isSeparator;      // False for alphanumeric strings of chars
            public NumberingSequence  numSequence;      // Specifies numbering sequence 
            public int                length;           // Minimum length of decimal numbers (if necessary, pad to left with zeros)
            public string             formatString;     // Format string for separator token

            public FormatInfo(bool isSeparator, string formatString) { 
                this.isSeparator  = isSeparator;
                this.formatString = formatString; 
            } 

            public FormatInfo() {} 
        }

        static FormatInfo DefaultFormat    = new FormatInfo(false, "0");
        static FormatInfo DefaultSeparator = new FormatInfo(true , "."); 

        class NumberingFormat : NumberFormatterBase { 
            NumberingSequence seq; 
            int     cMinLen;
            string  separator; 
            int     sizeGroup;

            internal NumberingFormat() {}
 
            internal void setNumberingType(NumberingSequence seq) { this.seq = seq; }
            //void setLangID(LID langid) {_langid = langid;} 
            //internal void setTraditional(bool fTraditional) {_grfnfc = fTraditional ? msofnfcTraditional : 0;} 
            internal void setMinLen(int cMinLen) { this.cMinLen = cMinLen; }
            internal void setGroupingSeparator(string separator) { this.separator = separator; } 

            internal void setGroupingSize(int sizeGroup) {
                if (0 <= sizeGroup && sizeGroup <= 9) {
                    this.sizeGroup = sizeGroup; 
                }
            } 
 
            internal String FormatItem(object value) {
                double dblVal; 

                if (value is int) {
                    dblVal = (int)value;
                } else { 
                    dblVal = XmlConvert.ToXPathDouble(value);
 
                    if (0.5 <= dblVal && !double.IsPositiveInfinity(dblVal)) { 
                        dblVal = XmlConvert.XPathRound(dblVal);
                    } else { 
                        // It is an error if the number is NaN, infinite or less than 0.5; an XSLT processor may signal the error;
                        // if it does not signal the error, it must recover by converting the number to a string as if by a call
                        // to the string function and inserting the resulting string into the result tree.
                        return XmlConvert.ToXPathString(value); 
                    }
                } 
 
                Debug.Assert(dblVal >= 1);
 
                switch (seq) {
                case NumberingSequence.Arabic :
                    break;
                case NumberingSequence.UCLetter : 
                case NumberingSequence.LCLetter :
                    if (dblVal <= MaxAlphabeticValue) { 
                        StringBuilder sb = new StringBuilder(); 
                        ConvertToAlphabetic(sb, dblVal, seq == NumberingSequence.UCLetter ? 'A' : 'a', 26);
                        return sb.ToString(); 
                    }
                    break;
                case NumberingSequence.UCRoman :
                case NumberingSequence.LCRoman: 
                    if (dblVal <= MaxRomanValue) {
                        StringBuilder sb = new StringBuilder(); 
                        ConvertToRoman(sb, dblVal, seq == NumberingSequence.UCRoman); 
                        return sb.ToString();
                    } 
                    break;
                }

                return ConvertToArabic(dblVal, cMinLen, sizeGroup, separator); 
            }
 
            static string ConvertToArabic(double val, int minLength, int groupSize, string groupSeparator) { 
                String str;
 
                if (groupSize != 0 && groupSeparator != null ) {
                    NumberFormatInfo NumberFormat = new NumberFormatInfo();
                    NumberFormat.NumberGroupSizes = new int[] { groupSize };
                    NumberFormat.NumberGroupSeparator = groupSeparator; 
                    if (Math.Floor(val) == val) {
                        NumberFormat.NumberDecimalDigits = 0; 
                    } 
                    str = val.ToString("N", NumberFormat);
                } 
                else {
                    str = Convert.ToString(val, CultureInfo.InvariantCulture);
                }
 
                if (str.Length >= minLength) {
                    return str; 
                } else { 
                    StringBuilder sb = new StringBuilder(minLength);
                    sb.Append('0', minLength - str.Length); 
                    sb.Append(str);
                    return sb.ToString();
                }
            } 
        }
 
    // States: 
        private const int OutputNumber = 2;
 
        private String    level;
        private String    countPattern;
        private int       countKey = Compiler.InvalidQueryKey;
        private String    from; 
        private int       fromKey = Compiler.InvalidQueryKey;
        private String    value; 
        private int       valueKey = Compiler.InvalidQueryKey; 
        private Avt       formatAvt;
        private Avt       langAvt; 
        private Avt       letterAvt;
        private Avt       groupingSepAvt;
        private Avt       groupingSizeAvt;
        // Compile time precalculated AVTs 
        private List formatTokens;
        private String    lang; 
        private String    letter; 
        private String    groupingSep;
        private String    groupingSize; 
        private bool      forwardCompatibility;

        internal override bool CompileAttribute(Compiler compiler) {
            string name   = compiler.Input.LocalName; 
            string value  = compiler.Input.Value;
            if (Keywords.Equals(name, compiler.Atoms.Level)) { 
                if (value != "any" && value != "multiple" && value != "single") { 
                    throw XsltException.Create(Res.Xslt_InvalidAttrValue, Keywords.s_Level, value);
                } 
                this.level = value;
            }
            else if (Keywords.Equals(name, compiler.Atoms.Count)) {
                this.countPattern = value; 
                this.countKey = compiler.AddQuery(value, /*allowVars:*/true, /*allowKey:*/true, /*pattern*/true);
            } 
            else if (Keywords.Equals(name, compiler.Atoms.From)) { 
                this.from = value;
                this.fromKey = compiler.AddQuery(value, /*allowVars:*/true, /*allowKey:*/true, /*pattern*/true); 
            }
            else if (Keywords.Equals(name, compiler.Atoms.Value)) {
                this.value = value;
                this.valueKey = compiler.AddQuery(value); 
            }
            else if (Keywords.Equals(name, compiler.Atoms.Format)) { 
                this.formatAvt = Avt.CompileAvt(compiler, value); 
            }
            else if (Keywords.Equals(name, compiler.Atoms.Lang)) { 
                this.langAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Keywords.Equals(name, compiler.Atoms.LetterValue)) {
                this.letterAvt = Avt.CompileAvt(compiler, value); 
            }
            else if (Keywords.Equals(name, compiler.Atoms.GroupingSeparator)) { 
                this.groupingSepAvt = Avt.CompileAvt(compiler, value); 
            }
            else if (Keywords.Equals(name, compiler.Atoms.GroupingSize)) { 
                this.groupingSizeAvt = Avt.CompileAvt(compiler, value);
            }
            else {
               return false; 
            }
            return true; 
        } 

        internal override void Compile(Compiler compiler) { 
            CompileAttributes(compiler);
            CheckEmpty(compiler);

            this.forwardCompatibility = compiler.ForwardCompatibility; 
            this.formatTokens  = ParseFormat(PrecalculateAvt(ref this.formatAvt));
            this.letter        = ParseLetter(PrecalculateAvt(ref this.letterAvt)); 
            this.lang          = PrecalculateAvt(ref this.langAvt); 
            this.groupingSep   = PrecalculateAvt(ref this.groupingSepAvt);
            if (this.groupingSep != null && this.groupingSep.Length > 1) { 
                throw XsltException.Create(Res.Xslt_CharAttribute, Keywords.s_GroupingSeparator);
            }
            this.groupingSize  = PrecalculateAvt(ref this.groupingSizeAvt);
        } 

        private int numberAny(Processor processor, ActionFrame frame) { 
            int result = 0; 
            // Our current point will be our end point in this search
            XPathNavigator endNode = frame.Node; 
            if(endNode.NodeType == XPathNodeType.Attribute || endNode.NodeType == XPathNodeType.Namespace) {
                endNode = endNode.Clone();
                endNode.MoveToParent();
            } 
            XPathNavigator startNode = endNode.Clone();
 
            if(this.fromKey != Compiler.InvalidQueryKey) { 
                bool hitFrom = false;
                // First try to find start by traversing up. This gives the best candidate or we hit root 
                do{
                    if(processor.Matches(startNode, this.fromKey)) {
                        hitFrom = true;
                        break; 
                    }
                }while(startNode.MoveToParent()); 
 
                Debug.Assert(
                    processor.Matches(startNode, this.fromKey) ||   // we hit 'from' or 
                    startNode.NodeType == XPathNodeType.Root        // we are at root
                );

                // from this point (matched parent | root) create descendent quiery: 
                // we have to reset 'result' on each 'from' node, because this point can' be not last from point;
                XPathNodeIterator  sel = startNode.SelectDescendants(XPathNodeType.All, /*matchSelf:*/ true); 
                while (sel.MoveNext()) { 
                    if(processor.Matches(sel.Current, this.fromKey)) {
                        hitFrom = true; 
                        result = 0;
                    }
                    else if(MatchCountKey(processor, frame.Node, sel.Current)) {
                        result ++; 
                    }
                    if(sel.Current.IsSamePosition(endNode)) { 
                        break; 
                    }
                } 
                if(! hitFrom) {
                    result = 0;
                }
            } 
            else {
                // without 'from' we startting from the root 
                startNode.MoveToRoot(); 
                XPathNodeIterator  sel = startNode.SelectDescendants(XPathNodeType.All, /*matchSelf:*/ true);
                // and count root node by itself 
                while (sel.MoveNext()) {
                    if (MatchCountKey(processor, frame.Node, sel.Current)) {
                        result ++;
                    } 
                    if (sel.Current.IsSamePosition(endNode)) {
                        break; 
                    } 
                }
            } 
            return result;
        }

        // check 'from' condition: 
        // if 'from' exist it has to be ancestor-or-self for the nav
        private bool checkFrom(Processor processor, XPathNavigator nav) { 
            if(this.fromKey == Compiler.InvalidQueryKey) { 
                return true;
            } 
            do {
                if (processor.Matches(nav, this.fromKey)) {
                    return true;
                } 
            }while (nav.MoveToParent());
            return false; 
        } 

        private bool moveToCount(XPathNavigator nav, Processor processor, XPathNavigator contextNode) { 
            do {
                if (this.fromKey != Compiler.InvalidQueryKey && processor.Matches(nav, this.fromKey)) {
                    return false;
                } 
                if (MatchCountKey(processor, contextNode, nav)) {
                    return true; 
                } 
            }while (nav.MoveToParent());
            return false; 
        }

        private int numberCount(XPathNavigator nav, Processor processor, XPathNavigator contextNode) {
            Debug.Assert(nav.NodeType != XPathNodeType.Attribute && nav.NodeType != XPathNodeType.Namespace); 
            Debug.Assert(MatchCountKey(processor, contextNode, nav));
            XPathNavigator runner = nav.Clone(); 
            int number = 1; 
            if (runner.MoveToParent()) {
                runner.MoveToFirstChild(); 
                while (! runner.IsSamePosition(nav)) {
                    if (MatchCountKey(processor, contextNode, runner)) {
                        number++;
                    } 
                    if (! runner.MoveToNext()) {
                        Debug.Fail("We implementing preceding-sibling::node() and some how miss context node 'nav'"); 
                        break; 
                    }
                } 
            }
            return number;
        }
 
        private static object SimplifyValue(object value) {
            // If result of xsl:number is not in correct range it should be returned as is. 
            // so we need intermidiate string value. 
            // If it's already a double we would like to keep it as double.
            // So this function converts to string only if if result is nodeset or RTF 
            Debug.Assert(!(value is int));
            if (Type.GetTypeCode(value.GetType()) == TypeCode.Object) {
                XPathNodeIterator nodeset = value as XPathNodeIterator;
                if (nodeset != null) { 
                    if (nodeset.MoveNext()) {
                        return nodeset.Current.Value; 
                    } 
                    return string.Empty;
                } 
                XPathNavigator nav = value as XPathNavigator;
                if (nav != null) {
                    return nav.Value;
                } 
            }
            return value; 
        } 

        internal override void Execute(Processor processor, ActionFrame frame) { 
            Debug.Assert(processor != null && frame != null);
            ArrayList list = processor.NumberList;
            switch (frame.State) {
            case Initialized: 
                Debug.Assert(frame != null);
                Debug.Assert(frame.NodeSet != null); 
                list.Clear(); 
                if (this.valueKey != Compiler.InvalidQueryKey) {
                    list.Add(SimplifyValue(processor.Evaluate(frame, this.valueKey))); 
                }
                else if (this.level == "any") {
                    int number = numberAny(processor, frame);
                    if (number != 0) { 
                        list.Add(number);
                    } 
                } 
                else {
                    bool multiple = (this.level == "multiple"); 
                    XPathNavigator contextNode = frame.Node;         // context of xsl:number element. We using this node in MatchCountKey()
                    XPathNavigator countNode   = frame.Node.Clone(); // node we count for
                    if (countNode.NodeType == XPathNodeType.Attribute || countNode.NodeType == XPathNodeType.Namespace) {
                        countNode.MoveToParent(); 
                    }
                    while (moveToCount(countNode, processor, contextNode)) { 
                        list.Insert(0, numberCount(countNode, processor, contextNode)); 
                        if(! multiple || ! countNode.MoveToParent()) {
                            break; 
                        }
                    }
                    if(! checkFrom(processor, countNode)) {
                        list.Clear(); 
                    }
                } 
 
                /*CalculatingFormat:*/
                frame.StoredOutput = Format(list, 
                    this.formatAvt       == null ? this.formatTokens : ParseFormat(this.formatAvt.Evaluate(processor, frame)),
                    this.langAvt         == null ? this.lang         : this.langAvt        .Evaluate(processor, frame),
                    this.letterAvt       == null ? this.letter       : ParseLetter(this.letterAvt.Evaluate(processor, frame)),
                    this.groupingSepAvt  == null ? this.groupingSep  : this.groupingSepAvt .Evaluate(processor, frame), 
                    this.groupingSizeAvt == null ? this.groupingSize : this.groupingSizeAvt.Evaluate(processor, frame)
                ); 
                goto case OutputNumber; 
            case OutputNumber :
                Debug.Assert(frame.StoredOutput != null); 
                if (! processor.TextEvent(frame.StoredOutput)) {
                    frame.State        = OutputNumber;
                    break;
                } 
                frame.Finished();
                break; 
            default: 
                Debug.Fail("Invalid Number Action execution state");
                break; 
            }
        }

        private bool MatchCountKey(Processor processor, XPathNavigator contextNode, XPathNavigator nav){ 
            if (this.countKey != Compiler.InvalidQueryKey) {
                return processor.Matches(nav, this.countKey); 
            } 
            if (contextNode.Name == nav.Name && BasicNodeType(contextNode.NodeType) == BasicNodeType(nav.NodeType)) {
                return true; 
            }
            return false;
        }
 
        private XPathNodeType BasicNodeType(XPathNodeType type) {
            if(type == XPathNodeType.SignificantWhitespace || type == XPathNodeType.Whitespace) { 
                return XPathNodeType.Text; 
            }
            else { 
                return type;
            }
        }
 
        // [....]: perf.
        // for each call to xsl:number Format() will build new NumberingFormat object. 
        // in case of no AVTs we can build this object at compile time and reuse it on execution time. 
        // even partial step in this d---- will be usefull (when cFormats == 0)
 
        private static string Format(ArrayList numberlist, List tokens, string lang, string letter, string groupingSep, string groupingSize) {
            StringBuilder result = new StringBuilder();
            int cFormats = 0;
            if (tokens != null) { 
                cFormats = tokens.Count;
            } 
 
            NumberingFormat numberingFormat = new NumberingFormat();
            if (groupingSize != null) { 
                try {
                    numberingFormat.setGroupingSize(Convert.ToInt32(groupingSize, CultureInfo.InvariantCulture));
                }
                catch (System.FormatException) {} 
                catch (System.OverflowException) {}
            } 
            if (groupingSep != null) { 
                if (groupingSep.Length > 1) {
                    // It is a breaking change to throw an exception, SQLBUDT 324367 
                    //throw XsltException.Create(Res.Xslt_CharAttribute, Keywords.s_GroupingSeparator);
                }
                numberingFormat.setGroupingSeparator(groupingSep);
            } 
            if (0 < cFormats) {
                FormatInfo prefix = tokens[0]; 
                Debug.Assert(prefix == null || prefix.isSeparator); 
                FormatInfo sufix = null;
                if (cFormats % 2 == 1) { 
                    sufix = tokens[cFormats - 1];
                    cFormats --;
                }
                FormatInfo periodicSeparator = 2 < cFormats ? tokens[cFormats - 2] : DefaultSeparator; 
                FormatInfo periodicFormat    = 0 < cFormats ? tokens[cFormats - 1] : DefaultFormat   ;
                if (prefix != null) { 
                    result.Append(prefix.formatString); 
                }
                int numberlistCount = numberlist.Count; 
                for(int i = 0; i < numberlistCount; i++ ) {
                    int formatIndex   = i * 2;
                    bool haveFormat = formatIndex < cFormats;
                    if (0 < i) { 
                        FormatInfo thisSeparator = haveFormat ? tokens[formatIndex + 0] : periodicSeparator;
                        Debug.Assert(thisSeparator.isSeparator); 
                        result.Append(thisSeparator.formatString); 
                    }
 
                    FormatInfo thisFormat = haveFormat ? tokens[formatIndex + 1] : periodicFormat;
                    Debug.Assert(!thisFormat.isSeparator);

                    //numberingFormat.setletter(this.letter); 
                    //numberingFormat.setLang(this.lang);
 
                    numberingFormat.setNumberingType(thisFormat.numSequence); 
                    numberingFormat.setMinLen(thisFormat.length);
                    result.Append(numberingFormat.FormatItem(numberlist[i])); 
                }

                if (sufix != null) {
                    result.Append(sufix.formatString); 
                }
            } 
            else { 
                numberingFormat.setNumberingType(NumberingSequence.Arabic);
                for (int i = 0; i < numberlist.Count; i++) { 
                    if (i != 0) {
                        result.Append(".");
                    }
                    result.Append(numberingFormat.FormatItem(numberlist[i])); 
                }
            } 
            return result.ToString(); 
        }
 
        /*
        ---------------------------------------------------------------------------
            mapFormatToken()
 
            Maps a token of alphanumeric characters to a numbering format ID and a
            minimum length bound.  Tokens specify the character(s) that begins a 
            Unicode 
            numbering sequence.  For example, "i" specifies lower case roman numeral
            numbering.  Leading "zeros" specify a minimum length to be maintained by 
            padding, if necessary.
        ---------------------------------------------------------------------------
        */
        private static void mapFormatToken(String wsToken, int startLen, int tokLen, out NumberingSequence seq, out int pminlen) { 
            char wch = wsToken[startLen];
            bool UseArabic = false; 
            pminlen = 1; 
            seq = NumberingSequence.Nil;
 
            switch ((int)wch) {
            case 0x0030:    // Digit zero
            case 0x0966:    // Hindi digit zero
            case 0x0e50:    // Thai digit zero 
            case 0xc77b:    // Korean digit zero
            case 0xff10:    // Digit zero (double-byte) 
                do { 
                    // Leading zeros request padding.  Track how much.
                    pminlen++; 
                } while ((--tokLen > 0) && (wch == wsToken[++startLen]));

                if (wsToken[startLen] != (char)(wch + 1)) {
                    // If next character isn't "one", then use Arabic 
                    UseArabic = true;
                } 
                break; 
            }
 
            if (!UseArabic) {
                // Map characters of token to number format ID
                switch ((int)wsToken[startLen]) {
                case 0x0031: seq = NumberingSequence.Arabic; break; 
                case 0x0041: seq = NumberingSequence.UCLetter; break;
                case 0x0049: seq = NumberingSequence.UCRoman; break; 
                case 0x0061: seq = NumberingSequence.LCLetter; break; 
                case 0x0069: seq = NumberingSequence.LCRoman; break;
                case 0x0410: seq = NumberingSequence.UCRus; break; 
                case 0x0430: seq = NumberingSequence.LCRus; break;
                case 0x05d0: seq = NumberingSequence.Hebrew; break;
                case 0x0623: seq = NumberingSequence.ArabicScript; break;
                case 0x0905: seq = NumberingSequence.Hindi2; break; 
                case 0x0915: seq = NumberingSequence.Hindi1; break;
                case 0x0967: seq = NumberingSequence.Hindi3; break; 
                case 0x0e01: seq = NumberingSequence.Thai1; break; 
                case 0x0e51: seq = NumberingSequence.Thai2; break;
                case 0x30a2: seq = NumberingSequence.DAiueo; break; 
                case 0x30a4: seq = NumberingSequence.DIroha; break;
                case 0x3131: seq = NumberingSequence.DChosung; break;
                case 0x4e00: seq = NumberingSequence.FEDecimal; break;
                case 0x58f1: seq = NumberingSequence.DbNum3; break; 
                case 0x58f9: seq = NumberingSequence.ChnCmplx; break;
                case 0x5b50: seq = NumberingSequence.Zodiac2; break; 
                case 0xac00: seq = NumberingSequence.Ganada; break; 
                case 0xc77c: seq = NumberingSequence.KorDbNum1; break;
                case 0xd558: seq = NumberingSequence.KorDbNum3; break; 
                case 0xff11: seq = NumberingSequence.DArabic; break;
                case 0xff71: seq = NumberingSequence.Aiueo; break;
                case 0xff72: seq = NumberingSequence.Iroha; break;
 
                case 0x7532:
                    if (tokLen > 1 && wsToken[startLen + 1] == 0x5b50) { 
                        // 60-based Zodiak numbering begins with two characters 
                        seq = NumberingSequence.Zodiac3;
                        tokLen--; 
                        startLen++;
                    }
                    else {
                        // 10-based Zodiak numbering begins with one character 
                        seq = NumberingSequence.Zodiac1;
                    } 
                    break; 
                default:
                    seq = NumberingSequence.Arabic; 
                    break;
                }
            }
 
            //if (tokLen != 1 || UseArabic) {
            if (UseArabic) { 
                // If remaining token length is not 1, then don't recognize 
                // sequence and default to Arabic with no zero padding.
                seq = NumberingSequence.Arabic; 
                pminlen = 0;
            }
        }
 

        /* 
        ---------------------------------------------------------------------------- 
            parseFormat()
 
            Parse format string into format tokens (alphanumeric) and separators
            (non-alphanumeric).

        */ 
        private static List ParseFormat(string formatString) {
            if (formatString == null || formatString.Length == 0) { 
                return null; 
            }
            int length = 0; 
            bool lastAlphaNumeric = CharUtil.IsAlphaNumeric(formatString[length]);
            List tokens = new List();
            int count = 0;
 
            if (lastAlphaNumeric) {
                // If the first one is alpha num add empty separator as a prefix. 
                tokens.Add(null); 
            }
 
            while (length <= formatString.Length) {
                // Loop until a switch from format token to separator is detected (or vice-versa)
                bool currentchar = length < formatString.Length ? CharUtil.IsAlphaNumeric(formatString[length]) : !lastAlphaNumeric;
                if (lastAlphaNumeric != currentchar) { 
                    FormatInfo formatInfo = new FormatInfo();
                    if (lastAlphaNumeric) { 
                        // We just finished a format token.  Map it to a numbering format ID and a min-length bound. 
                        mapFormatToken(formatString, count, length - count, out formatInfo.numSequence, out formatInfo.length);
                    } 
                    else {
                        formatInfo.isSeparator = true;
                        // We just finished a separator.  Save its length and a pointer to it.
                        formatInfo.formatString = formatString.Substring(count, length - count); 
                    }
                    count = length; 
                    length++; 
                    // Begin parsing the next format token or separator
 
                    tokens.Add(formatInfo);
                    // Flip flag from format token to separator (or vice-versa)
                    lastAlphaNumeric = currentchar;
                } 
                else {
                    length++; 
                } 
            }
 
            return tokens;
        }

        private string ParseLetter(string letter) { 
            if (letter == null || letter == "traditional" || letter == "alphabetic") {
                return letter; 
            } 
            if (! this.forwardCompatibility) {
                throw XsltException.Create(Res.Xslt_InvalidAttrValue, Keywords.s_LetterValue, letter); 
            }
            return null;
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK