2 // System.DecimalFormatter.cs
5 // Martin Weindel (martin.weindel@t-online.de)
7 // (C) Martin Weindel, Derek Holden dholden@draper.com
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 // Internal class for formatting decimal numbers.
36 using System
.Globalization
;
43 internal sealed class DecimalFormatter
46 private static bool ParseFormat (string format
, out char specifier
, out int precision
)
51 int length
= format
.Length
;
52 if (length
< 1 || length
> 3)
55 char[] chars
= format
.ToCharArray ();
56 specifier
= Char
.ToUpperInvariant(chars
[0]);
63 if (chars
[1] < '0' || chars
[1] > '9')
66 precision
= chars
[1] - '0';
70 if (chars
[1] < '0' || chars
[2] < '0' || chars
[1] > '9' || chars
[2] > '9')
73 precision
= (chars
[1] - '0') * 10 + (chars
[2] - '0');
79 public static string NumberToString(string format
, NumberFormatInfo nfi
, Decimal
value)
83 format
= format
.Trim ();
84 if (!DecimalFormatter
.ParseFormat(format
, out specifier
, out precision
))
86 throw new FormatException (Locale
.GetText ("The specified format is invalid"));
91 // first calculate number of digits or decimals needed for format
95 decimals
= (precision
>= 0) ? precision
: nfi
.CurrencyDecimalDigits
;
97 case 'F': goto case 'N';
99 decimals
= (precision
>= 0) ? precision
: nfi
.NumberDecimalDigits
;
102 digits
= (precision
>= 0) ? precision
: 0;
105 digits
= (precision
>= 0) ? precision
+1 : 7;
108 decimals
= (precision
>= 0) ? precision
+2 : nfi
.PercentDecimalDigits
+2;
116 const int bufSize
= 40;
117 int decPos
= 0, sign
= 0;
118 char[] buf
= new char[bufSize
];
119 if (Decimal
.decimal2string(ref value, digits
, decimals
, buf
, bufSize
, out decPos
, out sign
) != 0)
121 throw new FormatException(); // should never happen
124 string TempString
= new String(buf
);
125 TempString
= TempString
.Trim(new char[] {(char)0x0}
);
126 StringBuilder sb
= new StringBuilder(TempString
, TempString
.Length
);
128 if (sb
.ToString () == String
.Empty
&& decPos
> 0 && sign
== 0)
131 // now build the format
134 case 'C': return FormatCurrency(nfi
, sb
, decimals
, decPos
, sign
);
135 case 'N': return FormatNumber(nfi
, sb
, decimals
, decPos
, sign
);
136 case 'F': return FormatFixedPoint(nfi
, sb
, decimals
, decPos
, sign
);
137 case 'G': return FormatGeneral(nfi
, sb
, digits
, decPos
, sign
, format
[0]);
138 case 'E': return FormatExponential(nfi
, sb
, digits
, decPos
, sign
, format
[0], true);
139 case 'P': return FormatPercent(nfi
, sb
, decimals
, decPos
, sign
);
140 case 'Z': return FormatNormalized(nfi
, sb
, digits
, decPos
, sign
);
142 throw new FormatException (Locale
.GetText ("The specified format is invalid"));
146 private static string FormatFixedPoint(NumberFormatInfo nfi
, StringBuilder sb
,
147 int decimals
, int decPos
, int sign
)
151 sb
.Insert((decPos
<= 0) ? 1 : decPos
, nfi
.NumberDecimalSeparator
);
156 sb
.Insert(0, nfi
.NegativeSign
);
159 return sb
.ToString();
162 private static string FormatExponential(NumberFormatInfo nfi
, StringBuilder sb
,
163 int digits
, int decPos
, int sign
, char echar
, bool exp3flag
)
165 // insert decimal separator
166 if (digits
> 1 || (digits
== 0 && sb
.Length
> 1))
168 sb
.Insert(1, nfi
.NumberDecimalSeparator
);
174 sb
.Insert(0, nfi
.NegativeSign
);
180 sb
.Append((decPos
>= 0) ? nfi
.PositiveSign
: nfi
.NegativeSign
);
181 if (decPos
< 0) decPos
*= -1;
182 if (exp3flag
) sb
.Append('0');
183 sb
.Append((char)('0' + decPos
/10));
184 sb
.Append((char)('0' + decPos
%10));
186 return sb
.ToString();
189 private static string FormatGeneral(NumberFormatInfo nfi
, StringBuilder sb
,
190 int digits
, int decPos
, int sign
, char gchar
)
194 bool bFix
= (digits
== 0 && decPos
>= -3) || (digits
>= decPos
&& decPos
>= -3 && digits
!= 0);
196 bool bFix
= ((digits
== 0) || ((digits
>= decPos
) && (digits
!= 0)));
199 // remove trailing digits
200 while (sb
.Length
> 1 && (sb
.Length
> decPos
|| !bFix
) && sb
[sb
.Length
-1] == '0') {
201 sb
.Remove (sb
.Length
-1, 1);
208 while (decPos
<= 0) {
210 if (dig
!= 0 && decPos
!= 0)
214 return FormatFixedPoint(nfi
, sb
, sb
.Length
- decPos
, decPos
, sign
);
217 return FormatExponential(nfi
, sb
, dig
, decPos
, sign
, (char)(gchar
-2), false);
221 private static string FormatGroupAndDec(StringBuilder sb
, int decimals
, int decPos
,
222 int[] groupSizes
, string groupSeparator
, string decSeparator
)
229 if (groupSizes
!= null)
233 for (int i
= 0; i
< groupSizes
.GetLength(0); i
++)
235 int size
= groupSizes
[i
];
239 if (digitCount
< decPos
)
241 sb
.Insert(decPos
- digitCount
, groupSeparator
);
242 offset
+= groupSeparator
.Length
;
252 digitCount
+=lastSize
;
253 if (digitCount
>= decPos
) break;
254 sb
.Insert(decPos
- digitCount
, groupSeparator
);
255 offset
+= groupSeparator
.Length
;
261 if ((decimals
> 0) && (decPos
+offset
< sb
.Length
))
263 sb
.Insert(offset
+ ((decPos
<= 0) ? 1 : decPos
), decSeparator
);
266 return sb
.ToString();
269 private static string FormatNumber(NumberFormatInfo nfi
, StringBuilder sb
,
270 int decimals
, int decPos
, int sign
)
272 string s
= FormatGroupAndDec(sb
, decimals
, decPos
,
273 nfi
.NumberGroupSizes
, nfi
.NumberGroupSeparator
, nfi
.NumberDecimalSeparator
);
278 switch (nfi
.NumberNegativePattern
)
281 return "(" + s
+ ")";
283 return nfi
.NegativeSign
+ s
;
285 return nfi
.NegativeSign
+ " " + s
;
287 return s
+ nfi
.NegativeSign
;
289 return s
+ " " + nfi
.NegativeSign
;
291 throw new ArgumentException(Locale
.GetText ("Invalid NumberNegativePattern"));
300 private static string FormatCurrency(NumberFormatInfo nfi
, StringBuilder sb
,
301 int decimals
, int decPos
, int sign
)
303 string s
= FormatGroupAndDec(sb
, decimals
, decPos
,
304 nfi
.CurrencyGroupSizes
, nfi
.CurrencyGroupSeparator
, nfi
.CurrencyDecimalSeparator
);
308 switch (nfi
.CurrencyNegativePattern
)
311 return "(" + nfi
.CurrencySymbol
+ s
+ ")";
313 return nfi
.NegativeSign
+ nfi
.CurrencySymbol
+ s
;
315 return nfi
.CurrencySymbol
+ nfi
.NegativeSign
+ s
;
317 return nfi
.CurrencySymbol
+ s
+ nfi
.NegativeSign
;
319 return "(" + s
+ nfi
.CurrencySymbol
+ ")";
321 return nfi
.NegativeSign
+ s
+ nfi
.CurrencySymbol
;
323 return s
+ nfi
.NegativeSign
+ nfi
.CurrencySymbol
;
325 return s
+ nfi
.CurrencySymbol
+ nfi
.NegativeSign
;
327 return nfi
.NegativeSign
+ s
+ " " + nfi
.CurrencySymbol
;
329 return nfi
.NegativeSign
+ nfi
.CurrencySymbol
+ " " + s
;
331 return s
+ " " + nfi
.CurrencySymbol
+ nfi
.NegativeSign
;
333 return nfi
.CurrencySymbol
+ " " + s
+ nfi
.NegativeSign
;
335 return nfi
.CurrencySymbol
+ " " + nfi
.NegativeSign
+ s
;
337 return s
+ nfi
.NegativeSign
+ " " + nfi
.CurrencySymbol
;
339 return "(" + nfi
.CurrencySymbol
+ " " + s
+ ")";
341 return "(" + s
+ " " + nfi
.CurrencySymbol
+ ")";
343 throw new ArgumentException(Locale
.GetText ("Invalid CurrencyNegativePattern"));
348 switch (nfi
.CurrencyPositivePattern
)
351 return nfi
.CurrencySymbol
+ s
;
353 return s
+ nfi
.CurrencySymbol
;
355 return nfi
.CurrencySymbol
+ " " + s
;
357 return s
+ " " + nfi
.CurrencySymbol
;
359 throw new ArgumentException(Locale
.GetText ("Invalid CurrencyPositivePattern"));
364 private static string FormatPercent(NumberFormatInfo nfi
, StringBuilder sb
,
365 int decimals
, int decPos
, int sign
)
367 string s
= FormatGroupAndDec(sb
, decimals
, decPos
+2,
368 nfi
.PercentGroupSizes
, nfi
.PercentGroupSeparator
, nfi
.PercentDecimalSeparator
);
372 switch (nfi
.PercentNegativePattern
)
375 return nfi
.NegativeSign
+ s
+ " " + nfi
.PercentSymbol
;
377 return nfi
.NegativeSign
+ s
+ nfi
.PercentSymbol
;
379 return nfi
.NegativeSign
+ nfi
.PercentSymbol
+ s
;
381 throw new ArgumentException(Locale
.GetText ("Invalid PercentNegativePattern"));
386 switch (nfi
.PercentPositivePattern
)
389 return s
+ " " + nfi
.PercentSymbol
;
391 return s
+ nfi
.PercentSymbol
;
393 return nfi
.PercentSymbol
+ s
;
395 throw new ArgumentException("Invalid PercentPositivePattern");
401 private static string FormatNormalized(NumberFormatInfo nfi
, StringBuilder sb
,
402 int digits
, int decPos
, int sign
)
404 //LAMESPEC: how should this format look like ? Is this a fixed point format ?
405 throw new NotImplementedException ();