1
1
/*
2
2
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
3
+ * Copyright (c) 2023, Alibaba Group Holding Limited. All Rights Reserved.
3
4
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
5
*
5
6
* This code is free software; you can redistribute it and/or modify it
49
50
import java .text .DecimalFormatSymbols ;
50
51
import java .text .NumberFormat ;
51
52
import java .text .spi .NumberFormatProvider ;
52
- import java .util .regex .Matcher ;
53
- import java .util .regex .Pattern ;
54
53
55
54
import java .time .DateTimeException ;
56
55
import java .time .Instant ;
@@ -2810,20 +2809,14 @@ public Formatter format(Locale l, String format, Object ... args) {
2810
2809
return this ;
2811
2810
}
2812
2811
2813
- // %[argument_index$][flags][width][.precision][t]conversion
2814
- static final String FORMAT_SPECIFIER
2815
- = "%(\\ d+\\ $)?([-#+ 0,(\\ <]*)?(\\ d+)?(\\ .\\ d+)?([tT])?([a-zA-Z%])" ;
2816
-
2817
- static final Pattern FORMAT_SPECIFIER_PATTERN = Pattern .compile (FORMAT_SPECIFIER );
2818
-
2819
2812
/**
2820
2813
* Finds format specifiers in the format string.
2821
2814
*/
2822
2815
static List <FormatString > parse (String s ) {
2816
+ FormatSpecifierParser parser = null ;
2823
2817
ArrayList <FormatString > al = new ArrayList <>();
2824
2818
int i = 0 ;
2825
2819
int max = s .length ();
2826
- Matcher m = null ; // create if needed
2827
2820
while (i < max ) {
2828
2821
int n = s .indexOf ('%' , i );
2829
2822
if (n < 0 ) {
@@ -2846,14 +2839,16 @@ static List<FormatString> parse(String s) {
2846
2839
al .add (new FormatSpecifier (c ));
2847
2840
i ++;
2848
2841
} else {
2849
- if (m == null ) {
2850
- m = FORMAT_SPECIFIER_PATTERN .matcher (s );
2851
- }
2852
2842
// We have already parsed a '%' at n, so we either have a
2853
2843
// match or the specifier at n is invalid
2854
- if (m .find (n ) && m .start () == n ) {
2855
- al .add (new FormatSpecifier (s , m ));
2856
- i = m .end ();
2844
+ if (parser == null ) {
2845
+ parser = new FormatSpecifierParser (al , c , i , s , max );
2846
+ } else {
2847
+ parser .reset (c , i );
2848
+ }
2849
+ int off = parser .parse ();
2850
+ if (off > 0 ) {
2851
+ i += off ;
2857
2852
} else {
2858
2853
throw new UnknownFormatConversionException (String .valueOf (c ));
2859
2854
}
@@ -2862,6 +2857,159 @@ static List<FormatString> parse(String s) {
2862
2857
return al ;
2863
2858
}
2864
2859
2860
+ static final class FormatSpecifierParser {
2861
+ final ArrayList <FormatString > al ;
2862
+ final String s ;
2863
+ final int max ;
2864
+ char first ;
2865
+ int start ;
2866
+ int off ;
2867
+ char c ;
2868
+ int argSize ;
2869
+ int flagSize ;
2870
+ int widthSize ;
2871
+
2872
+ FormatSpecifierParser (ArrayList <FormatString > al , char first , int start , String s , int max ) {
2873
+ this .al = al ;
2874
+
2875
+ this .first = first ;
2876
+ this .c = first ;
2877
+ this .start = start ;
2878
+ this .off = start ;
2879
+
2880
+ this .s = s ;
2881
+ this .max = max ;
2882
+ }
2883
+
2884
+ void reset (char first , int start ) {
2885
+ this .first = first ;
2886
+ this .c = first ;
2887
+ this .start = start ;
2888
+ this .off = start ;
2889
+
2890
+ argSize = 0 ;
2891
+ flagSize = 0 ;
2892
+ widthSize = 0 ;
2893
+ }
2894
+
2895
+ /**
2896
+ * If a valid format specifier is found, construct a FormatString and add it to {@link #al}.
2897
+ * The format specifiers for general, character, and numeric types have
2898
+ * the following syntax:
2899
+ *
2900
+ * <blockquote><pre>
2901
+ * %[argument_index$][flags][width][.precision]conversion
2902
+ * </pre></blockquote>
2903
+ *
2904
+ * As described by the following regular expression:
2905
+ *
2906
+ * <blockquote><pre>
2907
+ * %(\d+\$)?([-#+ 0,(\<]*)?(\d+)?(\.\d+)?([tT])?([a-zA-Z%])
2908
+ * </pre></blockquote>
2909
+ *
2910
+ * @return the length of the format specifier. If no valid format specifier is found, 0 is returned.
2911
+ */
2912
+ int parse () {
2913
+ int precisionSize = 0 ;
2914
+
2915
+ // (\d+\$)?
2916
+ parseArgument ();
2917
+
2918
+ // ([-#+ 0,(\<]*)?
2919
+ parseFlag ();
2920
+
2921
+ // (\d+)?
2922
+ parseWidth ();
2923
+
2924
+ if (c == '.' ) {
2925
+ // (\.\d+)?
2926
+ precisionSize = parsePrecision ();
2927
+ if (precisionSize == -1 ) {
2928
+ return 0 ;
2929
+ }
2930
+ }
2931
+
2932
+ // ([tT])?([a-zA-Z%])
2933
+ char t = '\0' , conversion = '\0' ;
2934
+ if ((c == 't' || c == 'T' ) && off + 1 < max ) {
2935
+ char c1 = s .charAt (off + 1 );
2936
+ if (isConversion (c1 )) {
2937
+ t = c ;
2938
+ conversion = c1 ;
2939
+ off += 2 ;
2940
+ }
2941
+ } else if (isConversion (c )) {
2942
+ conversion = c ;
2943
+ ++off ;
2944
+ } else {
2945
+ return 0 ;
2946
+ }
2947
+
2948
+ if (argSize + flagSize + widthSize + precisionSize + t + conversion != 0 ) {
2949
+ if (al != null ) {
2950
+ FormatSpecifier formatSpecifier
2951
+ = new FormatSpecifier (s , start , argSize , flagSize , widthSize , precisionSize , t , conversion );
2952
+ al .add (formatSpecifier );
2953
+ }
2954
+ return off - start ;
2955
+ }
2956
+ return 0 ;
2957
+ }
2958
+
2959
+ private void parseArgument () {
2960
+ // (\d+\$)?
2961
+ int i = off ;
2962
+ for (; i < max && isDigit (c = s .charAt (i )); ++i ); // empty body
2963
+ if (i == off || c != '$' ) {
2964
+ c = first ;
2965
+ return ;
2966
+ }
2967
+
2968
+ i ++; // skip '$'
2969
+ if (i < max ) {
2970
+ c = s .charAt (i );
2971
+ }
2972
+
2973
+ argSize = i - off ;
2974
+ off = i ;
2975
+ }
2976
+
2977
+ private void parseFlag () {
2978
+ // ([-#+ 0,(\<]*)?
2979
+ int i = off ;
2980
+ for (; i < max && Flags .isFlag (c = s .charAt (i )); ++i ); // empty body
2981
+ flagSize = i - off ;
2982
+ off = i ;
2983
+ }
2984
+
2985
+ private void parseWidth () {
2986
+ // (\d+)?
2987
+ int i = off ;
2988
+ for (; i < max && isDigit (c = s .charAt (i )); ++i ); // empty body
2989
+ widthSize = i - off ;
2990
+ off = i ;
2991
+ }
2992
+
2993
+ private int parsePrecision () {
2994
+ int i = ++off ;
2995
+ for (; i < max && isDigit (c = s .charAt (i )); ++i ); // empty body
2996
+ if (i != off ) {
2997
+ int size = i - off + 1 ;
2998
+ off = i ;
2999
+ return size ;
3000
+ }
3001
+ return -1 ;
3002
+ }
3003
+ }
3004
+
3005
+ static boolean isConversion (char c ) {
3006
+ return (c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || c == '%' ;
3007
+ }
3008
+
3009
+ private static boolean isDigit (char c ) {
3010
+ return c >= '0' && c <= '9' ;
3011
+ }
3012
+
2865
3013
interface FormatString {
2866
3014
int index ();
2867
3015
void print (Formatter fmt , Object arg , Locale l ) throws IOException ;
@@ -2984,21 +3132,44 @@ private void conversion(char conv) {
2984
3132
}
2985
3133
}
2986
3134
2987
- FormatSpecifier (String s , Matcher m ) {
2988
- index (s , m .start (1 ), m .end (1 ));
2989
- flags (s , m .start (2 ), m .end (2 ));
2990
- width (s , m .start (3 ), m .end (3 ));
2991
- precision (s , m .start (4 ), m .end (4 ));
2992
-
2993
- int tTStart = m .start (5 );
2994
- if (tTStart >= 0 ) {
3135
+ FormatSpecifier (
3136
+ String s ,
3137
+ int i ,
3138
+ int argSize ,
3139
+ int flagSize ,
3140
+ int widthSize ,
3141
+ int precisionSize ,
3142
+ char t ,
3143
+ char conversion
3144
+ ) {
3145
+ int argEnd = i + argSize ;
3146
+ int flagEnd = argEnd + flagSize ;
3147
+ int widthEnd = flagEnd + widthSize ;
3148
+ int precisionEnd = widthEnd + precisionSize ;
3149
+
3150
+ if (argSize > 0 ) {
3151
+ index (s , i , argEnd );
3152
+ }
3153
+ if (flagSize > 0 ) {
3154
+ flags (s , argEnd , flagEnd );
3155
+ }
3156
+ if (widthSize > 0 ) {
3157
+ width (s , flagEnd , widthEnd );
3158
+ }
3159
+ if (precisionSize > 0 ) {
3160
+ precision (s , widthEnd , precisionEnd );
3161
+ }
3162
+ if (t != '\0' ) {
2995
3163
dt = true ;
2996
- if (s . charAt ( tTStart ) == 'T' ) {
3164
+ if (t == 'T' ) {
2997
3165
flags = Flags .add (flags , Flags .UPPERCASE );
2998
3166
}
2999
3167
}
3000
- conversion (s .charAt (m .start (6 )));
3168
+ conversion (conversion );
3169
+ check ();
3170
+ }
3001
3171
3172
+ private void check () {
3002
3173
if (dt )
3003
3174
checkDateTime ();
3004
3175
else if (Conversion .isGeneral (c ))
@@ -4705,6 +4876,13 @@ private static int parse(char c) {
4705
4876
};
4706
4877
}
4707
4878
4879
+ private static boolean isFlag (char c ) {
4880
+ return switch (c ) {
4881
+ case '-' , '#' , '+' , ' ' , '0' , ',' , '(' , '<' -> true ;
4882
+ default -> false ;
4883
+ };
4884
+ }
4885
+
4708
4886
// Returns a string representation of the current {@code Flags}.
4709
4887
public static String toString (int f ) {
4710
4888
StringBuilder sb = new StringBuilder ();
0 commit comments