@@ -69,7 +69,7 @@ public static abstract class AbstractFormatter
6969 protected int lenSign ;
7070 /**
7171 * The length of the whole part (to left of the decimal point or
72- * exponent)
72+ * exponent).
7373 */
7474 protected int lenWhole ;
7575
@@ -461,13 +461,21 @@ protected void pad(int leftIndex, int n) {
461461 // Append the trailing space
462462 for (int i = 0 ; i < trailing ; i ++) { result .append (fill ); }
463463
464- // Check for special case
465- if (align == '=' && fill == '0' && spec .grouping ) {
464+ /*
465+ * Check for special case where we shall extend the grouping
466+ * already applied to the whole part into the left
467+ * 0-padding.
468+ */
469+ if (align == '=' && fill == '0' && spec .group > 0 ) {
466470 /*
467- * We must extend the grouping separator into the
468- * padding
471+ * Extend the grouping separator into the padding,
472+ * EXCEPT if there was originally no whole part (occurs
473+ * for inf, nan).
469474 */
470- zeroPadAfterSignWithGroupingFixup (3 , ',' );
475+ if (lenWhole > leading ) {
476+ zeroPadAfterSignWithGroupingFixup (spec .group ,
477+ spec .grouper );
478+ }
471479 }
472480 }
473481
@@ -505,7 +513,7 @@ protected void pad(int leftIndex, int n) {
505513 * '-<b>0</b>,000,000,001,200,000,000.0000'
506514 * </pre>
507515 *
508- * @param groupSize normally 3.
516+ * @param groupSize normally 3 or 4 .
509517 * @param comma or some other character to use as a separator.
510518 */
511519 protected void zeroPadAfterSignWithGroupingFixup (int groupSize ,
@@ -787,11 +795,10 @@ public static class FormatSpec {
787795 public final boolean alternate ;
788796 /** Width to which to pad the result, or -1 if unspecified. */
789797 public final int width ;
790- /**
791- * Insert the grouping separator (which in Python always
792- * indicates a group-size of 3).
793- */
794- public final boolean grouping ;
798+ /** Grouping size (or ≤0 for no grouping)). */
799+ public final int group ;
800+ /** The group separator (if {@link #group} is >0). */
801+ public final char grouper ;
795802 /** Precision decoded from the format, or -1 if unspecified. */
796803 public final int precision ;
797804 /** Type key from the format, or U+FFFF if unspecified. */
@@ -827,7 +834,7 @@ public static final boolean specified(char c) {
827834 * has been specified).
828835 */
829836 public static final boolean specified (int value ) {
830- return value >= 0 ;
837+ return value >= 0 ; // UNSPECIFIED = -1
831838 }
832839
833840 /**
@@ -845,19 +852,21 @@ public static final boolean specified(int value) {
845852 * @param alternate true to request alternate formatting mode
846853 * ({@code '#'} flag).
847854 * @param width of field after padding or -1 to default
848- * @param grouping true to request comma-separated groups
855+ * @param group if non-zero digit group size (usually 3 or 4)
856+ * @param grouper character separating groups
849857 * @param precision (e.g. decimal places) or -1 to default
850858 * @param type indicator character
851859 */
852860 public FormatSpec (char fill , char align , char sign ,
853- boolean alternate , int width , boolean grouping ,
861+ boolean alternate , int width , int group , char grouper ,
854862 int precision , char type ) {
855863 this .fill = fill ;
856864 this .align = align ;
857865 this .sign = sign ;
858866 this .alternate = alternate ;
859867 this .width = width ;
860- this .grouping = grouping ;
868+ this .group = group ;
869+ this .grouper = grouper ;
861870 this .precision = precision ;
862871 this .type = type ;
863872 }
@@ -874,7 +883,7 @@ public String toString() {
874883 if (specified (sign )) { buf .append (sign ); }
875884 if (alternate ) { buf .append ('#' ); }
876885 if (specified (width )) { buf .append (width ); }
877- if (grouping ) { buf .append (',' ); }
886+ if (specified ( group )) { buf .append (grouper ); }
878887 if (specified (precision )) {
879888 buf .append ('.' ).append (precision );
880889 }
@@ -908,7 +917,8 @@ public FormatSpec withDefaults(FormatSpec other) {
908917 specified (sign ) ? sign : other .sign , //
909918 alternate || other .alternate , //
910919 specified (width ) ? width : other .width , //
911- grouping || other .grouping , //
920+ specified (group ) ? group : other .group , //
921+ specified (grouper ) ? grouper : other .grouper , //
912922 specified (precision ) ? precision : other .precision , //
913923 specified (type ) ? type : other .type //
914924 );
@@ -918,16 +928,16 @@ public FormatSpec withDefaults(FormatSpec other) {
918928 * Defaults applicable to most numeric types. Equivalent to "
919929 * >"
920930 */
921- public static final FormatSpec NUMERIC = new FormatSpec ( ' ' ,
922- ' >' , FormatSpec . NONE , false , FormatSpec . UNSPECIFIED ,
923- false , FormatSpec . UNSPECIFIED , FormatSpec . NONE );
931+ public static final FormatSpec NUMERIC =
932+ new FormatSpec ( ' ' , ' >' , NONE , false , UNSPECIFIED ,
933+ UNSPECIFIED , NONE , UNSPECIFIED , NONE );
924934
925935 /**
926936 * Defaults applicable to string types. Equivalent to " <"
927937 */
928- public static final FormatSpec STRING = new FormatSpec ( ' ' , '<' ,
929- FormatSpec . NONE , false , FormatSpec . UNSPECIFIED , false ,
930- FormatSpec . UNSPECIFIED , FormatSpec . NONE );
938+ public static final FormatSpec STRING =
939+ new FormatSpec ( ' ' , '<' , NONE , false , UNSPECIFIED ,
940+ UNSPECIFIED , NONE , UNSPECIFIED , NONE );
931941
932942 /**
933943 * Constructor offering just precision and type.
@@ -940,7 +950,7 @@ public FormatSpec withDefaults(FormatSpec other) {
940950 * @param type indicator character
941951 */
942952 public FormatSpec (int precision , char type ) {
943- this (' ' , '>' , FormatSpec . NONE , false , UNSPECIFIED , false ,
953+ this (' ' , '>' , NONE , false , UNSPECIFIED , UNSPECIFIED , NONE ,
944954 precision , type );
945955 }
946956
@@ -1009,6 +1019,11 @@ private static class Parser {
10091019 this .ptr = 0 ;
10101020 }
10111021
1022+ @ Override
1023+ public String toString () {
1024+ return spec .substring (0 , ptr ) + "|" + spec .substring (ptr );
1025+ }
1026+
10121027 /**
10131028 * Parse the specification with which this object was
10141029 * initialised into an {@link FormatSpec}, which is an object
@@ -1029,11 +1044,13 @@ private static class Parser {
10291044 */
10301045 FormatSpec parse () {
10311046
1032- char fill = FormatSpec .NONE , align = FormatSpec .NONE ;
1033- char sign = FormatSpec .NONE , type = FormatSpec .NONE ;
1034- boolean alternate = false , grouping = false ;
1035- int width = FormatSpec .UNSPECIFIED ,
1036- precision = FormatSpec .UNSPECIFIED ;
1047+ char fill = FormatSpec .NONE , align = FormatSpec .NONE ,
1048+ sign = FormatSpec .NONE , type = FormatSpec .NONE ,
1049+ grouper = FormatSpec .NONE ;
1050+ boolean alternate = false ;
1051+ int width = FormatSpec .UNSPECIFIED ;
1052+ int precision = FormatSpec .UNSPECIFIED ;
1053+ int group = FormatSpec .UNSPECIFIED ;
10371054
10381055 // Scan [[fill]align] ...
10391056 if (isAlign ()) {
@@ -1082,8 +1099,8 @@ FormatSpec parse() {
10821099 // Scan [width]
10831100 if (isDigit ()) { width = scanInteger (); }
10841101
1085- // Scan [, ][.precision][type]
1086- grouping = scanPast ( ',' );
1102+ // Scan [grouper ][.precision][type]
1103+ if ( isAt ( ",_" )) { grouper = spec . charAt ( ptr ++); }
10871104
10881105 // Scan [.precision]
10891106 if (scanPast ('.' )) {
@@ -1098,6 +1115,12 @@ FormatSpec parse() {
10981115 // Scan [type]
10991116 if (ptr < spec .length ()) { type = spec .charAt (ptr ++); }
11001117
1118+ // If grouping was specified, work out the group size.
1119+ if (grouper != FormatSpec .NONE ) {
1120+ // Hex, octal and binary group in 4s. Otherwise 3s.
1121+ group = "boxX" .indexOf (type ) < 0 ? 3 : 4 ;
1122+ }
1123+
11011124 // If we haven't reached the end, something is wrong
11021125 if (ptr != spec .length ()) {
11031126 throw new IllegalArgumentException (
@@ -1106,7 +1129,7 @@ FormatSpec parse() {
11061129
11071130 // Create a specification
11081131 return new FormatSpec (fill , align , sign , alternate , width ,
1109- grouping , precision , type );
1132+ group , grouper , precision , type );
11101133 }
11111134
11121135 /**
@@ -1152,7 +1175,6 @@ private int scanInteger() {
11521175 while (isDigit ()) { ptr ++; }
11531176 return Integer .parseInt (spec .substring (p , ptr ));
11541177 }
1155-
11561178 }
11571179
11581180 /**
0 commit comments