/[classpath]/classpath/java/util/Formatter.java
ViewVC logotype

Diff of /classpath/java/util/Formatter.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.1.2.2 by tromey, Sun Sep 25 22:02:33 2005 UTC revision 1.1.2.3 by tromey, Mon Sep 26 15:32:55 2005 UTC
# Line 49  import java.io.OutputStreamWriter; Line 49  import java.io.OutputStreamWriter;
49  import java.io.PrintStream;  import java.io.PrintStream;
50  import java.io.UnsupportedEncodingException;  import java.io.UnsupportedEncodingException;
51  import java.math.BigInteger;  import java.math.BigInteger;
52    import java.text.DateFormatSymbols;
53    import java.text.DecimalFormatSymbols;
54    
55  import gnu.classpath.SystemProperties;  import gnu.classpath.SystemProperties;
56    
# Line 226  public class Formatter implements Closea Line 228  public class Formatter implements Closea
228    }    }
229    
230    /**    /**
231       * Apply the numeric localization algorithm to a StringBuilder.
232       */
233      private void applyLocalization(StringBuilder builder, int flags, int width,
234                                     boolean isNegative)
235      {
236        DecimalFormatSymbols dfsyms;
237        if (fmtLocale == null)
238          dfsyms = new DecimalFormatSymbols();
239        else
240          dfsyms = new DecimalFormatSymbols(fmtLocale);
241    
242        // First replace each digit.
243        char zeroDigit = dfsyms.getZeroDigit();
244        int decimalOffset = -1;
245        for (int i = builder.length() - 1; i >= 0; --i)
246          {
247            char c = builder.charAt(i);
248            if (c >= '0' && c <= '9')
249              builder.setCharAt(i, (char) (c - '0' + zeroDigit));
250            else if (c == '.')
251              {
252                assert decimalOffset == -1;
253                decimalOffset = i;
254              }
255          }
256    
257        // Localize the decimal separator.
258        if (decimalOffset != -1)
259          {
260            builder.deleteCharAt(decimalOffset);
261            builder.insert(decimalOffset, dfsyms.getDecimalSeparator());
262          }
263            
264        // Insert the grouping separators.
265        if ((flags & FormattableFlags.COMMA) != 0)
266          {
267            char groupSeparator = dfsyms.getGroupingSeparator();
268            int groupSize = 3;      // FIXME
269            int offset = (decimalOffset == -1) ? builder.length() : decimalOffset;
270            // We use '>' because we don't want to insert a separator
271            // before the first digit.
272            for (int i = offset - groupSize; i > 0; i -= groupSize)
273              builder.insert(i, groupSeparator);
274          }
275    
276        if ((flags & FormattableFlags.ZERO) != 0)
277          {
278            // Zero fill.  Note that according to the algorithm we do not
279            // insert grouping separators here.
280            for (int i = width - builder.length(); i > 0; --i)
281              builder.insert(0, zeroDigit);
282          }
283    
284        if (isNegative)
285          {
286            if ((flags & FormattableFlags.PAREN) != 0)
287              {
288                builder.insert(0, '(');
289                builder.append(')');
290              }
291            else
292              builder.insert(0, '-');
293          }
294        else if ((flags & FormattableFlags.PLUS) != 0)
295          builder.insert(0, '+');
296        else if ((flags & FormattableFlags.SPACE) != 0)
297          builder.insert(0, ' ');
298      }
299    
300      /**
301     * A helper method that handles emitting a String after applying     * A helper method that handles emitting a String after applying
302     * precision, width, justification, and upper case flags.     * precision, width, justification, and upper case flags.
303     */     */
304    private void genericFormat(String arg, int flags, int width, int precision)    private void genericFormat(String arg, int flags, int width, int precision)
305      throws IOException      throws IOException
306    {    {
     if (precision >= 0 && arg.length() > precision)  
       arg = arg.substring(0, precision);  
     boolean left = (flags & FormattableFlags.LEFT_JUSTIFY) != 0;  
     if (left && width == -1)  
       throw new MissingFormatWidthException("fixme");  
     if (! left && arg.length() < width)  
       {  
         for (int i = arg.length() - width; i >= 0; --i)  
           out.append(' ');  
       }  
307      if ((flags & FormattableFlags.UPPERCASE) != 0)      if ((flags & FormattableFlags.UPPERCASE) != 0)
308        {        {
309          if (fmtLocale == null)          if (fmtLocale == null)
# Line 249  public class Formatter implements Closea Line 311  public class Formatter implements Closea
311          else          else
312            arg = arg.toUpperCase(fmtLocale);            arg = arg.toUpperCase(fmtLocale);
313        }        }
314    
315        if (precision >= 0 && arg.length() > precision)
316          arg = arg.substring(0, precision);
317    
318        boolean leftJustify = (flags & FormattableFlags.LEFT_JUSTIFY) != 0;
319        if (leftJustify && width == -1)
320          throw new MissingFormatWidthException("fixme");
321        if (! leftJustify && arg.length() < width)
322          {
323            for (int i = width - arg.length(); i > 0; --i)
324              out.append(' ');
325          }
326      out.append(arg);      out.append(arg);
327      if (left && arg.length() < width)      if (leftJustify && arg.length() < width)
328        {        {
329          for (int i = arg.length() - width; i >= 0; --i)          for (int i = width - arg.length(); i > 0; --i)
330            out.append(' ');            out.append(' ');
331        }        }
332    }    }
# Line 359  public class Formatter implements Closea Line 433  public class Formatter implements Closea
433      genericFormat(lineSeparator, flags, width, precision);      genericFormat(lineSeparator, flags, width, precision);
434    }    }
435    
436    /** Emit a hex or octal value.  */    /**
437    private void hexOrOctalConversion(Object arg, int flags, int width,     * Helper method to do initial formatting and checking for integral
438                                      int precision, int radix,     * conversions.
439                                      char conversion)     */
440      throws IOException    private StringBuilder basicIntegralConversion(Object arg, int flags,
441                                                    int width, int precision,
442                                                    int radix, char conversion)
443    {    {
444      assert radix == 8 || radix == 16;      assert radix == 8 || radix == 10 || radix == 16;
445      noPrecision(precision);      noPrecision(precision);
446    
447      // Some error checking.      // Some error checking.
# Line 373  public class Formatter implements Closea Line 449  public class Formatter implements Closea
449          && (flags & FormattableFlags.LEFT_JUSTIFY) == 0)          && (flags & FormattableFlags.LEFT_JUSTIFY) == 0)
450        throw new IllegalFormatFlagsException(getName(flags));        throw new IllegalFormatFlagsException(getName(flags));
451      if ((flags & FormattableFlags.PLUS) != 0      if ((flags & FormattableFlags.PLUS) != 0
452          && (flags & FormattableFlags.SPACE) == 0)          && (flags & FormattableFlags.SPACE) != 0)
453        throw new IllegalFormatFlagsException(getName(flags));        throw new IllegalFormatFlagsException(getName(flags));
454    
455      if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0 && width == -1)      if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0 && width == -1)
# Line 381  public class Formatter implements Closea Line 457  public class Formatter implements Closea
457    
458      // Do the base translation of the value to a string.      // Do the base translation of the value to a string.
459      String result;      String result;
460        int basicFlags = (FormattableFlags.LEFT_JUSTIFY
461                          // We already handled any possible error when
462                          // parsing.
463                          | FormattableFlags.UPPERCASE
464                          | FormattableFlags.ZERO);
465        if (radix == 10)
466          basicFlags |= (FormattableFlags.PLUS
467                         | FormattableFlags.SPACE
468                         | FormattableFlags.COMMA
469                         | FormattableFlags.PAREN);
470        else
471          basicFlags |= FormattableFlags.ALTERNATE;
472    
473      if (arg instanceof BigInteger)      if (arg instanceof BigInteger)
474        {        {
475          checkFlags(flags,          checkFlags(flags,
476                     (FormattableFlags.LEFT_JUSTIFY                     (basicFlags
                     // We already handled any possible error when  
                     // parsing.  
                     | FormattableFlags.UPPERCASE  
                     | FormattableFlags.ALTERNATE  
477                      | FormattableFlags.PLUS                      | FormattableFlags.PLUS
478                      | FormattableFlags.SPACE                      | FormattableFlags.SPACE
                     | FormattableFlags.ZERO  
479                      | FormattableFlags.PAREN),                      | FormattableFlags.PAREN),
480                     conversion);                     conversion);
481          BigInteger bi = (BigInteger) arg;          BigInteger bi = (BigInteger) arg;
# Line 401  public class Formatter implements Closea Line 485  public class Formatter implements Closea
485               && ! (arg instanceof Float)               && ! (arg instanceof Float)
486               && ! (arg instanceof Double))               && ! (arg instanceof Double))
487        {        {
488          checkFlags(flags,          checkFlags(flags, basicFlags, conversion);
                    (FormattableFlags.LEFT_JUSTIFY  
                     // We already handled any possible error when  
                     // parsing.  
                     | FormattableFlags.UPPERCASE  
                     | FormattableFlags.ALTERNATE  
                     | FormattableFlags.ZERO),  
                    conversion);  
489          long value = ((Number) arg).longValue ();          long value = ((Number) arg).longValue ();
490          result = (radix == 8 ? Long.toOctalString(value)          if (radix == 8)
491                    : Long.toHexString(value));            result = Long.toOctalString(value);
492            else if (radix == 16)
493              result = Long.toHexString(value);
494            else
495              result = Long.toString(value);
496        }        }
497      else      else
498        throw new IllegalFormatConversionException(conversion, arg.getClass());        throw new IllegalFormatConversionException(conversion, arg.getClass());
499    
500      // We use a string builder to do further manipulations.      return new StringBuilder(result);
501      StringBuilder builder = new StringBuilder(result);    }
502    
503      /** Emit a hex or octal value.  */
504      private void hexOrOctalConversion(Object arg, int flags, int width,
505                                        int precision, int radix,
506                                        char conversion)
507        throws IOException
508      {
509        assert radix == 8 || radix == 16;
510    
511        StringBuilder builder = basicIntegralConversion(arg, flags, width,
512                                                        precision, radix,
513                                                        conversion);
514      int insertPoint = 0;      int insertPoint = 0;
515    
516      // Insert the sign.      // Insert the sign.
# Line 447  public class Formatter implements Closea Line 540  public class Formatter implements Closea
540        }        }
541    
542      // Now justify the result.      // Now justify the result.
543      int resultWidth = result.length();      int resultWidth = builder.length();
544      if (resultWidth < width)      if (resultWidth < width)
545        {        {
546          char fill = ((flags & FormattableFlags.ZERO) != 0) ? '0' : ' ';          char fill = ((flags & FormattableFlags.ZERO) != 0) ? '0' : ' ';
547          if ((flags & FormattableFlags.LEFT_JUSTIFY) == 0)          if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0)
548            {            {
549              // Right justify.              // Left justify.  
550              insertPoint = builder.length();              if (fill == ' ')
551                  insertPoint = builder.length();
552            }            }
553          else if (fill == ' ')          else
554            {            {
555              // Insert spaces before the radix prefix and sign.              // Right justify.  Insert spaces before the radix prefix
556                // and sign.
557              insertPoint = 0;              insertPoint = 0;
558            }            }
559          while (resultWidth++ < width)          while (resultWidth++ < width)
560            builder.insert(insertPoint, fill);            builder.insert(insertPoint, fill);
561        }        }
562    
563      result = builder.toString();      String result = builder.toString();
564      if ((flags & FormattableFlags.UPPERCASE) != 0)      if ((flags & FormattableFlags.UPPERCASE) != 0)
565        {        {
566          if (fmtLocale == null)          if (fmtLocale == null)
# Line 477  public class Formatter implements Closea Line 572  public class Formatter implements Closea
572      out.append(result);      out.append(result);
573    }    }
574    
575      /** Emit a decimal value.  */
576      private void decimalConversion(Object arg, int flags, int width,
577                                     int precision, char conversion)
578        throws IOException
579      {
580        StringBuilder builder = basicIntegralConversion(arg, flags, width,
581                                                        precision, 10,
582                                                        conversion);
583        boolean isNegative = false;
584        if (builder.charAt(0) == '-')
585          {
586            // Sign handling is done during localization.
587            builder.deleteCharAt(0);
588            isNegative = true;
589          }
590    
591        applyLocalization(builder, flags, width, isNegative);
592        genericFormat(builder.toString(), flags, width, precision);
593      }
594    
595      /** Emit a single date or time conversion to a StringBuilder.  */
596      private void singleDateTimeConversion(StringBuilder builder, Calendar cal,
597                                            char conversion,
598                                            DateFormatSymbols syms)
599      {
600        int oldLen = builder.length();
601        int digits = -1;
602        switch (conversion)
603          {
604          case 'H':
605            builder.append(cal.get(Calendar.HOUR_OF_DAY));
606            digits = 2;
607            break;
608          case 'I':
609            builder.append(cal.get(Calendar.HOUR));
610            digits = 2;
611            break;
612          case 'k':
613            builder.append(cal.get(Calendar.HOUR_OF_DAY));
614            break;
615          case 'l':
616            builder.append(cal.get(Calendar.HOUR));
617            break;
618          case 'M':
619            builder.append(cal.get(Calendar.MINUTE));
620            digits = 2;
621            break;
622          case 'S':
623            builder.append(cal.get(Calendar.SECOND));
624            digits = 2;
625            break;
626          case 'N':
627            // FIXME: nanosecond ...
628            digits = 9;
629            break;
630          case 'p':
631            {
632              int ampm = cal.get(Calendar.AM_PM);
633              builder.append(syms.getAmPmStrings()[ampm]);
634            }
635            break;
636          case 'z':
637            {
638              int zone = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60);
639              builder.append(zone);
640              digits = 4;
641              // Skip the '-' sign.
642              if (zone < 0)
643                ++oldLen;
644            }
645            break;
646          case 'Z':
647            {
648              // FIXME: DST?
649              int zone = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60);
650              String[][] zs = syms.getZoneStrings();
651              builder.append(zs[zone + 12][1]);
652            }
653            break;
654          case 's':
655            {
656              long val = cal.getTime().getTime();
657              builder.append(val / 1000);
658            }
659            break;
660          case 'Q':
661            {
662              long val = cal.getTime().getTime();
663              builder.append(val);
664            }
665            break;
666          case 'B':
667            {
668              int month = cal.get(Calendar.MONTH);
669              builder.append(syms.getMonths()[month]);
670            }
671            break;
672          case 'b':
673          case 'h':
674            {
675              int month = cal.get(Calendar.MONTH);
676              builder.append(syms.getShortMonths()[month]);
677            }
678            break;
679          case 'A':
680            {
681              int day = cal.get(Calendar.DAY_OF_WEEK);
682              builder.append(syms.getWeekdays()[day]);
683            }
684            break;
685          case 'a':
686            {
687              int day = cal.get(Calendar.DAY_OF_WEEK);
688              builder.append(syms.getShortWeekdays()[day]);
689            }
690            break;
691          case 'C':
692            builder.append(cal.get(Calendar.YEAR) / 100);
693            digits = 2;
694            break;
695          case 'Y':
696            builder.append(cal.get(Calendar.YEAR));
697            digits = 4;
698            break;
699          case 'y':
700            builder.append(cal.get(Calendar.YEAR) % 100);
701            digits = 2;
702            break;
703          case 'j':
704            builder.append(cal.get(Calendar.DAY_OF_YEAR));
705            digits = 3;
706            break;
707          case 'm':
708            builder.append(cal.get(Calendar.MONTH) + 1);
709            digits = 2;
710            break;
711          case 'd':
712            builder.append(cal.get(Calendar.DAY_OF_MONTH));
713            digits = 2;
714            break;
715          case 'e':
716            builder.append(cal.get(Calendar.DAY_OF_MONTH));
717            break;
718          case 'R':
719            singleDateTimeConversion(builder, cal, 'H', syms);
720            builder.append(':');
721            singleDateTimeConversion(builder, cal, 'M', syms);
722            break;
723          case 'T':
724            singleDateTimeConversion(builder, cal, 'H', syms);
725            builder.append(':');
726            singleDateTimeConversion(builder, cal, 'M', syms);
727            builder.append(':');
728            singleDateTimeConversion(builder, cal, 'S', syms);
729            break;
730          case 'r':
731            singleDateTimeConversion(builder, cal, 'I', syms);
732            builder.append(':');
733            singleDateTimeConversion(builder, cal, 'M', syms);
734            builder.append(':');
735            singleDateTimeConversion(builder, cal, 'S', syms);
736            builder.append(' ');
737            singleDateTimeConversion(builder, cal, 'p', syms);
738            break;
739          case 'D':
740            singleDateTimeConversion(builder, cal, 'm', syms);
741            builder.append('/');
742            singleDateTimeConversion(builder, cal, 'd', syms);
743            builder.append('/');
744            singleDateTimeConversion(builder, cal, 'y', syms);
745            break;
746          case 'F':
747            singleDateTimeConversion(builder, cal, 'Y', syms);
748            builder.append('-');
749            singleDateTimeConversion(builder, cal, 'm', syms);
750            builder.append('-');
751            singleDateTimeConversion(builder, cal, 'd', syms);
752            break;
753          case 'c':
754            singleDateTimeConversion(builder, cal, 'a', syms);
755            builder.append(' ');
756            singleDateTimeConversion(builder, cal, 'b', syms);
757            builder.append(' ');
758            singleDateTimeConversion(builder, cal, 'd', syms);
759            builder.append(' ');
760            singleDateTimeConversion(builder, cal, 'T', syms);
761            builder.append(' ');
762            singleDateTimeConversion(builder, cal, 'Z', syms);
763            builder.append(' ');
764            singleDateTimeConversion(builder, cal, 'Y', syms);
765            break;
766          default:
767            throw new UnknownFormatConversionException(String.valueOf(conversion));
768          }
769    
770        if (digits > 0)
771          {
772            int newLen = builder.length();
773            int delta = newLen - oldLen;
774            while (delta++ < digits)
775              builder.insert(oldLen, '0');
776          }
777      }
778    
779      /** Emit a date or time value.  */
780      private void dateTimeConversion(Object arg, int flags, int width,
781                                      int precision, char conversion,
782                                      char subConversion)
783        throws IOException
784      {
785        noPrecision(precision);
786        checkFlags(flags,
787                   FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
788                   conversion);
789    
790        Calendar cal;
791        if (arg instanceof Calendar)
792          cal = (Calendar) arg;
793        else
794          {
795            Date date;
796            if (arg instanceof Date)
797              date = (Date) arg;
798            else if (arg instanceof Long)
799              date = new Date(((Long) arg).longValue());
800            else
801              throw new IllegalFormatConversionException(conversion,
802                                                         arg.getClass());
803            if (fmtLocale == null)
804              cal = Calendar.getInstance();
805            else
806              cal = Calendar.getInstance(fmtLocale);
807            cal.setTime(date);
808          }
809    
810        // We could try to be more efficient by computing this lazily.
811        DateFormatSymbols syms;
812        if (fmtLocale == null)
813          syms = new DateFormatSymbols();
814        else
815          syms = new DateFormatSymbols(fmtLocale);
816    
817        StringBuilder result = new StringBuilder();
818        singleDateTimeConversion(result, cal, subConversion, syms);
819    
820        genericFormat(result.toString(), flags, width, precision);
821      }
822    
823    /**    /**
824     * Advance the internal parsing index, and throw an exception     * Advance the internal parsing index, and throw an exception
# Line 595  public class Formatter implements Closea Line 937  public class Formatter implements Closea
937    
938      try      try
939        {        {
940            fmtLocale = loc;
941          format = fmt;          format = fmt;
942          length = format.length();          length = format.length();
943          for (index = 0; index < length; ++index)          for (index = 0; index < length; ++index)
# Line 666  public class Formatter implements Closea Line 1009  public class Formatter implements Closea
1009                                  origConversion);                                  origConversion);
1010                  break;                  break;
1011                case 'd':                case 'd':
1012                  // decimalFormat();                  checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'd');
1013                    decimalConversion(argument, flags, width, precision,
1014                                      origConversion);
1015                  break;                  break;
1016                case 'o':                case 'o':
1017                  checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'o');                  checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'o');
# Line 689  public class Formatter implements Closea Line 1034  public class Formatter implements Closea
1034                  // hexFloatingConversion();                  // hexFloatingConversion();
1035                  break;                  break;
1036                case 't':                case 't':
1037  //              char format = parseDateTimeFormat();                  advance();
1038  //              timeDateFormat();                  char subConversion = format.charAt(index);
1039                    dateTimeConversion(argument, flags, width, precision,
1040                                       origConversion, subConversion);
1041                  break;                  break;
1042                case '%':                case '%':
1043                  percentFormat(flags, width, precision);                  percentFormat(flags, width, precision);

Legend:
Removed from v.1.1.2.2  
changed lines
  Added in v.1.1.2.3

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26