Path: blob/master/test/jdk/java/time/test/java/time/format/TestReducedParser.java
43153 views
/*1* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* This file is available under and governed by the GNU General Public25* License version 2 only, as published by the Free Software Foundation.26* However, the following notice accompanied the original version of this27* file:28*29* Copyright (c) 2010-2012, Stephen Colebourne & Michael Nascimento Santos30*31* All rights reserved.32*33* Redistribution and use in source and binary forms, with or without34* modification, are permitted provided that the following conditions are met:35*36* * Redistributions of source code must retain the above copyright notice,37* this list of conditions and the following disclaimer.38*39* * Redistributions in binary form must reproduce the above copyright notice,40* this list of conditions and the following disclaimer in the documentation41* and/or other materials provided with the distribution.42*43* * Neither the name of JSR-310 nor the names of its contributors44* may be used to endorse or promote products derived from this software45* without specific prior written permission.46*47* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS48* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT49* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR50* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR51* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,52* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,53* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR54* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF55* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING56* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS57* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.58*/59package test.java.time.format;6061import static java.time.temporal.ChronoField.DAY_OF_MONTH;62import static java.time.temporal.ChronoField.DAY_OF_YEAR;63import static java.time.temporal.ChronoField.MONTH_OF_YEAR;64import static java.time.temporal.ChronoField.YEAR;65import static java.time.temporal.ChronoField.YEAR_OF_ERA;66import static java.time.temporal.ChronoUnit.YEARS;67import static org.testng.Assert.assertEquals;68import static org.testng.Assert.assertTrue;69import static org.testng.Assert.assertNotNull;7071import java.text.ParsePosition;72import java.time.LocalDate;73import java.time.chrono.Chronology;74import java.time.chrono.ChronoLocalDate;75import java.time.chrono.HijrahChronology;76import java.time.chrono.IsoChronology;77import java.time.chrono.JapaneseChronology;78import java.time.chrono.MinguoChronology;79import java.time.chrono.ThaiBuddhistChronology;80import java.time.chrono.ThaiBuddhistDate;81import java.time.format.DateTimeFormatter;82import java.time.format.DateTimeFormatterBuilder;83import java.time.temporal.TemporalAccessor;84import java.time.temporal.TemporalField;85import java.time.temporal.TemporalQueries;8687import org.testng.annotations.DataProvider;88import org.testng.annotations.Test;8990/**91* Test ReducedPrinterParser.92*/93@Test94public class TestReducedParser extends AbstractTestPrinterParser {95private static final boolean STRICT = true;96private static final boolean LENIENT = false;9798private DateTimeFormatter getFormatter0(TemporalField field, int width, int baseValue) {99return builder.appendValueReduced(field, width, width, baseValue).toFormatter(locale).withDecimalStyle(decimalStyle);100}101102private DateTimeFormatter getFormatter0(TemporalField field, int minWidth, int maxWidth, int baseValue) {103return builder.appendValueReduced(field, minWidth, maxWidth, baseValue).toFormatter(locale).withDecimalStyle(decimalStyle);104}105106private DateTimeFormatter getFormatterBaseDate(TemporalField field, int minWidth, int maxWidth, int baseValue) {107return builder.appendValueReduced(field, minWidth, maxWidth, LocalDate.of(baseValue, 1, 1)).toFormatter(locale).withDecimalStyle(decimalStyle);108}109110//-----------------------------------------------------------------------111@DataProvider(name="error")112Object[][] data_error() {113return new Object[][] {114{YEAR, 2, 2010, "12", -1, IndexOutOfBoundsException.class},115{YEAR, 2, 2010, "12", 3, IndexOutOfBoundsException.class},116};117}118119@Test(dataProvider="error")120public void test_parse_error(TemporalField field, int width, int baseValue, String text, int pos, Class<?> expected) {121try {122getFormatter0(field, width, baseValue).parseUnresolved(text, new ParsePosition(pos));123} catch (RuntimeException ex) {124assertTrue(expected.isInstance(ex));125}126}127128//-----------------------------------------------------------------------129public void test_parse_fieldRangeIgnored() throws Exception {130ParsePosition pos = new ParsePosition(0);131TemporalAccessor parsed = getFormatter0(DAY_OF_YEAR, 3, 10).parseUnresolved("456", pos);132assertEquals(pos.getIndex(), 3);133assertParsed(parsed, DAY_OF_YEAR, 456L); // parsed dayOfYear=456134}135136//-----------------------------------------------------------------------137// Parse data and values that are consistent whether strict or lenient138// The data is the ChronoField, width, baseValue, text, startPos, endPos, value139//-----------------------------------------------------------------------140@DataProvider(name="ParseAll")141Object[][] provider_parseAll() {142return new Object[][] {143// negative zero144{YEAR, 1, 2010, "-0", 0, 0, null},145146// general147{YEAR, 2, 2010, "Xxx12Xxx", 3, 5, 2012},148{YEAR, 2, 2010, "12-45", 0, 2, 2012},149150// other junk151{YEAR, 2, 2010, "A0", 0, 0, null},152{YEAR, 2, 2010, " 1", 0, 0, null},153{YEAR, 2, 2010, "-1", 0, 0, null},154{YEAR, 2, 2010, "-10", 0, 0, null},155{YEAR, 2, 2000, " 1", 0, 0, null},156157// parse OK 1158{YEAR, 1, 2010, "1", 0, 1, 2011},159{YEAR, 1, 2010, "3", 1, 1, null},160{YEAR, 1, 2010, "9", 0, 1, 2019},161162{YEAR, 1, 2005, "0", 0, 1, 2010},163{YEAR, 1, 2005, "4", 0, 1, 2014},164{YEAR, 1, 2005, "5", 0, 1, 2005},165{YEAR, 1, 2005, "9", 0, 1, 2009},166{YEAR, 1, 2010, "1-2", 0, 1, 2011},167168// parse OK 2169{YEAR, 2, 2010, "00", 0, 2, 2100},170{YEAR, 2, 2010, "09", 0, 2, 2109},171{YEAR, 2, 2010, "10", 0, 2, 2010},172{YEAR, 2, 2010, "99", 0, 2, 2099},173174// parse OK 2175{YEAR, 2, -2005, "05", 0, 2, -2005},176{YEAR, 2, -2005, "00", 0, 2, -2000},177{YEAR, 2, -2005, "99", 0, 2, -1999},178{YEAR, 2, -2005, "06", 0, 2, -1906},179180{YEAR, 2, -2005, "43", 0, 2, -1943},181};182}183184@Test(dataProvider="ParseAll")185public void test_parseAllStrict(TemporalField field, int width, int baseValue, String input, int pos, int parseLen, Integer parseVal) {186ParsePosition ppos = new ParsePosition(pos);187setStrict(true);188TemporalAccessor parsed = getFormatter0(field, width, baseValue).parseUnresolved(input, ppos);189if (ppos.getErrorIndex() != -1) {190assertEquals(ppos.getErrorIndex(), parseLen, "error case parse position");191assertEquals(parsed, parseVal, "unexpected parse result");192} else {193assertEquals(ppos.getIndex(), parseLen, "parse position");194assertParsed(parsed, YEAR, parseVal != null ? (long) parseVal : null);195}196}197198@Test(dataProvider="ParseAll")199public void test_parseAllLenient(TemporalField field, int width, int baseValue, String input, int pos, int parseLen, Integer parseVal) {200ParsePosition ppos = new ParsePosition(pos);201setStrict(false);202TemporalAccessor parsed = getFormatter0(field, width, baseValue).parseUnresolved(input, ppos);203if (ppos.getErrorIndex() != -1) {204assertEquals(ppos.getErrorIndex(), parseLen, "error case parse position");205assertEquals(parsed, parseVal, "unexpected parse result");206} else {207assertEquals(ppos.getIndex(), parseLen, "parse position");208assertParsed(parsed, YEAR, parseVal != null ? (long) parseVal : null);209}210}211212//-----------------------------------------------------------------------213// Parse data and values in strict and lenient modes.214// The data is the ChronoField, minWidth, maxWidth, baseValue, text, startPos,215// Strict Pair(endPos, value), Lenient Pair(endPos, value)216//-----------------------------------------------------------------------217@DataProvider(name="ParseLenientSensitive")218Object[][] provider_parseLenientSensitive() {219return new Object[][] {220// few digits supplied221{YEAR, 2, 2, 2010, "3", 0, strict(0, null), lenient(1, 3)},222{YEAR, 2, 2, 2010, "4", 0, strict(0, null), lenient(1, 4)},223{YEAR, 2, 2, 2010, "5", 1, strict(1, null), lenient(1, null)},224{YEAR, 2, 2, 2010, "6-2", 0, strict(0, null), lenient(1, 6)},225{YEAR, 2, 2, 2010, "9", 0, strict(0, null), lenient(1, 9)},226227// other junk228{YEAR, 1, 4, 2000, "7A", 0, strict(1, 2007), lenient(1, 2007)},229{YEAR, 2, 2, 2010, "8A", 0, strict(0, null), lenient(1, 8)},230231// Negative sign cases232{YEAR, 2, 4, 2000, "-1", 0, strict(0, null), lenient(2, -1)},233{YEAR, 2, 4, 2000, "-10", 0, strict(0, null), lenient(3, -10)},234235// Positive sign cases236{YEAR, 2, 4, 2000, "+1", 0, strict(0, null), lenient(2, 1)},237{YEAR, 2, 4, 2000, "+10", 0, strict(0, null), lenient(3, 2010)},238239// No sign cases240{YEAR, 1, 1, 2005, "21", 0, strict(1, 2012), lenient(2, 21)},241{YEAR, 1, 2, 2010, "12", 0, strict(2, 12), lenient(2, 12)},242{YEAR, 1, 4, 2000, "87", 0, strict(2, 87), lenient(2, 87)},243{YEAR, 1, 4, 2000, "9876", 0, strict(4, 9876), lenient(4, 9876)},244{YEAR, 2, 2, 2010, "321", 0, strict(2, 2032), lenient(3, 321)},245{YEAR, 2, 4, 2010, "2", 0, strict(0, null), lenient(1, 2)},246{YEAR, 2, 4, 2010, "21", 0, strict(2, 2021), lenient(2, 2021)},247{YEAR, 2, 4, 2010, "321", 0, strict(3, 321), lenient(3, 321)},248{YEAR, 2, 4, 2010, "4321", 0, strict(4, 4321), lenient(4, 4321)},249{YEAR, 2, 4, 2010, "54321", 0, strict(4, 5432), lenient(5, 54321)},250{YEAR, 2, 8, 2010, "87654321", 3, strict(8, 54321), lenient(8, 54321)},251{YEAR, 2, 9, 2010, "987654321", 0, strict(9, 987654321), lenient(9, 987654321)},252{YEAR, 3, 3, 2010, "765", 0, strict(3, 2765), lenient(3, 2765)},253{YEAR, 3, 4, 2010, "76", 0, strict(0, null), lenient(2, 76)},254{YEAR, 3, 4, 2010, "765", 0, strict(3, 2765), lenient(3, 2765)},255{YEAR, 3, 4, 2010, "7654", 0, strict(4, 7654), lenient(4, 7654)},256{YEAR, 3, 4, 2010, "76543", 0, strict(4, 7654), lenient(5, 76543)},257258// Negative baseValue259{YEAR, 2, 4, -2005, "123", 0, strict(3, 123), lenient(3, 123)},260261// Basics262{YEAR, 2, 4, 2010, "10", 0, strict(2, 2010), lenient(2, 2010)},263{YEAR, 2, 4, 2010, "09", 0, strict(2, 2109), lenient(2, 2109)},264};265}266267//-----------------------------------------------------------------------268// Parsing tests for strict mode269//-----------------------------------------------------------------------270@Test(dataProvider="ParseLenientSensitive")271public void test_parseStrict(TemporalField field, int minWidth, int maxWidth, int baseValue, String input, int pos,272Pair strict, Pair lenient) {273ParsePosition ppos = new ParsePosition(pos);274setStrict(true);275TemporalAccessor parsed = getFormatter0(field, minWidth, maxWidth, baseValue).parseUnresolved(input, ppos);276if (ppos.getErrorIndex() != -1) {277assertEquals(ppos.getErrorIndex(), strict.parseLen, "error case parse position");278assertEquals(parsed, strict.parseVal, "unexpected parse result");279} else {280assertEquals(ppos.getIndex(), strict.parseLen, "parse position");281assertParsed(parsed, YEAR, strict.parseVal != null ? (long) strict.parseVal : null);282}283}284285@Test(dataProvider="ParseLenientSensitive")286public void test_parseStrict_baseDate(TemporalField field, int minWidth, int maxWidth, int baseValue, String input, int pos,287Pair strict, Pair lenient) {288ParsePosition ppos = new ParsePosition(pos);289setStrict(true);290TemporalAccessor parsed = getFormatterBaseDate(field, minWidth, maxWidth, baseValue).parseUnresolved(input, ppos);291if (ppos.getErrorIndex() != -1) {292assertEquals(ppos.getErrorIndex(), strict.parseLen, "error case parse position");293assertEquals(parsed, strict.parseVal, "unexpected parse result");294} else {295assertEquals(ppos.getIndex(), strict.parseLen, "parse position");296assertParsed(parsed, YEAR, strict.parseVal != null ? (long) strict.parseVal : null);297}298}299300//-----------------------------------------------------------------------301// Parsing tests for lenient mode302//-----------------------------------------------------------------------303@Test(dataProvider="ParseLenientSensitive")304public void test_parseLenient(TemporalField field, int minWidth, int maxWidth, int baseValue, String input, int pos,305Pair strict, Pair lenient) {306ParsePosition ppos = new ParsePosition(pos);307setStrict(false);308TemporalAccessor parsed = getFormatter0(field, minWidth, maxWidth, baseValue).parseUnresolved(input, ppos);309if (ppos.getErrorIndex() != -1) {310assertEquals(ppos.getErrorIndex(), lenient.parseLen, "error case parse position");311assertEquals(parsed, lenient.parseVal, "unexpected parse result");312} else {313assertEquals(ppos.getIndex(), lenient.parseLen, "parse position");314assertParsed(parsed, YEAR, lenient.parseVal != null ? (long) lenient.parseVal : null);315}316}317318@Test(dataProvider="ParseLenientSensitive")319public void test_parseLenient_baseDate(TemporalField field, int minWidth, int maxWidth, int baseValue, String input, int pos,320Pair strict, Pair lenient) {321ParsePosition ppos = new ParsePosition(pos);322setStrict(false);323TemporalAccessor parsed = getFormatterBaseDate(field, minWidth, maxWidth, baseValue).parseUnresolved(input, ppos);324if (ppos.getErrorIndex() != -1) {325assertEquals(ppos.getErrorIndex(), lenient.parseLen, "error case parse position");326assertEquals(parsed, lenient.parseVal, "unexpected parse result");327} else {328assertEquals(ppos.getIndex(), lenient.parseLen, "parse position");329assertParsed(parsed, YEAR, lenient.parseVal != null ? (long) lenient.parseVal : null);330}331}332333private void assertParsed(TemporalAccessor parsed, TemporalField field, Long value) {334if (value == null) {335assertEquals(parsed, null, "Parsed Value");336} else {337assertEquals(parsed.isSupported(field), true, "isSupported: " + field);338assertEquals(parsed.getLong(field), (long) value, "Temporal.getLong: " + field);339}340}341342343//-----------------------------------------------------------------------344// Cases and values in adjacent parsing mode345//-----------------------------------------------------------------------346@DataProvider(name="ParseAdjacent")347Object[][] provider_parseAdjacent() {348return new Object[][] {349// general350{"yyMMdd", "19990703", LENIENT, 0, 8, 1999, 7, 3},351{"yyMMdd", "19990703", STRICT, 0, 6, 2019, 99, 7},352{"yyMMdd", "990703", LENIENT, 0, 6, 2099, 7, 3},353{"yyMMdd", "990703", STRICT, 0, 6, 2099, 7, 3},354{"yyMMdd", "200703", LENIENT, 0, 6, 2020, 7, 3},355{"yyMMdd", "200703", STRICT, 0, 6, 2020, 7, 3},356{"ddMMyy", "230714", LENIENT, 0, 6, 2014, 7, 23},357{"ddMMyy", "230714", STRICT, 0, 6, 2014, 7, 23},358{"ddMMyy", "25062001", LENIENT, 0, 8, 2001, 6, 25},359{"ddMMyy", "25062001", STRICT, 0, 6, 2020, 6, 25},360{"ddMMy", "27052002", LENIENT, 0, 8, 2002, 5, 27},361{"ddMMy", "27052002", STRICT, 0, 8, 2002, 5, 27},362};363}364365@Test(dataProvider="ParseAdjacent")366public void test_parseAdjacent(String pattern, String input, boolean strict, int pos, int parseLen, int year, int month, int day) {367ParsePosition ppos = new ParsePosition(0);368builder = new DateTimeFormatterBuilder();369setStrict(strict);370builder.appendPattern(pattern);371DateTimeFormatter dtf = builder.toFormatter();372373TemporalAccessor parsed = dtf.parseUnresolved(input, ppos);374assertNotNull(parsed, String.format("parse failed: ppos: %s, formatter: %s%n", ppos.toString(), dtf));375if (ppos.getErrorIndex() != -1) {376assertEquals(ppos.getErrorIndex(), parseLen, "error case parse position");377} else {378assertEquals(ppos.getIndex(), parseLen, "parse position");379assertParsed(parsed, YEAR_OF_ERA, Long.valueOf(year));380assertParsed(parsed, MONTH_OF_YEAR, Long.valueOf(month));381assertParsed(parsed, DAY_OF_MONTH, Long.valueOf(day));382}383}384385//-----------------------------------------------------------------------386// Cases and values in reduced value parsing mode387//-----------------------------------------------------------------------388@DataProvider(name="ReducedWithChrono")389Object[][] provider_reducedWithChrono() {390LocalDate baseYear = LocalDate.of(2000, 1, 1);391return new Object[][] {392{IsoChronology.INSTANCE.date(baseYear)},393{IsoChronology.INSTANCE.date(baseYear).plus(1, YEARS)},394{IsoChronology.INSTANCE.date(baseYear).plus(99, YEARS)},395{HijrahChronology.INSTANCE.date(baseYear)},396{HijrahChronology.INSTANCE.date(baseYear).plus(1, YEARS)},397{HijrahChronology.INSTANCE.date(baseYear).plus(99, YEARS)},398{JapaneseChronology.INSTANCE.date(baseYear)},399{JapaneseChronology.INSTANCE.date(baseYear).plus(1, YEARS)},400{JapaneseChronology.INSTANCE.date(baseYear).plus(99, YEARS)},401{MinguoChronology.INSTANCE.date(baseYear)},402{MinguoChronology.INSTANCE.date(baseYear).plus(1, YEARS)},403{MinguoChronology.INSTANCE.date(baseYear).plus(99, YEARS)},404{ThaiBuddhistChronology.INSTANCE.date(baseYear)},405{ThaiBuddhistChronology.INSTANCE.date(baseYear).plus(1, YEARS)},406{ThaiBuddhistChronology.INSTANCE.date(baseYear).plus(99, YEARS)},407};408}409410@Test(dataProvider="ReducedWithChrono")411public void test_reducedWithChronoYear(ChronoLocalDate date) {412Chronology chrono = date.getChronology();413DateTimeFormatter df414= new DateTimeFormatterBuilder().appendValueReduced(YEAR, 2, 2, LocalDate.of(2000, 1, 1))415.toFormatter()416.withChronology(chrono);417int expected = date.get(YEAR);418String input = df.format(date);419420ParsePosition pos = new ParsePosition(0);421TemporalAccessor parsed = df.parseUnresolved(input, pos);422int actual = parsed.get(YEAR);423assertEquals(actual, expected,424String.format("Wrong date parsed, chrono: %s, input: %s",425chrono, input));426427}428@Test(dataProvider="ReducedWithChrono")429public void test_reducedWithChronoYearOfEra(ChronoLocalDate date) {430Chronology chrono = date.getChronology();431DateTimeFormatter df432= new DateTimeFormatterBuilder().appendValueReduced(YEAR_OF_ERA, 2, 2, LocalDate.of(2000, 1, 1))433.toFormatter()434.withChronology(chrono);435int expected = date.get(YEAR_OF_ERA);436String input = df.format(date);437438ParsePosition pos = new ParsePosition(0);439TemporalAccessor parsed = df.parseUnresolved(input, pos);440int actual = parsed.get(YEAR_OF_ERA);441assertEquals(actual, expected,442String.format("Wrong date parsed, chrono: %s, input: %s",443chrono, input));444445}446447@Test448public void test_reducedWithLateChronoChange() {449ThaiBuddhistDate date = ThaiBuddhistDate.of(2543, 1, 1);450DateTimeFormatter df451= new DateTimeFormatterBuilder()452.appendValueReduced(YEAR, 2, 2, LocalDate.of(2000, 1, 1))453.appendLiteral(" ")454.appendChronologyId()455.toFormatter();456int expected = date.get(YEAR);457String input = df.format(date);458459ParsePosition pos = new ParsePosition(0);460TemporalAccessor parsed = df.parseUnresolved(input, pos);461assertEquals(pos.getIndex(), input.length(), "Input not parsed completely");462assertEquals(pos.getErrorIndex(), -1, "Error index should be -1 (no-error)");463int actual = parsed.get(YEAR);464assertEquals(actual, expected,465String.format("Wrong date parsed, chrono: %s, input: %s",466parsed.query(TemporalQueries.chronology()), input));467468}469470@Test471public void test_reducedWithLateChronoChangeTwice() {472DateTimeFormatter df473= new DateTimeFormatterBuilder()474.appendValueReduced(YEAR, 2, 2, LocalDate.of(2000, 1, 1))475.appendLiteral(" ")476.appendChronologyId()477.appendLiteral(" ")478.appendChronologyId()479.toFormatter();480int expected = 2044;481String input = "44 ThaiBuddhist ISO";482ParsePosition pos = new ParsePosition(0);483TemporalAccessor parsed = df.parseUnresolved(input, pos);484assertEquals(pos.getIndex(), input.length(), "Input not parsed completely: " + pos);485assertEquals(pos.getErrorIndex(), -1, "Error index should be -1 (no-error)");486int actual = parsed.get(YEAR);487assertEquals(actual, expected,488String.format("Wrong date parsed, chrono: %s, input: %s",489parsed.query(TemporalQueries.chronology()), input));490491}492493//-----------------------------------------------------------------------494// Class to structure the test data495//-----------------------------------------------------------------------496497private static Pair strict(int parseLen, Integer parseVal) {498return new Pair(parseLen, parseVal, STRICT);499}500private static Pair lenient(int parseLen, Integer parseVal) {501return new Pair(parseLen, parseVal, LENIENT);502}503504private static class Pair {505public final int parseLen;506public final Integer parseVal;507private final boolean strict;508public Pair(int parseLen, Integer parseVal, boolean strict) {509this.parseLen = parseLen;510this.parseVal = parseVal;511this.strict = strict;512}513public String toString() {514return (strict ? "strict" : "lenient") + "(" + parseLen + "," + parseVal + ")";515}516}517518}519520521