XslNumber.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / XmlUtils / System / Xml / Xsl / Runtime / XslNumber.cs / 1305376 / XslNumber.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
using System.Collections; 
using System.Collections.Generic;
using System.Diagnostics; 
using System.Text;
using System.Xml.XPath;

namespace System.Xml.Xsl.Runtime { 

    internal class TokenInfo { 
        public char     startChar;      // First element of numbering sequence for format token 
        public int      startIdx;       // Start index of separator token
        public string   formatString;   // Format string for separator token 
        public int      length;         // Length of separator token, or minimum length of decimal numbers for format token

        // Instances of this internal class must be created via CreateFormat and CreateSeparator
        private TokenInfo() { 
        }
 
        [Conditional("DEBUG")] 
        public void AssertSeparator(bool isSeparator) {
            Debug.Assert(isSeparator == (formatString != null), "AssertSeparator"); 
        }

        // Creates a TokenInfo for a separator token.
        public static TokenInfo CreateSeparator(string formatString, int startIdx, int tokLen) { 
            Debug.Assert(startIdx >= 0 && tokLen > 0);
            TokenInfo token = new TokenInfo(); { 
                token.startIdx     = startIdx; 
                token.formatString = formatString;
                token.length       = tokLen; 
            }
            return token;
        }
 
        // 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. 
        public static TokenInfo CreateFormat(string formatString, int startIdx, int tokLen) {
            Debug.Assert(startIdx >= 0 && tokLen > 0);
            TokenInfo token = new TokenInfo();
            token.formatString = null; 
            token.length = 1;
 
            bool useDefault = false; 
            char ch = formatString[startIdx];
 
            switch (ch) {
            case '1':
            case 'A':
            case 'I': 
            case 'a':
            case 'i': 
                break; 
            default:
                // NOTE: We do not support Tamil and Ethiopic numbering systems having no zeros 
                if (CharUtil.IsDecimalDigitOne(ch)) {
                    break;
                }
                if (CharUtil.IsDecimalDigitOne((char)(ch + 1))) { 
                    // Leading zeros request padding.  Track how much.
                    int idx = startIdx; 
                    do { 
                        token.length++;
                    } while (--tokLen > 0 && ch == formatString[++idx]); 

                    // Recognize the token only if the next character is "one"
                    if (formatString[idx] == ++ch) {
                        break; 
                    }
                } 
                useDefault = true; 
                break;
            } 

            if (tokLen != 1) {
                // If remaining token length is not 1, do not recognize the token
                useDefault = true; 
            }
 
            if (useDefault) { 
                // Default to Arabic numbering with no zero padding
                token.startChar = NumberFormatter.DefaultStartChar; 
                token.length = 1;
            } else {
                token.startChar = ch;
            } 
            return token;
        } 
    } 

    internal class NumberFormatter : NumberFormatterBase { 
        private string          formatString;
        private int             lang;
        private string          letterValue;
        private string          groupingSeparator; 
        private int             groupingSize;
 
        private List tokens; 

        public const char                   DefaultStartChar = '1'; 
        private static readonly TokenInfo   DefaultFormat    = TokenInfo.CreateFormat   ("0", 0, 1);
        private static readonly TokenInfo   DefaultSeparator = TokenInfo.CreateSeparator(".", 0, 1);

        // Creates a Format object parsing format string into format tokens (alphanumeric) and separators (non-alphanumeric). 
        public NumberFormatter(string formatString, int lang, string letterValue, string groupingSeparator, int groupingSize) {
            Debug.Assert(groupingSeparator.Length <= 1); 
            this.formatString       = formatString; 
            this.lang               = lang;
            this.letterValue        = letterValue; 
            this.groupingSeparator  = groupingSeparator;
            this.groupingSize       = groupingSeparator.Length > 0 ? groupingSize : 0;

            if (formatString == "1" || formatString.Length == 0) { 
                // Special case of the default format
                return; 
            } 

            this.tokens = new List(); 
            int idxStart = 0;
            bool isAlphaNumeric = CharUtil.IsAlphaNumeric(formatString[idxStart]);

            if (isAlphaNumeric) { 
                // If the first one is alpha num add empty separator as a prefix
                tokens.Add(null); 
            } 

            for (int idx = 0; idx <= formatString.Length; idx++) { 
                // Loop until a switch from formatString token to separator is detected (or vice-versa)
                if (idx == formatString.Length || isAlphaNumeric != CharUtil.IsAlphaNumeric(formatString[idx])) {
                    if (isAlphaNumeric) {
                        // Just finished a format token 
                        tokens.Add(TokenInfo.CreateFormat(formatString, idxStart, idx - idxStart));
                    } else { 
                        // Just finished a separator token 
                        tokens.Add(TokenInfo.CreateSeparator(formatString, idxStart, idx - idxStart));
                    } 

                    // Begin parsing the next format token or separator
                    idxStart = idx;
 
                    // Flip flag from format token to separator or vice-versa
                    isAlphaNumeric = !isAlphaNumeric; 
                } 
            }
        } 

        /// 
        /// Format the given xsl:number place marker
        ///  
        /// Place marker - either a sequence of ints, or a double singleton
        /// Formatted string 
        public string FormatSequence(IList val) { 
            StringBuilder sb = new StringBuilder();
 
            // If the value was supplied directly, in the 'value' attribute, check its validity
            if (val.Count == 1 && val[0].ValueType == typeof(double)) {
                double dblVal = val[0].ValueAsDouble;
                if (!(0.5 <= dblVal && dblVal < double.PositiveInfinity)) { 
                    // Errata E24: 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 XPathConvert.DoubleToString(dblVal);
                } 
            }

            if (tokens == null) {
                // Special case of the default format 
                for (int idx = 0; idx < val.Count; idx++) {
                    if (idx > 0) { 
                        sb.Append('.'); 
                    }
                    FormatItem(sb, val[idx], DefaultStartChar, 1); 
                }
            } else {
                int cFormats = tokens.Count;
                TokenInfo prefix = tokens[0], suffix; 

                if (cFormats % 2 == 0) { 
                    suffix = null; 
                } else {
                    suffix = tokens[--cFormats]; 
                }

                TokenInfo periodicSeparator = 2 < cFormats ? tokens[cFormats - 2] : DefaultSeparator;
                TokenInfo periodicFormat    = 0 < cFormats ? tokens[cFormats - 1] : DefaultFormat; 

                if (prefix != null) { 
                    prefix.AssertSeparator(true); 
                    sb.Append(prefix.formatString, prefix.startIdx, prefix.length);
                } 

                int valCount = val.Count;
                for (int i = 0; i < valCount; i++ ) {
                    int formatIndex = i * 2; 
                    bool haveFormat = formatIndex < cFormats;
 
                    if (i > 0) { 
                        TokenInfo thisSeparator = haveFormat ? tokens[formatIndex + 0] : periodicSeparator;
                        thisSeparator.AssertSeparator(true); 
                        sb.Append(thisSeparator.formatString, thisSeparator.startIdx, thisSeparator.length);
                    }

                    TokenInfo thisFormat = haveFormat ? tokens[formatIndex + 1] : periodicFormat; 
                    thisFormat.AssertSeparator(false);
                    FormatItem(sb, val[i], thisFormat.startChar, thisFormat.length); 
                } 

                if (suffix != null) { 
                    suffix.AssertSeparator(true);
                    sb.Append(suffix.formatString, suffix.startIdx, suffix.length);
                }
            } 
            return sb.ToString();
        } 
 
        private void FormatItem(StringBuilder sb, XPathItem item, char startChar, int length) {
            double dblVal; 

            if (item.ValueType == typeof(int)) {
                dblVal = (double)item.ValueAsInt;
            } else { 
                Debug.Assert(item.ValueType == typeof(double), "Item must be either of type int, or double");
                dblVal = XsltFunctions.Round(item.ValueAsDouble); 
            } 

            Debug.Assert(1 <= dblVal && dblVal < double.PositiveInfinity); 
            char zero = '0';

            switch (startChar) {
            case '1': 
                break;
            case 'A': 
            case 'a': 
                if (dblVal <= MaxAlphabeticValue) {
                    ConvertToAlphabetic(sb, dblVal, startChar, 26); 
                    return;
                }
                break;
            case 'I': 
            case 'i':
                if (dblVal <= MaxRomanValue) { 
                    ConvertToRoman(sb, dblVal, /*upperCase:*/ startChar == 'I'); 
                    return;
                } 
                break;
            default:
                Debug.Assert(CharUtil.IsDecimalDigitOne(startChar), "Unexpected startChar: " + startChar);
                zero = (char)(startChar - 1); 
                break;
            } 
 
            sb.Append(ConvertToDecimal(dblVal, length, zero, groupingSeparator, groupingSize));
        } 

        private static string ConvertToDecimal(double val, int minLen, char zero, string groupSeparator, int groupSize) {
            Debug.Assert(val >= 0 && val == Math.Round(val), "ConvertToArabic operates on non-negative integer numbers only");
            string str = XPathConvert.DoubleToString(val); 
            int  shift = zero - '0';
 
            // Figure out new string length without separators 
            int oldLen = str.Length;
            int newLen = Math.Max(oldLen, minLen); 

            // Calculate length of string with separators
            if (groupSize != 0) {
                Debug.Assert(groupSeparator.Length == 1); 
                checked { newLen += (newLen - 1) / groupSize; }
            } 
 
            // If the new number of characters equals the old one, no changes need to be made
            if (newLen == oldLen && shift == 0) { 
                return str;
            }

            // If grouping is not needed, add zero padding only 
            if (groupSize == 0 && shift == 0) {
                return str.PadLeft(newLen, zero); 
            } 

            // Add both grouping separators and zero padding to the string representation of a number 
        #if true
            unsafe {
                char *result = stackalloc char[newLen];
                char separator = (groupSeparator.Length > 0) ? groupSeparator[0] : ' '; 

                fixed (char *pin = str) { 
                    char *pOldEnd = pin + oldLen - 1; 
                    char *pNewEnd = result + newLen - 1;
                    int cnt = groupSize; 

                    while (true) {
                        // Move digit to its new location (zero if we've run out of digits)
                        *pNewEnd-- = (pOldEnd >= pin) ? (char)(*pOldEnd-- + shift) : zero; 
                        if (pNewEnd < result) {
                            break; 
                        } 
                        if (/*groupSize > 0 && */--cnt == 0) {
                            // Every groupSize digits insert the separator 
                            *pNewEnd-- = separator;
                            cnt = groupSize;
                            Debug.Assert(pNewEnd >= result, "Separator cannot be the first character");
                        } 
                    }
                } 
                return new string(result, 0, newLen); 
            }
        #else 
            // Safe version is about 20% slower after NGEN
            char[] result = new char[newLen];
            char separator = (groupSeparator.Length > 0) ? groupSeparator[0] : ' ';
 
            int oldEnd = oldLen - 1;
            int newEnd = newLen - 1; 
            int cnt = groupSize; 

            while (true) { 
                // Move digit to its new location (zero if we've run out of digits)
                result[newEnd--] = (oldEnd >= 0) ? (char)(str[oldEnd--] + shift) : zero;
                if (newEnd < 0) {
                    break; 
                }
                if (/*groupSize > 0 && */--cnt == 0) { 
                    // Every groupSize digits insert the separator 
                    result[newEnd--] = separator;
                    cnt = groupSize; 
                    Debug.Assert(newEnd >= 0, "Separator cannot be the first character");
                }
            }
            return new string(result, 0, newLen); 
        #endif
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
using System.Collections; 
using System.Collections.Generic;
using System.Diagnostics; 
using System.Text;
using System.Xml.XPath;

namespace System.Xml.Xsl.Runtime { 

    internal class TokenInfo { 
        public char     startChar;      // First element of numbering sequence for format token 
        public int      startIdx;       // Start index of separator token
        public string   formatString;   // Format string for separator token 
        public int      length;         // Length of separator token, or minimum length of decimal numbers for format token

        // Instances of this internal class must be created via CreateFormat and CreateSeparator
        private TokenInfo() { 
        }
 
        [Conditional("DEBUG")] 
        public void AssertSeparator(bool isSeparator) {
            Debug.Assert(isSeparator == (formatString != null), "AssertSeparator"); 
        }

        // Creates a TokenInfo for a separator token.
        public static TokenInfo CreateSeparator(string formatString, int startIdx, int tokLen) { 
            Debug.Assert(startIdx >= 0 && tokLen > 0);
            TokenInfo token = new TokenInfo(); { 
                token.startIdx     = startIdx; 
                token.formatString = formatString;
                token.length       = tokLen; 
            }
            return token;
        }
 
        // 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. 
        public static TokenInfo CreateFormat(string formatString, int startIdx, int tokLen) {
            Debug.Assert(startIdx >= 0 && tokLen > 0);
            TokenInfo token = new TokenInfo();
            token.formatString = null; 
            token.length = 1;
 
            bool useDefault = false; 
            char ch = formatString[startIdx];
 
            switch (ch) {
            case '1':
            case 'A':
            case 'I': 
            case 'a':
            case 'i': 
                break; 
            default:
                // NOTE: We do not support Tamil and Ethiopic numbering systems having no zeros 
                if (CharUtil.IsDecimalDigitOne(ch)) {
                    break;
                }
                if (CharUtil.IsDecimalDigitOne((char)(ch + 1))) { 
                    // Leading zeros request padding.  Track how much.
                    int idx = startIdx; 
                    do { 
                        token.length++;
                    } while (--tokLen > 0 && ch == formatString[++idx]); 

                    // Recognize the token only if the next character is "one"
                    if (formatString[idx] == ++ch) {
                        break; 
                    }
                } 
                useDefault = true; 
                break;
            } 

            if (tokLen != 1) {
                // If remaining token length is not 1, do not recognize the token
                useDefault = true; 
            }
 
            if (useDefault) { 
                // Default to Arabic numbering with no zero padding
                token.startChar = NumberFormatter.DefaultStartChar; 
                token.length = 1;
            } else {
                token.startChar = ch;
            } 
            return token;
        } 
    } 

    internal class NumberFormatter : NumberFormatterBase { 
        private string          formatString;
        private int             lang;
        private string          letterValue;
        private string          groupingSeparator; 
        private int             groupingSize;
 
        private List tokens; 

        public const char                   DefaultStartChar = '1'; 
        private static readonly TokenInfo   DefaultFormat    = TokenInfo.CreateFormat   ("0", 0, 1);
        private static readonly TokenInfo   DefaultSeparator = TokenInfo.CreateSeparator(".", 0, 1);

        // Creates a Format object parsing format string into format tokens (alphanumeric) and separators (non-alphanumeric). 
        public NumberFormatter(string formatString, int lang, string letterValue, string groupingSeparator, int groupingSize) {
            Debug.Assert(groupingSeparator.Length <= 1); 
            this.formatString       = formatString; 
            this.lang               = lang;
            this.letterValue        = letterValue; 
            this.groupingSeparator  = groupingSeparator;
            this.groupingSize       = groupingSeparator.Length > 0 ? groupingSize : 0;

            if (formatString == "1" || formatString.Length == 0) { 
                // Special case of the default format
                return; 
            } 

            this.tokens = new List(); 
            int idxStart = 0;
            bool isAlphaNumeric = CharUtil.IsAlphaNumeric(formatString[idxStart]);

            if (isAlphaNumeric) { 
                // If the first one is alpha num add empty separator as a prefix
                tokens.Add(null); 
            } 

            for (int idx = 0; idx <= formatString.Length; idx++) { 
                // Loop until a switch from formatString token to separator is detected (or vice-versa)
                if (idx == formatString.Length || isAlphaNumeric != CharUtil.IsAlphaNumeric(formatString[idx])) {
                    if (isAlphaNumeric) {
                        // Just finished a format token 
                        tokens.Add(TokenInfo.CreateFormat(formatString, idxStart, idx - idxStart));
                    } else { 
                        // Just finished a separator token 
                        tokens.Add(TokenInfo.CreateSeparator(formatString, idxStart, idx - idxStart));
                    } 

                    // Begin parsing the next format token or separator
                    idxStart = idx;
 
                    // Flip flag from format token to separator or vice-versa
                    isAlphaNumeric = !isAlphaNumeric; 
                } 
            }
        } 

        /// 
        /// Format the given xsl:number place marker
        ///  
        /// Place marker - either a sequence of ints, or a double singleton
        /// Formatted string 
        public string FormatSequence(IList val) { 
            StringBuilder sb = new StringBuilder();
 
            // If the value was supplied directly, in the 'value' attribute, check its validity
            if (val.Count == 1 && val[0].ValueType == typeof(double)) {
                double dblVal = val[0].ValueAsDouble;
                if (!(0.5 <= dblVal && dblVal < double.PositiveInfinity)) { 
                    // Errata E24: 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 XPathConvert.DoubleToString(dblVal);
                } 
            }

            if (tokens == null) {
                // Special case of the default format 
                for (int idx = 0; idx < val.Count; idx++) {
                    if (idx > 0) { 
                        sb.Append('.'); 
                    }
                    FormatItem(sb, val[idx], DefaultStartChar, 1); 
                }
            } else {
                int cFormats = tokens.Count;
                TokenInfo prefix = tokens[0], suffix; 

                if (cFormats % 2 == 0) { 
                    suffix = null; 
                } else {
                    suffix = tokens[--cFormats]; 
                }

                TokenInfo periodicSeparator = 2 < cFormats ? tokens[cFormats - 2] : DefaultSeparator;
                TokenInfo periodicFormat    = 0 < cFormats ? tokens[cFormats - 1] : DefaultFormat; 

                if (prefix != null) { 
                    prefix.AssertSeparator(true); 
                    sb.Append(prefix.formatString, prefix.startIdx, prefix.length);
                } 

                int valCount = val.Count;
                for (int i = 0; i < valCount; i++ ) {
                    int formatIndex = i * 2; 
                    bool haveFormat = formatIndex < cFormats;
 
                    if (i > 0) { 
                        TokenInfo thisSeparator = haveFormat ? tokens[formatIndex + 0] : periodicSeparator;
                        thisSeparator.AssertSeparator(true); 
                        sb.Append(thisSeparator.formatString, thisSeparator.startIdx, thisSeparator.length);
                    }

                    TokenInfo thisFormat = haveFormat ? tokens[formatIndex + 1] : periodicFormat; 
                    thisFormat.AssertSeparator(false);
                    FormatItem(sb, val[i], thisFormat.startChar, thisFormat.length); 
                } 

                if (suffix != null) { 
                    suffix.AssertSeparator(true);
                    sb.Append(suffix.formatString, suffix.startIdx, suffix.length);
                }
            } 
            return sb.ToString();
        } 
 
        private void FormatItem(StringBuilder sb, XPathItem item, char startChar, int length) {
            double dblVal; 

            if (item.ValueType == typeof(int)) {
                dblVal = (double)item.ValueAsInt;
            } else { 
                Debug.Assert(item.ValueType == typeof(double), "Item must be either of type int, or double");
                dblVal = XsltFunctions.Round(item.ValueAsDouble); 
            } 

            Debug.Assert(1 <= dblVal && dblVal < double.PositiveInfinity); 
            char zero = '0';

            switch (startChar) {
            case '1': 
                break;
            case 'A': 
            case 'a': 
                if (dblVal <= MaxAlphabeticValue) {
                    ConvertToAlphabetic(sb, dblVal, startChar, 26); 
                    return;
                }
                break;
            case 'I': 
            case 'i':
                if (dblVal <= MaxRomanValue) { 
                    ConvertToRoman(sb, dblVal, /*upperCase:*/ startChar == 'I'); 
                    return;
                } 
                break;
            default:
                Debug.Assert(CharUtil.IsDecimalDigitOne(startChar), "Unexpected startChar: " + startChar);
                zero = (char)(startChar - 1); 
                break;
            } 
 
            sb.Append(ConvertToDecimal(dblVal, length, zero, groupingSeparator, groupingSize));
        } 

        private static string ConvertToDecimal(double val, int minLen, char zero, string groupSeparator, int groupSize) {
            Debug.Assert(val >= 0 && val == Math.Round(val), "ConvertToArabic operates on non-negative integer numbers only");
            string str = XPathConvert.DoubleToString(val); 
            int  shift = zero - '0';
 
            // Figure out new string length without separators 
            int oldLen = str.Length;
            int newLen = Math.Max(oldLen, minLen); 

            // Calculate length of string with separators
            if (groupSize != 0) {
                Debug.Assert(groupSeparator.Length == 1); 
                checked { newLen += (newLen - 1) / groupSize; }
            } 
 
            // If the new number of characters equals the old one, no changes need to be made
            if (newLen == oldLen && shift == 0) { 
                return str;
            }

            // If grouping is not needed, add zero padding only 
            if (groupSize == 0 && shift == 0) {
                return str.PadLeft(newLen, zero); 
            } 

            // Add both grouping separators and zero padding to the string representation of a number 
        #if true
            unsafe {
                char *result = stackalloc char[newLen];
                char separator = (groupSeparator.Length > 0) ? groupSeparator[0] : ' '; 

                fixed (char *pin = str) { 
                    char *pOldEnd = pin + oldLen - 1; 
                    char *pNewEnd = result + newLen - 1;
                    int cnt = groupSize; 

                    while (true) {
                        // Move digit to its new location (zero if we've run out of digits)
                        *pNewEnd-- = (pOldEnd >= pin) ? (char)(*pOldEnd-- + shift) : zero; 
                        if (pNewEnd < result) {
                            break; 
                        } 
                        if (/*groupSize > 0 && */--cnt == 0) {
                            // Every groupSize digits insert the separator 
                            *pNewEnd-- = separator;
                            cnt = groupSize;
                            Debug.Assert(pNewEnd >= result, "Separator cannot be the first character");
                        } 
                    }
                } 
                return new string(result, 0, newLen); 
            }
        #else 
            // Safe version is about 20% slower after NGEN
            char[] result = new char[newLen];
            char separator = (groupSeparator.Length > 0) ? groupSeparator[0] : ' ';
 
            int oldEnd = oldLen - 1;
            int newEnd = newLen - 1; 
            int cnt = groupSize; 

            while (true) { 
                // Move digit to its new location (zero if we've run out of digits)
                result[newEnd--] = (oldEnd >= 0) ? (char)(str[oldEnd--] + shift) : zero;
                if (newEnd < 0) {
                    break; 
                }
                if (/*groupSize > 0 && */--cnt == 0) { 
                    // Every groupSize digits insert the separator 
                    result[newEnd--] = separator;
                    cnt = groupSize; 
                    Debug.Assert(newEnd >= 0, "Separator cannot be the first character");
                }
            }
            return new string(result, 0, newLen); 
        #endif
        } 
    } 
}

// 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