1 /*
2 * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
267 * encoding a date or time: {@code long}, {@link Long}, {@link Calendar},
268 * {@link Date} and {@link TemporalAccessor TemporalAccessor}
269 *
270 * <li> <b>Percent</b> - produces a literal {@code '%'}
271 * (<code>'\u0025'</code>)
272 *
273 * <li> <b>Line Separator</b> - produces the platform-specific line separator
274 *
275 * </ol>
276 *
277 * <p> For category <i>General</i>, <i>Character</i>, <i>Numberic</i>,
278 * <i>Integral</i> and <i>Date/Time</i> conversion, unless otherwise specified,
279 * if the argument <i>arg</i> is {@code null}, then the result is "{@code null}".
280 *
281 * <p> The following table summarizes the supported conversions. Conversions
282 * denoted by an upper-case character (i.e. {@code 'B'}, {@code 'H'},
283 * {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'}, {@code 'G'},
284 * {@code 'A'}, and {@code 'T'}) are the same as those for the corresponding
285 * lower-case conversion characters except that the result is converted to
286 * upper case according to the rules of the prevailing {@link java.util.Locale
287 * Locale}. The result is equivalent to the following invocation of {@link
288 * String#toUpperCase(Locale)}
289 *
290 * <pre>
291 * out.toUpperCase(Locale.getDefault(Locale.Category.FORMAT)) </pre>
292 *
293 * <table class="striped">
294 * <caption style="display:none">genConv</caption>
295 * <thead>
296 * <tr><th scope="col" style="vertical-align:bottom"> Conversion
297 * <th scope="col" style="vertical-align:bottom"> Argument Category
298 * <th scope="col" style="vertical-align:bottom"> Description
299 * </thead>
300 * <tbody>
301 * <tr><th scope="row" style="vertical-align:top"> {@code 'b'}, {@code 'B'}
302 * <td style="vertical-align:top"> general
303 * <td> If the argument <i>arg</i> is {@code null}, then the result is
304 * "{@code false}". If <i>arg</i> is a {@code boolean} or {@link
305 * Boolean}, then the result is the string returned by {@link
306 * String#valueOf(boolean) String.valueOf(arg)}. Otherwise, the result is
307 * "true".
308 *
309 * <tr><th scope="row" style="vertical-align:top"> {@code 'h'}, {@code 'H'}
310 * <td style="vertical-align:top"> general
311 * <td> The result is obtained by invoking
692 * respectively will be thrown.
693 *
694 * <p> If a format specifier contains a conversion character that is not
695 * applicable to the corresponding argument, then an {@link
696 * IllegalFormatConversionException} will be thrown.
697 *
698 * <p> All specified exceptions may be thrown by any of the {@code format}
699 * methods of {@code Formatter} as well as by any {@code format} convenience
700 * methods such as {@link String#format(String,Object...) String.format} and
701 * {@link java.io.PrintStream#printf(String,Object...) PrintStream.printf}.
702 *
703 * <p> For category <i>General</i>, <i>Character</i>, <i>Numberic</i>,
704 * <i>Integral</i> and <i>Date/Time</i> conversion, unless otherwise specified,
705 * if the argument <i>arg</i> is {@code null}, then the result is "{@code null}".
706 *
707 * <p> Conversions denoted by an upper-case character (i.e. {@code 'B'},
708 * {@code 'H'}, {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'},
709 * {@code 'G'}, {@code 'A'}, and {@code 'T'}) are the same as those for the
710 * corresponding lower-case conversion characters except that the result is
711 * converted to upper case according to the rules of the prevailing {@link
712 * java.util.Locale Locale}. The result is equivalent to the following
713 * invocation of {@link String#toUpperCase(Locale)}
714 *
715 * <pre>
716 * out.toUpperCase(Locale.getDefault(Locale.Category.FORMAT)) </pre>
717 *
718 * <h4><a id="dgen">General</a></h4>
719 *
720 * <p> The following general conversions may be applied to any argument type:
721 *
722 * <table class="striped">
723 * <caption style="display:none">dgConv</caption>
724 * <tbody>
725 *
726 * <tr><th scope="row" style="vertical-align:top"> {@code 'b'}
727 * <td style="vertical-align:top"> <code>'\u0062'</code>
728 * <td> Produces either "{@code true}" or "{@code false}" as returned by
729 * {@link Boolean#toString(boolean)}.
730 *
731 * <p> If the argument is {@code null}, then the result is
732 * "{@code false}". If the argument is a {@code boolean} or {@link
733 * Boolean}, then the result is the string returned by {@link
734 * String#valueOf(boolean) String.valueOf()}. Otherwise, the result is
735 * "{@code true}".
736 *
2880
2881 public void print(Object arg, Locale l) throws IOException {
2882 if (dt) {
2883 printDateTime(arg, l);
2884 return;
2885 }
2886 switch(c) {
2887 case Conversion.DECIMAL_INTEGER:
2888 case Conversion.OCTAL_INTEGER:
2889 case Conversion.HEXADECIMAL_INTEGER:
2890 printInteger(arg, l);
2891 break;
2892 case Conversion.SCIENTIFIC:
2893 case Conversion.GENERAL:
2894 case Conversion.DECIMAL_FLOAT:
2895 case Conversion.HEXADECIMAL_FLOAT:
2896 printFloat(arg, l);
2897 break;
2898 case Conversion.CHARACTER:
2899 case Conversion.CHARACTER_UPPER:
2900 printCharacter(arg);
2901 break;
2902 case Conversion.BOOLEAN:
2903 printBoolean(arg);
2904 break;
2905 case Conversion.STRING:
2906 printString(arg, l);
2907 break;
2908 case Conversion.HASHCODE:
2909 printHashCode(arg);
2910 break;
2911 case Conversion.LINE_SEPARATOR:
2912 a.append(System.lineSeparator());
2913 break;
2914 case Conversion.PERCENT_SIGN:
2915 a.append('%');
2916 break;
2917 default:
2918 assert false;
2919 }
2920 }
2921
2922 private void printInteger(Object arg, Locale l) throws IOException {
2923 if (arg == null)
2924 print("null");
2925 else if (arg instanceof Byte)
2926 print(((Byte)arg).byteValue(), l);
2927 else if (arg instanceof Short)
2928 print(((Short)arg).shortValue(), l);
2929 else if (arg instanceof Integer)
2930 print(((Integer)arg).intValue(), l);
2931 else if (arg instanceof Long)
2932 print(((Long)arg).longValue(), l);
2933 else if (arg instanceof BigInteger)
2934 print(((BigInteger)arg), l);
2935 else
2936 failConversion(c, arg);
2937 }
2938
2939 private void printFloat(Object arg, Locale l) throws IOException {
2940 if (arg == null)
2941 print("null");
2942 else if (arg instanceof Float)
2943 print(((Float)arg).floatValue(), l);
2944 else if (arg instanceof Double)
2945 print(((Double)arg).doubleValue(), l);
2946 else if (arg instanceof BigDecimal)
2947 print(((BigDecimal)arg), l);
2948 else
2949 failConversion(c, arg);
2950 }
2951
2952 private void printDateTime(Object arg, Locale l) throws IOException {
2953 if (arg == null) {
2954 print("null");
2955 return;
2956 }
2957 Calendar cal = null;
2958
2959 // Instead of Calendar.setLenient(true), perhaps we should
2960 // wrap the IllegalArgumentException that might be thrown?
2961 if (arg instanceof Long) {
2962 // Note that the following method uses an instance of the
2963 // default time zone (TimeZone.getDefaultRef().
2964 cal = Calendar.getInstance(l == null ? Locale.US : l);
2965 cal.setTimeInMillis((Long)arg);
2966 } else if (arg instanceof Date) {
2967 // Note that the following method uses an instance of the
2968 // default time zone (TimeZone.getDefaultRef().
2969 cal = Calendar.getInstance(l == null ? Locale.US : l);
2970 cal.setTime((Date)arg);
2971 } else if (arg instanceof Calendar) {
2972 cal = (Calendar) ((Calendar) arg).clone();
2973 cal.setLenient(true);
2974 } else if (arg instanceof TemporalAccessor) {
2975 print((TemporalAccessor) arg, c, l);
2976 return;
2977 } else {
2978 failConversion(c, arg);
2979 }
2980 // Use the provided locale so that invocations of
2981 // localizedMagnitude() use optimizations for null.
2982 print(cal, c, l);
2983 }
2984
2985 private void printCharacter(Object arg) throws IOException {
2986 if (arg == null) {
2987 print("null");
2988 return;
2989 }
2990 String s = null;
2991 if (arg instanceof Character) {
2992 s = ((Character)arg).toString();
2993 } else if (arg instanceof Byte) {
2994 byte i = ((Byte)arg).byteValue();
2995 if (Character.isValidCodePoint(i))
2996 s = new String(Character.toChars(i));
2997 else
2998 throw new IllegalFormatCodePointException(i);
2999 } else if (arg instanceof Short) {
3000 short i = ((Short)arg).shortValue();
3001 if (Character.isValidCodePoint(i))
3002 s = new String(Character.toChars(i));
3003 else
3004 throw new IllegalFormatCodePointException(i);
3005 } else if (arg instanceof Integer) {
3006 int i = ((Integer)arg).intValue();
3007 if (Character.isValidCodePoint(i))
3008 s = new String(Character.toChars(i));
3009 else
3010 throw new IllegalFormatCodePointException(i);
3011 } else {
3012 failConversion(c, arg);
3013 }
3014 print(s);
3015 }
3016
3017 private void printString(Object arg, Locale l) throws IOException {
3018 if (arg instanceof Formattable) {
3019 Formatter fmt = Formatter.this;
3020 if (fmt.locale() != l)
3021 fmt = new Formatter(fmt.out(), l);
3022 ((Formattable)arg).formatTo(fmt, f.valueOf(), width, precision);
3023 } else {
3024 if (f.contains(Flags.ALTERNATE))
3025 failMismatch(Flags.ALTERNATE, 's');
3026 if (arg == null)
3027 print("null");
3028 else
3029 print(arg.toString());
3030 }
3031 }
3032
3033 private void printBoolean(Object arg) throws IOException {
3034 String s;
3035 if (arg != null)
3036 s = ((arg instanceof Boolean)
3037 ? ((Boolean)arg).toString()
3038 : Boolean.toString(true));
3039 else
3040 s = Boolean.toString(false);
3041 print(s);
3042 }
3043
3044 private void printHashCode(Object arg) throws IOException {
3045 String s = (arg == null
3046 ? "null"
3047 : Integer.toHexString(arg.hashCode()));
3048 print(s);
3049 }
3050
3051 private void print(String s) throws IOException {
3052 if (precision != -1 && precision < s.length())
3053 s = s.substring(0, precision);
3054 if (f.contains(Flags.UPPERCASE))
3055 s = s.toUpperCase(Locale.getDefault(Locale.Category.FORMAT));
3056 appendJustified(a, s);
3057 }
3058
3059 private Appendable appendJustified(Appendable a, CharSequence cs) throws IOException {
3060 if (width == -1) {
3061 return a.append(cs);
3062 }
3063 boolean padRight = f.contains(Flags.LEFT_JUSTIFY);
3064 int sp = width - cs.length();
3065 if (padRight) {
3066 a.append(cs);
3067 }
3068 for (int i = 0; i < sp; i++) {
3069 a.append(' ');
3070 }
3071 if (!padRight) {
3072 a.append(cs);
3073 }
3074 return a;
3075 }
3076
3077 public String toString() {
3078 StringBuilder sb = new StringBuilder("%");
3259 sb.append('0');
3260 if (f.contains(Flags.ZERO_PAD)) {
3261 trailingZeros(sb, width - len);
3262 }
3263 sb.append(s);
3264 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3265 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3266 Flags.PLUS);
3267 String s = Long.toHexString(value);
3268 int len = (f.contains(Flags.ALTERNATE)
3269 ? s.length() + 2
3270 : s.length());
3271
3272 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3273 if (f.contains(Flags.ALTERNATE))
3274 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3275 if (f.contains(Flags.ZERO_PAD)) {
3276 trailingZeros(sb, width - len);
3277 }
3278 if (f.contains(Flags.UPPERCASE))
3279 s = s.toUpperCase(Locale.getDefault(Locale.Category.FORMAT));
3280 sb.append(s);
3281 }
3282
3283 // justify based on width
3284 appendJustified(a, sb);
3285 }
3286
3287 // neg := val < 0
3288 private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3289 if (!neg) {
3290 if (f.contains(Flags.PLUS)) {
3291 sb.append('+');
3292 } else if (f.contains(Flags.LEADING_SPACE)) {
3293 sb.append(' ');
3294 }
3295 } else {
3296 if (f.contains(Flags.PARENTHESES))
3297 sb.append('(');
3298 else
3299 sb.append('-');
3334 if (f.contains(Flags.ZERO_PAD)) {
3335 trailingZeros(sb, width - len);
3336 }
3337 sb.append(s);
3338 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3339 String s = v.toString(16);
3340
3341 int len = s.length() + sb.length();
3342 if (neg && f.contains(Flags.PARENTHESES))
3343 len++;
3344
3345 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3346 if (f.contains(Flags.ALTERNATE)) {
3347 len += 2;
3348 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3349 }
3350 if (f.contains(Flags.ZERO_PAD)) {
3351 trailingZeros(sb, width - len);
3352 }
3353 if (f.contains(Flags.UPPERCASE))
3354 s = s.toUpperCase(Locale.getDefault(Locale.Category.FORMAT));
3355 sb.append(s);
3356 }
3357
3358 // trailing sign indicator
3359 trailingSign(sb, (value.signum() == -1));
3360
3361 // justify based on width
3362 appendJustified(a, sb);
3363 }
3364
3365 private void print(float value, Locale l) throws IOException {
3366 print((double) value, l);
3367 }
3368
3369 private void print(double value, Locale l) throws IOException {
3370 StringBuilder sb = new StringBuilder();
3371 boolean neg = Double.compare(value, 0.0) == -1;
3372
3373 if (!Double.isNaN(value)) {
3374 double v = Math.abs(value);
3933 private int adjustWidth(int width, Flags f, boolean neg) {
3934 int newW = width;
3935 if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3936 newW--;
3937 return newW;
3938 }
3939
3940 // Add trailing zeros
3941 private void trailingZeros(StringBuilder sb, int nzeros) {
3942 for (int i = 0; i < nzeros; i++) {
3943 sb.append('0');
3944 }
3945 }
3946
3947 private void print(Calendar t, char c, Locale l) throws IOException {
3948 StringBuilder sb = new StringBuilder();
3949 print(sb, t, c, l);
3950
3951 // justify based on width
3952 if (f.contains(Flags.UPPERCASE)) {
3953 appendJustified(a, sb.toString().toUpperCase(Locale.getDefault(Locale.Category.FORMAT)));
3954 } else {
3955 appendJustified(a, sb);
3956 }
3957 }
3958
3959 private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3960 throws IOException {
3961 if (sb == null)
3962 sb = new StringBuilder();
3963 switch (c) {
3964 case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3965 case DateTime.HOUR_0: // 'I' (01 - 12)
3966 case DateTime.HOUR_OF_DAY: // 'k' (0 - 23) -- like H
3967 case DateTime.HOUR: { // 'l' (1 - 12) -- like I
3968 int i = t.get(Calendar.HOUR_OF_DAY);
3969 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3970 i = (i == 0 || i == 12 ? 12 : i % 12);
3971 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3972 || c == DateTime.HOUR_0
3973 ? Flags.ZERO_PAD
4115 case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4116 case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)
4117 char sep = ':';
4118 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4119 print(sb, t, DateTime.MINUTE, l);
4120 if (c == DateTime.TIME) {
4121 sb.append(sep);
4122 print(sb, t, DateTime.SECOND, l);
4123 }
4124 break;
4125 }
4126 case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)
4127 char sep = ':';
4128 print(sb, t, DateTime.HOUR_0, l).append(sep);
4129 print(sb, t, DateTime.MINUTE, l).append(sep);
4130 print(sb, t, DateTime.SECOND, l).append(' ');
4131 // this may be in wrong place for some locales
4132 StringBuilder tsb = new StringBuilder();
4133 print(tsb, t, DateTime.AM_PM, l);
4134
4135 sb.append(tsb.toString().toUpperCase(Objects.requireNonNullElse(l,
4136 Locale.getDefault(Locale.Category.FORMAT))));
4137 break;
4138 }
4139 case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4140 char sep = ' ';
4141 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4142 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4143 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4144 print(sb, t, DateTime.TIME, l).append(sep);
4145 print(sb, t, DateTime.ZONE, l).append(sep);
4146 print(sb, t, DateTime.YEAR_4, l);
4147 break;
4148 }
4149 case DateTime.DATE: { // 'D' (mm/dd/yy)
4150 char sep = '/';
4151 print(sb, t, DateTime.MONTH, l).append(sep);
4152 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4153 print(sb, t, DateTime.YEAR_2, l);
4154 break;
4155 }
4156 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4157 char sep = '-';
4158 print(sb, t, DateTime.YEAR_4, l).append(sep);
4159 print(sb, t, DateTime.MONTH, l).append(sep);
4160 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4161 break;
4162 }
4163 default:
4164 assert false;
4165 }
4166 return sb;
4167 }
4168
4169 private void print(TemporalAccessor t, char c, Locale l) throws IOException {
4170 StringBuilder sb = new StringBuilder();
4171 print(sb, t, c, l);
4172 // justify based on width
4173 if (f.contains(Flags.UPPERCASE)) {
4174 appendJustified(a, sb.toString().toUpperCase(Locale.getDefault(Locale.Category.FORMAT)));
4175 } else {
4176 appendJustified(a, sb);
4177 }
4178 }
4179
4180 private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4181 Locale l) throws IOException {
4182 if (sb == null)
4183 sb = new StringBuilder();
4184 try {
4185 switch (c) {
4186 case DateTime.HOUR_OF_DAY_0: { // 'H' (00 - 23)
4187 int i = t.get(ChronoField.HOUR_OF_DAY);
4188 sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4189 break;
4190 }
4191 case DateTime.HOUR_OF_DAY: { // 'k' (0 - 23) -- like H
4192 int i = t.get(ChronoField.HOUR_OF_DAY);
4193 sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4194 break;
4356 // Composites
4357 case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4358 case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)
4359 char sep = ':';
4360 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4361 print(sb, t, DateTime.MINUTE, l);
4362 if (c == DateTime.TIME) {
4363 sb.append(sep);
4364 print(sb, t, DateTime.SECOND, l);
4365 }
4366 break;
4367 }
4368 case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)
4369 char sep = ':';
4370 print(sb, t, DateTime.HOUR_0, l).append(sep);
4371 print(sb, t, DateTime.MINUTE, l).append(sep);
4372 print(sb, t, DateTime.SECOND, l).append(' ');
4373 // this may be in wrong place for some locales
4374 StringBuilder tsb = new StringBuilder();
4375 print(tsb, t, DateTime.AM_PM, l);
4376 sb.append(tsb.toString().toUpperCase(Objects.requireNonNullElse(l,
4377 Locale.getDefault(Locale.Category.FORMAT))));
4378 break;
4379 }
4380 case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4381 char sep = ' ';
4382 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4383 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4384 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4385 print(sb, t, DateTime.TIME, l).append(sep);
4386 print(sb, t, DateTime.ZONE, l).append(sep);
4387 print(sb, t, DateTime.YEAR_4, l);
4388 break;
4389 }
4390 case DateTime.DATE: { // 'D' (mm/dd/yy)
4391 char sep = '/';
4392 print(sb, t, DateTime.MONTH, l).append(sep);
4393 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4394 print(sb, t, DateTime.YEAR_2, l);
4395 break;
4396 }
4397 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
|
1 /*
2 * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
267 * encoding a date or time: {@code long}, {@link Long}, {@link Calendar},
268 * {@link Date} and {@link TemporalAccessor TemporalAccessor}
269 *
270 * <li> <b>Percent</b> - produces a literal {@code '%'}
271 * (<code>'\u0025'</code>)
272 *
273 * <li> <b>Line Separator</b> - produces the platform-specific line separator
274 *
275 * </ol>
276 *
277 * <p> For category <i>General</i>, <i>Character</i>, <i>Numberic</i>,
278 * <i>Integral</i> and <i>Date/Time</i> conversion, unless otherwise specified,
279 * if the argument <i>arg</i> is {@code null}, then the result is "{@code null}".
280 *
281 * <p> The following table summarizes the supported conversions. Conversions
282 * denoted by an upper-case character (i.e. {@code 'B'}, {@code 'H'},
283 * {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'}, {@code 'G'},
284 * {@code 'A'}, and {@code 'T'}) are the same as those for the corresponding
285 * lower-case conversion characters except that the result is converted to
286 * upper case according to the rules of the prevailing {@link java.util.Locale
287 * Locale}. If there is no explicit locale specified, either at the
288 * construction of the instance or as a parameter to its method
289 * invocation, then the {@link java.util.Locale.Category#FORMAT default locale}
290 * is used.
291 *
292 *
293 * <table class="striped">
294 * <caption style="display:none">genConv</caption>
295 * <thead>
296 * <tr><th scope="col" style="vertical-align:bottom"> Conversion
297 * <th scope="col" style="vertical-align:bottom"> Argument Category
298 * <th scope="col" style="vertical-align:bottom"> Description
299 * </thead>
300 * <tbody>
301 * <tr><th scope="row" style="vertical-align:top"> {@code 'b'}, {@code 'B'}
302 * <td style="vertical-align:top"> general
303 * <td> If the argument <i>arg</i> is {@code null}, then the result is
304 * "{@code false}". If <i>arg</i> is a {@code boolean} or {@link
305 * Boolean}, then the result is the string returned by {@link
306 * String#valueOf(boolean) String.valueOf(arg)}. Otherwise, the result is
307 * "true".
308 *
309 * <tr><th scope="row" style="vertical-align:top"> {@code 'h'}, {@code 'H'}
310 * <td style="vertical-align:top"> general
311 * <td> The result is obtained by invoking
692 * respectively will be thrown.
693 *
694 * <p> If a format specifier contains a conversion character that is not
695 * applicable to the corresponding argument, then an {@link
696 * IllegalFormatConversionException} will be thrown.
697 *
698 * <p> All specified exceptions may be thrown by any of the {@code format}
699 * methods of {@code Formatter} as well as by any {@code format} convenience
700 * methods such as {@link String#format(String,Object...) String.format} and
701 * {@link java.io.PrintStream#printf(String,Object...) PrintStream.printf}.
702 *
703 * <p> For category <i>General</i>, <i>Character</i>, <i>Numberic</i>,
704 * <i>Integral</i> and <i>Date/Time</i> conversion, unless otherwise specified,
705 * if the argument <i>arg</i> is {@code null}, then the result is "{@code null}".
706 *
707 * <p> Conversions denoted by an upper-case character (i.e. {@code 'B'},
708 * {@code 'H'}, {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'},
709 * {@code 'G'}, {@code 'A'}, and {@code 'T'}) are the same as those for the
710 * corresponding lower-case conversion characters except that the result is
711 * converted to upper case according to the rules of the prevailing {@link
712 * java.util.Locale Locale}. If there is no explicit locale specified,
713 * either at the construction of the instance or as a parameter to its method
714 * invocation, then the {@link java.util.Locale.Category#FORMAT default locale}
715 * is used.
716 *
717 * <h4><a id="dgen">General</a></h4>
718 *
719 * <p> The following general conversions may be applied to any argument type:
720 *
721 * <table class="striped">
722 * <caption style="display:none">dgConv</caption>
723 * <tbody>
724 *
725 * <tr><th scope="row" style="vertical-align:top"> {@code 'b'}
726 * <td style="vertical-align:top"> <code>'\u0062'</code>
727 * <td> Produces either "{@code true}" or "{@code false}" as returned by
728 * {@link Boolean#toString(boolean)}.
729 *
730 * <p> If the argument is {@code null}, then the result is
731 * "{@code false}". If the argument is a {@code boolean} or {@link
732 * Boolean}, then the result is the string returned by {@link
733 * String#valueOf(boolean) String.valueOf()}. Otherwise, the result is
734 * "{@code true}".
735 *
2879
2880 public void print(Object arg, Locale l) throws IOException {
2881 if (dt) {
2882 printDateTime(arg, l);
2883 return;
2884 }
2885 switch(c) {
2886 case Conversion.DECIMAL_INTEGER:
2887 case Conversion.OCTAL_INTEGER:
2888 case Conversion.HEXADECIMAL_INTEGER:
2889 printInteger(arg, l);
2890 break;
2891 case Conversion.SCIENTIFIC:
2892 case Conversion.GENERAL:
2893 case Conversion.DECIMAL_FLOAT:
2894 case Conversion.HEXADECIMAL_FLOAT:
2895 printFloat(arg, l);
2896 break;
2897 case Conversion.CHARACTER:
2898 case Conversion.CHARACTER_UPPER:
2899 printCharacter(arg, l);
2900 break;
2901 case Conversion.BOOLEAN:
2902 printBoolean(arg, l);
2903 break;
2904 case Conversion.STRING:
2905 printString(arg, l);
2906 break;
2907 case Conversion.HASHCODE:
2908 printHashCode(arg, l);
2909 break;
2910 case Conversion.LINE_SEPARATOR:
2911 a.append(System.lineSeparator());
2912 break;
2913 case Conversion.PERCENT_SIGN:
2914 a.append('%');
2915 break;
2916 default:
2917 assert false;
2918 }
2919 }
2920
2921 private void printInteger(Object arg, Locale l) throws IOException {
2922 if (arg == null)
2923 print("null", l);
2924 else if (arg instanceof Byte)
2925 print(((Byte)arg).byteValue(), l);
2926 else if (arg instanceof Short)
2927 print(((Short)arg).shortValue(), l);
2928 else if (arg instanceof Integer)
2929 print(((Integer)arg).intValue(), l);
2930 else if (arg instanceof Long)
2931 print(((Long)arg).longValue(), l);
2932 else if (arg instanceof BigInteger)
2933 print(((BigInteger)arg), l);
2934 else
2935 failConversion(c, arg);
2936 }
2937
2938 private void printFloat(Object arg, Locale l) throws IOException {
2939 if (arg == null)
2940 print("null", l);
2941 else if (arg instanceof Float)
2942 print(((Float)arg).floatValue(), l);
2943 else if (arg instanceof Double)
2944 print(((Double)arg).doubleValue(), l);
2945 else if (arg instanceof BigDecimal)
2946 print(((BigDecimal)arg), l);
2947 else
2948 failConversion(c, arg);
2949 }
2950
2951 private void printDateTime(Object arg, Locale l) throws IOException {
2952 if (arg == null) {
2953 print("null", l);
2954 return;
2955 }
2956 Calendar cal = null;
2957
2958 // Instead of Calendar.setLenient(true), perhaps we should
2959 // wrap the IllegalArgumentException that might be thrown?
2960 if (arg instanceof Long) {
2961 // Note that the following method uses an instance of the
2962 // default time zone (TimeZone.getDefaultRef().
2963 cal = Calendar.getInstance(l == null ? Locale.US : l);
2964 cal.setTimeInMillis((Long)arg);
2965 } else if (arg instanceof Date) {
2966 // Note that the following method uses an instance of the
2967 // default time zone (TimeZone.getDefaultRef().
2968 cal = Calendar.getInstance(l == null ? Locale.US : l);
2969 cal.setTime((Date)arg);
2970 } else if (arg instanceof Calendar) {
2971 cal = (Calendar) ((Calendar) arg).clone();
2972 cal.setLenient(true);
2973 } else if (arg instanceof TemporalAccessor) {
2974 print((TemporalAccessor) arg, c, l);
2975 return;
2976 } else {
2977 failConversion(c, arg);
2978 }
2979 // Use the provided locale so that invocations of
2980 // localizedMagnitude() use optimizations for null.
2981 print(cal, c, l);
2982 }
2983
2984 private void printCharacter(Object arg, Locale l) throws IOException {
2985 if (arg == null) {
2986 print("null", l);
2987 return;
2988 }
2989 String s = null;
2990 if (arg instanceof Character) {
2991 s = ((Character)arg).toString();
2992 } else if (arg instanceof Byte) {
2993 byte i = ((Byte)arg).byteValue();
2994 if (Character.isValidCodePoint(i))
2995 s = new String(Character.toChars(i));
2996 else
2997 throw new IllegalFormatCodePointException(i);
2998 } else if (arg instanceof Short) {
2999 short i = ((Short)arg).shortValue();
3000 if (Character.isValidCodePoint(i))
3001 s = new String(Character.toChars(i));
3002 else
3003 throw new IllegalFormatCodePointException(i);
3004 } else if (arg instanceof Integer) {
3005 int i = ((Integer)arg).intValue();
3006 if (Character.isValidCodePoint(i))
3007 s = new String(Character.toChars(i));
3008 else
3009 throw new IllegalFormatCodePointException(i);
3010 } else {
3011 failConversion(c, arg);
3012 }
3013 print(s, l);
3014 }
3015
3016 private void printString(Object arg, Locale l) throws IOException {
3017 if (arg instanceof Formattable) {
3018 Formatter fmt = Formatter.this;
3019 if (fmt.locale() != l)
3020 fmt = new Formatter(fmt.out(), l);
3021 ((Formattable)arg).formatTo(fmt, f.valueOf(), width, precision);
3022 } else {
3023 if (f.contains(Flags.ALTERNATE))
3024 failMismatch(Flags.ALTERNATE, 's');
3025 if (arg == null)
3026 print("null", l);
3027 else
3028 print(arg.toString(), l);
3029 }
3030 }
3031
3032 private void printBoolean(Object arg, Locale l) throws IOException {
3033 String s;
3034 if (arg != null)
3035 s = ((arg instanceof Boolean)
3036 ? ((Boolean)arg).toString()
3037 : Boolean.toString(true));
3038 else
3039 s = Boolean.toString(false);
3040 print(s, l);
3041 }
3042
3043 private void printHashCode(Object arg, Locale l) throws IOException {
3044 String s = (arg == null
3045 ? "null"
3046 : Integer.toHexString(arg.hashCode()));
3047 print(s, l);
3048 }
3049
3050 private void print(String s, Locale l) throws IOException {
3051 if (precision != -1 && precision < s.length())
3052 s = s.substring(0, precision);
3053 if (f.contains(Flags.UPPERCASE))
3054 s = toUpperCaseWithLocale(s, l);
3055 appendJustified(a, s);
3056 }
3057
3058 private String toUpperCaseWithLocale(String s, Locale l) {
3059 return s.toUpperCase(Objects.requireNonNullElse(l,
3060 Locale.getDefault(Locale.Category.FORMAT)));
3061 }
3062
3063 private Appendable appendJustified(Appendable a, CharSequence cs) throws IOException {
3064 if (width == -1) {
3065 return a.append(cs);
3066 }
3067 boolean padRight = f.contains(Flags.LEFT_JUSTIFY);
3068 int sp = width - cs.length();
3069 if (padRight) {
3070 a.append(cs);
3071 }
3072 for (int i = 0; i < sp; i++) {
3073 a.append(' ');
3074 }
3075 if (!padRight) {
3076 a.append(cs);
3077 }
3078 return a;
3079 }
3080
3081 public String toString() {
3082 StringBuilder sb = new StringBuilder("%");
3263 sb.append('0');
3264 if (f.contains(Flags.ZERO_PAD)) {
3265 trailingZeros(sb, width - len);
3266 }
3267 sb.append(s);
3268 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3269 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3270 Flags.PLUS);
3271 String s = Long.toHexString(value);
3272 int len = (f.contains(Flags.ALTERNATE)
3273 ? s.length() + 2
3274 : s.length());
3275
3276 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3277 if (f.contains(Flags.ALTERNATE))
3278 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3279 if (f.contains(Flags.ZERO_PAD)) {
3280 trailingZeros(sb, width - len);
3281 }
3282 if (f.contains(Flags.UPPERCASE))
3283 s = toUpperCaseWithLocale(s, l);
3284 sb.append(s);
3285 }
3286
3287 // justify based on width
3288 appendJustified(a, sb);
3289 }
3290
3291 // neg := val < 0
3292 private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3293 if (!neg) {
3294 if (f.contains(Flags.PLUS)) {
3295 sb.append('+');
3296 } else if (f.contains(Flags.LEADING_SPACE)) {
3297 sb.append(' ');
3298 }
3299 } else {
3300 if (f.contains(Flags.PARENTHESES))
3301 sb.append('(');
3302 else
3303 sb.append('-');
3338 if (f.contains(Flags.ZERO_PAD)) {
3339 trailingZeros(sb, width - len);
3340 }
3341 sb.append(s);
3342 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3343 String s = v.toString(16);
3344
3345 int len = s.length() + sb.length();
3346 if (neg && f.contains(Flags.PARENTHESES))
3347 len++;
3348
3349 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3350 if (f.contains(Flags.ALTERNATE)) {
3351 len += 2;
3352 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3353 }
3354 if (f.contains(Flags.ZERO_PAD)) {
3355 trailingZeros(sb, width - len);
3356 }
3357 if (f.contains(Flags.UPPERCASE))
3358 s = toUpperCaseWithLocale(s, l);
3359 sb.append(s);
3360 }
3361
3362 // trailing sign indicator
3363 trailingSign(sb, (value.signum() == -1));
3364
3365 // justify based on width
3366 appendJustified(a, sb);
3367 }
3368
3369 private void print(float value, Locale l) throws IOException {
3370 print((double) value, l);
3371 }
3372
3373 private void print(double value, Locale l) throws IOException {
3374 StringBuilder sb = new StringBuilder();
3375 boolean neg = Double.compare(value, 0.0) == -1;
3376
3377 if (!Double.isNaN(value)) {
3378 double v = Math.abs(value);
3937 private int adjustWidth(int width, Flags f, boolean neg) {
3938 int newW = width;
3939 if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3940 newW--;
3941 return newW;
3942 }
3943
3944 // Add trailing zeros
3945 private void trailingZeros(StringBuilder sb, int nzeros) {
3946 for (int i = 0; i < nzeros; i++) {
3947 sb.append('0');
3948 }
3949 }
3950
3951 private void print(Calendar t, char c, Locale l) throws IOException {
3952 StringBuilder sb = new StringBuilder();
3953 print(sb, t, c, l);
3954
3955 // justify based on width
3956 if (f.contains(Flags.UPPERCASE)) {
3957 appendJustified(a, toUpperCaseWithLocale(sb.toString(), l));
3958 } else {
3959 appendJustified(a, sb);
3960 }
3961 }
3962
3963 private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3964 throws IOException {
3965 if (sb == null)
3966 sb = new StringBuilder();
3967 switch (c) {
3968 case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3969 case DateTime.HOUR_0: // 'I' (01 - 12)
3970 case DateTime.HOUR_OF_DAY: // 'k' (0 - 23) -- like H
3971 case DateTime.HOUR: { // 'l' (1 - 12) -- like I
3972 int i = t.get(Calendar.HOUR_OF_DAY);
3973 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3974 i = (i == 0 || i == 12 ? 12 : i % 12);
3975 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3976 || c == DateTime.HOUR_0
3977 ? Flags.ZERO_PAD
4119 case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4120 case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)
4121 char sep = ':';
4122 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4123 print(sb, t, DateTime.MINUTE, l);
4124 if (c == DateTime.TIME) {
4125 sb.append(sep);
4126 print(sb, t, DateTime.SECOND, l);
4127 }
4128 break;
4129 }
4130 case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)
4131 char sep = ':';
4132 print(sb, t, DateTime.HOUR_0, l).append(sep);
4133 print(sb, t, DateTime.MINUTE, l).append(sep);
4134 print(sb, t, DateTime.SECOND, l).append(' ');
4135 // this may be in wrong place for some locales
4136 StringBuilder tsb = new StringBuilder();
4137 print(tsb, t, DateTime.AM_PM, l);
4138
4139 sb.append(toUpperCaseWithLocale(tsb.toString(), l));
4140 break;
4141 }
4142 case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4143 char sep = ' ';
4144 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4145 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4146 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4147 print(sb, t, DateTime.TIME, l).append(sep);
4148 print(sb, t, DateTime.ZONE, l).append(sep);
4149 print(sb, t, DateTime.YEAR_4, l);
4150 break;
4151 }
4152 case DateTime.DATE: { // 'D' (mm/dd/yy)
4153 char sep = '/';
4154 print(sb, t, DateTime.MONTH, l).append(sep);
4155 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4156 print(sb, t, DateTime.YEAR_2, l);
4157 break;
4158 }
4159 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4160 char sep = '-';
4161 print(sb, t, DateTime.YEAR_4, l).append(sep);
4162 print(sb, t, DateTime.MONTH, l).append(sep);
4163 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4164 break;
4165 }
4166 default:
4167 assert false;
4168 }
4169 return sb;
4170 }
4171
4172 private void print(TemporalAccessor t, char c, Locale l) throws IOException {
4173 StringBuilder sb = new StringBuilder();
4174 print(sb, t, c, l);
4175 // justify based on width
4176 if (f.contains(Flags.UPPERCASE)) {
4177 appendJustified(a, toUpperCaseWithLocale(sb.toString(), l));
4178 } else {
4179 appendJustified(a, sb);
4180 }
4181 }
4182
4183 private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4184 Locale l) throws IOException {
4185 if (sb == null)
4186 sb = new StringBuilder();
4187 try {
4188 switch (c) {
4189 case DateTime.HOUR_OF_DAY_0: { // 'H' (00 - 23)
4190 int i = t.get(ChronoField.HOUR_OF_DAY);
4191 sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4192 break;
4193 }
4194 case DateTime.HOUR_OF_DAY: { // 'k' (0 - 23) -- like H
4195 int i = t.get(ChronoField.HOUR_OF_DAY);
4196 sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4197 break;
4359 // Composites
4360 case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4361 case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)
4362 char sep = ':';
4363 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4364 print(sb, t, DateTime.MINUTE, l);
4365 if (c == DateTime.TIME) {
4366 sb.append(sep);
4367 print(sb, t, DateTime.SECOND, l);
4368 }
4369 break;
4370 }
4371 case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)
4372 char sep = ':';
4373 print(sb, t, DateTime.HOUR_0, l).append(sep);
4374 print(sb, t, DateTime.MINUTE, l).append(sep);
4375 print(sb, t, DateTime.SECOND, l).append(' ');
4376 // this may be in wrong place for some locales
4377 StringBuilder tsb = new StringBuilder();
4378 print(tsb, t, DateTime.AM_PM, l);
4379 sb.append(toUpperCaseWithLocale(tsb.toString(), l));
4380 break;
4381 }
4382 case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4383 char sep = ' ';
4384 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4385 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4386 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4387 print(sb, t, DateTime.TIME, l).append(sep);
4388 print(sb, t, DateTime.ZONE, l).append(sep);
4389 print(sb, t, DateTime.YEAR_4, l);
4390 break;
4391 }
4392 case DateTime.DATE: { // 'D' (mm/dd/yy)
4393 char sep = '/';
4394 print(sb, t, DateTime.MONTH, l).append(sep);
4395 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4396 print(sb, t, DateTime.YEAR_2, l);
4397 break;
4398 }
4399 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
|