KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > es > Printf


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.es;
30
31 import com.caucho.util.CharBuffer;
32
33 class Printf {
34   private final static int ALT = 0x01;
35   private final static int ZERO_FILL = 0x02;
36   private final static int POS_PLUS = 0x04;
37   private final static int POS_SPACE = 0x08;
38   private final static int LALIGN = 0x10;
39   private final static int BIG = 0x20;
40   private final static int NO_TRAIL_ZERO = 0x40;
41
42   private static char []digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
43               'a', 'b', 'c', 'd', 'e', 'f'};
44   private static char []bigDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
45                  'A', 'B', 'C', 'D', 'E', 'F'};
46
47   private Printf()
48   {
49   }
50
51   public static String JavaDoc sprintf(Call eval, int length)
52     throws Throwable JavaDoc
53   {
54     if (length == 0)
55       return "";
56
57     CharBuffer buf = new CharBuffer();
58
59     printf(buf, eval.getArg(0).toStr(), eval, length);
60
61     return buf.toString();
62   }
63
64   public static CharBuffer printf(CharBuffer result, ESString format,
65                   Call eval, int length)
66                   throws Throwable JavaDoc
67   {
68     int arg = 1;
69     int len = format.length();
70
71     for (int i = 0; i < len; i++) {
72       int ch;
73       int start = i;
74
75       if ((ch = format.charAt(i)) != '%') {
76     result.append((char) ch);
77     continue;
78       }
79
80       int flags = 0;
81     loop:
82       while (++i < len) {
83     switch ((ch = format.charAt(i))) {
84     case '0': flags |= ZERO_FILL; break;
85     case '+': flags |= POS_PLUS; break;
86     case ' ': flags |= POS_SPACE; break;
87     case '#': flags |= ALT; break;
88     case '-': flags |= LALIGN; break;
89
90     default: break loop;
91     }
92       }
93
94       int width = 0;
95       for (; i < len && (ch = format.charAt(i)) >= '0' && ch <= '9'; i++) {
96     width = 10 * width + ch - '0';
97       }
98
99       if (i >= len) {
100     fixBits(result, format, start, i);
101     break;
102       }
103
104       int prec = 0;
105       if (ch == '.') {
106     while (++i < len && (ch = format.charAt(i)) >= '0' && ch <= '9') {
107       prec = 10 * prec + ch - '0';
108     }
109       } else
110     prec = -1;
111
112       if (i >= len) {
113     fixBits(result, format, start, i);
114     break;
115       }
116
117       switch (ch) {
118       case '%':
119     result.append((char) '%');
120     break;
121
122       case 'd':
123     if (arg >= length)
124       throw new ESException("missing printf argument");
125     formatInteger(result, eval.getArg(arg++).toNum(),
126               width, prec, flags, 10);
127     break;
128
129       case 'o':
130     if (arg >= length)
131       throw new ESException("missing printf argument");
132     formatInteger(result, eval.getArg(arg++).toNum(),
133               width, prec, flags, 8);
134     break;
135
136       case 'X':
137     flags |= BIG;
138       case 'x':
139     if (arg >= length)
140       throw new ESException("missing printf argument");
141     formatInteger(result, eval.getArg(arg++).toNum(),
142               width, prec, flags, 16);
143     break;
144
145       case 'E':
146       case 'G':
147     flags |= BIG;
148       case 'f':
149       case 'e':
150       case 'g':
151     if (arg >= length)
152       throw new ESException("missing printf argument");
153     formatDouble(result, eval.getArg(arg++).toNum(),
154               width, prec, flags, ch);
155     break;
156
157       case 'c':
158     if (arg >= length)
159       throw new ESException("missing printf argument");
160     formatChar(result, (int) eval.getArg(arg++).toNum(), width, flags);
161     break;
162
163       case 's':
164     if (arg >= length)
165       throw new ESException("missing printf argument");
166     formatString(result, eval.getArg(arg++).toStr(),
167              prec, width, flags);
168     break;
169
170       default:
171     fixBits(result, format, start, i + 1);
172     break;
173       }
174     }
175
176     return result;
177   }
178
179   private static void formatDouble(CharBuffer cb, double value,
180                   int prec, int flags, int type)
181   {
182     String JavaDoc raw = Double.toString(value);
183     int expt = 0;
184     int i = 0;
185     CharBuffer digits = new CharBuffer();
186     int ch = raw.charAt(i);
187     boolean seenDigit = false;
188
189     // XXX: locale screws us?
190
for (; i < raw.length(); i++) {
191       if ((ch = raw.charAt(i)) == '.' || ch == 'e' || ch == 'E')
192     break;
193       else if (! seenDigit && ch == '0') {
194       }
195       else {
196     seenDigit = true;
197     digits.append((char) ch);
198     expt++;
199       }
200     }
201
202     if (ch == '.')
203       i++;
204
205     for (; i < raw.length(); i++) {
206       ch = raw.charAt(i);
207
208       if (! seenDigit && ch == '0') {
209     expt--;
210       } else if (ch >= '0' && ch <= '9') {
211     digits.append((char) ch);
212     seenDigit = true;
213       }
214       else {
215     int sign = 1;
216     i++;
217     if ((ch = raw.charAt(i)) == '+') {
218       i++;
219     }
220     else if (ch == '-') {
221       i++;
222       sign = -1;
223     }
224       
225     int e = 0;
226     for (; i < raw.length() && (ch = raw.charAt(i)) >= '0' && ch <= '9';
227          i++) {
228       e = 10 * e + ch - '0';
229     }
230     
231     expt += sign * e;
232     break;
233       }
234     }
235
236     if (! seenDigit)
237       expt = 1;
238
239     while (digits.length() > 0 && digits.charAt(digits.length() - 1) == '0')
240       digits.setLength(digits.length() - 1);
241
242     if (type == 'f') {
243       if (roundDigits(digits, expt + prec)) {
244     expt++;
245       }
246
247       formatFixed(cb, digits, expt, prec, flags);
248     }
249     else if (type == 'e' || type == 'E') {
250       if (roundDigits(digits, prec + 1))
251     expt++;
252
253       formatExpt(cb, digits, expt, prec, flags);
254     }
255     else {
256       if (roundDigits(digits, prec))
257     expt++;
258
259       if (expt < -3 || expt > prec)
260     formatExpt(cb, digits, expt, prec - 1, flags|NO_TRAIL_ZERO);
261       else
262     formatFixed(cb, digits, expt, prec - expt, flags|NO_TRAIL_ZERO);
263     }
264   }
265
266   private static void formatDouble(CharBuffer cb, double value,
267                   int width, int prec, int flags,
268                   int type)
269   {
270     if (prec < 0)
271       prec = 6;
272
273     int offset = cb.length();
274
275     if ((flags & ZERO_FILL) != 0 &&
276     (value < 0 || (flags & (POS_PLUS|POS_SPACE)) != 0)) {
277     offset++;
278     width--;
279     }
280
281     if (value < 0) {
282       cb.append((char) '-');
283       value = -value;
284     } else if ((flags & POS_PLUS) != 0) {
285       cb.append((char) '+');
286     } else if ((flags & POS_SPACE) != 0) {
287       cb.append((char) ' ');
288     }
289
290     formatDouble(cb, value, prec, flags, type);
291
292     width -= cb.length() - offset;
293
294     for (int i = 0; i < width; i++) {
295       if ((flags & LALIGN) != 0)
296     cb.append(' ');
297       else
298     cb.insert(offset, (flags & ZERO_FILL) == 0 ? ' ' : '0');
299     }
300   }
301
302   private static boolean roundDigits(CharBuffer digits, int len)
303   {
304     if (len < 0 || digits.length() <= len)
305       return false;
306
307     int value = digits.charAt(len);
308     if (value < '5')
309       return false;
310
311     for (int i = len - 1; i >= 0; i--) {
312       int ch = digits.charAt(i);
313
314       if (ch != '9') {
315     digits.setCharAt(i, (char) (ch + 1));
316     return false;
317       }
318       digits.setCharAt(i, '0');
319     }
320
321     digits.insert(0, '1');
322
323     return true;
324   }
325
326   private static void formatFixed(CharBuffer cb, CharBuffer digits,
327                  int expt, int prec, int flags)
328   {
329     int i = 0;
330     int origExpt = expt;
331
332     for (; expt > 0; expt--) {
333       if (i < digits.length())
334     cb.append((char) digits.charAt(i++));
335       else
336     cb.append('0');
337     }
338     
339     if (origExpt <= 0) // || digits.length() == 0)
340
cb.append('0');
341
342     if (prec > 0 || (flags & ALT) != 0)
343       cb.append('.');
344
345     for (; expt < 0 && prec > 0; expt++) {
346       cb.append('0');
347       prec--;
348     }
349
350     for (; prec > 0 && i < digits.length(); i++) {
351       cb.append(digits.charAt(i));
352       prec--;
353     }
354
355     for (; prec > 0 && (flags & (NO_TRAIL_ZERO|ALT)) != NO_TRAIL_ZERO; prec--)
356       cb.append('0');
357   }
358
359   private static void formatExpt(CharBuffer cb, CharBuffer digits,
360                  int expt, int prec, int flags)
361   {
362     if (digits.length() == 0)
363       cb.append('0');
364     else
365       cb.append((char) digits.charAt(0));
366
367     if (prec > 0 || (flags & ALT) != 0)
368       cb.append('.');
369
370     for (int i = 1; i < digits.length(); i++) {
371       if (prec > 0)
372     cb.append((char) digits.charAt(i));
373       prec--;
374     }
375
376     for (; prec > 0 && (flags & (NO_TRAIL_ZERO|ALT)) != NO_TRAIL_ZERO; prec--)
377       cb.append('0');
378
379     if ((flags & BIG) != 0)
380       cb.append('E');
381     else
382       cb.append('e');
383
384     formatInteger(cb, expt - 1, 0, 2, POS_PLUS, 10);
385   }
386
387   private static void formatInteger(CharBuffer cb, double dvalue,
388                    int width, int prec, int flags, int radix)
389   {
390     boolean isBig = (flags & BIG) != 0;
391     int begin = cb.length();
392
393     long value;
394     if (dvalue > 0)
395       value = (long) (dvalue + 0.5);
396     else
397       value = (long) (dvalue - 0.5);
398
399     if (value < 0 && radix == 10) {
400       cb.append((char) '-');
401       value = -value;
402     } else if (value >= 0 && radix == 10 && (flags & POS_PLUS) != 0)
403       cb.append((char) '+');
404     else if (value >= 0 && radix == 10 && (flags & POS_SPACE) != 0)
405       cb.append((char) ' ');
406     else if (value < 0)
407       value &= 0xffffffffL;
408     else if (radix == 8 && (flags & ALT) != 0 && value != 0)
409       cb.append('0');
410     else if (radix == 16 && (flags & ALT) != 0)
411       cb.append((flags & BIG) == 0 ? "0x" : "0X");
412
413     if ((flags & ZERO_FILL) != 0) {
414       width -= cb.length() - begin;
415       begin = cb.length();
416     }
417
418     int offset = cb.length();
419     int len = 0;
420     while (value != 0) {
421       len++;
422       cb.insert(offset, (isBig ? bigDigits : digits)[(int) (value % radix)]);
423       value /= radix;
424     }
425
426     for (int i = 0; i < prec - len; i++)
427       cb.insert(offset, '0');
428     if (len == 0 && prec == 0)
429       cb.insert(offset, '0');
430
431     width -= cb.length() - begin;
432     for (; width > 0; width--) {
433       if ((flags & LALIGN) != 0)
434     cb.append(' ');
435       else if ((flags & ZERO_FILL) != 0 && prec < 0)
436     cb.insert(begin, '0');
437       else
438     cb.insert(begin, ' ');
439     }
440
441     if (cb.length() == begin)
442       cb.append('0');
443   }
444
445   private static void formatChar(CharBuffer cb, int ch, int width, int flags)
446   {
447     int offset = cb.length();
448
449     cb.append((char) ch);
450
451     if ((flags & LALIGN) == 0) {
452       for (int i = 0; i < width - 1; i++)
453     cb.insert(offset, (char) ' ');
454     } else {
455       for (int i = 0; i < width - 1; i++)
456     cb.append((char) ' ');
457     }
458   }
459
460   private static void formatString(CharBuffer cb, ESString string,
461                   int prec, int width, int flags)
462   {
463     int offset = cb.length();
464
465     if (prec < 0)
466       prec = Integer.MAX_VALUE;
467
468     for (int i = 0; i < string.length() && i < prec; i++) {
469       width--;
470       cb.append(string.charAt(i));
471     }
472
473     if ((flags & LALIGN) == 0) {
474       for (int i = 0; i < width; i++)
475     cb.insert(offset, (char) ' ');
476     } else {
477       for (int i = 0; i < width; i++)
478     cb.append((char) ' ');
479     }
480   }
481
482   private static void fixBits(CharBuffer cb, ESString format, int s, int i)
483   {
484     for (; s < i; s++)
485       cb.append((char) format.charAt(s));
486   }
487 }
488
Popular Tags