OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [java/] [util/] [Formatter.java] - Blame information for rev 771

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 771 jeremybenn
/* 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 gnu.java.lang.CPStringBuilder;
42
 
43
import java.io.Closeable;
44
import java.io.File;
45
import java.io.FileNotFoundException;
46
import java.io.FileOutputStream;
47
import java.io.Flushable;
48
import java.io.IOException;
49
import java.io.OutputStream;
50
import java.io.OutputStreamWriter;
51
import java.io.PrintStream;
52
import java.io.UnsupportedEncodingException;
53
import java.math.BigInteger;
54
import java.text.DateFormatSymbols;
55
import java.text.DecimalFormatSymbols;
56
 
57
import gnu.classpath.SystemProperties;
58
 
59
/**
60
 * <p>
61
 * A Java formatter for <code>printf</code>-style format strings,
62
 * as seen in the C programming language.   This differs from the
63
 * C interpretation of such strings by performing much stricter
64
 * checking of format specifications and their corresponding
65
 * arguments.  While unknown conversions will be ignored in C,
66
 * and invalid conversions will only produce compiler warnings,
67
 * the Java version utilises a full range of run-time exceptions to
68
 * handle these cases.  The Java version is also more customisable
69
 * by virtue of the provision of the {@link Formattable} interface,
70
 * which allows an arbitrary class to be formatted by the formatter.
71
 * </p>
72
 * <p>
73
 * The formatter is accessible by more convienient static methods.
74
 * For example, streams now have appropriate format methods
75
 * (the equivalent of <code>fprintf</code>) as do <code>String</code>
76
 * objects (the equivalent of <code>sprintf</code>).
77
 * </p>
78
 * <p>
79
 * <strong>Note</strong>: the formatter is not thread-safe.  For
80
 * multi-threaded access, external synchronization should be provided.
81
 * </p>
82
 *
83
 * @author Tom Tromey (tromey@redhat.com)
84
 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
85
 * @since 1.5
86
 */
87
public final class Formatter
88
  implements Closeable, Flushable
89
{
90
 
91
  /**
92
   * The output of the formatter.
93
   */
94
  private Appendable out;
95
 
96
  /**
97
   * The locale used by the formatter.
98
   */
99
  private Locale locale;
100
 
101
  /**
102
   * Whether or not the formatter is closed.
103
   */
104
  private boolean closed;
105
 
106
  /**
107
   * The last I/O exception thrown by the output stream.
108
   */
109
  private IOException ioException;
110
 
111
  // Some state used when actually formatting.
112
  /**
113
   * The format string.
114
   */
115
  private String format;
116
 
117
  /**
118
   * The current index into the string.
119
   */
120
  private int index;
121
 
122
  /**
123
   * The length of the format string.
124
   */
125
  private int length;
126
 
127
  /**
128
   * The formatting locale.
129
   */
130
  private Locale fmtLocale;
131
 
132
  // Note that we include '-' twice.  The flags are ordered to
133
  // correspond to the values in FormattableFlags, and there is no
134
  // flag (in the sense of this field used when parsing) for
135
  // UPPERCASE; the second '-' serves as a placeholder.
136
  /**
137
   * A string used to index into the formattable flags.
138
   */
139
  private static final String FLAGS = "--#+ 0,(";
140
 
141
  /**
142
   * The system line separator.
143
   */
144
  private static final String lineSeparator
145
    = SystemProperties.getProperty("line.separator");
146
 
147
  /**
148
   * The type of numeric output format for a {@link BigDecimal}.
149
   */
150
  public enum BigDecimalLayoutForm
151
  {
152
    DECIMAL_FLOAT,
153
    SCIENTIFIC
154
  }
155
 
156
  /**
157
   * Constructs a new <code>Formatter</code> using the default
158
   * locale and a {@link StringBuilder} as the output stream.
159
   */
160
  public Formatter()
161
  {
162
    this(null, Locale.getDefault());
163
  }
164
 
165
  /**
166
   * Constructs a new <code>Formatter</code> using the specified
167
   * locale and a {@link StringBuilder} as the output stream.
168
   * If the locale is <code>null</code>, then no localization
169
   * is applied.
170
   *
171
   * @param loc the locale to use.
172
   */
173
  public Formatter(Locale loc)
174
  {
175
    this(null, loc);
176
  }
177
 
178
  /**
179
   * Constructs a new <code>Formatter</code> using the default
180
   * locale and the specified output stream.
181
   *
182
   * @param app the output stream to use.
183
   */
184
  public Formatter(Appendable app)
185
  {
186
    this(app, Locale.getDefault());
187
  }
188
 
189
  /**
190
   * Constructs a new <code>Formatter</code> using the specified
191
   * locale and the specified output stream.  If the locale is
192
   * <code>null</code>, then no localization is applied.
193
   *
194
   * @param app the output stream to use.
195
   * @param loc the locale to use.
196
   */
197
  public Formatter(Appendable app, Locale loc)
198
  {
199
    this.out = app == null ? new StringBuilder() : app;
200
    this.locale = loc;
201
  }
202
 
203
  /**
204
   * Constructs a new <code>Formatter</code> using the default
205
   * locale and character set, with the specified file as the
206
   * output stream.
207
   *
208
   * @param file the file to use for output.
209
   * @throws FileNotFoundException if the file does not exist
210
   *                               and can not be created.
211
   * @throws SecurityException if a security manager is present
212
   *                           and doesn't allow writing to the file.
213
   */
214
  public Formatter(File file)
215
    throws FileNotFoundException
216
  {
217
    this(new OutputStreamWriter(new FileOutputStream(file)));
218
  }
219
 
220
  /**
221
   * Constructs a new <code>Formatter</code> using the default
222
   * locale, with the specified file as the output stream
223
   * and the supplied character set.
224
   *
225
   * @param file the file to use for output.
226
   * @param charset the character set to use for output.
227
   * @throws FileNotFoundException if the file does not exist
228
   *                               and can not be created.
229
   * @throws SecurityException if a security manager is present
230
   *                           and doesn't allow writing to the file.
231
   * @throws UnsupportedEncodingException if the supplied character
232
   *                                      set is not supported.
233
   */
234
  public Formatter(File file, String charset)
235
    throws FileNotFoundException, UnsupportedEncodingException
236
  {
237
    this(file, charset, Locale.getDefault());
238
  }
239
 
240
  /**
241
   * Constructs a new <code>Formatter</code> using the specified
242
   * file as the output stream with the supplied character set
243
   * and locale.  If the locale is <code>null</code>, then no
244
   * localization is applied.
245
   *
246
   * @param file the file to use for output.
247
   * @param charset the character set to use for output.
248
   * @param loc the locale to use.
249
   * @throws FileNotFoundException if the file does not exist
250
   *                               and can not be created.
251
   * @throws SecurityException if a security manager is present
252
   *                           and doesn't allow writing to the file.
253
   * @throws UnsupportedEncodingException if the supplied character
254
   *                                      set is not supported.
255
   */
256
  public Formatter(File file, String charset, Locale loc)
257
    throws FileNotFoundException, UnsupportedEncodingException
258
  {
259
    this(new OutputStreamWriter(new FileOutputStream(file), charset),
260
         loc);
261
  }
262
 
263
  /**
264
   * Constructs a new <code>Formatter</code> using the default
265
   * locale and character set, with the specified output stream.
266
   *
267
   * @param out the output stream to use.
268
   */
269
  public Formatter(OutputStream out)
270
  {
271
    this(new OutputStreamWriter(out));
272
  }
273
 
274
  /**
275
   * Constructs a new <code>Formatter</code> using the default
276
   * locale, with the specified file output stream and the
277
   * supplied character set.
278
   *
279
   * @param out the output stream.
280
   * @param charset the character set to use for output.
281
   * @throws UnsupportedEncodingException if the supplied character
282
   *                                      set is not supported.
283
   */
284
  public Formatter(OutputStream out, String charset)
285
    throws UnsupportedEncodingException
286
  {
287
    this(out, charset, Locale.getDefault());
288
  }
289
 
290
  /**
291
   * Constructs a new <code>Formatter</code> using the specified
292
   * output stream with the supplied character set and locale.
293
   * If the locale is <code>null</code>, then no localization is
294
   * applied.
295
   *
296
   * @param out the output stream.
297
   * @param charset the character set to use for output.
298
   * @param loc the locale to use.
299
   * @throws UnsupportedEncodingException if the supplied character
300
   *                                      set is not supported.
301
   */
302
  public Formatter(OutputStream out, String charset, Locale loc)
303
    throws UnsupportedEncodingException
304
  {
305
    this(new OutputStreamWriter(out, charset), loc);
306
  }
307
 
308
  /**
309
   * Constructs a new <code>Formatter</code> using the default
310
   * locale with the specified output stream.  The character
311
   * set used is that of the output stream.
312
   *
313
   * @param out the output stream to use.
314
   */
315
  public Formatter(PrintStream out)
316
  {
317
    this((Appendable) out);
318
  }
319
 
320
  /**
321
   * Constructs a new <code>Formatter</code> using the default
322
   * locale and character set, with the specified file as the
323
   * output stream.
324
   *
325
   * @param file the file to use for output.
326
   * @throws FileNotFoundException if the file does not exist
327
   *                               and can not be created.
328
   * @throws SecurityException if a security manager is present
329
   *                           and doesn't allow writing to the file.
330
   */
331
  public Formatter(String file) throws FileNotFoundException
332
  {
333
    this(new OutputStreamWriter(new FileOutputStream(file)));
334
  }
335
 
336
  /**
337
   * Constructs a new <code>Formatter</code> using the default
338
   * locale, with the specified file as the output stream
339
   * and the supplied character set.
340
   *
341
   * @param file the file to use for output.
342
   * @param charset the character set to use for output.
343
   * @throws FileNotFoundException if the file does not exist
344
   *                               and can not be created.
345
   * @throws SecurityException if a security manager is present
346
   *                           and doesn't allow writing to the file.
347
   * @throws UnsupportedEncodingException if the supplied character
348
   *                                      set is not supported.
349
   */
350
  public Formatter(String file, String charset)
351
    throws FileNotFoundException, UnsupportedEncodingException
352
  {
353
    this(file, charset, Locale.getDefault());
354
  }
355
 
356
  /**
357
   * Constructs a new <code>Formatter</code> using the specified
358
   * file as the output stream with the supplied character set
359
   * and locale.  If the locale is <code>null</code>, then no
360
   * localization is applied.
361
   *
362
   * @param file the file to use for output.
363
   * @param charset the character set to use for output.
364
   * @param loc the locale to use.
365
   * @throws FileNotFoundException if the file does not exist
366
   *                               and can not be created.
367
   * @throws SecurityException if a security manager is present
368
   *                           and doesn't allow writing to the file.
369
   * @throws UnsupportedEncodingException if the supplied character
370
   *                                      set is not supported.
371
   */
372
  public Formatter(String file, String charset, Locale loc)
373
    throws FileNotFoundException, UnsupportedEncodingException
374
  {
375
    this(new OutputStreamWriter(new FileOutputStream(file), charset),
376
         loc);
377
  }
378
 
379
  /**
380
   * Closes the formatter, so as to release used resources.
381
   * If the underlying output stream supports the {@link Closeable}
382
   * interface, then this is also closed.  Attempts to use
383
   * a formatter instance, via any method other than
384
   * {@link #ioException()}, after closure results in a
385
   * {@link FormatterClosedException}.
386
   */
387
  public void close()
388
  {
389
    if (closed)
390
      return;
391
    try
392
      {
393
        if (out instanceof Closeable)
394
          ((Closeable) out).close();
395
      }
396
    catch (IOException _)
397
      {
398
        // FIXME: do we ignore these or do we set ioException?
399
        // The docs seem to indicate that we should ignore.
400
      }
401
    closed = true;
402
  }
403
 
404
  /**
405
   * Flushes the formatter, writing any cached data to the output
406
   * stream.  If the underlying output stream supports the
407
   * {@link Flushable} interface, it is also flushed.
408
   *
409
   * @throws FormatterClosedException if the formatter is closed.
410
   */
411
  public void flush()
412
  {
413
    if (closed)
414
      throw new FormatterClosedException();
415
    try
416
      {
417
        if (out instanceof Flushable)
418
          ((Flushable) out).flush();
419
      }
420
    catch (IOException _)
421
      {
422
        // FIXME: do we ignore these or do we set ioException?
423
        // The docs seem to indicate that we should ignore.
424
      }
425
  }
426
 
427
  /**
428
   * Return the name corresponding to a flag.
429
   *
430
   * @param flags the flag to return the name of.
431
   * @return the name of the flag.
432
   */
433
  private String getName(int flags)
434
  {
435
    // FIXME: do we want all the flags in here?
436
    // Or should we redo how this is reported?
437
    int bit = Integer.numberOfTrailingZeros(flags);
438
    return FLAGS.substring(bit, bit + 1);
439
  }
440
 
441
  /**
442
   * Verify the flags passed to a conversion.
443
   *
444
   * @param flags the flags to verify.
445
   * @param allowed the allowed flags mask.
446
   * @param conversion the conversion character.
447
   */
448
  private void checkFlags(int flags, int allowed, char conversion)
449
  {
450
    flags &= ~allowed;
451
    if (flags != 0)
452
      throw new FormatFlagsConversionMismatchException(getName(flags),
453
                                                       conversion);
454
  }
455
 
456
  /**
457
   * Throw an exception if a precision was specified.
458
   *
459
   * @param precision the precision value (-1 indicates not specified).
460
   */
461
  private void noPrecision(int precision)
462
  {
463
    if (precision != -1)
464
      throw new IllegalFormatPrecisionException(precision);
465
  }
466
 
467
  /**
468
   * Apply the numeric localization algorithm to a StringBuilder.
469
   *
470
   * @param builder the builder to apply to.
471
   * @param flags the formatting flags to use.
472
   * @param width the width of the numeric value.
473
   * @param isNegative true if the value is negative.
474
   */
475
  private void applyLocalization(CPStringBuilder builder, int flags, int width,
476
                                 boolean isNegative)
477
  {
478
    DecimalFormatSymbols dfsyms;
479
    if (fmtLocale == null)
480
      dfsyms = new DecimalFormatSymbols();
481
    else
482
      dfsyms = new DecimalFormatSymbols(fmtLocale);
483
 
484
    // First replace each digit.
485
    char zeroDigit = dfsyms.getZeroDigit();
486
    int decimalOffset = -1;
487
    for (int i = builder.length() - 1; i >= 0; --i)
488
      {
489
        char c = builder.charAt(i);
490
        if (c >= '0' && c <= '9')
491
          builder.setCharAt(i, (char) (c - '0' + zeroDigit));
492
        else if (c == '.')
493
          {
494
            assert decimalOffset == -1;
495
            decimalOffset = i;
496
          }
497
      }
498
 
499
    // Localize the decimal separator.
500
    if (decimalOffset != -1)
501
      {
502
        builder.deleteCharAt(decimalOffset);
503
        builder.insert(decimalOffset, dfsyms.getDecimalSeparator());
504
      }
505
 
506
    // Insert the grouping separators.
507
    if ((flags & FormattableFlags.COMMA) != 0)
508
      {
509
        char groupSeparator = dfsyms.getGroupingSeparator();
510
        int groupSize = 3;      // FIXME
511
        int offset = (decimalOffset == -1) ? builder.length() : decimalOffset;
512
        // We use '>' because we don't want to insert a separator
513
        // before the first digit.
514
        for (int i = offset - groupSize; i > 0; i -= groupSize)
515
          builder.insert(i, groupSeparator);
516
      }
517
 
518
    if ((flags & FormattableFlags.ZERO) != 0)
519
      {
520
        // Zero fill.  Note that according to the algorithm we do not
521
        // insert grouping separators here.
522
        for (int i = width - builder.length(); i > 0; --i)
523
          builder.insert(0, zeroDigit);
524
      }
525
 
526
    if (isNegative)
527
      {
528
        if ((flags & FormattableFlags.PAREN) != 0)
529
          {
530
            builder.insert(0, '(');
531
            builder.append(')');
532
          }
533
        else
534
          builder.insert(0, '-');
535
      }
536
    else if ((flags & FormattableFlags.PLUS) != 0)
537
      builder.insert(0, '+');
538
    else if ((flags & FormattableFlags.SPACE) != 0)
539
      builder.insert(0, ' ');
540
  }
541
 
542
  /**
543
   * A helper method that handles emitting a String after applying
544
   * precision, width, justification, and upper case flags.
545
   *
546
   * @param arg the string to emit.
547
   * @param flags the formatting flags to use.
548
   * @param width the width to use.
549
   * @param precision the precision to use.
550
   * @throws IOException if the output stream throws an I/O error.
551
   */
552
  private void genericFormat(String arg, int flags, int width, int precision)
553
    throws IOException
554
  {
555
    if ((flags & FormattableFlags.UPPERCASE) != 0)
556
      {
557
        if (fmtLocale == null)
558
          arg = arg.toUpperCase();
559
        else
560
          arg = arg.toUpperCase(fmtLocale);
561
      }
562
 
563
    if (precision >= 0 && arg.length() > precision)
564
      arg = arg.substring(0, precision);
565
 
566
    boolean leftJustify = (flags & FormattableFlags.LEFT_JUSTIFY) != 0;
567
    if (leftJustify && width == -1)
568
      throw new MissingFormatWidthException("fixme");
569
    if (! leftJustify && arg.length() < width)
570
      {
571
        for (int i = width - arg.length(); i > 0; --i)
572
          out.append(' ');
573
      }
574
    out.append(arg);
575
    if (leftJustify && arg.length() < width)
576
      {
577
        for (int i = width - arg.length(); i > 0; --i)
578
          out.append(' ');
579
      }
580
  }
581
 
582
  /**
583
   * Emit a boolean.
584
   *
585
   * @param arg the boolean to emit.
586
   * @param flags the formatting flags to use.
587
   * @param width the width to use.
588
   * @param precision the precision to use.
589
   * @param conversion the conversion character.
590
   * @throws IOException if the output stream throws an I/O error.
591
   */
592
  private void booleanFormat(Object arg, int flags, int width, int precision,
593
                             char conversion)
594
    throws IOException
595
  {
596
    checkFlags(flags,
597
               FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
598
               conversion);
599
    String result;
600
    if (arg instanceof Boolean)
601
      result = String.valueOf((Boolean) arg);
602
    else
603
      result = arg == null ? "false" : "true";
604
    genericFormat(result, flags, width, precision);
605
  }
606
 
607
  /**
608
   * Emit a hash code.
609
   *
610
   * @param arg the hash code to emit.
611
   * @param flags the formatting flags to use.
612
   * @param width the width to use.
613
   * @param precision the precision to use.
614
   * @param conversion the conversion character.
615
   * @throws IOException if the output stream throws an I/O error.
616
   */
617
  private void hashCodeFormat(Object arg, int flags, int width, int precision,
618
                              char conversion)
619
    throws IOException
620
  {
621
    checkFlags(flags,
622
               FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
623
               conversion);
624
    genericFormat(arg == null ? "null" : Integer.toHexString(arg.hashCode()),
625
                  flags, width, precision);
626
  }
627
 
628
  /**
629
   * Emit a String or Formattable conversion.
630
   *
631
   * @param arg the String or Formattable to emit.
632
   * @param flags the formatting flags to use.
633
   * @param width the width to use.
634
   * @param precision the precision to use.
635
   * @param conversion the conversion character.
636
   * @throws IOException if the output stream throws an I/O error.
637
   */
638
  private void stringFormat(Object arg, int flags, int width, int precision,
639
                            char conversion)
640
    throws IOException
641
  {
642
    if (arg instanceof Formattable)
643
      {
644
        checkFlags(flags,
645
                   (FormattableFlags.LEFT_JUSTIFY
646
                    | FormattableFlags.UPPERCASE
647
                    | FormattableFlags.ALTERNATE),
648
                   conversion);
649
        Formattable fmt = (Formattable) arg;
650
        fmt.formatTo(this, flags, width, precision);
651
      }
652
    else
653
      {
654
        checkFlags(flags,
655
                   FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
656
                   conversion);
657
        genericFormat(arg == null ? "null" : arg.toString(), flags, width,
658
                      precision);
659
      }
660
  }
661
 
662
  /**
663
   * Emit a character.
664
   *
665
   * @param arg the character to emit.
666
   * @param flags the formatting flags to use.
667
   * @param width the width to use.
668
   * @param precision the precision to use.
669
   * @param conversion the conversion character.
670
   * @throws IOException if the output stream throws an I/O error.
671
   */
672
  private void characterFormat(Object arg, int flags, int width, int precision,
673
                               char conversion)
674
    throws IOException
675
  {
676
    checkFlags(flags,
677
               FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
678
               conversion);
679
    noPrecision(precision);
680
 
681
    int theChar;
682
    if (arg instanceof Character)
683
      theChar = ((Character) arg).charValue();
684
    else if (arg instanceof Byte)
685
      theChar = (char) (((Byte) arg).byteValue ());
686
    else if (arg instanceof Short)
687
      theChar = (char) (((Short) arg).shortValue ());
688
    else if (arg instanceof Integer)
689
      {
690
        theChar = ((Integer) arg).intValue();
691
        if (! Character.isValidCodePoint(theChar))
692
          throw new IllegalFormatCodePointException(theChar);
693
      }
694
    else
695
      throw new IllegalFormatConversionException(conversion, arg.getClass());
696
    String result = new String(Character.toChars(theChar));
697
    genericFormat(result, flags, width, precision);
698
  }
699
 
700
  /**
701
   * Emit a '%'.
702
   *
703
   * @param flags the formatting flags to use.
704
   * @param width the width to use.
705
   * @param precision the precision to use.
706
   * @throws IOException if the output stream throws an I/O error.
707
   */
708
  private void percentFormat(int flags, int width, int precision)
709
    throws IOException
710
  {
711
    checkFlags(flags, FormattableFlags.LEFT_JUSTIFY, '%');
712
    noPrecision(precision);
713
    genericFormat("%", flags, width, precision);
714
  }
715
 
716
  /**
717
   * Emit a newline.
718
   *
719
   * @param flags the formatting flags to use.
720
   * @param width the width to use.
721
   * @param precision the precision to use.
722
   * @throws IOException if the output stream throws an I/O error.
723
   */
724
  private void newLineFormat(int flags, int width, int precision)
725
    throws IOException
726
  {
727
    checkFlags(flags, 0, 'n');
728
    noPrecision(precision);
729
    if (width != -1)
730
      throw new IllegalFormatWidthException(width);
731
    genericFormat(lineSeparator, flags, width, precision);
732
  }
733
 
734
  /**
735
   * Helper method to do initial formatting and checking for integral
736
   * conversions.
737
   *
738
   * @param arg the formatted argument.
739
   * @param flags the formatting flags to use.
740
   * @param width the width to use.
741
   * @param precision the precision to use.
742
   * @param radix the radix of the number.
743
   * @param conversion the conversion character.
744
   * @return the result.
745
   */
746
  private CPStringBuilder basicIntegralConversion(Object arg, int flags,
747
                                                  int width, int precision,
748
                                                  int radix, char conversion)
749
  {
750
    assert radix == 8 || radix == 10 || radix == 16;
751
    noPrecision(precision);
752
 
753
    // Some error checking.
754
    if ((flags & FormattableFlags.PLUS) != 0
755
        && (flags & FormattableFlags.SPACE) != 0)
756
      throw new IllegalFormatFlagsException(getName(flags));
757
 
758
    if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0 && width == -1)
759
      throw new MissingFormatWidthException("fixme");
760
 
761
    // Do the base translation of the value to a string.
762
    String result;
763
    int basicFlags = (FormattableFlags.LEFT_JUSTIFY
764
                      // We already handled any possible error when
765
                      // parsing.
766
                      | FormattableFlags.UPPERCASE
767
                      | FormattableFlags.ZERO);
768
    if (radix == 10)
769
      basicFlags |= (FormattableFlags.PLUS
770
                     | FormattableFlags.SPACE
771
                     | FormattableFlags.COMMA
772
                     | FormattableFlags.PAREN);
773
    else
774
      basicFlags |= FormattableFlags.ALTERNATE;
775
 
776
    if (arg instanceof BigInteger)
777
      {
778
        checkFlags(flags,
779
                   (basicFlags
780
                    | FormattableFlags.PLUS
781
                    | FormattableFlags.SPACE
782
                    | FormattableFlags.PAREN),
783
                   conversion);
784
        BigInteger bi = (BigInteger) arg;
785
        result = bi.toString(radix);
786
      }
787
    else if (arg instanceof Number
788
             && ! (arg instanceof Float)
789
             && ! (arg instanceof Double))
790
      {
791
        checkFlags(flags, basicFlags, conversion);
792
        long value = ((Number) arg).longValue ();
793
        if (radix == 8)
794
          result = Long.toOctalString(value);
795
        else if (radix == 16)
796
          result = Long.toHexString(value);
797
        else
798
          result = Long.toString(value);
799
      }
800
    else
801
      throw new IllegalFormatConversionException(conversion, arg.getClass());
802
 
803
    return new CPStringBuilder(result);
804
  }
805
 
806
  /**
807
   * Emit a hex or octal value.
808
   *
809
   * @param arg the hexadecimal or octal value.
810
   * @param flags the formatting flags to use.
811
   * @param width the width to use.
812
   * @param precision the precision to use.
813
   * @param radix the radix of the number.
814
   * @param conversion the conversion character.
815
   * @throws IOException if the output stream throws an I/O error.
816
   */
817
  private void hexOrOctalConversion(Object arg, int flags, int width,
818
                                    int precision, int radix,
819
                                    char conversion)
820
    throws IOException
821
  {
822
    assert radix == 8 || radix == 16;
823
 
824
    CPStringBuilder builder = basicIntegralConversion(arg, flags, width,
825
                                                      precision, radix,
826
                                                      conversion);
827
    int insertPoint = 0;
828
 
829
    // Insert the sign.
830
    if (builder.charAt(0) == '-')
831
      {
832
        // Already inserted.  Note that we don't insert a sign, since
833
        // the only case where it is needed it BigInteger, and it has
834
        // already been inserted by toString.
835
        ++insertPoint;
836
      }
837
    else if ((flags & FormattableFlags.PLUS) != 0)
838
      {
839
        builder.insert(insertPoint, '+');
840
        ++insertPoint;
841
      }
842
    else if ((flags & FormattableFlags.SPACE) != 0)
843
      {
844
        builder.insert(insertPoint, ' ');
845
        ++insertPoint;
846
      }
847
 
848
    // Insert the radix prefix.
849
    if ((flags & FormattableFlags.ALTERNATE) != 0)
850
      {
851
        builder.insert(insertPoint, radix == 8 ? "0" : "0x");
852
        insertPoint += radix == 8 ? 1 : 2;
853
      }
854
 
855
    // Now justify the result.
856
    int resultWidth = builder.length();
857
    if (resultWidth < width)
858
      {
859
        char fill = ((flags & FormattableFlags.ZERO) != 0) ? '0' : ' ';
860
        if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0)
861
          {
862
            // Left justify.
863
            if (fill == ' ')
864
              insertPoint = builder.length();
865
          }
866
        else
867
          {
868
            // Right justify.  Insert spaces before the radix prefix
869
            // and sign.
870
            insertPoint = 0;
871
          }
872
        while (resultWidth++ < width)
873
          builder.insert(insertPoint, fill);
874
      }
875
 
876
    String result = builder.toString();
877
    if ((flags & FormattableFlags.UPPERCASE) != 0)
878
      {
879
        if (fmtLocale == null)
880
          result = result.toUpperCase();
881
        else
882
          result = result.toUpperCase(fmtLocale);
883
      }
884
 
885
    out.append(result);
886
  }
887
 
888
  /**
889
   * Emit a decimal value.
890
   *
891
   * @param arg the hexadecimal or octal value.
892
   * @param flags the formatting flags to use.
893
   * @param width the width to use.
894
   * @param precision the precision to use.
895
   * @param conversion the conversion character.
896
   * @throws IOException if the output stream throws an I/O error.
897
   */
898
  private void decimalConversion(Object arg, int flags, int width,
899
                                 int precision, char conversion)
900
    throws IOException
901
  {
902
    CPStringBuilder builder = basicIntegralConversion(arg, flags, width,
903
                                                      precision, 10,
904
                                                      conversion);
905
    boolean isNegative = false;
906
    if (builder.charAt(0) == '-')
907
      {
908
        // Sign handling is done during localization.
909
        builder.deleteCharAt(0);
910
        isNegative = true;
911
      }
912
 
913
    applyLocalization(builder, flags, width, isNegative);
914
    genericFormat(builder.toString(), flags, width, precision);
915
  }
916
 
917
  /**
918
   * Emit a single date or time conversion to a StringBuilder.
919
   *
920
   * @param builder the builder to write to.
921
   * @param cal the calendar to use in the conversion.
922
   * @param conversion the formatting character to specify the type of data.
923
   * @param syms the date formatting symbols.
924
   */
925
  private void singleDateTimeConversion(CPStringBuilder builder, Calendar cal,
926
                                        char conversion,
927
                                        DateFormatSymbols syms)
928
  {
929
    int oldLen = builder.length();
930
    int digits = -1;
931
    switch (conversion)
932
      {
933
      case 'H':
934
        builder.append(cal.get(Calendar.HOUR_OF_DAY));
935
        digits = 2;
936
        break;
937
      case 'I':
938
        builder.append(cal.get(Calendar.HOUR));
939
        digits = 2;
940
        break;
941
      case 'k':
942
        builder.append(cal.get(Calendar.HOUR_OF_DAY));
943
        break;
944
      case 'l':
945
        builder.append(cal.get(Calendar.HOUR));
946
        break;
947
      case 'M':
948
        builder.append(cal.get(Calendar.MINUTE));
949
        digits = 2;
950
        break;
951
      case 'S':
952
        builder.append(cal.get(Calendar.SECOND));
953
        digits = 2;
954
        break;
955
      case 'N':
956
        // FIXME: nanosecond ...
957
        digits = 9;
958
        break;
959
      case 'p':
960
        {
961
          int ampm = cal.get(Calendar.AM_PM);
962
          builder.append(syms.getAmPmStrings()[ampm]);
963
        }
964
        break;
965
      case 'z':
966
        {
967
          int zone = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60);
968
          builder.append(zone);
969
          digits = 4;
970
          // Skip the '-' sign.
971
          if (zone < 0)
972
            ++oldLen;
973
        }
974
        break;
975
      case 'Z':
976
        {
977
          // FIXME: DST?
978
          int zone = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60);
979
          String[][] zs = syms.getZoneStrings();
980
          builder.append(zs[zone + 12][1]);
981
        }
982
        break;
983
      case 's':
984
        {
985
          long val = cal.getTime().getTime();
986
          builder.append(val / 1000);
987
        }
988
        break;
989
      case 'Q':
990
        {
991
          long val = cal.getTime().getTime();
992
          builder.append(val);
993
        }
994
        break;
995
      case 'B':
996
        {
997
          int month = cal.get(Calendar.MONTH);
998
          builder.append(syms.getMonths()[month]);
999
        }
1000
        break;
1001
      case 'b':
1002
      case 'h':
1003
        {
1004
          int month = cal.get(Calendar.MONTH);
1005
          builder.append(syms.getShortMonths()[month]);
1006
        }
1007
        break;
1008
      case 'A':
1009
        {
1010
          int day = cal.get(Calendar.DAY_OF_WEEK);
1011
          builder.append(syms.getWeekdays()[day]);
1012
        }
1013
        break;
1014
      case 'a':
1015
        {
1016
          int day = cal.get(Calendar.DAY_OF_WEEK);
1017
          builder.append(syms.getShortWeekdays()[day]);
1018
        }
1019
        break;
1020
      case 'C':
1021
        builder.append(cal.get(Calendar.YEAR) / 100);
1022
        digits = 2;
1023
        break;
1024
      case 'Y':
1025
        builder.append(cal.get(Calendar.YEAR));
1026
        digits = 4;
1027
        break;
1028
      case 'y':
1029
        builder.append(cal.get(Calendar.YEAR) % 100);
1030
        digits = 2;
1031
        break;
1032
      case 'j':
1033
        builder.append(cal.get(Calendar.DAY_OF_YEAR));
1034
        digits = 3;
1035
        break;
1036
      case 'm':
1037
        builder.append(cal.get(Calendar.MONTH) + 1);
1038
        digits = 2;
1039
        break;
1040
      case 'd':
1041
        builder.append(cal.get(Calendar.DAY_OF_MONTH));
1042
        digits = 2;
1043
        break;
1044
      case 'e':
1045
        builder.append(cal.get(Calendar.DAY_OF_MONTH));
1046
        break;
1047
      case 'R':
1048
        singleDateTimeConversion(builder, cal, 'H', syms);
1049
        builder.append(':');
1050
        singleDateTimeConversion(builder, cal, 'M', syms);
1051
        break;
1052
      case 'T':
1053
        singleDateTimeConversion(builder, cal, 'H', syms);
1054
        builder.append(':');
1055
        singleDateTimeConversion(builder, cal, 'M', syms);
1056
        builder.append(':');
1057
        singleDateTimeConversion(builder, cal, 'S', syms);
1058
        break;
1059
      case 'r':
1060
        singleDateTimeConversion(builder, cal, 'I', syms);
1061
        builder.append(':');
1062
        singleDateTimeConversion(builder, cal, 'M', syms);
1063
        builder.append(':');
1064
        singleDateTimeConversion(builder, cal, 'S', syms);
1065
        builder.append(' ');
1066
        singleDateTimeConversion(builder, cal, 'p', syms);
1067
        break;
1068
      case 'D':
1069
        singleDateTimeConversion(builder, cal, 'm', syms);
1070
        builder.append('/');
1071
        singleDateTimeConversion(builder, cal, 'd', syms);
1072
        builder.append('/');
1073
        singleDateTimeConversion(builder, cal, 'y', syms);
1074
        break;
1075
      case 'F':
1076
        singleDateTimeConversion(builder, cal, 'Y', syms);
1077
        builder.append('-');
1078
        singleDateTimeConversion(builder, cal, 'm', syms);
1079
        builder.append('-');
1080
        singleDateTimeConversion(builder, cal, 'd', syms);
1081
        break;
1082
      case 'c':
1083
        singleDateTimeConversion(builder, cal, 'a', syms);
1084
        builder.append(' ');
1085
        singleDateTimeConversion(builder, cal, 'b', syms);
1086
        builder.append(' ');
1087
        singleDateTimeConversion(builder, cal, 'd', syms);
1088
        builder.append(' ');
1089
        singleDateTimeConversion(builder, cal, 'T', syms);
1090
        builder.append(' ');
1091
        singleDateTimeConversion(builder, cal, 'Z', syms);
1092
        builder.append(' ');
1093
        singleDateTimeConversion(builder, cal, 'Y', syms);
1094
        break;
1095
      default:
1096
        throw new UnknownFormatConversionException(String.valueOf(conversion));
1097
      }
1098
 
1099
    if (digits > 0)
1100
      {
1101
        int newLen = builder.length();
1102
        int delta = newLen - oldLen;
1103
        while (delta++ < digits)
1104
          builder.insert(oldLen, '0');
1105
      }
1106
  }
1107
 
1108
  /**
1109
   * Emit a date or time value.
1110
   *
1111
   * @param arg the date or time value.
1112
   * @param flags the formatting flags to use.
1113
   * @param width the width to use.
1114
   * @param precision the precision to use.
1115
   * @param conversion the conversion character.
1116
   * @param subConversion the sub conversion character.
1117
   * @throws IOException if the output stream throws an I/O error.
1118
   */
1119
  private void dateTimeConversion(Object arg, int flags, int width,
1120
                                  int precision, char conversion,
1121
                                  char subConversion)
1122
    throws IOException
1123
  {
1124
    noPrecision(precision);
1125
    checkFlags(flags,
1126
               FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
1127
               conversion);
1128
 
1129
    Calendar cal;
1130
    if (arg instanceof Calendar)
1131
      cal = (Calendar) arg;
1132
    else
1133
      {
1134
        Date date;
1135
        if (arg instanceof Date)
1136
          date = (Date) arg;
1137
        else if (arg instanceof Long)
1138
          date = new Date(((Long) arg).longValue());
1139
        else
1140
          throw new IllegalFormatConversionException(conversion,
1141
                                                     arg.getClass());
1142
        if (fmtLocale == null)
1143
          cal = Calendar.getInstance();
1144
        else
1145
          cal = Calendar.getInstance(fmtLocale);
1146
        cal.setTime(date);
1147
      }
1148
 
1149
    // We could try to be more efficient by computing this lazily.
1150
    DateFormatSymbols syms;
1151
    if (fmtLocale == null)
1152
      syms = new DateFormatSymbols();
1153
    else
1154
      syms = new DateFormatSymbols(fmtLocale);
1155
 
1156
    CPStringBuilder result = new CPStringBuilder();
1157
    singleDateTimeConversion(result, cal, subConversion, syms);
1158
 
1159
    genericFormat(result.toString(), flags, width, precision);
1160
  }
1161
 
1162
  /**
1163
   * Advance the internal parsing index, and throw an exception
1164
   * on overrun.
1165
   *
1166
   * @throws IllegalArgumentException on overrun.
1167
   */
1168
  private void advance()
1169
  {
1170
    ++index;
1171
    if (index >= length)
1172
      {
1173
        // FIXME: what exception here?
1174
        throw new IllegalArgumentException();
1175
      }
1176
  }
1177
 
1178
  /**
1179
   * Parse an integer appearing in the format string.  Will return -1
1180
   * if no integer was found.
1181
   *
1182
   * @return the parsed integer.
1183
   */
1184
  private int parseInt()
1185
  {
1186
    int start = index;
1187
    while (Character.isDigit(format.charAt(index)))
1188
      advance();
1189
    if (start == index)
1190
      return -1;
1191
    return Integer.decode(format.substring(start, index));
1192
  }
1193
 
1194
  /**
1195
   * Parse the argument index.  Returns -1 if there was no index, 0 if
1196
   * we should re-use the previous index, and a positive integer to
1197
   * indicate an absolute index.
1198
   *
1199
   * @return the parsed argument index.
1200
   */
1201
  private int parseArgumentIndex()
1202
  {
1203
    int result = -1;
1204
    int start = index;
1205
    if (format.charAt(index) == '<')
1206
      {
1207
        result = 0;
1208
        advance();
1209
      }
1210
    else if (Character.isDigit(format.charAt(index)))
1211
      {
1212
        result = parseInt();
1213
        if (format.charAt(index) == '$')
1214
          advance();
1215
        else
1216
          {
1217
            // Reset.
1218
            index = start;
1219
            result = -1;
1220
          }
1221
      }
1222
    return result;
1223
  }
1224
 
1225
  /**
1226
   * Parse a set of flags and return a bit mask of values from
1227
   * FormattableFlags.  Will throw an exception if a flag is
1228
   * duplicated.
1229
   *
1230
   * @return the parsed flags.
1231
   */
1232
  private int parseFlags()
1233
  {
1234
    int value = 0;
1235
    int start = index;
1236
    while (true)
1237
      {
1238
        int x = FLAGS.indexOf(format.charAt(index));
1239
        if (x == -1)
1240
          break;
1241
        int newValue = 1 << x;
1242
        if ((value & newValue) != 0)
1243
          throw new DuplicateFormatFlagsException(format.substring(start,
1244
                                                                   index + 1));
1245
        value |= newValue;
1246
        advance();
1247
      }
1248
    return value;
1249
  }
1250
 
1251
  /**
1252
   * Parse the width part of a format string.  Returns -1 if no width
1253
   * was specified.
1254
   *
1255
   * @return the parsed width.
1256
   */
1257
  private int parseWidth()
1258
  {
1259
    return parseInt();
1260
  }
1261
 
1262
  /**
1263
   * If the current character is '.', parses the precision part of a
1264
   * format string.  Returns -1 if no precision was specified.
1265
   *
1266
   * @return the parsed precision.
1267
   */
1268
  private int parsePrecision()
1269
  {
1270
    if (format.charAt(index) != '.')
1271
      return -1;
1272
    advance();
1273
    int precision = parseInt();
1274
    if (precision == -1)
1275
      // FIXME
1276
      throw new IllegalArgumentException();
1277
    return precision;
1278
  }
1279
 
1280
  /**
1281
   * Outputs a formatted string based on the supplied specification,
1282
   * <code>fmt</code>, and its arguments using the specified locale.
1283
   * The locale of the formatter does not change as a result; the
1284
   * specified locale is just used for this particular formatting
1285
   * operation.  If the locale is <code>null</code>, then no
1286
   * localization is applied.
1287
   *
1288
   * @param loc the locale to use for this format.
1289
   * @param fmt the format specification.
1290
   * @param args the arguments to apply to the specification.
1291
   * @throws IllegalFormatException if there is a problem with
1292
   *                                the syntax of the format
1293
   *                                specification or a mismatch
1294
   *                                between it and the arguments.
1295
   * @throws FormatterClosedException if the formatter is closed.
1296
   */
1297
  public Formatter format(Locale loc, String fmt, Object... args)
1298
  {
1299
    if (closed)
1300
      throw new FormatterClosedException();
1301
 
1302
    // Note the arguments are indexed starting at 1.
1303
    int implicitArgumentIndex = 1;
1304
    int previousArgumentIndex = 0;
1305
 
1306
    try
1307
      {
1308
        fmtLocale = loc;
1309
        format = fmt;
1310
        length = format.length();
1311
        for (index = 0; index < length; ++index)
1312
          {
1313
            char c = format.charAt(index);
1314
            if (c != '%')
1315
              {
1316
                out.append(c);
1317
                continue;
1318
              }
1319
 
1320
            int start = index;
1321
            advance();
1322
 
1323
            // We do the needed post-processing of this later, when we
1324
            // determine whether an argument is actually needed by
1325
            // this conversion.
1326
            int argumentIndex = parseArgumentIndex();
1327
 
1328
            int flags = parseFlags();
1329
            int width = parseWidth();
1330
            int precision = parsePrecision();
1331
            char origConversion = format.charAt(index);
1332
            char conversion = origConversion;
1333
            if (Character.isUpperCase(conversion))
1334
              {
1335
                flags |= FormattableFlags.UPPERCASE;
1336
                conversion = Character.toLowerCase(conversion);
1337
              }
1338
 
1339
            Object argument = null;
1340
            if (conversion == '%' || conversion == 'n')
1341
              {
1342
                if (argumentIndex != -1)
1343
                  {
1344
                    // FIXME: not sure about this.
1345
                    throw new UnknownFormatConversionException("FIXME");
1346
                  }
1347
              }
1348
            else
1349
              {
1350
                if (argumentIndex == -1)
1351
                  argumentIndex = implicitArgumentIndex++;
1352
                else if (argumentIndex == 0)
1353
                  argumentIndex = previousArgumentIndex;
1354
                // Argument indices start at 1 but array indices at 0.
1355
                --argumentIndex;
1356
                if (argumentIndex < 0 || argumentIndex >= args.length)
1357
                  throw new MissingFormatArgumentException(format.substring(start, index));
1358
                argument = args[argumentIndex];
1359
              }
1360
 
1361
            switch (conversion)
1362
              {
1363
              case 'b':
1364
                booleanFormat(argument, flags, width, precision,
1365
                              origConversion);
1366
                break;
1367
              case 'h':
1368
                hashCodeFormat(argument, flags, width, precision,
1369
                               origConversion);
1370
                break;
1371
              case 's':
1372
                stringFormat(argument, flags, width, precision,
1373
                             origConversion);
1374
                break;
1375
              case 'c':
1376
                characterFormat(argument, flags, width, precision,
1377
                                origConversion);
1378
                break;
1379
              case 'd':
1380
                checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'd');
1381
                decimalConversion(argument, flags, width, precision,
1382
                                  origConversion);
1383
                break;
1384
              case 'o':
1385
                checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'o');
1386
                hexOrOctalConversion(argument, flags, width, precision, 8,
1387
                                     origConversion);
1388
                break;
1389
              case 'x':
1390
                hexOrOctalConversion(argument, flags, width, precision, 16,
1391
                                     origConversion);
1392
              case 'e':
1393
                // scientificNotationConversion();
1394
                break;
1395
              case 'f':
1396
                // floatingDecimalConversion();
1397
                break;
1398
              case 'g':
1399
                // smartFloatingConversion();
1400
                break;
1401
              case 'a':
1402
                // hexFloatingConversion();
1403
                break;
1404
              case 't':
1405
                advance();
1406
                char subConversion = format.charAt(index);
1407
                dateTimeConversion(argument, flags, width, precision,
1408
                                   origConversion, subConversion);
1409
                break;
1410
              case '%':
1411
                percentFormat(flags, width, precision);
1412
                break;
1413
              case 'n':
1414
                newLineFormat(flags, width, precision);
1415
                break;
1416
              default:
1417
                throw new UnknownFormatConversionException(String.valueOf(origConversion));
1418
              }
1419
          }
1420
      }
1421
    catch (IOException exc)
1422
      {
1423
        ioException = exc;
1424
      }
1425
    return this;
1426
  }
1427
 
1428
  /**
1429
   * Outputs a formatted string based on the supplied specification,
1430
   * <code>fmt</code>, and its arguments using the formatter's locale.
1431
   *
1432
   * @param format the format specification.
1433
   * @param args the arguments to apply to the specification.
1434
   * @throws IllegalFormatException if there is a problem with
1435
   *                                the syntax of the format
1436
   *                                specification or a mismatch
1437
   *                                between it and the arguments.
1438
   * @throws FormatterClosedException if the formatter is closed.
1439
   */
1440
  public Formatter format(String format, Object... args)
1441
  {
1442
    return format(locale, format, args);
1443
  }
1444
 
1445
  /**
1446
   * Returns the last I/O exception thrown by the
1447
   * <code>append()</code> operation of the underlying
1448
   * output stream.
1449
   *
1450
   * @return the last I/O exception.
1451
   */
1452
  public IOException ioException()
1453
  {
1454
    return ioException;
1455
  }
1456
 
1457
  /**
1458
   * Returns the locale used by this formatter.
1459
   *
1460
   * @return the formatter's locale.
1461
   * @throws FormatterClosedException if the formatter is closed.
1462
   */
1463
  public Locale locale()
1464
  {
1465
    if (closed)
1466
      throw new FormatterClosedException();
1467
    return locale;
1468
  }
1469
 
1470
  /**
1471
   * Returns the output stream used by this formatter.
1472
   *
1473
   * @return the formatter's output stream.
1474
   * @throws FormatterClosedException if the formatter is closed.
1475
   */
1476
  public Appendable out()
1477
  {
1478
    if (closed)
1479
      throw new FormatterClosedException();
1480
    return out;
1481
  }
1482
 
1483
  /**
1484
   * Returns the result of applying {@link Object#toString()}
1485
   * to the underlying output stream.  The results returned
1486
   * depend on the particular {@link Appendable} being used.
1487
   * For example, a {@link StringBuilder} will return the
1488
   * formatted output but an I/O stream will not.
1489
   *
1490
   * @throws FormatterClosedException if the formatter is closed.
1491
   */
1492
  public String toString()
1493
  {
1494
    if (closed)
1495
      throw new FormatterClosedException();
1496
    return out.toString();
1497
  }
1498
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.