Welcome to mirror list, hosted at ThFree Co, Russian Federation.

NumberAction.cs « XsltOld « Xsl « Xml « System « System.Data.SqlXml « referencesource « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 85038e93bad0c191ad3ad120a1a623a9b760b210 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
//------------------------------------------------------------------------------
// <copyright file="NumberAction.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.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<FormatInfo> 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 (Ref.Equal(name, compiler.Atoms.Level)) {
                if (value != "any" && value != "multiple" && value != "single") {
                    throw XsltException.Create(Res.Xslt_InvalidAttrValue, "level", value);
                }
                this.level = value;
            }
            else if (Ref.Equal(name, compiler.Atoms.Count)) {
                this.countPattern = value;
                this.countKey = compiler.AddQuery(value, /*allowVars:*/true, /*allowKey:*/true, /*pattern*/true);
            }
            else if (Ref.Equal(name, compiler.Atoms.From)) {
                this.from = value;
                this.fromKey = compiler.AddQuery(value, /*allowVars:*/true, /*allowKey:*/true, /*pattern*/true);
            }
            else if (Ref.Equal(name, compiler.Atoms.Value)) {
                this.value = value;
                this.valueKey = compiler.AddQuery(value);
            }
            else if (Ref.Equal(name, compiler.Atoms.Format)) {
                this.formatAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Ref.Equal(name, compiler.Atoms.Lang)) {
                this.langAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Ref.Equal(name, compiler.Atoms.LetterValue)) {
                this.letterAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Ref.Equal(name, compiler.Atoms.GroupingSeparator)) {
                this.groupingSepAvt = Avt.CompileAvt(compiler, value);
            }
            else if (Ref.Equal(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, "grouping-separator");
            }
            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;
            }
        }

        // Microsoft: 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<FormatInfo> 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, "grouping-separator");
                }
                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<FormatInfo> ParseFormat(string formatString) {
            if (formatString == null || formatString.Length == 0) {
                return null;
            }
            int length = 0;
            bool lastAlphaNumeric = CharUtil.IsAlphaNumeric(formatString[length]);
            List<FormatInfo> tokens = new List<FormatInfo>();
            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, "letter-value", letter);
            }
            return null;
        }
    }
}