2002-08-21 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / class / corlib / System / IntegerFormatter.cs
index 423b2354a47706e98da29eaf3d135a64e02baa0c..b53ad1f46b68c991ba634fc258b9d8631664c220 100644 (file)
 //   }
 // }
 //
+// There is a property in NumberFormatInfo for NegativeSign, though the 
+// definition of IFormattable just uses '-' in context. So all the 
+// hardcoded uses of '-' in here may need to be changed to nfi.NegativeSign
+//
 // For every integral type.
 //
 // Before every Format<Format Type> block there is a small paragraph
 //     function call.
 //
 
+using System;
+using System.Collections;
 using System.Globalization;
 
 namespace System {
 
-       internal sealed class IntegerFormatter {
+       public sealed class IntegerFormatter {
 
                private static int maxByteLength = 4;
                private static int maxShortLength = 6;
                private static int maxIntLength = 12;
                private static int maxLongLength = 22;
 
-               private static char[] digitLowerTable = 
-                       { '0', '1', '2', '3', '4', '5', '6', '7', 
-                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+               private static char[] digitLowerTable;
+/**
+ * This makes a TypeNotInitialized exception be thrown.
+ *             { '0', '1', '2', '3', '4', '5', '6', '7', 
+ *               '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+ */
+
+               private static char[] digitUpperTable;
+/*
+ *             { '0', '1', '2', '3', '4', '5', '6', '7', 
+ *               '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ */
+
+               static IntegerFormatter ()
+               {
+                       int i;
 
-               private static char[] digitUpperTable = 
-               { '0', '1', '2', '3', '4', '5', '6', '7', 
-                 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+                       digitLowerTable = new char[16];
+                       digitUpperTable = new char[16];
 
-               private static bool ParseFormat (string format, out char specifier,  out int precision)
+                       for (i = 0; i < 10; i++){
+                               digitLowerTable[i] = (char) ('0' + i);
+                               digitUpperTable[i] = (char) ('0' + i);
+                       }
+
+                       char lc = (char ) ('a' - i);
+                       char uc = (char ) ('A' - i);
+                       while (i < 16){
+                               digitLowerTable[i] = (char) (lc + i);
+                               digitUpperTable[i] = (char) (uc + i);
+                               i++;
+                       }
+               }
+
+               private static bool IsDigit (char c)
+               {
+                       return !(c < '0' || c > '9'); 
+               }
+               
+               private static bool IsLetter (char c)
+               {
+                       return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); 
+               }
+               private static bool ParseFormat (string format, out char specifier,  out int precision, out bool custom)
                {                                
                        precision = -1;
                        specifier = '\0';
+                       custom = false;
                        
                        int length = format.Length;
-                       if (length < 1 || length > 3)
+                       // TODO: Could an empty string be a custom format string?
+                       if (length < 1)
                                return false;
                        
                        char[] chars = format.ToCharArray ();
                        specifier = chars[0];
 
-                       if (length == 1) 
-                               return true;
-                       
-                       if (length == 2) {
-                               if (chars[1] < '0' || chars[1] > '9')
-                                       return false;
-                               
-                               precision = chars[1] - '0';
-                       } else {
-                               if (chars[1] < '0' || chars[2] < '0' || chars[1] > '9' || chars[2] > '9')
-                                       return false;
+                       // TODO: IsLetter() and IsDigit() should be replaced by Char.Is*()
+                       if (IsLetter(specifier) && length <= 3) {
+                               switch (length){
+                               case 1:
+                                       return true;
+                               case 2:
+                                       if (IsDigit(chars[1])) {
+                                               precision = chars[1] - '0';
+                                               return true;
+                                       }
+                                       break;
+                               case 3:
+                                       if (IsDigit(chars[1]) && IsDigit(chars[2])) {
+                                               precision = chars[1] - '0';
+                                               precision = precision * 10 + chars[2] - '0';
+                                               return true;
+                                       }
+                                       break;
+                               }
                                
-                               precision = (chars[1] - '0') * 10 + (chars[2] - '0');
                        }
                        
+                       // We've got a custom format string.
+                       custom = true;
                        return true;
                }        
 
@@ -135,9 +188,14 @@ namespace System {
                {
                        char specifier;
                        int precision;
+                       bool custom;
+
+                       if (!ParseFormat (format, out specifier, out precision, out custom))
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        
-                       if (!ParseFormat (format, out specifier, out precision))
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                       if (custom){
+                               return FormatCustom (format, value, nfi);
+                       }
 
                        switch(specifier) {
                        case 'c': return FormatCurrency (value, precision, nfi);
@@ -159,7 +217,7 @@ namespace System {
                        case 'x': return FormatHexadecimal (value, precision, false);
                        case 'X': return FormatHexadecimal (value, precision, true);
                        default: 
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        }
                }               
 
@@ -167,9 +225,14 @@ namespace System {
                {
                        char specifier;
                        int precision;
+                       bool custom;
+                       
+                       if (!ParseFormat (format, out specifier, out precision, out custom))
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        
-                       if (!ParseFormat (format, out specifier, out precision))
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                       if (custom){
+                               return FormatCustom (format, value, nfi);
+                       }
 
                        switch(specifier) {
                        case 'c': return FormatCurrency (value, precision, nfi);
@@ -191,7 +254,7 @@ namespace System {
                        case 'x': return FormatHexadecimal (value, precision, false);
                        case 'X': return FormatHexadecimal (value, precision, true);
                        default: 
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        }
                }
 
@@ -199,10 +262,15 @@ namespace System {
                {
                        char specifier;
                        int precision;
+                       bool custom;
                        
-                       if (!ParseFormat (format, out specifier, out precision))
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                       if (!ParseFormat (format, out specifier, out precision, out custom))
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        
+                       if (custom){
+                               return FormatCustom (format, value, nfi);
+                       }
+
                        switch(specifier) {
                        case 'c': return FormatCurrency (value, precision, nfi);        
                        case 'C': return FormatCurrency (value, precision, nfi);        
@@ -223,7 +291,7 @@ namespace System {
                        case 'x': return FormatHexadecimal (value, precision, false);
                        case 'X': return FormatHexadecimal (value, precision, true);
                        default: 
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        }
                }
 
@@ -231,10 +299,15 @@ namespace System {
                {
                        char specifier;
                        int precision;
+                       bool custom;
                        
-                       if (!ParseFormat (format, out specifier, out precision))
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                       if (!ParseFormat (format, out specifier, out precision, out custom))
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        
+                       if (custom){
+                               return FormatCustom (format, value, nfi);
+                       }
+
                        switch(specifier) {
                        case 'c': return FormatCurrency (value, precision, nfi);
                        case 'C': return FormatCurrency (value, precision, nfi);
@@ -255,17 +328,23 @@ namespace System {
                        case 'x': return FormatHexadecimal (value, precision, false);
                        case 'X': return FormatHexadecimal (value, precision, true);
                        default: 
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        }                       
                }
 
+               [CLSCompliant (false)]
                public static string NumberToString (string format, NumberFormatInfo nfi, sbyte value)
                {
                        char specifier;
                        int precision;
+                       bool custom;
+                       
+                       if (!ParseFormat (format, out specifier, out precision, out custom))
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        
-                       if (!ParseFormat (format, out specifier, out precision))
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                       if (custom){
+                               return FormatCustom (format, value, nfi);
+                       }
                        
                        switch(specifier) {
                        case 'c': return FormatCurrency (value, precision, nfi);
@@ -287,17 +366,23 @@ namespace System {
                        case 'x': return FormatHexadecimal (value, precision, false);
                        case 'X': return FormatHexadecimal (value, precision, true);
                        default: 
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        }
                }
 
+               [CLSCompliant (false)]
                public static string NumberToString (string format, NumberFormatInfo nfi, ushort value)
                {
                        char specifier;
                        int precision;
+                       bool custom;
                        
-                       if (!ParseFormat (format, out specifier, out precision))
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                       if (!ParseFormat (format, out specifier, out precision, out custom))
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
+                       
+                       if (custom){
+                               return FormatCustom (format, value, nfi);
+                       }
                        
                        switch(specifier) {
                        case 'c': return FormatCurrency (value, precision, nfi);
@@ -319,17 +404,23 @@ namespace System {
                        case 'x': return FormatHexadecimal (value, precision, false);
                        case 'X': return FormatHexadecimal (value, precision, true);
                        default: 
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        }
                }
 
+               [CLSCompliant (false)]
                public static string NumberToString (string format, NumberFormatInfo nfi, uint value)
                {
                        char specifier;
                        int precision;
+                       bool custom;
+                       
+                       if (!ParseFormat (format, out specifier, out precision, out custom))
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        
-                       if (!ParseFormat (format, out specifier, out precision))
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                       if (custom){
+                               return FormatCustom (format, value, nfi);
+                       }
                        
                        switch(specifier) {
                        case 'c': return FormatCurrency (value, precision, nfi);
@@ -351,17 +442,23 @@ namespace System {
                        case 'x': return FormatHexadecimal (value, precision, false);
                        case 'X': return FormatHexadecimal (value, precision, true);
                        default: 
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        }
                }
 
+               [CLSCompliant (false)]
                public static string NumberToString (string format, NumberFormatInfo nfi, ulong value)
                {
                        char specifier;
                        int precision;
+                       bool custom;
+                       
+                       if (!ParseFormat (format, out specifier, out precision, out custom))
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        
-                       if (!ParseFormat (format, out specifier, out precision))
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                       if (custom){
+                               return FormatCustom (format, value, nfi);
+                       }
                        
                        switch(specifier) {
                        case 'c': return FormatCurrency (value, precision, nfi);
@@ -383,7 +480,7 @@ namespace System {
                        case 'x': return FormatHexadecimal (value, precision, false);
                        case 'X': return FormatHexadecimal (value, precision, true);
                        default: 
-                               throw new FormatException (Locale.GetText ("The specified format is invalid"));
+                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        }
                }
 
@@ -1854,15 +1951,15 @@ namespace System {
                // discarded.
                //
 
-               private static string FormatGeneral (byte value, int precision, NumberFormatInfo nfi, bool upper) {
+               internal static string FormatGeneral (byte value, int precision, NumberFormatInfo nfi, bool upper) {
                        return FormatGeneral ((uint)value, precision, nfi, upper);
                }
 
-               private static string FormatGeneral (short value, int precision, NumberFormatInfo nfi, bool upper) {
+               internal static string FormatGeneral (short value, int precision, NumberFormatInfo nfi, bool upper) {
                        return FormatGeneral ((int)value, precision, nfi, upper);
                }
 
-               private static string FormatGeneral (int value, int precision, NumberFormatInfo nfi, bool upper) 
+               internal static string FormatGeneral (int value, int precision, NumberFormatInfo nfi, bool upper) 
                {
                        bool negative = (value < 0);
                        char[] tmp = new char [maxIntLength];
@@ -1969,7 +2066,7 @@ namespace System {
                        return new string (buffy, 0, position);
                }
 
-               private static string FormatGeneral (long value, int precision, NumberFormatInfo nfi, bool upper) 
+               internal static string FormatGeneral (long value, int precision, NumberFormatInfo nfi, bool upper) 
                {
                        bool negative = (value < 0);
                        char[] tmp = new char [maxLongLength];
@@ -2075,15 +2172,15 @@ namespace System {
                        return new string (buffy, 0, position);
                }
 
-               private static string FormatGeneral (sbyte value, int precision, NumberFormatInfo nfi, bool upper) {
+               internal static string FormatGeneral (sbyte value, int precision, NumberFormatInfo nfi, bool upper) {
                        return FormatGeneral ((int)value, precision, nfi, upper);
                }
 
-               private static string FormatGeneral (ushort value, int precision, NumberFormatInfo nfi, bool upper) {
+               internal static string FormatGeneral (ushort value, int precision, NumberFormatInfo nfi, bool upper) {
                        return FormatGeneral ((uint)value, precision, nfi, upper);
                }
 
-               private static string FormatGeneral (uint value, int precision, NumberFormatInfo nfi, bool upper) 
+               internal static string FormatGeneral (uint value, int precision, NumberFormatInfo nfi, bool upper) 
                {
                        char[] tmp = new char [maxIntLength];
                        int exponent = 0;
@@ -2173,7 +2270,7 @@ namespace System {
                        return new string (buffy, 0, position);
                }
 
-               private static string FormatGeneral (ulong value, int precision, NumberFormatInfo nfi, bool upper) 
+               internal static string FormatGeneral (ulong value, int precision, NumberFormatInfo nfi, bool upper) 
                {
                        char[] tmp = new char [maxLongLength];
                        int exponent = 0;
@@ -2313,12 +2410,33 @@ namespace System {
                        int[] groupSizes = nfi.NumberGroupSizes;
 
                        int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
+                       int pattern = nfi.NumberNegativePattern;
                        int size = maxIntLength + (maxIntLength * groupSeparator.Length) + padding +
-                       decimalSeparator.Length + 2;
+                       decimalSeparator.Length + 4;
                        char[] buffy = new char[size];
                        int position = size;
                        bool negative = (value < 0);
                        
+                       // pattern for negative values, defined in NumberFormatInfo
+                       if (negative) {
+                               switch (pattern) {
+                               case 0: // (nnn)
+                                       buffy[--position] = ')'; 
+                                       break;
+                               // case 1: // -nnn
+                               //      break;
+                               // case 2: // - nnn
+                               //      break;
+                               case 3: // nnn-
+                                       buffy[--position] = '-'; 
+                                       break;
+                               case 4: // nnn -
+                                       buffy[--position] = '-'; 
+                                       buffy[--position] = ' '; 
+                                       break;
+                               }
+                       }
+
                        // right pad it w/ precision 0's
                        while (padding-- > 0)
                                buffy[--position] = '0';
@@ -2371,8 +2489,25 @@ namespace System {
 
                        buffy[--position] = digitLowerTable[value];
 
-                       if (negative)
-                               buffy[--position] = '-';
+                       // pattern for negative values, defined in NumberFormatInfo
+                       if (negative) {
+                               switch (pattern) {
+                               case 0: // (nnn)
+                                       buffy[--position] = '('; 
+                                       break;
+                               case 1: // -nnn
+                                       buffy[--position] = '-'; 
+                                       break;
+                               case 2: // - nnn
+                                       buffy[--position] = ' '; 
+                                       buffy[--position] = '-'; 
+                                       break;
+                               // case 3: // nnn-
+                               //      break;
+                               // case 4: // nnn -
+                               //      break;
+                               }
+                       }
                        
                        return new string (buffy, position, (size - position));
                }
@@ -2385,12 +2520,33 @@ namespace System {
                        int[] groupSizes = nfi.NumberGroupSizes;
 
                        int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
+                       int pattern = nfi.NumberNegativePattern;
                        int size = maxLongLength + (maxLongLength * groupSeparator.Length) + padding +
-                       decimalSeparator.Length + 2;
+                       decimalSeparator.Length + 4;
                        char[] buffy = new char[size];
                        int position = size;
                        bool negative = (value < 0);
                        
+                       // pattern for negative values, defined in NumberFormatInfo
+                       if (negative) {
+                               switch (pattern) {
+                               case 0: // (nnn)
+                                       buffy[--position] = ')'; 
+                                       break;
+                               // case 1: // -nnn
+                               //      break;
+                               // case 2: // - nnn
+                               //      break;
+                               case 3: // nnn-
+                                       buffy[--position] = '-'; 
+                                       break;
+                               case 4: // nnn -
+                                       buffy[--position] = '-'; 
+                                       buffy[--position] = ' '; 
+                                       break;
+                               }
+                       }
+
                        // right pad it w/ precision 0's
                        while (padding-- > 0)
                                buffy[--position] = '0';
@@ -2443,8 +2599,25 @@ namespace System {
 
                        buffy[--position] = digitLowerTable[value];
 
-                       if (negative)
-                               buffy[--position] = '-';
+                       // pattern for negative values, defined in NumberFormatInfo
+                       if (negative) {
+                               switch (pattern) {
+                               case 0: // (nnn)
+                                       buffy[--position] = '('; 
+                                       break;
+                               case 1: // -nnn
+                                       buffy[--position] = '-'; 
+                                       break;
+                               case 2: // - nnn
+                                       buffy[--position] = ' '; 
+                                       buffy[--position] = '-'; 
+                                       break;
+                               // case 3: // nnn-
+                               //      break;
+                               // case 4: // nnn -
+                               //      break;
+                               }
+                       }
                        
                        return new string (buffy, position, (size - position));
                }
@@ -3348,5 +3521,510 @@ namespace System {
                        return new string(buffy, position, (size - position));
                }
 
+               // ============ Format Custom ============ //
+
+               private static string FormatCustom (string format, sbyte number, NumberFormatInfo nfi)
+               {
+                       string strnum = FormatGeneral (number, -1, nfi, true);
+                       FormatParse fp = new FormatParse (format); // FIXME: use nfi!
+                       int sign = (number < 0) ? -1 : (number > 0) ? 1 : 0;
+                       return fp.FormatNumber (strnum, sign);
+               }
+
+               private static string FormatCustom (string format, short number, NumberFormatInfo nfi)
+               {
+                       string strnum = FormatGeneral (number, -1, nfi, true);
+                       FormatParse fp = new FormatParse (format); // FIXME: use nfi!
+                       int sign = (number < 0) ? -1 : (number > 0) ? 1 : 0;
+                       return fp.FormatNumber (strnum, sign);
+               }
+
+               private static string FormatCustom (string format, int number, NumberFormatInfo nfi)
+               {
+                       string strnum = FormatGeneral (number, -1, nfi, true);
+                       FormatParse fp = new FormatParse (format); // FIXME: use nfi!
+                       int sign = (number < 0) ? -1 : (number > 0) ? 1 : 0;
+                       return fp.FormatNumber (strnum, sign);
+               }
+
+               private static string FormatCustom (string format, long number, NumberFormatInfo nfi)
+               {
+                       string strnum = FormatGeneral (number, -1, nfi, true);
+                       FormatParse fp = new FormatParse (format); // FIXME: use nfi!
+                       int sign = (number < 0) ? -1 : (number > 0) ? 1 : 0;
+                       return fp.FormatNumber (strnum, sign);
+               }
+
+               private static string FormatCustom (string format, byte number, NumberFormatInfo nfi)
+               {
+                       string strnum = FormatGeneral (number, -1, nfi, true);
+                       FormatParse fp = new FormatParse (format); // FIXME: use nfi!
+                       return fp.FormatNumber (strnum, (number == 0) ? 0 : 1);
+               }
+
+               private static string FormatCustom (string format, ushort number, NumberFormatInfo nfi)
+               {
+                       string strnum = FormatGeneral (number, -1, nfi, true);
+                       FormatParse fp = new FormatParse (format); // FIXME: use nfi!
+                       return fp.FormatNumber (strnum, (number == 0) ? 0 : 1);
+               }
+
+               private static string FormatCustom (string format, uint number, NumberFormatInfo nfi)
+               {
+                       string strnum = FormatGeneral (number, -1, nfi, true);
+                       FormatParse fp = new FormatParse (format); // FIXME: use nfi!
+                       return fp.FormatNumber (strnum, (number == 0) ? 0 : 1);
+               }
+
+               private static string FormatCustom (string format, ulong number, NumberFormatInfo nfi)
+               {
+                       string strnum = FormatGeneral (number, -1, nfi, true);
+                       FormatParse fp = new FormatParse (format); // FIXME: use nfi!
+                       return fp.FormatNumber (strnum, (number == 0) ? 0 : 1);
+               }
+       }
+
+class FormatSection {
+       public int nph;
+       public int nphPreDot;
+       public int npercent;
+       public int ndividers;
+       public int ntokens;
+       public string [] tokens;
+       public int [] TokenTypes;
+       public bool HaveDot;
+       public bool HaveSci;
+       public bool sciSignAlways = false;
+       public int sciDigits;
+       public int numCommas;
+}
+
+class FormatParse {
+       const int AS_IS = 0;
+       const int PH_0 = 1;
+       const int PH_NUMBER = 2;
+       const int COMMA = 3;
+       const int PERCENT = 4;
+       const int DIVIDERS = 5;
+       const int DOT = 6;
+       const int ESCAPE_SEQ = 7;
+       const int SCIENTIFIC = 8;
+       const int NEW_SECTION = 9;
+       private FormatSection [] sections = new FormatSection[3];
+       private int nsections = 0;
+       private int pos; // Position in the format string
+       private int group = 0; // Used in FormatPlain to insert a comma between groups of digits
+       private bool isNegative;
+
+       private FormatParse ()
+       {
        }
+       
+       public FormatParse (string format)
+       {
+               parseFormat (format);
+       }
+
+       private void FormatSci (char [] digits, ArrayList outputList, FormatSection sec)
+       {
+               int tokidx = sec.ntokens - 1;
+
+               // Output everything until we get to the SCIENTIFIC
+               while (tokidx >= 0 && sec.TokenTypes [tokidx] != SCIENTIFIC){
+                       outputList.Add ((string) sec.tokens [tokidx--]);
+               }
+
+               // Exponent
+               int exponent = digits.Length - sec.nph;
+               outputList.Add ((string) exponent.ToString ());
+               if (sec.sciSignAlways && exponent > 0)
+                       outputList.Add ("+");
+               outputList.Add ((string) sec.tokens [tokidx--]);
+
+               if (exponent < 0) {
+                       char [] newDigits;
+                       exponent = -exponent;
+                       newDigits = new char [digits.Length + exponent];
+                       Array.Copy (digits, 0, newDigits, exponent, digits.Length);
+                       for (int i = 0; i < exponent; i++)
+                               newDigits[i] = '0';
+                       digits = newDigits;
+               }
+
+               // Now format the rest
+               int digitIdx = 0;
+               if (sec.HaveDot)
+                       FormatDot (digits, ref digitIdx, outputList, sec, tokidx, 0);
+               else
+                       FormatPlain (digits, ref digitIdx, outputList, sec, tokidx, 0, sec.numCommas > 0);
+       }
+
+       private void FormatDot (char [] digits, ref int digitIdx, ArrayList outputList, 
+                               FormatSection sec, int lastToken, int firstToken)
+       {
+               int tokidx = lastToken;
+               int type;
+
+               while (tokidx >= firstToken) {
+                       type = sec.TokenTypes [tokidx];
+                       if (type == DOT || type == PH_NUMBER || type == PH_0)
+                               break;
+                       tokidx--;
+               }
+
+               if (tokidx > 0) {
+                       char [] postDotDigits = new char [sec.nph - sec.nphPreDot];
+                       int max = (postDotDigits.Length > digits.Length) ? digits.Length : postDotDigits.Length;
+                       Array.Copy (digits, 0, postDotDigits, 0, max);
+                       int postDotDigitsIdx = 0;
+                       FormatPlain (postDotDigits, ref postDotDigitsIdx, outputList, sec, lastToken, tokidx, false);
+                       tokidx--;
+                       digitIdx += max;
+                       FormatPlain (digits, ref digitIdx, outputList, sec, tokidx, 0, sec.numCommas > 0);
+               }
+       }
+
+       private void FormatPlain (char [] digits, ref int digitIdx, ArrayList outputList, 
+                               FormatSection sec, int lastToken, int firstToken, bool insertComma)
+       {
+               int tokidx = lastToken;
+               int type;
+
+               while (tokidx >= firstToken) {
+                       type = sec.TokenTypes [tokidx];
+                       if (type == PH_0 || type == PH_NUMBER) {
+                               //FIXME: PH_NUMBER should also check for significant digits
+                               // Console.WriteLine ("group : {0}", group);
+                               int i = sec.tokens [tokidx].Length - 1;
+                               while (i >= 0) {
+                                       if (insertComma && group == 3) {
+                                               outputList.Add (","); // FIXME: from NumberFormatInfo
+                                               group = 0;
+                                       }
+
+                                       if (digitIdx < digits.Length)
+                                               outputList.Add ((string) digits[digitIdx++].ToString ());
+                                       else
+                                               outputList.Add ("0");
+                                       i--;
+                                       if (insertComma)
+                                               group++;
+                                       sec.nph--;
+                                       while (sec.nph == 0 && digitIdx < digits.Length) {
+                                               // Flush the numbers left
+                                               if (insertComma && group == 3){
+                                                       outputList.Add (","); // FIXME: from NumberFormatInfo
+                                                       group = 0;
+                                               }
+                                               outputList.Add ((string) digits [digitIdx++].ToString ());
+                                               if (insertComma)
+                                                       group++;
+                                       }
+
+                                       if (sec.nph == 0 && isNegative)
+                                               outputList.Add ("-");
+                               }
+                       } else {
+                               outputList.Add ((string) sec.tokens [tokidx]);
+                       }
+                       tokidx--;
+               }
+
+       }
+
+       private char [] AdjustDigits (string number, FormatSection sec)
+       {
+               char [] digits = number.ToCharArray ();
+               char [] newDigits = digits;
+               int decPointIdx = 0;
+               int postDot = 0;
+               
+               decPointIdx -= sec.ndividers * 3;
+               decPointIdx += sec.npercent * 2;
+               if (sec.HaveDot){
+                       postDot = sec.nph - sec.nphPreDot;
+                       decPointIdx += postDot;
+               }
+
+               if (decPointIdx > 0) {
+                       newDigits = new char [digits.Length + decPointIdx];
+                       Array.Copy (digits, 0, newDigits, 0, digits.Length);
+                       for (int i = 0; i < decPointIdx; i++)
+                               newDigits[digits.Length + i] = '0';
+               } else if (decPointIdx < 0) {
+                       decPointIdx = -decPointIdx;
+                       if (decPointIdx >= digits.Length) {
+                               if (sec.HaveSci){
+                               } else {
+                                       // The numbers turns into 0 when formatting applied
+                                       digits = new char [1] {'0'};
+                               }
+                       } else {
+                               int newLength = digits.Length - decPointIdx + postDot - 1;
+                               newDigits = new char [newLength];
+                               int max = digits.Length >= newLength ? newLength : digits.Length;
+                               Array.Copy (digits, 0, newDigits, 0, max);
+                               if (newLength > digits.Length)
+                                       for (int i = 0; i < decPointIdx; i++)
+                                               newDigits[digits.Length + i] = '0';
+                       }
+               }
+
+               return newDigits;
+       }
+
+       public string FormatNumber (string number, int signValue)
+       {
+               char [] digits;
+
+               isNegative = signValue < 0;
+               int section = 0;
+               if (signValue < 0 && nsections > 0)
+                       section = 1;
+               if (signValue == 0 && nsections > 1)
+                       section = 2;
+
+               if (number [0] == '-')
+                       number = number.Substring (1);
+
+               FormatSection sec = sections [section];
+               digits = AdjustDigits (number.ToString (), sec);
+               if (digits.Length == 1 && digits [0] == '0')
+                       if (nsections > 2)
+                               sec = sections [2]; // Format as a 0
+                       else
+                               sec = sections [0]; // Format as positive
+
+               ArrayList outputList = new ArrayList ();
+
+               int digitIdx = 0;
+               Array.Reverse (digits);
+
+               if (sec.HaveSci)
+                       FormatSci (digits, outputList, sec);
+               else if (sec.HaveDot)
+                       FormatDot (digits, ref digitIdx, outputList, sec, sec.ntokens - 1, 0);
+               else
+                       FormatPlain (digits, ref digitIdx, outputList, sec, sec.ntokens - 1, 0, sec.numCommas > 0);
+
+               string result = "";
+               for (int i = outputList.Count - 1; i >= 0; i--) {
+                       result += (string) outputList[i];
+               }
+
+               return result;
+       }
+
+       private void parseFormat (string format)
+       {
+               char [] fmt_chars = format.ToCharArray ();
+               int fmtlen = fmt_chars.Length;
+               int type = AS_IS;
+               int prevType = AS_IS;
+               string token;
+
+               sections[0] = new FormatSection();
+               while (pos < fmtlen) {
+
+                       token = getNextToken (fmt_chars, fmtlen, out type);
+                       if (type == NEW_SECTION) {
+                               nsections++;
+                               if (nsections > 3)
+                                       break;
+                               sections[nsections] = new FormatSection();
+                       } else {
+                               prevType = AddToken (token, type, prevType);
+                       }                       
+               }
+       }
+
+       private int AddToken (string token, int type, int prevType)
+       {
+               FormatSection sec = sections[nsections];
+               string [] newTokens = new string [sec.ntokens + 1];
+               int [] newTokenTypes = new int [sec.ntokens + 1];
+               for (int i = 0; i < sec.ntokens; i++) {
+                       newTokens[i] = sec.tokens[i];
+                       newTokenTypes[i] = sec.TokenTypes[i];
+               }
+
+               switch (type) {
+               case ESCAPE_SEQ :
+                       type = AS_IS;
+                       break;
+               case COMMA :
+                       if (!sec.HaveDot && (prevType == PH_0 || prevType == PH_NUMBER)) {
+                               sec.numCommas++;
+                       } else
+                               type = AS_IS;
+
+                       token = "";
+                       break;
+               case DOT :
+                       if (!sec.HaveDot && (prevType == PH_0 || prevType == PH_NUMBER ||
+                           prevType == DIVIDERS || prevType == COMMA)) {
+                               sec.HaveDot = true;
+                               sec.nphPreDot = sec.nph;
+                       } else
+                               type = AS_IS;
+
+                       break;
+               case PERCENT :
+                       sec.npercent++;
+                       break;
+               case DIVIDERS :
+                       token = "";
+                       if (!sec.HaveDot)
+                               sec.ndividers = token.Length;
+                       else
+                               type = AS_IS;
+                       break;
+               case PH_0 :
+                       if (!sec.HaveSci)
+                               sec.nph += token.Length;
+                       else
+                               type = AS_IS;
+                       break;
+               case PH_NUMBER :
+                       if (!sec.HaveSci)
+                               sec.nph += token.Length;
+                       else
+                               type = AS_IS;
+                       break;
+               case SCIENTIFIC :
+                       if (!sec.HaveSci && sec.nph > 0) {
+                               sec.HaveSci = true;
+                               char [] sci = token.ToCharArray ();
+                               sec.sciSignAlways = sci[1] == '+' ? true : false;
+                               int expLen = sci[1] == '0' ? token.Length - 1 : token.Length - 2;
+                               sec.sciDigits = expLen;
+                               token = sci[0].ToString ();
+                       } else {
+                               type = AS_IS;
+                       }
+                       break;
+               }
+
+               newTokens[sec.ntokens] = token;
+               newTokenTypes[sec.ntokens] = type;
+               sec.tokens = newTokens;
+               sec.TokenTypes = newTokenTypes;
+               sec.ntokens++;
+               return type;
+       }
+       
+       private string getNextToken (char [] fmt_chars, int fmtlen, out int type)
+       {
+               int curpos = pos;
+               string result = null;
+               char current;
+               
+               type = AS_IS; // Default
+               current = fmt_chars[curpos];
+               if (current == ';'){
+                       type = NEW_SECTION;
+                       result = "NEW_SECTION";
+                       pos++;
+               }
+               else if (current == '\'' || current == '"') {
+                       char Quote = current;
+                       curpos++;
+                       int endpos = Array.IndexOf (fmt_chars, current, curpos);
+                       if (endpos == -1)
+                               endpos = fmtlen;
+                       result = new string (fmt_chars, curpos, endpos - curpos);
+                       pos = endpos + 1;
+               } 
+               else if (current == '\\') { //MS seems not to translate escape seqs!
+                       type = ESCAPE_SEQ;
+                       current = fmt_chars[++pos];
+                       result = current.ToString ();
+                       pos++;
+               }
+               else if (current == '%') {
+                       type = PERCENT;
+                       result = "%";
+                       pos++;
+               }
+               else if (current == '.') {
+                       type = DOT;
+                       result = ".";
+                       pos++;
+               }
+               else if (current == ',') {
+                       int begpos = curpos;
+
+                       while (++curpos < fmtlen && fmt_chars[curpos] == ',');
+                       if (curpos == fmtlen || fmt_chars[curpos] == '.') {
+                               // ,,,,
+                               result = new string (fmt_chars, begpos, curpos - begpos);
+                               type = DIVIDERS;
+                               pos = curpos;
+                       } else {
+                               result = ",";
+                               type = COMMA;
+                               pos++;
+                       }
+               }
+               else if (current == '0' || current == '#') {
+                       char placeHolder = current;
+                       int begpos = curpos;
+                       type = placeHolder == '0' ? PH_0 : PH_NUMBER;
+                       curpos++;
+                       while (curpos < fmtlen && fmt_chars [curpos] == placeHolder)
+                               curpos++;
+                       result = new string (fmt_chars, begpos, curpos - begpos);
+                       pos = curpos;
+               }
+               else if (current == 'e' || current == 'E') {
+                       if (fmtlen <= curpos + 1){
+                               result = current.ToString ();
+                               pos++;
+                       }
+                       else {
+                               char next1 = fmt_chars [curpos + 1];
+
+                               if (next1 != '-' && next1 != '+' && next1 != '0') {
+                                       result = new string (fmt_chars, curpos, 2);
+                                       pos += 2;
+                               }
+                               else {
+                                       int begpos = curpos;
+
+                                       if (next1 == '-' || next1 == '+')
+                                               curpos++;
+
+                                       curpos++;
+
+                                       if (curpos < fmtlen && fmt_chars [curpos] == '0'){
+                                               type = SCIENTIFIC;
+                                               while (curpos < fmtlen && fmt_chars [curpos] == '0')
+                                                       curpos++;
+                                       }
+
+                                       result = new string (fmt_chars, begpos, curpos - begpos);
+                                       pos = curpos;
+                               }
+                       }
+               }
+               else {
+                       char [] format_spec = { '0', '#', ',', '.', '%', 'E', 'e', '"', '\'', '\\' };
+                       int nextFE;
+
+                       while (curpos < fmtlen) {
+                               current = fmt_chars[curpos];
+                               nextFE = Array.IndexOf (format_spec, current);
+                               if (nextFE != -1)
+                                       break;
+                               curpos++;
+                       }
+
+                       result = new string (fmt_chars, pos, curpos - pos);
+                       pos = curpos;
+               }
+
+               return result;
+       }
+}
+
 }