New test.
[mono.git] / mcs / class / System.XML / Mono.Xml.Xsl.Operations / XslNumber.cs
index 24d69b841b54ede36f8c23163862292f37d867e4..b857262141dfbbaffd3c4e9da98d4b0f11677d5f 100644 (file)
@@ -9,8 +9,30 @@
 // (C) 2003 Atsushi Enomoto
 //
 
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.Collections;
+using System.Globalization;
 using System.Xml;
 using System.Xml.XPath;
 using System.Xml.Xsl;
@@ -41,7 +63,15 @@ namespace Mono.Xml.Xsl.Operations {
                XslAvt groupingSize;
                
                public XslNumber (Compiler c) : base (c) {}
-               
+
+               // This behaves differently from Math.Round. For n + 0.5,
+               // Math.Round() truncates, while XSLT expects ceiling.
+               public static double Round (double n)
+               {
+                       double f = System.Math.Floor (n);
+                       return (n - f >= 0.5) ? f + 1.0 : f;
+               }
+
                protected override void Compile (Compiler c)
                {
                        switch (c.GetAttribute ("level"))
@@ -86,7 +116,7 @@ namespace Mono.Xml.Xsl.Operations {
                        string lang = null;
                        string letterValue = null;
                        char groupingSeparatorChar = '\0';
-                       int groupingSize = 0;
+                       decimal groupingSize = 0;
                        
                        if (this.format != null)
                                formatStr = this.format.Evaluate (p);
@@ -101,9 +131,13 @@ namespace Mono.Xml.Xsl.Operations {
                                groupingSeparatorChar = this.groupingSeparator.Evaluate (p) [0];
                        
                        if (this.groupingSize != null)
-                               groupingSize = int.Parse (this.groupingSize.Evaluate (p));
+                               groupingSize = decimal.Parse (this.groupingSize.Evaluate (p), CultureInfo.InvariantCulture);
                        
-                       return new XslNumberFormatter (formatStr, lang, letterValue, groupingSeparatorChar, groupingSize);
+                       //FIXME: Negative test compliency: .NET throws exception on negative grouping-size
+                       if (groupingSize > Int32.MaxValue || groupingSize < 1)
+                               groupingSize = 0;
+
+                       return new XslNumberFormatter (formatStr, lang, letterValue, groupingSeparatorChar, (int)groupingSize);
                }
                
                string GetFormat (XslTransformProcessor p)
@@ -112,8 +146,9 @@ namespace Mono.Xml.Xsl.Operations {
                        
                        if (this.value != null) {
                                double result = p.EvaluateNumber (this.value);
-                               result = (int) ((result - (int) result >= 0.5) ? result + 1 : result);
-                               return nf.Format ((int) result);
+                               //Do we need to round the result here???
+                               //result = (int) ((result - (int) result >= 0.5) ? result + 1 : result); 
+                               return nf.Format (result);
                        }
                        
                        switch (this.level) {
@@ -185,7 +220,7 @@ namespace Mono.Xml.Xsl.Operations {
                                                        return 0;
                                        };
                                }
-                       } while (true);\r
+                       } while (true);
                }
 
                int NumberSingle (XslTransformProcessor p)
@@ -282,19 +317,18 @@ namespace Mono.Xml.Xsl.Operations {
                        }
                        
                        // return the format for a single value, ie, if using Single or Any
-                       public string Format (int value)
+                       public string Format (double value)
                        {
                                return Format (value, true);
                        }
 
-                       public string Format (int value, bool formatContent)
+                       public string Format (double value, bool formatContent)
                        {
                                StringBuilder b = new StringBuilder ();
                                if (firstSep != null) b.Append (firstSep);
                                if (formatContent)
                                        ((FormatItem)fmtList [0]).Format (b, value);
                                if (lastSep != null) b.Append (lastSep);
-
                                return b.ToString ();
                        }
                        
@@ -357,15 +391,20 @@ namespace Mono.Xml.Xsl.Operations {
                                        this.sep = sep;
                                }
                                
-                               public abstract void Format (StringBuilder b, int num);
+                               public abstract void Format (StringBuilder b, double num);
                                        
                                public static FormatItem GetItem (string sep, string item, char gpSep, int gpSize)
                                {
                                        switch (item [0])
                                        {
                                                default: // See XSLT 1.0 spec 7.7.1.
+                                                       return new DigitItem (sep, 1, gpSep, gpSize);
                                                case '0': case '1':
-                                                       return new DigitItem (sep, item.Length, gpSep, gpSize);
+                                                       int len = 1;
+                                                       for (; len < item.Length; len++)
+                                                               if (!Char.IsDigit (item, len))
+                                                                       break;
+                                                       return new DigitItem (sep, len, gpSep, gpSize);
                                                case 'a':
                                                        return new AlphaItem (sep, false);
                                                case 'A':
@@ -388,15 +427,18 @@ namespace Mono.Xml.Xsl.Operations {
                                        this.uc = uc;
                                }
                                
-                               public override void Format (StringBuilder b, int num)
+                               public override void Format (StringBuilder b, double num)
                                {
                                        alphaSeq (b, num, uc ? ucl : lcl);
                                }
                                
-                               static void alphaSeq (StringBuilder b, int n, char [] alphabet) {
+                               static void alphaSeq (StringBuilder b, double n, char [] alphabet) {
+                                       n = XslNumber.Round (n);
+                                       if (n == 0)
+                                               return;
                                        if (n > alphabet.Length)
-                                               alphaSeq (b, (n-1) / alphabet.Length, alphabet);
-                                       b.Append (alphabet [(n-1) % alphabet.Length]); 
+                                               alphaSeq (b, System.Math.Floor ((n - 1) / alphabet.Length), alphabet);
+                                       b.Append (alphabet [((int) n - 1) % alphabet.Length]); 
                                }
                        }
                        
@@ -413,13 +455,13 @@ namespace Mono.Xml.Xsl.Operations {
                                static readonly int [] decValues =
                                {1000, 900 , 500, 400 , 100, 90  , 50 , 40  , 10 , 9   , 5  , 4   , 1   };
                                
-                               public override void Format (StringBuilder b, int num)
+                               public override void Format (StringBuilder b, double num)
                                {
                                        if (num < 1 || num > 4999) {
                                                b.Append (num);
                                                return;
                                        }
-                                       
+                                       num = XslNumber.Round (num);
                                        for (int i = 0; i < decValues.Length; i++) {
                                                while (decValues [i] <= num) {
                                                        if (uc)
@@ -435,14 +477,15 @@ namespace Mono.Xml.Xsl.Operations {
                        }
                        
                        class DigitItem : FormatItem {
-                               System.Globalization.NumberFormatInfo nfi;
+                               NumberFormatInfo nfi;
                                int decimalSectionLength;
                                StringBuilder numberBuilder;
                                
                                public DigitItem (string sep, int len, char gpSep, int gpSize) : base (sep)
                                {
-                                       nfi = new System.Globalization.NumberFormatInfo  ();
+                                       nfi = new NumberFormatInfo  ();
                                        nfi.NumberDecimalDigits = 0;
+                                       nfi.NumberGroupSizes = new int [] {0};
                                        if (gpSep != '\0' && gpSize > 0) {
                                                // ignored if either of them doesn't exist.
                                                nfi.NumberGroupSeparator = gpSep.ToString ();
@@ -451,7 +494,7 @@ namespace Mono.Xml.Xsl.Operations {
                                        decimalSectionLength = len;
                                }
                                
-                               public override void Format (StringBuilder b, int num)
+                               public override void Format (StringBuilder b, double num)
                                {
                                        string number = num.ToString ("N", nfi);
                                        int len = decimalSectionLength;