Skip to content

Commit 7088d79

Browse files
committed
unrolling
1 parent 9db0a6d commit 7088d79

File tree

4 files changed

+242
-54
lines changed

4 files changed

+242
-54
lines changed

make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ public class CLDRConverter {
127127
static int copyrightYear;
128128
static String jdkHeaderTemplate;
129129
private static String zoneNameTempFile;
130+
private static String dateTimePrinterFactoryTempFile;
130131
private static String tzDataDir;
131132
private static final Map<String, String> canonicalTZMap = new HashMap<>();
132133

@@ -242,6 +243,10 @@ public static void main(String[] args) throws Exception {
242243
zoneNameTempFile = args[++i];
243244
break;
244245

246+
case "-dtpftempfile":
247+
dateTimePrinterFactoryTempFile = args[++i];
248+
break;
249+
245250
case "-tzdatadir":
246251
tzDataDir = args[++i];
247252
break;
@@ -310,6 +315,9 @@ public static void main(String[] args) throws Exception {
310315
// Generate java.time.format.ZoneName.java
311316
generateZoneName();
312317

318+
// Generate java.time.format.DateTimePrinterParserFactory.java
319+
generateDateTimePrinterParserFactory();
320+
313321
// Generate Windows tzmappings
314322
generateWindowsTZMappings();
315323
}
@@ -327,6 +335,7 @@ private static void usage() {
327335
+ "\t-o dir output directory (default: ./build/gensrc)%n"
328336
+ "\t-year year copyright year in output%n"
329337
+ "\t-zntempfile template file for java.time.format.ZoneName.java%n"
338+
+ "\t-dtpftempfile template file for java.time.format.DateTimePrinterFactory.java%n"
330339
+ "\t-tzdatadir tzdata directory for java.time.format.ZoneName.java%n"
331340
+ "\t-utf8 use UTF-8 rather than \\uxxxx (for debug)%n"
332341
+ "\t-jdk-header-template <file>%n"
@@ -1214,6 +1223,78 @@ private static void generateZoneName() throws Exception {
12141223
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
12151224
}
12161225

1226+
private static void generateDateTimePrinterParserFactory() throws Exception {
1227+
if (dateTimePrinterFactoryTempFile == null) {
1228+
return;
1229+
}
1230+
Files.createDirectories(Paths.get(DESTINATION_DIR, "java", "time", "format"));
1231+
Files.write(Paths.get(DESTINATION_DIR, "java", "time", "format", "DateTimePrinterParserFactory.java"),
1232+
Files.lines(Paths.get(dateTimePrinterFactoryTempFile))
1233+
.flatMap(l -> {
1234+
if (l.startsWith("%%%%CASES-FORMAT:")) {
1235+
return generateDateTimePrinterCases(l, "%%%%CASES-FORMAT:", false); // formatter cases
1236+
} else if (l.startsWith("%%%%CASES-PARSE:")) {
1237+
return generateDateTimePrinterCases(l, "%%%%CASES-PARSE:", true); // parser cases
1238+
} else {
1239+
return Stream.of(l);
1240+
}
1241+
})
1242+
.collect(Collectors.toList()),
1243+
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
1244+
}
1245+
1246+
private static Stream<String> generateDateTimePrinterCases(String line, String prefix, boolean isParse) {
1247+
// Parse the range, defaulting to 1-16 if no range is specified
1248+
int start = 1;
1249+
int end = 16;
1250+
1251+
String rangePart = line.substring(prefix.length(), line.length() - 4); // Remove trailing%%%%
1252+
String[] parts = rangePart.split("-");
1253+
if (parts.length == 2) {
1254+
try {
1255+
start = Integer.parseInt(parts[0]);
1256+
end = Integer.parseInt(parts[1]);
1257+
} catch (NumberFormatException e) {
1258+
// Use defaults if parsing fails
1259+
}
1260+
}
1261+
1262+
return IntStream.rangeClosed(start, end)
1263+
.mapToObj(i -> {
1264+
if (isParse) {
1265+
// Generate parser cases
1266+
var sb = new StringBuilder(" case ").append(i).append(" -> (context, text, position)\n");
1267+
if (i == 1) {
1268+
// Special case for 1 - direct method reference used instead
1269+
// This shouldn't happen since case 1 is handled separately in the template
1270+
sb.append(" -> printerParsers[0].parse(context, text, position);");
1271+
} else {
1272+
// For i >= 2, build the sequence
1273+
sb.append(" -> (position = printerParsers[0].parse(context, text, position)) < 0\n");
1274+
for (int j = 1; j < i - 1; j++) {
1275+
sb.append(" ? position : (position = printerParsers[").append(j).append("].parse(context, text, position)) < 0\n");
1276+
}
1277+
// The last parser in the chain doesn't check for failure, just returns its result
1278+
if (i > 1) {
1279+
sb.append(" ? position : printerParsers[").append(i - 1).append("].parse(context, text, position);");
1280+
}
1281+
}
1282+
return sb.toString();
1283+
} else {
1284+
// Generate formatter cases (original behavior)
1285+
var sb = new StringBuilder(" case ").append(i).append(" -> (context, buf, optional)\n")
1286+
.append(" -> ");
1287+
for (int j = 0; j < i; j++) {
1288+
sb.append("printerParsers[").append(j).append("].format(context, buf, optional)");
1289+
if (j < i - 1) {
1290+
sb.append("\n && ");
1291+
}
1292+
}
1293+
return sb.append(";").toString();
1294+
}
1295+
});
1296+
}
1297+
12171298
// This method assumes handlerMetaZones is already initialized
12181299
private static Set<String> getAvailableZoneIds() {
12191300
assert handlerMetaZones != null;

make/modules/java.base/Gensrc.gmk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ GENSRC_DIR := $(SUPPORT_OUTPUTDIR)/gensrc/java.base
4646
CLDR_GEN_DONE := $(GENSRC_DIR)/_cldr-gensrc.marker
4747
TZ_DATA_DIR := $(MODULE_SRC)/share/data/tzdata
4848
ZONENAME_TEMPLATE := $(MODULE_SRC)/share/classes/java/time/format/ZoneName.java.template
49+
DTPFACTORY_TEMPLATE := $(MODULE_SRC)/share/classes/java/time/format/DateTimePrinterParserFactory.java.template
4950

5051
# The `-utf8` option is used even for US English, as some names
5152
# may contain non-ASCII characters, such as “Türkiye”.
@@ -54,6 +55,7 @@ $(CLDR_GEN_DONE): $(wildcard $(CLDR_DATA_DIR)/dtd/*.dtd) \
5455
$(wildcard $(CLDR_DATA_DIR)/supplemental/*.xml) \
5556
$(wildcard $(TZ_DATA_DIR)/*) \
5657
$(ZONENAME_TEMPLATE) \
58+
$(DTPFACTORY_TEMPLATE) \
5759
$(BUILD_TOOLS_JDK)
5860
$(call MakeDir, $(GENSRC_DIR))
5961
$(call LogInfo, Processing CLDR data for java.base)
@@ -64,6 +66,7 @@ $(CLDR_GEN_DONE): $(wildcard $(CLDR_DATA_DIR)/dtd/*.dtd) \
6466
-basemodule \
6567
-year $(COPYRIGHT_YEAR) \
6668
-zntempfile $(ZONENAME_TEMPLATE) \
69+
-dtpftempfile $(DTPFACTORY_TEMPLATE) \
6770
-tzdatadir $(TZ_DATA_DIR) \
6871
-utf8)
6972
$(TOUCH) $@

src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java

Lines changed: 41 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,34 +2447,7 @@ private DateTimeFormatter toFormatter(Locale locale, ResolverStyle resolverStyle
24472447
}
24482448

24492449
//-----------------------------------------------------------------------
2450-
/**
2451-
* Strategy for formatting/parsing date-time information.
2452-
* <p>
2453-
* The printer may format any part, or the whole, of the input date-time object.
2454-
* Typically, a complete format is constructed from a number of smaller
2455-
* units, each outputting a single field.
2456-
* <p>
2457-
* The parser may parse any piece of text from the input, storing the result
2458-
* in the context. Typically, each individual parser will just parse one
2459-
* field, such as the day-of-month, storing the value in the context.
2460-
* Once the parse is complete, the caller will then resolve the parsed values
2461-
* to create the desired object, such as a {@code LocalDate}.
2462-
* <p>
2463-
* The parse position will be updated during the parse. Parsing will start at
2464-
* the specified index and the return value specifies the new parse position
2465-
* for the next parser. If an error occurs, the returned index will be negative
2466-
* and will have the error position encoded using the complement operator.
2467-
*
2468-
* @implSpec
2469-
* This interface must be implemented with care to ensure other classes operate correctly.
2470-
* All implementations that can be instantiated must be final, immutable and thread-safe.
2471-
* <p>
2472-
* The context is not a thread-safe object and a new instance will be created
2473-
* for each format that occurs. The context must not be stored in an instance
2474-
* variable or shared with any other threads.
2475-
*/
2476-
interface DateTimePrinterParser {
2477-
2450+
interface DateTimePrinter {
24782451
/**
24792452
* Prints the date-time object to the buffer.
24802453
* <p>
@@ -2494,6 +2467,9 @@ interface DateTimePrinterParser {
24942467
* @throws DateTimeException if the date-time cannot be printed successfully
24952468
*/
24962469
boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional);
2470+
}
2471+
2472+
interface DateTimeParser {
24972473

24982474
/**
24992475
* Parses text into date-time information.
@@ -2512,13 +2488,44 @@ interface DateTimePrinterParser {
25122488
int parse(DateTimeParseContext context, CharSequence text, int position);
25132489
}
25142490

2491+
/**
2492+
* Strategy for formatting/parsing date-time information.
2493+
* <p>
2494+
* The printer may format any part, or the whole, of the input date-time object.
2495+
* Typically, a complete format is constructed from a number of smaller
2496+
* units, each outputting a single field.
2497+
* <p>
2498+
* The parser may parse any piece of text from the input, storing the result
2499+
* in the context. Typically, each individual parser will just parse one
2500+
* field, such as the day-of-month, storing the value in the context.
2501+
* Once the parse is complete, the caller will then resolve the parsed values
2502+
* to create the desired object, such as a {@code LocalDate}.
2503+
* <p>
2504+
* The parse position will be updated during the parse. Parsing will start at
2505+
* the specified index and the return value specifies the new parse position
2506+
* for the next parser. If an error occurs, the returned index will be negative
2507+
* and will have the error position encoded using the complement operator.
2508+
*
2509+
* @implSpec
2510+
* This interface must be implemented with care to ensure other classes operate correctly.
2511+
* All implementations that can be instantiated must be final, immutable and thread-safe.
2512+
* <p>
2513+
* The context is not a thread-safe object and a new instance will be created
2514+
* for each format that occurs. The context must not be stored in an instance
2515+
* variable or shared with any other threads.
2516+
*/
2517+
interface DateTimePrinterParser extends DateTimePrinter, DateTimeParser {
2518+
}
2519+
25152520
//-----------------------------------------------------------------------
25162521
/**
25172522
* Composite printer and parser.
25182523
*/
25192524
static final class CompositePrinterParser implements DateTimePrinterParser {
25202525
private final DateTimePrinterParser[] printerParsers;
25212526
private final boolean optional;
2527+
private final DateTimePrinter formatter;
2528+
private final DateTimeParser parser;
25222529

25232530
private CompositePrinterParser(List<DateTimePrinterParser> printerParsers, boolean optional) {
25242531
this(printerParsers.toArray(new DateTimePrinterParser[0]), optional);
@@ -2527,6 +2534,8 @@ private CompositePrinterParser(List<DateTimePrinterParser> printerParsers, boole
25272534
private CompositePrinterParser(DateTimePrinterParser[] printerParsers, boolean optional) {
25282535
this.printerParsers = printerParsers;
25292536
this.optional = optional;
2537+
this.formatter = DateTimePrinterParserFactory.createFormatter(printerParsers);
2538+
this.parser = DateTimePrinterParserFactory.createParser(printerParsers, optional);
25302539
}
25312540

25322541
/**
@@ -2546,38 +2555,16 @@ public CompositePrinterParser withOptional(boolean optional) {
25462555
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
25472556
int length = buf.length();
25482557
boolean effectiveOptional = optional | this.optional;
2549-
for (DateTimePrinterParser pp : printerParsers) {
2550-
if (!pp.format(context, buf, effectiveOptional)) {
2551-
buf.setLength(length); // reset buffer
2552-
return true;
2553-
}
2558+
if (!formatter.format(context, buf, effectiveOptional)) {
2559+
buf.setLength(length); // reset buffer
2560+
return true;
25542561
}
25552562
return true;
25562563
}
25572564

25582565
@Override
25592566
public int parse(DateTimeParseContext context, CharSequence text, int position) {
2560-
if (optional) {
2561-
context.startOptional();
2562-
int pos = position;
2563-
for (DateTimePrinterParser pp : printerParsers) {
2564-
pos = pp.parse(context, text, pos);
2565-
if (pos < 0) {
2566-
context.endOptional(false);
2567-
return position; // return original position
2568-
}
2569-
}
2570-
context.endOptional(true);
2571-
return pos;
2572-
} else {
2573-
for (DateTimePrinterParser pp : printerParsers) {
2574-
position = pp.parse(context, text, position);
2575-
if (position < 0) {
2576-
break;
2577-
}
2578-
}
2579-
return position;
2580-
}
2567+
return parser.parse(context, text, position);
25812568
}
25822569

25832570
@Override

0 commit comments

Comments
 (0)