| GNU Classpath (0.95) | |
| Frames | No Frames |
1: /* Formatter.java -- printf-style formatting 2: Copyright (C) 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.util; 40: 41: import java.io.Closeable; 42: import java.io.File; 43: import java.io.FileNotFoundException; 44: import java.io.FileOutputStream; 45: import java.io.Flushable; 46: import java.io.IOException; 47: import java.io.OutputStream; 48: import java.io.OutputStreamWriter; 49: import java.io.PrintStream; 50: import java.io.UnsupportedEncodingException; 51: import java.math.BigInteger; 52: import java.text.DateFormatSymbols; 53: import java.text.DecimalFormatSymbols; 54: 55: import gnu.classpath.SystemProperties; 56: 57: /** 58: * <p> 59: * A Java formatter for <code>printf</code>-style format strings, 60: * as seen in the C programming language. This differs from the 61: * C interpretation of such strings by performing much stricter 62: * checking of format specifications and their corresponding 63: * arguments. While unknown conversions will be ignored in C, 64: * and invalid conversions will only produce compiler warnings, 65: * the Java version utilises a full range of run-time exceptions to 66: * handle these cases. The Java version is also more customisable 67: * by virtue of the provision of the {@link Formattable} interface, 68: * which allows an arbitrary class to be formatted by the formatter. 69: * </p> 70: * <p> 71: * The formatter is accessible by more convienient static methods. 72: * For example, streams now have appropriate format methods 73: * (the equivalent of <code>fprintf</code>) as do <code>String</code> 74: * objects (the equivalent of <code>sprintf</code>). 75: * </p> 76: * <p> 77: * <strong>Note</strong>: the formatter is not thread-safe. For 78: * multi-threaded access, external synchronization should be provided. 79: * </p> 80: * 81: * @author Tom Tromey (tromey@redhat.com) 82: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 83: * @since 1.5 84: */ 85: public final class Formatter 86: implements Closeable, Flushable 87: { 88: 89: /** 90: * The output of the formatter. 91: */ 92: private Appendable out; 93: 94: /** 95: * The locale used by the formatter. 96: */ 97: private Locale locale; 98: 99: /** 100: * Whether or not the formatter is closed. 101: */ 102: private boolean closed; 103: 104: /** 105: * The last I/O exception thrown by the output stream. 106: */ 107: private IOException ioException; 108: 109: // Some state used when actually formatting. 110: /** 111: * The format string. 112: */ 113: private String format; 114: 115: /** 116: * The current index into the string. 117: */ 118: private int index; 119: 120: /** 121: * The length of the format string. 122: */ 123: private int length; 124: 125: /** 126: * The formatting locale. 127: */ 128: private Locale fmtLocale; 129: 130: // Note that we include '-' twice. The flags are ordered to 131: // correspond to the values in FormattableFlags, and there is no 132: // flag (in the sense of this field used when parsing) for 133: // UPPERCASE; the second '-' serves as a placeholder. 134: /** 135: * A string used to index into the formattable flags. 136: */ 137: private static final String FLAGS = "--#+ 0,("; 138: 139: /** 140: * The system line separator. 141: */ 142: private static final String lineSeparator 143: = SystemProperties.getProperty("line.separator"); 144: 145: /** 146: * The type of numeric output format for a {@link BigDecimal}. 147: */ 148: public enum BigDecimalLayoutForm 149: { 150: DECIMAL_FLOAT, 151: SCIENTIFIC 152: } 153: 154: /** 155: * Constructs a new <code>Formatter</code> using the default 156: * locale and a {@link StringBuilder} as the output stream. 157: */ 158: public Formatter() 159: { 160: this(null, Locale.getDefault()); 161: } 162: 163: /** 164: * Constructs a new <code>Formatter</code> using the specified 165: * locale and a {@link StringBuilder} as the output stream. 166: * If the locale is <code>null</code>, then no localization 167: * is applied. 168: * 169: * @param loc the locale to use. 170: */ 171: public Formatter(Locale loc) 172: { 173: this(null, loc); 174: } 175: 176: /** 177: * Constructs a new <code>Formatter</code> using the default 178: * locale and the specified output stream. 179: * 180: * @param app the output stream to use. 181: */ 182: public Formatter(Appendable app) 183: { 184: this(app, Locale.getDefault()); 185: } 186: 187: /** 188: * Constructs a new <code>Formatter</code> using the specified 189: * locale and the specified output stream. If the locale is 190: * <code>null</code>, then no localization is applied. 191: * 192: * @param app the output stream to use. 193: * @param loc the locale to use. 194: */ 195: public Formatter(Appendable app, Locale loc) 196: { 197: this.out = app == null ? new StringBuilder() : app; 198: this.locale = loc; 199: } 200: 201: /** 202: * Constructs a new <code>Formatter</code> using the default 203: * locale and character set, with the specified file as the 204: * output stream. 205: * 206: * @param file the file to use for output. 207: * @throws FileNotFoundException if the file does not exist 208: * and can not be created. 209: * @throws SecurityException if a security manager is present 210: * and doesn't allow writing to the file. 211: */ 212: public Formatter(File file) 213: throws FileNotFoundException 214: { 215: this(new OutputStreamWriter(new FileOutputStream(file))); 216: } 217: 218: /** 219: * Constructs a new <code>Formatter</code> using the default 220: * locale, with the specified file as the output stream 221: * and the supplied character set. 222: * 223: * @param file the file to use for output. 224: * @param charset the character set to use for output. 225: * @throws FileNotFoundException if the file does not exist 226: * and can not be created. 227: * @throws SecurityException if a security manager is present 228: * and doesn't allow writing to the file. 229: * @throws UnsupportedEncodingException if the supplied character 230: * set is not supported. 231: */ 232: public Formatter(File file, String charset) 233: throws FileNotFoundException, UnsupportedEncodingException 234: { 235: this(file, charset, Locale.getDefault()); 236: } 237: 238: /** 239: * Constructs a new <code>Formatter</code> using the specified 240: * file as the output stream with the supplied character set 241: * and locale. If the locale is <code>null</code>, then no 242: * localization is applied. 243: * 244: * @param file the file to use for output. 245: * @param charset the character set to use for output. 246: * @param loc the locale to use. 247: * @throws FileNotFoundException if the file does not exist 248: * and can not be created. 249: * @throws SecurityException if a security manager is present 250: * and doesn't allow writing to the file. 251: * @throws UnsupportedEncodingException if the supplied character 252: * set is not supported. 253: */ 254: public Formatter(File file, String charset, Locale loc) 255: throws FileNotFoundException, UnsupportedEncodingException 256: { 257: this(new OutputStreamWriter(new FileOutputStream(file), charset), 258: loc); 259: } 260: 261: /** 262: * Constructs a new <code>Formatter</code> using the default 263: * locale and character set, with the specified output stream. 264: * 265: * @param out the output stream to use. 266: */ 267: public Formatter(OutputStream out) 268: { 269: this(new OutputStreamWriter(out)); 270: } 271: 272: /** 273: * Constructs a new <code>Formatter</code> using the default 274: * locale, with the specified file output stream and the 275: * supplied character set. 276: * 277: * @param out the output stream. 278: * @param charset the character set to use for output. 279: * @throws UnsupportedEncodingException if the supplied character 280: * set is not supported. 281: */ 282: public Formatter(OutputStream out, String charset) 283: throws UnsupportedEncodingException 284: { 285: this(out, charset, Locale.getDefault()); 286: } 287: 288: /** 289: * Constructs a new <code>Formatter</code> using the specified 290: * output stream with the supplied character set and locale. 291: * If the locale is <code>null</code>, then no localization is 292: * applied. 293: * 294: * @param file the output stream. 295: * @param charset the character set to use for output. 296: * @param loc the locale to use. 297: * @throws UnsupportedEncodingException if the supplied character 298: * set is not supported. 299: */ 300: public Formatter(OutputStream out, String charset, Locale loc) 301: throws UnsupportedEncodingException 302: { 303: this(new OutputStreamWriter(out, charset), loc); 304: } 305: 306: /** 307: * Constructs a new <code>Formatter</code> using the default 308: * locale with the specified output stream. The character 309: * set used is that of the output stream. 310: * 311: * @param out the output stream to use. 312: */ 313: public Formatter(PrintStream out) 314: { 315: this((Appendable) out); 316: } 317: 318: /** 319: * Constructs a new <code>Formatter</code> using the default 320: * locale and character set, with the specified file as the 321: * output stream. 322: * 323: * @param file the file to use for output. 324: * @throws FileNotFoundException if the file does not exist 325: * and can not be created. 326: * @throws SecurityException if a security manager is present 327: * and doesn't allow writing to the file. 328: */ 329: public Formatter(String file) throws FileNotFoundException 330: { 331: this(new OutputStreamWriter(new FileOutputStream(file))); 332: } 333: 334: /** 335: * Constructs a new <code>Formatter</code> using the default 336: * locale, with the specified file as the output stream 337: * and the supplied character set. 338: * 339: * @param file the file to use for output. 340: * @param charset the character set to use for output. 341: * @throws FileNotFoundException if the file does not exist 342: * and can not be created. 343: * @throws SecurityException if a security manager is present 344: * and doesn't allow writing to the file. 345: * @throws UnsupportedEncodingException if the supplied character 346: * set is not supported. 347: */ 348: public Formatter(String file, String charset) 349: throws FileNotFoundException, UnsupportedEncodingException 350: { 351: this(file, charset, Locale.getDefault()); 352: } 353: 354: /** 355: * Constructs a new <code>Formatter</code> using the specified 356: * file as the output stream with the supplied character set 357: * and locale. If the locale is <code>null</code>, then no 358: * localization is applied. 359: * 360: * @param file the file to use for output. 361: * @param charset the character set to use for output. 362: * @param loc the locale to use. 363: * @throws FileNotFoundException if the file does not exist 364: * and can not be created. 365: * @throws SecurityException if a security manager is present 366: * and doesn't allow writing to the file. 367: * @throws UnsupportedEncodingException if the supplied character 368: * set is not supported. 369: */ 370: public Formatter(String file, String charset, Locale loc) 371: throws FileNotFoundException, UnsupportedEncodingException 372: { 373: this(new OutputStreamWriter(new FileOutputStream(file), charset), 374: loc); 375: } 376: 377: /** 378: * Closes the formatter, so as to release used resources. 379: * If the underlying output stream supports the {@link Closeable} 380: * interface, then this is also closed. Attempts to use 381: * a formatter instance, via any method other than 382: * {@link #ioException()}, after closure results in a 383: * {@link FormatterClosedException}. 384: */ 385: public void close() 386: { 387: if (closed) 388: return; 389: try 390: { 391: if (out instanceof Closeable) 392: ((Closeable) out).close(); 393: } 394: catch (IOException _) 395: { 396: // FIXME: do we ignore these or do we set ioException? 397: // The docs seem to indicate that we should ignore. 398: } 399: closed = true; 400: } 401: 402: /** 403: * Flushes the formatter, writing any cached data to the output 404: * stream. If the underlying output stream supports the 405: * {@link Flushable} interface, it is also flushed. 406: * 407: * @throws FormatterClosedException if the formatter is closed. 408: */ 409: public void flush() 410: { 411: if (closed) 412: throw new FormatterClosedException(); 413: try 414: { 415: if (out instanceof Flushable) 416: ((Flushable) out).flush(); 417: } 418: catch (IOException _) 419: { 420: // FIXME: do we ignore these or do we set ioException? 421: // The docs seem to indicate that we should ignore. 422: } 423: } 424: 425: /** 426: * Return the name corresponding to a flag. 427: * 428: * @param flags the flag to return the name of. 429: * @return the name of the flag. 430: */ 431: private String getName(int flags) 432: { 433: // FIXME: do we want all the flags in here? 434: // Or should we redo how this is reported? 435: int bit = Integer.numberOfTrailingZeros(flags); 436: return FLAGS.substring(bit, bit + 1); 437: } 438: 439: /** 440: * Verify the flags passed to a conversion. 441: * 442: * @param flags the flags to verify. 443: * @param allowed the allowed flags mask. 444: * @param conversion the conversion character. 445: */ 446: private void checkFlags(int flags, int allowed, char conversion) 447: { 448: flags &= ~allowed; 449: if (flags != 0) 450: throw new FormatFlagsConversionMismatchException(getName(flags), 451: conversion); 452: } 453: 454: /** 455: * Throw an exception if a precision was specified. 456: * 457: * @param precision the precision value (-1 indicates not specified). 458: */ 459: private void noPrecision(int precision) 460: { 461: if (precision != -1) 462: throw new IllegalFormatPrecisionException(precision); 463: } 464: 465: /** 466: * Apply the numeric localization algorithm to a StringBuilder. 467: * 468: * @param builder the builder to apply to. 469: * @param flags the formatting flags to use. 470: * @param width the width of the numeric value. 471: * @param isNegative true if the value is negative. 472: */ 473: private void applyLocalization(StringBuilder builder, int flags, int width, 474: boolean isNegative) 475: { 476: DecimalFormatSymbols dfsyms; 477: if (fmtLocale == null) 478: dfsyms = new DecimalFormatSymbols(); 479: else 480: dfsyms = new DecimalFormatSymbols(fmtLocale); 481: 482: // First replace each digit. 483: char zeroDigit = dfsyms.getZeroDigit(); 484: int decimalOffset = -1; 485: for (int i = builder.length() - 1; i >= 0; --i) 486: { 487: char c = builder.charAt(i); 488: if (c >= '0' && c <= '9') 489: builder.setCharAt(i, (char) (c - '0' + zeroDigit)); 490: else if (c == '.') 491: { 492: assert decimalOffset == -1; 493: decimalOffset = i; 494: } 495: } 496: 497: // Localize the decimal separator. 498: if (decimalOffset != -1) 499: { 500: builder.deleteCharAt(decimalOffset); 501: builder.insert(decimalOffset, dfsyms.getDecimalSeparator()); 502: } 503: 504: // Insert the grouping separators. 505: if ((flags & FormattableFlags.COMMA) != 0) 506: { 507: char groupSeparator = dfsyms.getGroupingSeparator(); 508: int groupSize = 3; // FIXME 509: int offset = (decimalOffset == -1) ? builder.length() : decimalOffset; 510: // We use '>' because we don't want to insert a separator 511: // before the first digit. 512: for (int i = offset - groupSize; i > 0; i -= groupSize) 513: builder.insert(i, groupSeparator); 514: } 515: 516: if ((flags & FormattableFlags.ZERO) != 0) 517: { 518: // Zero fill. Note that according to the algorithm we do not 519: // insert grouping separators here. 520: for (int i = width - builder.length(); i > 0; --i) 521: builder.insert(0, zeroDigit); 522: } 523: 524: if (isNegative) 525: { 526: if ((flags & FormattableFlags.PAREN) != 0) 527: { 528: builder.insert(0, '('); 529: builder.append(')'); 530: } 531: else 532: builder.insert(0, '-'); 533: } 534: else if ((flags & FormattableFlags.PLUS) != 0) 535: builder.insert(0, '+'); 536: else if ((flags & FormattableFlags.SPACE) != 0) 537: builder.insert(0, ' '); 538: } 539: 540: /** 541: * A helper method that handles emitting a String after applying 542: * precision, width, justification, and upper case flags. 543: * 544: * @param arg the string to emit. 545: * @param flags the formatting flags to use. 546: * @param width the width to use. 547: * @param precision the precision to use. 548: * @throws IOException if the output stream throws an I/O error. 549: */ 550: private void genericFormat(String arg, int flags, int width, int precision) 551: throws IOException 552: { 553: if ((flags & FormattableFlags.UPPERCASE) != 0) 554: { 555: if (fmtLocale == null) 556: arg = arg.toUpperCase(); 557: else 558: arg = arg.toUpperCase(fmtLocale); 559: } 560: 561: if (precision >= 0 && arg.length() > precision) 562: arg = arg.substring(0, precision); 563: 564: boolean leftJustify = (flags & FormattableFlags.LEFT_JUSTIFY) != 0; 565: if (leftJustify && width == -1) 566: throw new MissingFormatWidthException("fixme"); 567: if (! leftJustify && arg.length() < width) 568: { 569: for (int i = width - arg.length(); i > 0; --i) 570: out.append(' '); 571: } 572: out.append(arg); 573: if (leftJustify && arg.length() < width) 574: { 575: for (int i = width - arg.length(); i > 0; --i) 576: out.append(' '); 577: } 578: } 579: 580: /** 581: * Emit a boolean. 582: * 583: * @param arg the boolean to emit. 584: * @param flags the formatting flags to use. 585: * @param width the width to use. 586: * @param precision the precision to use. 587: * @param conversion the conversion character. 588: * @throws IOException if the output stream throws an I/O error. 589: */ 590: private void booleanFormat(Object arg, int flags, int width, int precision, 591: char conversion) 592: throws IOException 593: { 594: checkFlags(flags, 595: FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, 596: conversion); 597: String result; 598: if (arg instanceof Boolean) 599: result = String.valueOf((Boolean) arg); 600: else 601: result = arg == null ? "false" : "true"; 602: genericFormat(result, flags, width, precision); 603: } 604: 605: /** 606: * Emit a hash code. 607: * 608: * @param arg the hash code to emit. 609: * @param flags the formatting flags to use. 610: * @param width the width to use. 611: * @param precision the precision to use. 612: * @param conversion the conversion character. 613: * @throws IOException if the output stream throws an I/O error. 614: */ 615: private void hashCodeFormat(Object arg, int flags, int width, int precision, 616: char conversion) 617: throws IOException 618: { 619: checkFlags(flags, 620: FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, 621: conversion); 622: genericFormat(arg == null ? "null" : Integer.toHexString(arg.hashCode()), 623: flags, width, precision); 624: } 625: 626: /** 627: * Emit a String or Formattable conversion. 628: * 629: * @param arg the String or Formattable to emit. 630: * @param flags the formatting flags to use. 631: * @param width the width to use. 632: * @param precision the precision to use. 633: * @param conversion the conversion character. 634: * @throws IOException if the output stream throws an I/O error. 635: */ 636: private void stringFormat(Object arg, int flags, int width, int precision, 637: char conversion) 638: throws IOException 639: { 640: if (arg instanceof Formattable) 641: { 642: checkFlags(flags, 643: (FormattableFlags.LEFT_JUSTIFY 644: | FormattableFlags.UPPERCASE 645: | FormattableFlags.ALTERNATE), 646: conversion); 647: Formattable fmt = (Formattable) arg; 648: fmt.formatTo(this, flags, width, precision); 649: } 650: else 651: { 652: checkFlags(flags, 653: FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, 654: conversion); 655: genericFormat(arg == null ? "null" : arg.toString(), flags, width, 656: precision); 657: } 658: } 659: 660: /** 661: * Emit a character. 662: * 663: * @param arg the character to emit. 664: * @param flags the formatting flags to use. 665: * @param width the width to use. 666: * @param precision the precision to use. 667: * @param conversion the conversion character. 668: * @throws IOException if the output stream throws an I/O error. 669: */ 670: private void characterFormat(Object arg, int flags, int width, int precision, 671: char conversion) 672: throws IOException 673: { 674: checkFlags(flags, 675: FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, 676: conversion); 677: noPrecision(precision); 678: 679: int theChar; 680: if (arg instanceof Character) 681: theChar = ((Character) arg).charValue(); 682: else if (arg instanceof Byte) 683: theChar = (char) (((Byte) arg).byteValue ()); 684: else if (arg instanceof Short) 685: theChar = (char) (((Short) arg).shortValue ()); 686: else if (arg instanceof Integer) 687: { 688: theChar = ((Integer) arg).intValue(); 689: if (! Character.isValidCodePoint(theChar)) 690: throw new IllegalFormatCodePointException(theChar); 691: } 692: else 693: throw new IllegalFormatConversionException(conversion, arg.getClass()); 694: String result = new String(Character.toChars(theChar)); 695: genericFormat(result, flags, width, precision); 696: } 697: 698: /** 699: * Emit a '%'. 700: * 701: * @param flags the formatting flags to use. 702: * @param width the width to use. 703: * @param precision the precision to use. 704: * @throws IOException if the output stream throws an I/O error. 705: */ 706: private void percentFormat(int flags, int width, int precision) 707: throws IOException 708: { 709: checkFlags(flags, FormattableFlags.LEFT_JUSTIFY, '%'); 710: noPrecision(precision); 711: genericFormat("%", flags, width, precision); 712: } 713: 714: /** 715: * Emit a newline. 716: * 717: * @param flags the formatting flags to use. 718: * @param width the width to use. 719: * @param precision the precision to use. 720: * @throws IOException if the output stream throws an I/O error. 721: */ 722: private void newLineFormat(int flags, int width, int precision) 723: throws IOException 724: { 725: checkFlags(flags, 0, 'n'); 726: noPrecision(precision); 727: if (width != -1) 728: throw new IllegalFormatWidthException(width); 729: genericFormat(lineSeparator, flags, width, precision); 730: } 731: 732: /** 733: * Helper method to do initial formatting and checking for integral 734: * conversions. 735: * 736: * @param arg the formatted argument. 737: * @param flags the formatting flags to use. 738: * @param width the width to use. 739: * @param precision the precision to use. 740: * @param radix the radix of the number. 741: * @param conversion the conversion character. 742: * @return the result. 743: */ 744: private StringBuilder basicIntegralConversion(Object arg, int flags, 745: int width, int precision, 746: int radix, char conversion) 747: { 748: assert radix == 8 || radix == 10 || radix == 16; 749: noPrecision(precision); 750: 751: // Some error checking. 752: if ((flags & FormattableFlags.PLUS) != 0 753: && (flags & FormattableFlags.SPACE) != 0) 754: throw new IllegalFormatFlagsException(getName(flags)); 755: 756: if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0 && width == -1) 757: throw new MissingFormatWidthException("fixme"); 758: 759: // Do the base translation of the value to a string. 760: String result; 761: int basicFlags = (FormattableFlags.LEFT_JUSTIFY 762: // We already handled any possible error when 763: // parsing. 764: | FormattableFlags.UPPERCASE 765: | FormattableFlags.ZERO); 766: if (radix == 10) 767: basicFlags |= (FormattableFlags.PLUS 768: | FormattableFlags.SPACE 769: | FormattableFlags.COMMA 770: | FormattableFlags.PAREN); 771: else 772: basicFlags |= FormattableFlags.ALTERNATE; 773: 774: if (arg instanceof BigInteger) 775: { 776: checkFlags(flags, 777: (basicFlags 778: | FormattableFlags.PLUS 779: | FormattableFlags.SPACE 780: | FormattableFlags.PAREN), 781: conversion); 782: BigInteger bi = (BigInteger) arg; 783: result = bi.toString(radix); 784: } 785: else if (arg instanceof Number 786: && ! (arg instanceof Float) 787: && ! (arg instanceof Double)) 788: { 789: checkFlags(flags, basicFlags, conversion); 790: long value = ((Number) arg).longValue (); 791: if (radix == 8) 792: result = Long.toOctalString(value); 793: else if (radix == 16) 794: result = Long.toHexString(value); 795: else 796: result = Long.toString(value); 797: } 798: else 799: throw new IllegalFormatConversionException(conversion, arg.getClass()); 800: 801: return new StringBuilder(result); 802: } 803: 804: /** 805: * Emit a hex or octal value. 806: * 807: * @param arg the hexadecimal or octal value. 808: * @param flags the formatting flags to use. 809: * @param width the width to use. 810: * @param precision the precision to use. 811: * @param radix the radix of the number. 812: * @param conversion the conversion character. 813: * @throws IOException if the output stream throws an I/O error. 814: */ 815: private void hexOrOctalConversion(Object arg, int flags, int width, 816: int precision, int radix, 817: char conversion) 818: throws IOException 819: { 820: assert radix == 8 || radix == 16; 821: 822: StringBuilder builder = basicIntegralConversion(arg, flags, width, 823: precision, radix, 824: conversion); 825: int insertPoint = 0; 826: 827: // Insert the sign. 828: if (builder.charAt(0) == '-') 829: { 830: // Already inserted. Note that we don't insert a sign, since 831: // the only case where it is needed it BigInteger, and it has 832: // already been inserted by toString. 833: ++insertPoint; 834: } 835: else if ((flags & FormattableFlags.PLUS) != 0) 836: { 837: builder.insert(insertPoint, '+'); 838: ++insertPoint; 839: } 840: else if ((flags & FormattableFlags.SPACE) != 0) 841: { 842: builder.insert(insertPoint, ' '); 843: ++insertPoint; 844: } 845: 846: