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 }