KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jodd > format > PrintfFormat


1 // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
2

3 package jodd.format;
4
5 import java.math.BigInteger JavaDoc;
6
7 /**
8  * Fast simple and yet useful formattings.
9  */

10 public class PrintfFormat {
11     
12     protected int width;
13     protected int precision;
14     protected StringBuffer JavaDoc pre;
15     protected StringBuffer JavaDoc post;
16     protected boolean leadingZeroes;
17     protected boolean showPlus;
18     protected boolean alternate;
19     protected boolean showSpace;
20     protected boolean leftAlign;
21     protected boolean groupDigits;
22     protected char fmt; // one of cdeEfgGiosxXos
23
protected boolean countSignInLen;
24     private final static BigInteger JavaDoc bgInt = new BigInteger JavaDoc("9223372036854775808"); // 2^63
25

26     /**
27      * Formats a number in a printf format, like C.
28      *
29      * @param s the format string following printf format string
30      * The string has a prefix, a format code and a suffix. The prefix and suffix
31      * become part of the formatted output. The format code directs the
32      * formatting of the (single) parameter to be formatted. The code has the
33      * following structure
34      * <ul>
35      * <li> a <b>%</b> (required)
36      *
37      * <li> a modifier (optional)
38      * <dl>
39      * <dt> + <dd> forces display of + for positive numbers
40      * <dt> ~ <dd> do not count leading + or - in length
41      * <dt> 0 <dd> show leading zeroes
42      * <dt> - <dd> align left in the field
43      * <dt> space <dd> prepend a space in front of positive numbers
44      * <dt> # <dd> use "alternate" format. Add 0 or 0x for octal or hexadecimal numbers.
45      * Don't suppress trailing zeroes in general floating point format.
46      * <dt> , <dd> groups decimal values by thousands (for 'diuxXb' formats)
47      * </dl>
48      *
49      * <li> an integer denoting field width (optional)
50      *
51      * <li> a period (<b>.</b>) followed by an integer denoting precision (optional)
52      *
53      * <li> a format descriptor (required)
54      * <dl>
55      * <dt>f <dd> floating point number in fixed format,
56      * <dt>e, E <dd> floating point number in exponential notation (scientific format).
57      * The E format results in an uppercase E for the exponent (1.14130E+003), the e
58      * format in a lowercase e,
59      * <dt>g, G <dd> floating point number in general format (fixed format for small
60      * numbers, exponential format for large numbers). Trailing zeroes are suppressed.
61      * The G format results in an uppercase E for the exponent (if any), the g format
62      * in a lowercase e,.
63      * <dt>d, i <dd> signed long and integer in decimal,
64      * <dt>u <dd> unsigned long or integer in decimal,
65      * <dt>x <dd> unsigned long or integer in hexadecimal,
66      * <dt>o <dd> unsigned long or integer in octal,
67      * <dt>b <dd> unsigned long or integer in binary,
68      * <dt>s <dd> string,
69      * <dt>c <dd> character,
70      * <dt>l, L <dd> boolean in lower or upper case (for booleans and int/longs).
71      * </dl>
72      * </ul>
73      */

74     public PrintfFormat(String JavaDoc s) {
75         init(s, 0);
76     }
77
78     /**
79      * For internal use with {@link #init(String, int)} and {@link #reinit(String)}.
80      */

81     protected PrintfFormat() {
82     }
83
84     protected PrintfFormat reinit(String JavaDoc s) {
85         if (pre == null) {
86             init(s, 0);
87         } else {
88             init(s, pre.length());
89         }
90         return this;
91     }
92
93     protected void init(String JavaDoc s, int i) {
94         width = 0;
95         precision = -1;
96         pre = (i == 0 ? new StringBuffer JavaDoc() : new StringBuffer JavaDoc(s.substring(0, i)));
97         post = new StringBuffer JavaDoc();
98         leadingZeroes = false;
99         showPlus = false;
100         alternate = false;
101         showSpace = false;
102         leftAlign = false;
103         countSignInLen = true;
104         fmt = ' ';
105
106         int length = s.length();
107         int parseState; // 0 = prefix, 1 = flags, 2 = width, 3 = precision, 4 = format, 5 = end
108

109         // 0: parse string prefix upto first '%'.
110
while (true) {
111             if (i >= length) {
112                 throw new IllegalArgumentException JavaDoc("Format string requires '%'.");
113             }
114             char c = s.charAt(i);
115             if (c != '%') {
116                 pre.append(c);
117                 i++;
118                 continue;
119             }
120             if (i >= length - 1) {
121                 throw new IllegalArgumentException JavaDoc("Format string can not end with '%'.");
122             }
123             if (s.charAt(i + 1) == '%') { // double '%%'
124
pre.append('%');
125                 i += 2;
126                 continue;
127             }
128             parseState = 1; // single $ founded
129
i++;
130             break;
131         }
132
133         // 1: parse flags
134
flagsloop:
135         while (parseState == 1) {
136             if (i >= length) {
137                 parseState = 5;
138                 break;
139             }
140             char c = s.charAt(i);
141             switch (c) {
142                 case ' ': showSpace = true; break;
143                 case '-': leftAlign = true; break;
144                 case '+': showPlus = true; break;
145                 case '0': leadingZeroes = true; break;
146                 case '#': alternate = true; break;
147                 case '~': countSignInLen = false; break;
148                 case ',': groupDigits = true; break;
149                 default:
150                     parseState = 2;
151                     break flagsloop;
152             }
153             i++;
154         }
155
156         // 2: parse width
157
while (parseState == 2) {
158             if (i >= length) {
159                 parseState = 5;
160                 break;
161             }
162             char c = s.charAt(i);
163             if (('0' <= c) && (c <= '9')) {
164                 width = (width * 10) + s.charAt(i) - '0';
165                 i++;
166                 continue;
167             }
168             if (s.charAt(i) == '.') {
169                 parseState = 3;
170                 precision = 0;
171                 i++;
172             } else {
173                 parseState = 4;
174             }
175             break;
176         }
177
178         // 3: parse precision
179
while (parseState == 3) {
180             if (i >= length) {
181                 parseState = 5;
182                 break;
183             }
184             char c = s.charAt(i);
185             if (('0' <= c) && (c <= '9')) {
186                 precision = (precision * 10) + s.charAt(i) - '0';
187                 i++;
188                 continue;
189             }
190             parseState = 4;
191             break;
192         }
193
194         // 4: parse format
195
if (parseState == 4) {
196             if (i < length) {
197                 fmt = s.charAt(i);
198                 i++;
199 // } else {
200
// parseState = 5;
201
}
202         }
203
204         // append suffix
205
if (i < length) {
206             post.append(s.substring(i, length));
207         }
208     }
209
210     /**
211      * Formats a double with exp format.
212      */

213     protected String JavaDoc expFormat(double d) {
214         StringBuffer JavaDoc f = new StringBuffer JavaDoc();
215         int e = 0;
216         double dd = d;
217         double factor = 1;
218
219         if (d != 0) {
220             while (dd > 10) {
221                 e++;
222                 factor /= 10;
223                 dd /= 10;
224             }
225             while (dd < 1) {
226                 e--;
227                 factor *= 10;
228                 dd *= 10;
229             }
230         }
231         if (((fmt == 'g') || (fmt == 'G')) && (e >= -4) && (e < precision)) {
232             return fixedFormat(d);
233         }
234
235         d *= factor;
236         f.append(fixedFormat(d));
237
238         if (fmt == 'e' || fmt == 'g') {
239             f.append('e');
240         } else {
241             f.append('E');
242         }
243
244         StringBuffer JavaDoc p = new StringBuffer JavaDoc("000");
245         if (e >= 0) {
246             f.append('+');
247             p.append(e);
248         } else {
249             f.append('-');
250             p.append(-e);
251         }
252
253         char[] data = new char[3];
254         p.getChars(p.length() - 3, p.length(), data, 0);
255         return f.append(data).toString();
256     }
257
258     /**
259      * Formats a double with fixed format.
260      */

261     protected String JavaDoc fixedFormat(double d) {
262         boolean removeTrailing = (fmt == 'G' || fmt == 'g') && !alternate;
263
264         // remove trailing zeroes and decimal point
265
if (d > 0x7FFFFFFFFFFFFFFFL) {
266             return expFormat(d);
267         }
268         if (precision == 0) {
269             return (long) (d /*+ 0.5*/) + (removeTrailing ? "" : "."); // no rounding
270
}
271
272         long whole = (long) d;
273         double fr = d - whole; // fractional part
274

275         if (fr >= 1 || fr < 0) {
276             return expFormat(d);
277         }
278
279         double factor = 1;
280         StringBuffer JavaDoc leadingZeroesStr = new StringBuffer JavaDoc();
281
282         for (int i = 1; i <= precision && factor <= 0x7FFFFFFFFFFFFFFFL; i++) {
283             factor *= 10;
284             leadingZeroesStr.append('0');
285         }
286
287         long l = (long) (factor * fr /*+ 0.5*/); // no rounding
288
if (l >= factor) {
289             l = 0;
290             whole++;
291         }
292
293         String JavaDoc z = leadingZeroesStr.toString() + l;
294         z = '.' + z.substring(z.length() - precision, z.length());
295
296         if (removeTrailing) {
297             int t = z.length() - 1;
298             while (t >= 0 && z.charAt(t) == '0') {
299                 t--;
300             }
301             if (t >= 0 && z.charAt(t) == '.') {
302                 t--;
303             }
304             z = z.substring(0, t + 1);
305         }
306         return whole + z;
307     }
308
309     /**
310      * Pads the value with spaces and adds prefix and suffix.
311      */

312     protected String JavaDoc pad(String JavaDoc value) {
313         String JavaDoc spaces = repeat(' ', width - value.length());
314         if (leftAlign) {
315             return pre + value + spaces + post;
316         } else {
317             return pre + spaces + value + post;
318         }
319     }
320
321     /**
322      * Returns new string created by repeating a single character.
323      */

324     protected static String JavaDoc repeat(char c, int n) {
325         if (n <= 0) {
326             return ("");
327         }
328         char[] buffer = new char[n];
329         for (int i = 0; i < n; i++) {
330             buffer[i] = c;
331         }
332         return new String JavaDoc(buffer);
333     }
334
335     protected String JavaDoc sign(int s, String JavaDoc r) {
336         String JavaDoc p = "";
337
338         if (s < 0) {
339             p = "-";
340         } else if (s > 0) {
341             if (showPlus) {
342                 p = "+";
343             } else if (showSpace) {
344                 p = " ";
345             }
346         } else {
347             if (fmt == 'o' && alternate && r.length() > 0 && r.charAt(0) != '0') {
348                 p = "0";
349             } else if (fmt == 'x' && alternate) {
350                 p = "0x";
351             } else if (fmt == 'X' && alternate) {
352                 p = "0X";
353             }
354         }
355
356         int w = 0;
357
358         if (leadingZeroes) {
359             w = width;
360         } else if ((fmt == 'u' || fmt == 'd' || fmt == 'i' || fmt == 'x' || fmt == 'X' || fmt == 'o') && precision > 0) {
361             w = precision;
362         }
363
364         if (countSignInLen) {
365             return p + repeat('0', w - p.length() - r.length()) + r;
366         } else {
367             return p + repeat('0', w - r.length()) + r;
368         }
369     }
370
371     /**
372      * Groups numbers by inserting 'separator' after every group of 'size' digits,
373      * starting from the right.
374      */

375     protected String JavaDoc groupDigits(String JavaDoc value, int size, char separator) {
376         if (groupDigits == false) {
377             return value;
378         }
379         StringBuffer JavaDoc r = new StringBuffer JavaDoc(value.length() + 10);
380         int ndx = 0;
381         int len = value.length() - 1;
382         int mod = len % size;
383         while (ndx < len) {
384             r.append(value.charAt(ndx));
385             if (mod == 0) {
386                 r.append(separator);
387                 mod = size;
388             }
389             mod--;
390             ndx++;
391         }
392         r.append(value.charAt(ndx));
393         return r.toString();
394     }
395
396
397
398     // ---------------------------------------------------------------- public form methods
399

400     /**
401      * Formats a character into a string (like sprintf in C).
402      */

403     public String JavaDoc form(char value) {
404         if (fmt != 'c') {
405             throw new IllegalArgumentException JavaDoc("Invalid character format: '" + fmt + "' is not 'c'.");
406         }
407         return pad(String.valueOf(value));
408     }
409
410     /**
411      * Formats a boolean into a string (like sprintf in C).
412      */

413     public String JavaDoc form(boolean value) {
414         if (fmt == 'l') {
415             return pad(value ? "true" : "false");
416         }
417         if (fmt == 'L') {
418             return pad(value ? "TRUE" : "FALSE");
419         }
420         throw new IllegalArgumentException JavaDoc("Invalid boolean format: '" + fmt + "' is not one of 'bB'.");
421
422     }
423
424     /**
425      * Formats a double into a string (like sprintf in C).
426      */

427     public String JavaDoc form(double x) {
428         String JavaDoc r;
429
430         if (precision < 0) {
431             precision = 6;
432         }
433
434         int s = 1;
435         if (x < 0) {
436             x = -x;
437             s = -1;
438         }
439         if (fmt == 'f') {
440             r = fixedFormat(x);
441         } else if (fmt == 'e' || fmt == 'E' || fmt == 'g' || fmt == 'G') {
442             r = expFormat(x);
443         } else {
444             throw new IllegalArgumentException JavaDoc("Invalid floating format: '" + fmt + "' is not one of 'feEgG'.");
445         }
446         return pad(sign(s, r));
447     }
448
449     /**
450      * Formats a long integer into a string (like sprintf in C).
451      */

452     public String JavaDoc form(long x) {
453         String JavaDoc r;
454         int s = 0;
455
456         switch (fmt) {
457             case 'd':
458                 if (x < 0) {
459                     r = Long.toString(x).substring(1);
460                     s = -1;
461                 } else {
462                     r = Long.toString(x);
463                     s = 1;
464                 }
465                 r = groupDigits(r, 3, ',');
466                 break;
467             case 'i':
468                 int xx = (int) x;
469                 if (xx < 0) {
470                     r = Integer.toString(xx).substring(1);
471                     s = -1;
472                 } else {
473                     r = Integer.toString(xx);
474                     s = 1;
475                 }
476                 r = groupDigits(r, 3, ',');
477                 break;
478             case 'u':
479                 if (x < 0) {
480                     long xl = x & 0x7FFFFFFFFFFFFFFFL;
481                     r = Long.toString(xl);
482                     BigInteger JavaDoc bi = new BigInteger JavaDoc(r);
483                     r = bi.add(bgInt).toString();
484                 } else {
485                     r = Long.toString(x);
486                 }
487                 r = groupDigits(r, 3, ',');
488                 s = 1;
489                 break;
490             case 'o':
491                 r = Long.toOctalString(x);
492                 break;
493             case 'x':
494                 r = Long.toHexString(x);
495                 r = groupDigits(r, 4, ' ');
496                 break;
497             case 'X':
498                 r = Long.toHexString(x).toUpperCase();
499                 r = groupDigits(r, 4, ' ');
500                 break;
501             case 'b':
502                 r = Long.toBinaryString(x);
503                 r = groupDigits(r, 8, ' ');
504                 break;
505             case 'l':
506                 r = (x == 0 ? "false" : "true");
507                 break;
508             case 'L':
509                 r = (x == 0 ? "FALSE" : "TRUE");
510                 break;
511             default:
512                 throw new IllegalArgumentException JavaDoc("Invalid long format: '" + fmt + "' is not one of 'diuoxXblL'.");
513         }
514
515         return pad(sign(s, r));
516     }
517
518     /**
519      * Formats an integer into a string (like sprintf in C).
520      */

521     public String JavaDoc form(int x) {
522         String JavaDoc r;
523         int s = 0;
524
525         switch (fmt) {
526             case 'd':
527             case 'i':
528                 if (x < 0) {
529                     r = Integer.toString(x).substring(1);
530                     s = -1;
531                 } else {
532                     r = Integer.toString(x);
533                     s = 1;
534                 }
535                 r = groupDigits(r, 3, ',');
536                 break;
537             case 'u':
538                 long xl = x & 0x00000000FFFFFFFFL;
539                 r = Long.toString(xl);
540                 r = groupDigits(r, 3, ',');
541                 s = 1;
542                 break;
543             case 'o':
544                 r = Integer.toOctalString(x);
545                 break;
546             case 'x':
547                 r = Integer.toHexString(x);
548                 r = groupDigits(r, 4, ' ');
549                 break;
550             case 'X':
551                 r = Integer.toHexString(x).toUpperCase();
552                 r = groupDigits(r, 4, ' ');
553                 break;
554             case 'b':
555                 r = Integer.toBinaryString(x);
556                 r = groupDigits(r, 8, ' ');
557                 break;
558             case 'l':
559                 r = (x == 0 ? "false" : "true");
560                 break;
561             case 'L':
562                 r = (x == 0 ? "FALSE" : "TRUE");
563                 break;
564             default:
565                 throw new IllegalArgumentException JavaDoc("Invalid int format: '" + fmt + "' is not one of 'diuoxXblL'.");
566         }
567         return pad(sign(s, r));
568     }
569
570     /**
571      * Formats a string into a larger string (like sprintf in C).
572      */

573     public String JavaDoc form(String JavaDoc s) {
574         if (fmt != 's') {
575             throw new IllegalArgumentException JavaDoc("Invalid long format: '" + fmt + "' is not 's'.");
576         }
577         if (precision >= 0 && precision < s.length()) {
578             s = s.substring(0, precision);
579         }
580
581         return pad(s);
582     }
583
584
585 }
586
Popular Tags