1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * ident "%Z%%M% %I% %E% SMI"
24 *
25 * Copyright (c) 2001 by Sun Microsystems, Inc.
26 * All rights reserved.
27 */
28
29 /*
30 * Cay S. Horstmann & Gary Cornell, Core Java
31 * Published By Sun Microsystems Press/Prentice-Hall
32 * Copyright (C) 1997 Sun Microsystems Inc.
33 * All Rights Reserved.
34 *
35 * Permission to use, copy, modify, and distribute this
36 * software and its documentation for NON-COMMERCIAL purposes
37 * and without fee is hereby granted provided that this
38 * copyright notice appears in all copies.
39 *
40 * THE AUTHORS AND PUBLISHER MAKE NO REPRESENTATIONS OR
41 * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER
42 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
44 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. THE AUTHORS
45 * AND PUBLISHER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED
46 * BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
47 * THIS SOFTWARE OR ITS DERIVATIVES.
48 */
49
50 /**
51 * A class for formatting numbers that follows printf conventions.
52 * Also implements C-like atoi and atof functions
53 * @version 1.01 15 Feb 1996
54 * @author Cay Horstmann
55 */
56
57 package com.sun.dhcpmgr.cli.common;
58
59 import java.io.*;
60
61 public class Format
62
63 {
64 /**
65 * Formats the number following printf conventions.
66 * Main limitation: Can only handle one format parameter at a time
67 * Use multiple Format objects to format more than one number
68 * @param s the format string following printf conventions
69 * The string has a prefix, a format code and a suffix. The prefix and
70 * suffix become part of the formatted output. The format code directs the
71 * formatting of the (single) parameter to be formatted. The code has the
72 * following structure
73 * <ul>
74 * <li> a % (required)
75 * <li> a modifier (optional)
76 * <dl>
77 * <dt> + <dd> forces display of + for positive numbers
78 * <dt> 0 <dd> show leading zeroes
79 * <dt> - <dd> align left in the field
80 * <dt> space <dd> prepend a space in front of positive numbers
81 * <dt> # <dd> use "alternate" format. Add 0 or 0x for octal or hexadecimal
82 * numbers. Don't suppress trailing zeroes in general floating point format.
83 * </dl>
84 * <li> an integer denoting field width (optional)
85 * <li> a period followed by an integer denoting precision (optional)
86 * <li> a format descriptor (required)
87 * <dl>
88 * <dt>f <dd> floating point number in fixed format
89 * <dt>e, E <dd> floating point number in exponential notation (scientific
90 * format). The E format results in an uppercase E for the exponent
91 * (1.14130E+003), the e format in a lowercase e.
92 * <dt>g, G <dd> floating point number in general format (fixed format for
93 * small numbers, exponential format for large numbers). Trailing zeroes
94 * are suppressed. The G format results in an uppercase E for the exponent
95 * (if any), the g format in a lowercase e.
96 * <dt>d, i <dd> integer in decimal
97 * <dt>x <dd> integer in hexadecimal
98 * <dt>o <dd> integer in octal
99 * <dt>s <dd> string
100 * <dt>c <dd> character
101 * </dl>
102 * </ul>
103 * @exception IllegalArgumentException if bad format
104 */
105 public Format(String s) {
106 width = 0;
107 precision = -1;
108 pre = "";
109 post = "";
110 leading_zeroes = false;
111 show_plus = false;
112 alternate = false;
113 show_space = false;
114 left_align = false;
115 fmt = ' ';
116
117 int state = 0;
118 int length = s.length();
119 int parse_state = 0;
120 // 0 = prefix, 1 = flags, 2 = width, 3 = precision,
121 // 4 = format, 5 = end
122 int i = 0;
123
124 while (parse_state == 0) {
125 if (i >= length) {
126 parse_state = 5;
127 } else if (s.charAt(i) == '%') {
128 if (i < length - 1) {
129 if (s.charAt(i + 1) == '%') {
130 pre = pre + '%';
131 i++;
132 } else {
133 parse_state = 1;
134 }
135 } else {
136 throw new java.lang.IllegalArgumentException();
137 }
138 } else {
139 pre = pre + s.charAt(i);
140 }
141 i++;
142 }
143
144 while (parse_state == 1) {
145 if (i >= length) {
146 parse_state = 5;
147 } else if (s.charAt(i) == ' ') {
148 show_space = true;
149 } else if (s.charAt(i) == '-') {
150 left_align = true;
151 } else if (s.charAt(i) == '+') {
152 show_plus = true;
153 } else if (s.charAt(i) == '0') {
154 leading_zeroes = true;
155 } else if (s.charAt(i) == '#') {
156 alternate = true;
157 } else {
158 parse_state = 2; i--;
159 }
160 i++;
161 }
162
163 while (parse_state == 2) {
164 if (i >= length) {
165 parse_state = 5;
166 } else if ('0' <= s.charAt(i) && s.charAt(i) <= '9') {
167 width = width * 10 + s.charAt(i) - '0';
168 i++;
169 } else if (s.charAt(i) == '.') {
170 parse_state = 3;
171 precision = 0;
172 i++;
173 } else {
174 parse_state = 4;
175 }
176 }
177
178 while (parse_state == 3) {
179 if (i >= length) {
180 parse_state = 5;
181 } else if ('0' <= s.charAt(i) && s.charAt(i) <= '9') {
182 precision = precision * 10 + s.charAt(i) - '0';
183 i++;
184 } else {
185 parse_state = 4;
186 }
187 }
188
189 if (parse_state == 4) {
190 if (i >= length) {
191 parse_state = 5;
192 } else {
193 fmt = s.charAt(i);
194 }
195 i++;
196 }
197
198 if (i < length) {
199 post = s.substring(i, length);
200 }
201 }
202
203 /**
204 * prints a formatted number following printf conventions
205 * @param s a PrintStream
206 * @param fmt the format string
207 * @param x the double to print
208 */
209 public static void print(java.io.PrintStream s, String fmt, double x) {
210 s.print(new Format(fmt).form(x));
211 }
212
213 /**
214 * prints a formatted number following printf conventions
215 * @param s a PrintStream
216 * @param fmt the format string
217 * @param x the long to print
218 */
219 public static void print(java.io.PrintStream s, String fmt, long x) {
220 s.print(new Format(fmt).form(x));
221 }
222
223 /**
224 * prints a formatted number following printf conventions
225 * @param s a PrintStream
226 * @param fmt the format string
227 * @param x the character to
228 */
229 public static void print(java.io.PrintStream s, String fmt, char x) {
230 s.print(new Format(fmt).form(x));
231 }
232
233 /**
234 * prints a formatted number following printf conventions
235 * @param s a PrintStream, fmt the format string
236 * @param x a string that represents the digits to print
237 */
238 public static void print(java.io.PrintStream s, String fmt, String x) {
239 s.print(new Format(fmt).form(x));
240 }
241
242 /**
243 * Converts a string of digits(decimal, octal or hex) to an integer
244 * @param s a string
245 * @return the numeric value of the prefix of s representing a base
246 * 10 integer
247 */
248 public static int atoi(String s) {
249 return (int)atol(s);
250 }
251
252 /**
253 * Converts a string of digits(decimal, octal or hex) to a long integer
254 * @param s a string
255 * @return the numeric value of the prefix of s representing a base
256 * 10 integer
257 */
258 public static long atol(String s) {
259 int i = 0;
260
261 while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
262 i++;
263 }
264
265 if (i < s.length() && s.charAt(i) == '0') {
266 if (i + 1 < s.length() && (s.charAt(i + 1) == 'x' ||
267 s.charAt(i + 1) == 'X')) {
268 return parseLong(s.substring(i + 2), 16);
269 } else {
270 return parseLong(s, 8);
271 }
272 } else {
273 return parseLong(s, 10);
274 }
275 }
276
277 private static long parseLong(String s, int base) {
278 int i = 0;
279 int sign = 1;
280 long r = 0;
281
282 while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
283 i++;
284 }
285
286 if (i < s.length() && s.charAt(i) == '-') {
287 sign = -1; i++;
288 } else if (i < s.length() && s.charAt(i) == '+') {
289 i++;
290 }
291
292 while (i < s.length()) {
293 char ch = s.charAt(i);
294 if ('0' <= ch && ch < '0' + base) {
295 r = r * base + ch - '0';
296 } else if ('A' <= ch && ch < 'A' + base - 10) {
297 r = r * base + ch - 'A' + 10;
298 } else if ('a' <= ch && ch < 'a' + base - 10) {
299 r = r * base + ch - 'a' + 10;
300 } else {
301 return r * sign;
302 }
303 i++;
304 }
305 return r * sign;
306 }
307
308 /**
309 * Converts a string of digits to an double
310 * @param s a string
311 */
312 public static double atof(String s) {
313 int i = 0;
314 int sign = 1;
315 double r = 0; // integer part
316 double f = 0; // fractional part
317 double p = 1; // exponent of fractional part
318 int state = 0; // 0 = int part, 1 = frac part
319
320 while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
321 i++;
322 }
323
324 if (i < s.length() && s.charAt(i) == '-') {
325 sign = -1; i++;
326 } else if (i < s.length() && s.charAt(i) == '+') {
327 i++;
328 }
329
330 while (i < s.length()) {
331 char ch = s.charAt(i);
332 if ('0' <= ch && ch <= '9') {
333 if (state == 0) {
334 r = r * 10 + ch - '0';
335 } else if (state == 1) {
336 p = p / 10;
337 r = r + p * (ch - '0');
338 }
339 } else if (ch == '.') {
340 if (state == 0) {
341 state = 1;
342 } else {
343 return sign * r;
344 }
345 } else if (ch == 'e' || ch == 'E') {
346 long e = (int)parseLong(s.substring(i + 1), 10);
347 return sign * r * Math.pow(10, e);
348 } else {
349 return sign * r;
350 }
351 i++;
352 }
353 return sign * r;
354 }
355
356 /**
357 * Formats a double into a string (like sprintf in C)
358 * @param x the number to format
359 * @return the formatted string
360 * @exception IllegalArgumentException if bad argument
361 */
362 public String form(double x) {
363 String r;
364 if (precision < 0) {
365 precision = 6;
366 }
367
368 int s = 1;
369 if (x < 0) {
370 x = -x;
371 s = -1;
372 }
373
374 if (fmt == 'f') {
375 r = fixed_format(x);
376 } else if (fmt == 'e' || fmt == 'E' || fmt == 'g' || fmt == 'G') {
377 r = exp_format(x);
378 } else {
379 throw new java.lang.IllegalArgumentException();
380 }
381
382 return pad(sign(s, r));
383 }
384
385 /**
386 * Formats a long integer into a string (like sprintf in C)
387 * @param x the number to format
388 * @return the formatted string
389 */
390 public String form(long x) {
391 String r;
392 int s = 0;
393 if (fmt == 'd' || fmt == 'i') {
394 s = 1;
395 if (x < 0) {
396 x = -x; s = -1;
397 }
398 r = "" + x;
399 } else if (fmt == 'o') {
400 r = convert(x, 3, 7, "01234567");
401 } else if (fmt == 'x') {
402 r = convert(x, 4, 15, "0123456789abcdef");
403 } else if (fmt == 'X') {
404 r = convert(x, 4, 15, "0123456789ABCDEF");
405 } else {
406 throw new java.lang.IllegalArgumentException();
407 }
408 return pad(sign(s, r));
409 }
410
411 /**
412 * Formats a character into a string (like sprintf in C)
413 * @param x the value to format
414 * @return the formatted string
415 */
416 public String form(char c) {
417 if (fmt != 'c') {
418 throw new java.lang.IllegalArgumentException();
419 }
420 String r = "" + c;
421 return pad(r);
422 }
423
424 /**
425 * Formats a string into a larger string (like sprintf in C)
426 * @param x the value to format
427 * @return the formatted string
428 */
429 public String form(String s) {
430 if (fmt != 's') {
431 throw new java.lang.IllegalArgumentException();
432 }
433
434 if (precision >= 0) {
435 s = s.substring(0, precision);
436 }
437 return pad(s);
438 }
439
440 private static String repeat(char c, int n) {
441 if (n <= 0) {
442 return "";
443 }
444 StringBuffer s = new StringBuffer(n);
445 for (int i = 0; i < n; i++) {
446 s.append(c);
447 }
448 return s.toString();
449 }
450
451 private static String convert(long x, int n, int m, String d) {
452 if (x == 0) {
453 return "0";
454 }
455 String r = "";
456 while (x != 0) {
457 r = d.charAt((int)(x & m)) + r;
458 x = x >>> n;
459 }
460 return r;
461 }
462
463 private String pad(String r) {
464 String p = repeat(' ', width - r.length());
465 if (left_align) {
466 return pre + r + p + post;
467 } else {
468 return pre + p + r + post;
469 }
470 }
471
472 private String sign(int s, String r) {
473 String p = "";
474 if (s < 0) {
475 p = "-";
476 } else if (s > 0) {
477 if (show_plus) {
478 p = "+";
479 } else if (show_space) {
480 p = " ";
481 }
482 } else {
483 if (fmt == 'o' && alternate && r.length() > 0 &&
484 r.charAt(0) != '0') {
485 p = "0";
486 } else if (fmt == 'x' && alternate) {
487 p = "0x";
488 } else if (fmt == 'X' && alternate) {
489 p = "0X";
490 }
491 }
492
493 int w = 0;
494 if (leading_zeroes) {
495 w = width;
496 } else if ((fmt == 'd' || fmt == 'i' || fmt == 'x' ||
497 fmt == 'X' || fmt == 'o') && precision > 0) {
498 w = precision;
499 }
500
501 return p + repeat('0', w - p.length() - r.length()) + r;
502 }
503
504
505 private String fixed_format(double d) {
506 String f = "";
507
508 if (d > 0x7FFFFFFFFFFFFFFFL) {
509 return exp_format(d);
510 }
511
512 long l = (long)(precision == 0 ? d + 0.5 : d);
513 f = f + l;
514
515 double fr = d - l; // fractional part
516 if (fr >= 1 || fr < 0) {
517 return exp_format(d);
518 }
519
520 return f + frac_part(fr);
521 }
522
523 private String frac_part(double fr) {
524 // precondition: 0 <= fr < 1
525 String z = "";
526 if (precision > 0) {
527 double factor = 1;
528 String leading_zeroes = "";
529 for (int i = 1; i <= precision && factor <= 0x7FFFFFFFFFFFFFFFL;
530 i++) {
531 factor *= 10;
532 leading_zeroes = leading_zeroes + "0";
533 }
534 long l = (long) (factor * fr + 0.5);
535
536 z = leading_zeroes + l;
537 z = z.substring(z.length() - precision, z.length());
538 }
539
540
541 if (precision > 0 || alternate) {
542 z = "." + z;
543 }
544
545 if ((fmt == 'G' || fmt == 'g') && !alternate) {
546 // remove trailing zeroes and decimal point
547 int t = z.length() - 1;
548 while (t >= 0 && z.charAt(t) == '0') {
549 t--;
550 }
551 if (t >= 0 && z.charAt(t) == '.') {
552 t--;
553 }
554 z = z.substring(0, t + 1);
555 }
556 return z;
557 }
558
559 private String exp_format(double d) {
560 String f = "";
561 int e = 0;
562 double dd = d;
563 double factor = 1;
564
565 while (dd > 10) {
566 e++; factor /= 10; dd = dd / 10;
567 }
568
569 while (dd < 1) {
570 e--; factor *= 10; dd = dd * 10;
571 }
572
573 if ((fmt == 'g' || fmt == 'G') && e >= -4 && e < precision) {
574 return fixed_format(d);
575 }
576
577 d = d * factor;
578 f = f + fixed_format(d);
579
580 if (fmt == 'e' || fmt == 'g') {
581 f = f + "e";
582 } else {
583 f = f + "E";
584 }
585
586 String p = "000";
587 if (e >= 0) {
588 f = f + "+";
589 p = p + e;
590 } else {
591 f = f + "-";
592 p = p + (-e);
593 }
594
595 return f + p.substring(p.length() - 3, p.length());
596 }
597
598 private int width;
599 private int precision;
600 private String pre;
601 private String post;
602 private boolean leading_zeroes;
603 private boolean show_plus;
604 private boolean alternate;
605 private boolean show_space;
606 private boolean left_align;
607 private char fmt; // one of cdeEfgGiosxXos
608 }