8368172: Make java.time.format.DateTimePrintContext immutable

Reviewed-by: liach
This commit is contained in:
Shaojin Wen 2025-10-29 01:28:20 +00:00
parent 723d6f83a2
commit a588c120fc
3 changed files with 56 additions and 79 deletions

View File

@ -1904,11 +1904,11 @@ public final class DateTimeFormatter {
try {
DateTimePrintContext context = new DateTimePrintContext(temporal, this);
if (appendable instanceof StringBuilder) {
printerParser.format(context, (StringBuilder) appendable);
printerParser.format(context, (StringBuilder) appendable, false);
} else {
// buffer output to avoid writing to appendable in case of error
StringBuilder buf = new StringBuilder(32);
printerParser.format(context, buf);
printerParser.format(context, buf, false);
appendable.append(buf);
}
} catch (IOException ex) {

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -2484,10 +2485,15 @@ public final class DateTimeFormatterBuilder {
*
* @param context the context to format using, not null
* @param buf the buffer to append to, not null
* @param optional whether the enclosing formatter is optional.
* If true and this formatter is nested in an optional formatter
* and the data is not available, then no error is returned and
* nothing is appended to the buffer. If false and the data is not available
* then an exception is thrown or false is returned as appropriate.
* @return false if unable to query the value from the date-time, true otherwise
* @throws DateTimeException if the date-time cannot be printed successfully
*/
boolean format(DateTimePrintContext context, StringBuilder buf);
boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional);
/**
* Parses text into date-time information.
@ -2537,21 +2543,13 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
int length = buf.length();
if (optional) {
context.startOptional();
}
try {
for (DateTimePrinterParser pp : printerParsers) {
if (pp.format(context, buf) == false) {
buf.setLength(length); // reset buffer
return true;
}
}
} finally {
if (optional) {
context.endOptional();
boolean effectiveOptional = optional | this.optional;
for (DateTimePrinterParser pp : printerParsers) {
if (!pp.format(context, buf, effectiveOptional)) {
buf.setLength(length); // reset buffer
return true;
}
}
return true;
@ -2620,9 +2618,9 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
int preLen = buf.length();
if (printerParser.format(context, buf) == false) {
if (printerParser.format(context, buf, optional) == false) {
return false;
}
int len = buf.length() - preLen;
@ -2689,7 +2687,7 @@ public final class DateTimeFormatterBuilder {
LENIENT;
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
return true; // nothing to do here
}
@ -2731,7 +2729,7 @@ public final class DateTimeFormatterBuilder {
this.value = value;
}
public boolean format(DateTimePrintContext context, StringBuilder buf) {
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
return true;
}
@ -2757,7 +2755,7 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
buf.append(literal);
return true;
}
@ -2807,7 +2805,7 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
buf.append(literal);
return true;
}
@ -2919,8 +2917,8 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
Long valueLong = context.getValue(field);
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
Long valueLong = context.getValue(field, optional);
if (valueLong == null) {
return false;
}
@ -3374,8 +3372,8 @@ public final class DateTimeFormatterBuilder {
};
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
Long value = context.getValue(field);
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
Long value = context.getValue(field, optional);
if (value == null) {
return false;
}
@ -3563,8 +3561,8 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
Long value = context.getValue(field);
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
Long value = context.getValue(field, optional);
if (value == null) {
return false;
}
@ -3711,8 +3709,8 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
Long value = context.getValue(field);
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
Long value = context.getValue(field, optional);
if (value == null) {
return false;
}
@ -3724,7 +3722,7 @@ public final class DateTimeFormatterBuilder {
text = provider.getText(chrono, field, value, textStyle, context.getLocale());
}
if (text == null) {
return numberPrinterParser().format(context, buf);
return numberPrinterParser().format(context, buf, optional);
}
buf.append(text);
return true;
@ -3806,9 +3804,9 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
// use INSTANT_SECONDS, thus this code is not bound by Instant.MAX
Long inSecs = context.getValue(INSTANT_SECONDS);
Long inSecs = context.getValue(INSTANT_SECONDS, optional);
Long inNanos = null;
if (context.getTemporal().isSupported(NANO_OF_SECOND)) {
inNanos = context.getTemporal().getLong(NANO_OF_SECOND);
@ -3992,8 +3990,8 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
Long offsetSecs = context.getValue(OFFSET_SECONDS);
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
Long offsetSecs = context.getValue(OFFSET_SECONDS, optional);
if (offsetSecs == null) {
return false;
}
@ -4291,8 +4289,8 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
Long offsetSecs = context.getValue(OFFSET_SECONDS);
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
Long offsetSecs = context.getValue(OFFSET_SECONDS, optional);
if (offsetSecs == null) {
return false;
}
@ -4505,8 +4503,8 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
ZoneId zone = context.getValue(TemporalQueries.zoneId());
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
ZoneId zone = context.getValue(TemporalQueries.zoneId(), optional);
if (zone == null) {
return false;
}
@ -4627,8 +4625,8 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
ZoneId zone = context.getValue(query);
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
ZoneId zone = context.getValue(query, optional);
if (zone == null) {
return false;
}
@ -5052,8 +5050,8 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
Chronology chrono = context.getValue(TemporalQueries.chronology());
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
Chronology chrono = context.getValue(TemporalQueries.chronology(), optional);
if (chrono == null) {
return false;
}
@ -5149,9 +5147,9 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
Chronology chrono = Chronology.from(context.getTemporal());
return formatter(context.getLocale(), chrono).toPrinterParser(false).format(context, buf);
return formatter(context.getLocale(), chrono).toPrinterParser(false).format(context, buf, optional);
}
@Override
@ -5260,8 +5258,8 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
return printerParser(context.getLocale()).format(context, buf);
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
return printerParser(context.getLocale()).format(context, buf, optional);
}
@Override
@ -5364,12 +5362,12 @@ public final class DateTimeFormatterBuilder {
}
@Override
public boolean format(DateTimePrintContext context, StringBuilder buf) {
Long hod = context.getValue(HOUR_OF_DAY);
public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) {
Long hod = context.getValue(HOUR_OF_DAY, optional);
if (hod == null) {
return false;
}
Long moh = context.getValue(MINUTE_OF_HOUR);
Long moh = context.getValue(MINUTE_OF_HOUR, optional);
long value = Math.floorMod(hod, 24) * 60 + (moh != null ? Math.floorMod(moh, 60) : 0);
Locale locale = context.getLocale();
LocaleStore store = findDayPeriodStore(locale);

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -86,11 +87,6 @@ import java.util.Objects;
* <p>
* This class provides a single wrapper to items used in the format.
*
* @implSpec
* This class is a mutable context intended for use from a single thread.
* Usage of the class is thread-safe within standard printing as the framework creates
* a new instance of the class for each format and printing is single-threaded.
*
* @since 1.8
*/
final class DateTimePrintContext {
@ -103,10 +99,6 @@ final class DateTimePrintContext {
* The formatter, not null.
*/
private final DateTimeFormatter formatter;
/**
* Whether the current formatter is optional.
*/
private int optional;
/**
* Creates a new instance of the context.
@ -115,7 +107,6 @@ final class DateTimePrintContext {
* @param formatter the formatter controlling the format, not null
*/
DateTimePrintContext(TemporalAccessor temporal, DateTimeFormatter formatter) {
super();
this.temporal = adjust(temporal, formatter);
this.formatter = formatter;
}
@ -348,30 +339,17 @@ final class DateTimePrintContext {
}
//-----------------------------------------------------------------------
/**
* Starts the printing of an optional segment of the input.
*/
void startOptional() {
this.optional++;
}
/**
* Ends the printing of an optional segment of the input.
*/
void endOptional() {
this.optional--;
}
/**
* Gets a value using a query.
*
* @param query the query to use, not null
* @param optional whether the query is optional, true if the query may be missing
* @return the result, null if not found and optional is true
* @throws DateTimeException if the type is not available and the section is not optional
*/
<R> R getValue(TemporalQuery<R> query) {
<R> R getValue(TemporalQuery<R> query, boolean optional) {
R result = temporal.query(query);
if (result == null && optional == 0) {
if (result == null && !optional) {
throw new DateTimeException("Unable to extract " +
query + " from temporal " + temporal);
}
@ -384,11 +362,12 @@ final class DateTimePrintContext {
* This will return the value for the specified field.
*
* @param field the field to find, not null
* @param optional whether the field is optional, true if the field may be missing
* @return the value, null if not found and optional is true
* @throws DateTimeException if the field is not available and the section is not optional
*/
Long getValue(TemporalField field) {
if (optional > 0 && !temporal.isSupported(field)) {
Long getValue(TemporalField field, boolean optional) {
if (optional && !temporal.isSupported(field)) {
return null;
}
return temporal.getLong(field);