2 // System.DecimalFormatter.cs
5 // Martin Weindel (martin.weindel@t-online.de)
7 // (C) Martin Weindel, Derek Holden dholden@draper.com
11 // Internal class for formatting decimal numbers.
13 using System.Globalization;
15 using S = System; // only used for switching test implementation
\r
20 internal sealed class DecimalFormatter
\r
23 private static bool ParseFormat (string format, out char specifier, out int precision)
28 int length = format.Length;
29 if (length < 1 || length > 3)
32 char[] chars = format.ToCharArray ();
33 specifier = Char.ToUpper(chars[0]);
40 if (chars[1] < '0' || chars[1] > '9')
43 precision = chars[1] - '0';
47 if (chars[1] < '0' || chars[2] < '0' || chars[1] > '9' || chars[2] > '9')
50 precision = (chars[1] - '0') * 10 + (chars[2] - '0');
56 public static string NumberToString(string format, NumberFormatInfo nfi, S.Decimal value)
\r
60 if (!DecimalFormatter.ParseFormat(format, out specifier, out precision))
\r
62 throw new FormatException (Locale.GetText ("The specified format is invalid"));
\r
67 // first calculate number of digits or decimals needed for format
\r
71 decimals = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
\r
73 case 'F': goto case 'N';
\r
75 decimals = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
\r
78 digits = (precision >= 0) ? precision : 0;
\r
81 digits = (precision >= 0) ? precision+1 : 7;
\r
84 decimals = (precision >= 0) ? precision+2 : nfi.PercentDecimalDigits+2;
\r
92 const int bufSize = 40;
\r
93 int decPos = 0, sign = 0;
\r
95 char[] buf = new char[bufSize];
\r
96 if (S.Decimal.decimal2string(ref value, digits, decimals, buf, bufSize, out decPos, out sign) != 0)
\r
98 throw new FormatException(); // should never happen
\r
101 StringBuilder sb = new StringBuilder(new String(buf), bufSize);
\r
103 StringBuilder sb = new StringBuilder(bufSize);
\r
104 if (S.Decimal.decimal2string(ref value, digits, decimals, sb, bufSize, out decPos, out sign) != 0)
\r
106 throw new FormatException(); // should never happen
\r
110 // now build the format
\r
113 case 'C': return FormatCurrency(nfi, sb, decimals, decPos, sign);
\r
114 case 'N': return FormatNumber(nfi, sb, decimals, decPos, sign);
\r
115 case 'F': return FormatFixedPoint(nfi, sb, decimals, decPos, sign);
\r
116 case 'G': return FormatGeneral(nfi, sb, digits, decPos, sign, format[0]);
\r
117 case 'E': return FormatExponential(nfi, sb, digits, decPos, sign, format[0], true);
\r
118 case 'P': return FormatPercent(nfi, sb, decimals, decPos, sign);
\r
119 case 'Z': return FormatNormalized(nfi, sb, digits, decPos, sign);
\r
121 throw new FormatException (Locale.GetText ("The specified format is invalid"));
\r
125 private static string FormatFixedPoint(NumberFormatInfo nfi, StringBuilder sb,
126 int decimals, int decPos, int sign)
130 sb.Insert((decPos <= 0) ? 1 : decPos, nfi.NumberDecimalSeparator);
135 sb.Insert(0, nfi.NegativeSign);
138 return sb.ToString();
141 private static string FormatExponential(NumberFormatInfo nfi, StringBuilder sb,
142 int digits, int decPos, int sign, char echar, bool exp3flag)
144 // insert decimal separator
145 if (digits > 1 || (digits == 0 && sb.Length > 1))
\r
147 sb.Insert(1, nfi.NumberDecimalSeparator);
153 sb.Insert(0, nfi.NegativeSign);
159 sb.Append((decPos >= 0) ? nfi.PositiveSign : nfi.NegativeSign);
160 if (decPos < 0) decPos *= -1;
161 if (exp3flag) sb.Append('0');
162 sb.Append((char)('0' + decPos/10));
163 sb.Append((char)('0' + decPos%10));
165 return sb.ToString();
168 private static string FormatGeneral(NumberFormatInfo nfi, StringBuilder sb,
169 int digits, int decPos, int sign, char gchar)
172 bool bFix = (digits == 0 && decPos >= -3) || (digits >= decPos && decPos >= -3 && digits != 0);
174 // remove trailing digits
175 while (sb.Length > 1 && (sb.Length > decPos || !bFix) && sb[sb.Length-1] == '0')
177 sb.Remove(sb.Length-1, 1);
183 while (decPos <= 0)
\r
186 if (dig != 0 && decPos != 0) dig++;
189 return FormatFixedPoint(nfi, sb, sb.Length - decPos, decPos, sign);
193 return FormatExponential(nfi, sb, dig, decPos, sign, (char)(gchar-2), false);
197 private static string FormatGroupAndDec(StringBuilder sb, int decimals, int decPos,
198 int[] groupSizes, string groupSeparator, string decSeparator)
205 if (groupSizes != null)
\r
209 for (int i = 0; i < groupSizes.GetLength(0); i++)
\r
211 int size = groupSizes[i];
215 if (digitCount < decPos)
\r
217 sb.Insert(decPos - digitCount, groupSeparator);
218 offset += groupSeparator.Length;
228 digitCount +=lastSize;
229 if (digitCount >= decPos) break;
230 sb.Insert(decPos - digitCount, groupSeparator);
231 offset += groupSeparator.Length;
239 sb.Insert(offset + ((decPos <= 0) ? 1 : decPos), decSeparator);
242 return sb.ToString();
245 private static string FormatNumber(NumberFormatInfo nfi, StringBuilder sb,
246 int decimals, int decPos, int sign)
248 string s = FormatGroupAndDec(sb, decimals, decPos,
249 nfi.NumberGroupSizes, nfi.NumberGroupSeparator, nfi.NumberDecimalSeparator);
254 switch (nfi.NumberNegativePattern)
\r
257 return "(" + s + ")";
259 return nfi.NegativeSign + s;
261 return nfi.NegativeSign + " " + s;
263 return s + nfi.NegativeSign;
265 return s + " " + nfi.NegativeSign;
267 throw new ArgumentException(Locale.GetText ("Invalid NumberNegativePattern"));
276 private static string FormatCurrency(NumberFormatInfo nfi, StringBuilder sb,
277 int decimals, int decPos, int sign)
279 string s = FormatGroupAndDec(sb, decimals, decPos,
280 nfi.CurrencyGroupSizes, nfi.CurrencyGroupSeparator, nfi.CurrencyDecimalSeparator);
284 switch (nfi.CurrencyNegativePattern)
\r
287 return "(" + nfi.CurrencySymbol + s + ")";
289 return nfi.NegativeSign + nfi.CurrencySymbol + s;
291 return nfi.CurrencySymbol + nfi.NegativeSign + s;
293 return nfi.CurrencySymbol + s + nfi.NegativeSign;
295 return "(" + s + nfi.CurrencySymbol + ")";
297 return nfi.NegativeSign + s + nfi.CurrencySymbol;
299 return s + nfi.NegativeSign + nfi.CurrencySymbol;
301 return s + nfi.CurrencySymbol + nfi.NegativeSign;
303 return nfi.NegativeSign + s + " " + nfi.CurrencySymbol;
305 return nfi.NegativeSign + nfi.CurrencySymbol + " " + s;
307 return s + " " + nfi.CurrencySymbol + nfi.NegativeSign;
309 return nfi.CurrencySymbol + " " + s + nfi.NegativeSign;
311 return nfi.CurrencySymbol + " " + nfi.NegativeSign + s;
313 return s + nfi.NegativeSign + " " + nfi.CurrencySymbol;
315 return "(" + nfi.CurrencySymbol + " " + s + ")";
317 return "(" + s + " " + nfi.CurrencySymbol + ")";
319 throw new ArgumentException(Locale.GetText ("Invalid CurrencyNegativePattern"));
324 switch (nfi.CurrencyPositivePattern)
\r
327 return nfi.CurrencySymbol + s;
329 return s + nfi.CurrencySymbol;
331 return nfi.CurrencySymbol + " " + s;
333 return s + " " + nfi.CurrencySymbol;
335 throw new ArgumentException(Locale.GetText ("Invalid CurrencyPositivePattern"));
340 private static string FormatPercent(NumberFormatInfo nfi, StringBuilder sb,
341 int decimals, int decPos, int sign)
343 string s = FormatGroupAndDec(sb, decimals, decPos+2,
344 nfi.PercentGroupSizes, nfi.PercentGroupSeparator, nfi.PercentDecimalSeparator);
348 switch (nfi.PercentNegativePattern)
\r
351 return nfi.NegativeSign + s + " " + nfi.PercentSymbol;
353 return nfi.NegativeSign + s + nfi.PercentSymbol;
355 return nfi.NegativeSign + nfi.PercentSymbol + s;
357 throw new ArgumentException(Locale.GetText ("Invalid PercentNegativePattern"));
362 switch (nfi.PercentPositivePattern)
\r
365 return s + " " + nfi.PercentSymbol;
367 return s + nfi.PercentSymbol;
369 return nfi.PercentSymbol + s;
371 throw new ArgumentException("Invalid PercentPositivePattern");
376 private static string FormatNormalized(NumberFormatInfo nfi, StringBuilder sb,
377 int digits, int decPos, int sign)
379 //LAMESPEC: how should this format look like ? Is this a fixed point format ?
380 throw new NotImplementedException ();