提交 9d338455 编写于 作者: R rriggs

8025828: Late binding of Chronology to appendValueReduced

Summary: Add a listener to the parseContext called when the Chronology changes
Reviewed-by: sherman
上级 764913d7
...@@ -557,12 +557,13 @@ public final class DateTimeFormatterBuilder { ...@@ -557,12 +557,13 @@ public final class DateTimeFormatterBuilder {
* a two digit year parse will be in the range 1950-01-01 to 2049-12-31. * a two digit year parse will be in the range 1950-01-01 to 2049-12-31.
* Only the year would be extracted from the date, thus a base date of * Only the year would be extracted from the date, thus a base date of
* 1950-08-25 would also parse to the range 1950-01-01 to 2049-12-31. * 1950-08-25 would also parse to the range 1950-01-01 to 2049-12-31.
* This behaviour is necessary to support fields such as week-based-year * This behavior is necessary to support fields such as week-based-year
* or other calendar systems where the parsed value does not align with * or other calendar systems where the parsed value does not align with
* standard ISO years. * standard ISO years.
* <p> * <p>
* The exact behavior is as follows. Parse the full set of fields and * The exact behavior is as follows. Parse the full set of fields and
* determine the effective chronology. Then convert the base date to the * determine the effective chronology using the last chronology if
* it appears more than once. Then convert the base date to the
* effective chronology. Then extract the specified field from the * effective chronology. Then extract the specified field from the
* chronology-specific base date and use it to determine the * chronology-specific base date and use it to determine the
* {@code baseValue} used below. * {@code baseValue} used below.
...@@ -2809,9 +2810,19 @@ public final class DateTimeFormatterBuilder { ...@@ -2809,9 +2810,19 @@ public final class DateTimeFormatterBuilder {
int setValue(DateTimeParseContext context, long value, int errorPos, int successPos) { int setValue(DateTimeParseContext context, long value, int errorPos, int successPos) {
int baseValue = this.baseValue; int baseValue = this.baseValue;
if (baseDate != null) { if (baseDate != null) {
// TODO: effective chrono is inaccurate at this point
Chronology chrono = context.getEffectiveChronology(); Chronology chrono = context.getEffectiveChronology();
baseValue = chrono.date(baseDate).get(field); baseValue = chrono.date(baseDate).get(field);
// In case the Chronology is changed later, add a callback when/if it changes
final long initialValue = value;
context.addChronoChangedListener(
(_unused) -> {
/* Repeat the set of the field using the current Chronology
* The success/error position is ignored because the value is
* intentionally being overwritten.
*/
setValue(context, initialValue, errorPos, successPos);
});
} }
int parseLen = successPos - errorPos; int parseLen = successPos - errorPos;
if (parseLen == minWidth && value >= 0) { if (parseLen == minWidth && value >= 0) {
......
...@@ -61,7 +61,6 @@ ...@@ -61,7 +61,6 @@
*/ */
package java.time.format; package java.time.format;
import java.time.Duration;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.chrono.Chronology; import java.time.chrono.Chronology;
import java.time.chrono.IsoChronology; import java.time.chrono.IsoChronology;
...@@ -69,6 +68,7 @@ import java.time.temporal.TemporalField; ...@@ -69,6 +68,7 @@ import java.time.temporal.TemporalField;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer;
/** /**
* Context object used during date and time parsing. * Context object used during date and time parsing.
...@@ -105,6 +105,10 @@ final class DateTimeParseContext { ...@@ -105,6 +105,10 @@ final class DateTimeParseContext {
* The list of parsed data. * The list of parsed data.
*/ */
private final ArrayList<Parsed> parsed = new ArrayList<>(); private final ArrayList<Parsed> parsed = new ArrayList<>();
/**
* List of Consumers<Chronology> to be notified if the Chronology changes.
*/
private ArrayList<Consumer<Chronology>> chronoListeners = null;
/** /**
* Creates a new instance of the context. * Creates a new instance of the context.
...@@ -354,12 +358,36 @@ final class DateTimeParseContext { ...@@ -354,12 +358,36 @@ final class DateTimeParseContext {
* <p> * <p>
* This stores the chronology that has been parsed. * This stores the chronology that has been parsed.
* No validation is performed other than ensuring it is not null. * No validation is performed other than ensuring it is not null.
* <p>
* The list of listeners is copied and cleared so that each
* listener is called only once. A listener can add itself again
* if it needs to be notified of future changes.
* *
* @param chrono the parsed chronology, not null * @param chrono the parsed chronology, not null
*/ */
void setParsed(Chronology chrono) { void setParsed(Chronology chrono) {
Objects.requireNonNull(chrono, "chrono"); Objects.requireNonNull(chrono, "chrono");
currentParsed().chrono = chrono; currentParsed().chrono = chrono;
if (chronoListeners != null && !chronoListeners.isEmpty()) {
Consumer[] tmp = new Consumer[1];
Consumer<Chronology>[] listeners = chronoListeners.toArray(tmp);
chronoListeners.clear();
for (Consumer<Chronology> l : listeners) {
l.accept(chrono);
}
}
}
/**
* Adds a Consumer<Chronology> to the list of listeners to be notified
* if the Chronology changes.
* @param listener a Consumer<Chronology> to be called when Chronology changes
*/
void addChronoChangedListener(Consumer<Chronology> listener) {
if (chronoListeners == null) {
chronoListeners = new ArrayList<Consumer<Chronology>>();
}
chronoListeners.add(listener);
} }
/** /**
......
...@@ -78,10 +78,12 @@ import java.time.chrono.IsoChronology; ...@@ -78,10 +78,12 @@ import java.time.chrono.IsoChronology;
import java.time.chrono.JapaneseChronology; import java.time.chrono.JapaneseChronology;
import java.time.chrono.MinguoChronology; import java.time.chrono.MinguoChronology;
import java.time.chrono.ThaiBuddhistChronology; import java.time.chrono.ThaiBuddhistChronology;
import java.time.chrono.ThaiBuddhistDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField; import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQueries;
import org.testng.annotations.DataProvider; import org.testng.annotations.DataProvider;
import org.testng.annotations.Test; import org.testng.annotations.Test;
...@@ -443,6 +445,52 @@ public class TestReducedParser extends AbstractTestPrinterParser { ...@@ -443,6 +445,52 @@ public class TestReducedParser extends AbstractTestPrinterParser {
} }
@Test
public void test_reducedWithLateChronoChange() {
ThaiBuddhistDate date = ThaiBuddhistDate.of(2543, 1, 1);
DateTimeFormatter df
= new DateTimeFormatterBuilder()
.appendValueReduced(YEAR, 2, 2, LocalDate.of(2000, 1, 1))
.appendLiteral(" ")
.appendChronologyId()
.toFormatter();
int expected = date.get(YEAR);
String input = df.format(date);
ParsePosition pos = new ParsePosition(0);
TemporalAccessor parsed = df.parseUnresolved(input, pos);
assertEquals(pos.getIndex(), input.length(), "Input not parsed completely");
assertEquals(pos.getErrorIndex(), -1, "Error index should be -1 (no-error)");
int actual = parsed.get(YEAR);
assertEquals(actual, expected,
String.format("Wrong date parsed, chrono: %s, input: %s",
parsed.query(TemporalQueries.chronology()), input));
}
@Test
public void test_reducedWithLateChronoChangeTwice() {
DateTimeFormatter df
= new DateTimeFormatterBuilder()
.appendValueReduced(YEAR, 2, 2, LocalDate.of(2000, 1, 1))
.appendLiteral(" ")
.appendChronologyId()
.appendLiteral(" ")
.appendChronologyId()
.toFormatter();
int expected = 2044;
String input = "44 ThaiBuddhist ISO";
ParsePosition pos = new ParsePosition(0);
TemporalAccessor parsed = df.parseUnresolved(input, pos);
assertEquals(pos.getIndex(), input.length(), "Input not parsed completely: " + pos);
assertEquals(pos.getErrorIndex(), -1, "Error index should be -1 (no-error)");
int actual = parsed.get(YEAR);
assertEquals(actual, expected,
String.format("Wrong date parsed, chrono: %s, input: %s",
parsed.query(TemporalQueries.chronology()), input));
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Class to structure the test data // Class to structure the test data
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册