1 /* $Header: //info.ravenbrook.com/project/jili/version/1.1/code/mnj/lua/StringLib.java#1 $
2 * Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject
11 * to the following conditions:
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
20 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
21 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 // Modified 2009-12-26 by Ilari Liusvaara
26 // Split -> StringLib -> StringLib, MatchState and FormatItem as J2SE
27 // doesn't like multiple classes in the same file.
31 final class FormatItem
34 private boolean left
; // '-' flag
35 private boolean sign
; // '+' flag
36 private boolean space
; // ' ' flag
37 private boolean alt
; // '#' flag
38 private boolean zero
; // '0' flag
39 private int width
; // minimum field width
40 private int precision
= -1; // precision, -1 when no precision specified.
41 private char type
; // the type of the conversion
42 private int length
; // length of the format item in the format string.
45 * Character used in formatted output when %e or %g format is used.
47 static char E_LOWER
= 'E';
49 * Character used in formatted output when %E or %G format is used.
51 static char E_UPPER
= 'E';
54 * Parse a format item (starting from after the <code>L_ESC</code>).
55 * If you promise that there won't be any format errors, then
56 * <var>L</var> can be <code>null</code>.
58 FormatItem(Lua L
, String s
)
68 L
.error("invalid format");
92 int widths
= i
; // index of start of width specifier
96 L
.error("invalid format");
97 if (Syntax
.isdigit(s
.charAt(i
)))
106 width
= Integer
.parseInt(s
.substring(widths
, i
));
108 catch (NumberFormatException e_
)
113 if (s
.charAt(i
) == '.')
116 int precisions
= i
; // index of start of precision specifier
120 L
.error("invalid format");
121 if (Syntax
.isdigit(s
.charAt(i
)))
130 precision
= Integer
.parseInt(s
.substring(precisions
, i
));
132 catch (NumberFormatException e_
)
141 case 'o': case 'u': case 'x': case 'X':
142 case 'e': case 'E': case 'f': case 'g': case 'G':
149 L
.error("invalid option to 'format'");
163 * Format the converted string according to width, and left.
164 * zero padding is handled in either {@link FormatItem#formatInteger}
165 * or {@link FormatItem#formatFloat}
166 * (and width is fixed to 0 in such cases). Therefore we can ignore
169 private void format(StringBuffer b
, String s
)
177 StringBuffer pad
= new StringBuffer();
195 // All the format* methods take a StringBuffer and append the
196 // formatted representation of the value to it.
197 // Sadly after a format* method has been invoked the object is left in
198 // an unusable state and should not be used again.
200 void formatChar(StringBuffer b
, char c
)
202 String s
= String
.valueOf(c
);
206 void formatInteger(StringBuffer b
, long i
)
208 // :todo: improve inefficient use of implicit StringBuffer
221 case 'd': case 'i': case 'u':
228 L
.error("invalid format");
230 String s
= Long
.toString(i
, radix
);
233 if (precision
== 0 && s
.equals("0"))
236 // form a prefix by strippping possible leading '-',
240 // extra wart: padding with '0' is implemented using precision
241 // because this makes handling the prefix easier.
243 if (s
.startsWith("-"))
248 if (alt
&& radix
== 16)
257 if (alt
&& radix
== 8 && !s
.startsWith("0"))
262 precision
= width
- prefix
.length();
267 StringBuffer p
= new StringBuffer();
268 while (l
< precision
)
280 void formatFloat(StringBuffer b
, double d
)
296 private void formatFloatE(StringBuffer b
, double d
)
298 String s
= formatFloatRawE(d
);
303 * Returns the formatted string for the number without any padding
304 * (which can be added by invoking {@link FormatItem#format} later).
306 private String
formatFloatRawE(double d
)
308 double m
= Math
.abs(d
);
310 if (m
>= 1e-3 && m
< 1e7
)
316 String s
= Double
.toString(d
);
317 StringBuffer t
= new StringBuffer(s
);
318 int e
; // Exponent value
325 int ei
= s
.indexOf('E');
326 e
= Integer
.parseInt(s
.substring(ei
+1));
327 t
.delete(ei
, Integer
.MAX_VALUE
);
333 if (Character
.isLowerCase(type
))
345 t
.append(Integer
.toString(e
));
351 private void formatFloatF(StringBuffer b
, double d
)
353 String s
= formatFloatRawF(d
);
358 * Returns the formatted string for the number without any padding
359 * (which can be added by invoking {@link FormatItem#format} later).
361 private String
formatFloatRawF(double d
)
363 String s
= Double
.toString(d
);
364 StringBuffer t
= new StringBuffer(s
);
366 int di
= s
.indexOf('.');
367 int ei
= s
.indexOf('E');
370 t
.delete(ei
, Integer
.MAX_VALUE
);
371 int e
= Integer
.parseInt(s
.substring(ei
+1));
373 StringBuffer z
= new StringBuffer();
374 for (int i
=0; i
<Math
.abs(e
); ++i
)
388 int at
= t
.charAt(0) == '-' ?
1 : 0;
400 private void formatFloatG(StringBuffer b
, double d
)
411 // Decide whether to use %e or %f style.
412 double m
= Math
.abs(d
);
415 // :todo: Could test for -0 and use "-0" appropriately.
418 else if (m
< 1e-4 || m
>= Lua
.iNumpow(10, precision
))
422 s
= formatFloatRawE(d
);
423 int di
= s
.indexOf('.');
426 // Trim trailing zeroes from fractional part
427 int ei
= s
.indexOf('E');
433 while (s
.charAt(i
) == '0')
437 if (s
.charAt(i
) != '.')
441 StringBuffer a
= new StringBuffer(s
);
449 // For %g precision specifies the number of significant digits,
450 // for %f precision specifies the number of fractional digits.
451 // There is a problem because it's not obvious how many fractional
452 // digits to format, it could be more than precision
453 // (when .0001 <= m < 1) or it could be less than precision
455 // Instead of trying to work out the correct precision to use for
456 // %f formatting we use a worse case to get at least all the
457 // necessary digits, then we trim using string editing. The worst
458 // case is that 3 zeroes come after the decimal point before there
459 // are any significant digits.
460 // Save the required number of significant digits
461 int required
= precision
;
463 s
= formatFloatRawF(d
);
464 int fsd
= 0; // First Significant Digit
465 while (s
.charAt(fsd
) == '0' || s
.charAt(fsd
) == '.')
469 // Note that all the digits to the left of the decimal point in
470 // the formatted number are required digits (either significant
471 // when m >= 1 or 0 when m < 1). We know this because otherwise
472 // m >= (10**precision) and so formatting falls under the %e case.
473 // That means that we can always trim the string at fsd+required
474 // (this will remove the decimal point when m >=
475 // (10**(precision-1)).
476 StringBuffer a
= new StringBuffer(s
);
477 a
.delete(fsd
+required
, Integer
.MAX_VALUE
);
478 if (s
.indexOf('.') < a
.length())
480 // Trim trailing zeroes
481 int i
= a
.length() - 1;
482 while (a
.charAt(i
) == '0')
487 if (a
.charAt(i
) == '.')
497 void formatString(StringBuffer b
, String s
)
501 if (precision
>= 0 && precision
< s
.length())
503 p
= s
.substring(0, precision
);
508 private void precisionTrim(StringBuffer t
)
515 String s
= t
.toString();
516 int di
= s
.indexOf('.');
520 t
.delete(di
, Integer
.MAX_VALUE
);
522 else if (l
> di
+precision
)
524 t
.delete(di
+precision
+1, Integer
.MAX_VALUE
);
528 for(; l
<= di
+precision
; ++l
)
535 private void zeroPad(StringBuffer t
)
537 if (zero
&& t
.length() < width
)
539 int at
= t
.charAt(0) == '-' ?
1 : 0;
540 while (t
.length() < width
)