1/*
2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * This file is available under and governed by the GNU General Public
26 * License version 2 only, as published by the Free Software Foundation.
27 * However, the following notice accompanied the original version of this
28 * file:
29 *
30 * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos
31 *
32 * All rights reserved.
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions are met:
36 *
37 *  * Redistributions of source code must retain the above copyright notice,
38 *    this list of conditions and the following disclaimer.
39 *
40 *  * Redistributions in binary form must reproduce the above copyright notice,
41 *    this list of conditions and the following disclaimer in the documentation
42 *    and/or other materials provided with the distribution.
43 *
44 *  * Neither the name of JSR-310 nor the names of its contributors
45 *    may be used to endorse or promote products derived from this software
46 *    without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
51 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
52 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
53 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
54 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
55 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
56 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
57 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
58 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 */
60package test.java.time.format;
61
62import static java.time.temporal.ChronoField.DAY_OF_MONTH;
63import static java.time.temporal.ChronoField.DAY_OF_WEEK;
64import static java.time.temporal.ChronoField.DAY_OF_YEAR;
65import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
66import static org.testng.Assert.assertEquals;
67import static org.testng.Assert.assertTrue;
68import static org.testng.Assert.fail;
69
70import java.text.ParsePosition;
71import java.time.format.DateTimeFormatter;
72import java.time.format.SignStyle;
73import java.time.temporal.TemporalAccessor;
74import java.time.temporal.TemporalField;
75import java.time.temporal.TemporalQueries;
76
77import org.testng.annotations.DataProvider;
78import org.testng.annotations.Test;
79
80/**
81 * Test NumberPrinterParser.
82 */
83@Test
84public class TestNumberParser extends AbstractTestPrinterParser {
85
86    //-----------------------------------------------------------------------
87    @DataProvider(name="error")
88    Object[][] data_error() {
89        return new Object[][] {
90            {DAY_OF_MONTH, 1, 2, SignStyle.NEVER, "12", -1, IndexOutOfBoundsException.class},
91            {DAY_OF_MONTH, 1, 2, SignStyle.NEVER, "12", 3, IndexOutOfBoundsException.class},
92        };
93    }
94
95    @Test(dataProvider="error")
96    public void test_parse_error(TemporalField field, int min, int max, SignStyle style, String text, int pos, Class<?> expected) {
97        try {
98            getFormatter(field, min, max, style).parseUnresolved(text, new ParsePosition(pos));
99            fail();
100        } catch (RuntimeException ex) {
101            assertTrue(expected.isInstance(ex));
102        }
103    }
104
105    //-----------------------------------------------------------------------
106    @DataProvider(name="parseData")
107    Object[][] provider_parseData() {
108        return new Object[][] {
109            // normal
110            {1, 2, SignStyle.NEVER, 0, "12", 0, 2, 12L},       // normal
111            {1, 2, SignStyle.NEVER, 0, "Xxx12Xxx", 3, 5, 12L}, // parse in middle
112            {1, 2, SignStyle.NEVER, 0, "99912999", 3, 5, 12L}, // parse in middle
113            {2, 4, SignStyle.NEVER, 0, "12345", 0, 4, 1234L},  // stops at max width
114            {2, 4, SignStyle.NEVER, 0, "12-45", 0, 2, 12L},    // stops at dash
115            {2, 4, SignStyle.NEVER, 0, "123-5", 0, 3, 123L},   // stops at dash
116            {1, 10, SignStyle.NORMAL, 0, "2147483647", 0, 10, Integer.MAX_VALUE},
117            {1, 10, SignStyle.NORMAL, 0, "-2147483648", 0, 11, Integer.MIN_VALUE},
118            {1, 10, SignStyle.NORMAL, 0, "2147483648", 0, 10, 2147483648L},
119            {1, 10, SignStyle.NORMAL, 0, "-2147483649", 0, 11, -2147483649L},
120            {1, 10, SignStyle.NORMAL, 0, "987659876598765", 0, 10, 9876598765L},
121            {1, 19, SignStyle.NORMAL, 0, "999999999999999999", 0, 18, 999999999999999999L},
122            {1, 19, SignStyle.NORMAL, 0, "-999999999999999999", 0, 19, -999999999999999999L},
123            {1, 19, SignStyle.NORMAL, 0, "1000000000000000000", 0, 19, 1000000000000000000L},
124            {1, 19, SignStyle.NORMAL, 0, "-1000000000000000000", 0, 20, -1000000000000000000L},
125            {1, 19, SignStyle.NORMAL, 0, "000000000000000000", 0, 18, 0L},
126            {1, 19, SignStyle.NORMAL, 0, "0000000000000000000", 0, 19, 0L},
127            {1, 19, SignStyle.NORMAL, 0, "9223372036854775807", 0, 19, Long.MAX_VALUE},
128            {1, 19, SignStyle.NORMAL, 0, "-9223372036854775808", 0, 20, Long.MIN_VALUE},
129            {1, 19, SignStyle.NORMAL, 0, "9223372036854775808", 0, 18, 922337203685477580L},  // last digit not parsed
130            {1, 19, SignStyle.NORMAL, 0, "-9223372036854775809", 0, 19, -922337203685477580L}, // last digit not parsed
131            // no match
132            {1, 2, SignStyle.NEVER, 1, "A1", 0, 0, 0},
133            {1, 2, SignStyle.NEVER, 1, " 1", 0, 0, 0},
134            {1, 2, SignStyle.NEVER, 1, "  1", 1, 1, 0},
135            {2, 2, SignStyle.NEVER, 1, "1", 0, 0, 0},
136            {2, 2, SignStyle.NEVER, 1, "Xxx1", 0, 0, 0},
137            {2, 2, SignStyle.NEVER, 1, "1", 1, 1, 0},
138            {2, 2, SignStyle.NEVER, 1, "Xxx1", 4, 4, 0},
139            {2, 2, SignStyle.NEVER, 1, "1-2", 0, 0, 0},
140            {1, 19, SignStyle.NORMAL, 0, "-000000000000000000", 0, 0, 0},
141            {1, 19, SignStyle.NORMAL, 0, "-0000000000000000000", 0, 0, 0},
142            // parse reserving space 1 (adjacent-parsing)
143            {1, 1, SignStyle.NEVER, 1, "12", 0, 1, 1L},
144            {1, 19, SignStyle.NEVER, 1, "12", 0, 1, 1L},
145            {1, 19, SignStyle.NEVER, 1, "12345", 0, 4, 1234L},
146            {1, 19, SignStyle.NEVER, 1, "12345678901", 0, 10, 1234567890L},
147            {1, 19, SignStyle.NEVER, 1, "123456789012345678901234567890", 0, 19, 1234567890123456789L},
148            {1, 19, SignStyle.NEVER, 1, "1", 0, 1, 1L},  // error from next field
149            {2, 2, SignStyle.NEVER, 1, "12", 0, 2, 12L},  // error from next field
150            {2, 19, SignStyle.NEVER, 1, "1", 0, 0, 0},
151            // parse reserving space 2 (adjacent-parsing)
152            {1, 1, SignStyle.NEVER, 2, "123", 0, 1, 1L},
153            {1, 19, SignStyle.NEVER, 2, "123", 0, 1, 1L},
154            {1, 19, SignStyle.NEVER, 2, "12345", 0, 3, 123L},
155            {1, 19, SignStyle.NEVER, 2, "12345678901", 0, 9, 123456789L},
156            {1, 19, SignStyle.NEVER, 2, "123456789012345678901234567890", 0, 19, 1234567890123456789L},
157            {1, 19, SignStyle.NEVER, 2, "1", 0, 1, 1L},  // error from next field
158            {1, 19, SignStyle.NEVER, 2, "12", 0, 1, 1L},  // error from next field
159            {2, 2, SignStyle.NEVER, 2, "12", 0, 2, 12L},  // error from next field
160            {2, 19, SignStyle.NEVER, 2, "1", 0, 0, 0},
161            {2, 19, SignStyle.NEVER, 2, "1AAAAABBBBBCCCCC", 0, 0, 0},
162        };
163    }
164
165    //-----------------------------------------------------------------------
166    @Test(dataProvider="parseData")
167    public void test_parse_fresh(int minWidth, int maxWidth, SignStyle signStyle, int subsequentWidth, String text, int pos, int expectedPos, long expectedValue) {
168        ParsePosition ppos = new ParsePosition(pos);
169        DateTimeFormatter dtf = getFormatter(DAY_OF_MONTH, minWidth, maxWidth, signStyle);
170        if (subsequentWidth > 0) {
171            // hacky, to reserve space
172            dtf = builder.appendValue(DAY_OF_YEAR, subsequentWidth).toFormatter(locale).withDecimalStyle(decimalStyle);
173        }
174        TemporalAccessor parsed = dtf.parseUnresolved(text, ppos);
175        if (ppos.getErrorIndex() != -1) {
176            assertEquals(ppos.getErrorIndex(), expectedPos);
177        } else {
178            assertTrue(subsequentWidth >= 0);
179            assertEquals(ppos.getIndex(), expectedPos + subsequentWidth);
180            assertEquals(parsed.getLong(DAY_OF_MONTH), expectedValue);
181            assertEquals(parsed.query(TemporalQueries.chronology()), null);
182            assertEquals(parsed.query(TemporalQueries.zoneId()), null);
183        }
184    }
185
186    @Test(dataProvider="parseData")
187    public void test_parse_textField(int minWidth, int maxWidth, SignStyle signStyle, int subsequentWidth, String text, int pos, int expectedPos, long expectedValue) {
188        ParsePosition ppos = new ParsePosition(pos);
189        DateTimeFormatter dtf = getFormatter(DAY_OF_WEEK, minWidth, maxWidth, signStyle);
190        if (subsequentWidth > 0) {
191            // hacky, to reserve space
192            dtf = builder.appendValue(DAY_OF_YEAR, subsequentWidth).toFormatter(locale).withDecimalStyle(decimalStyle);
193        }
194        TemporalAccessor parsed = dtf.parseUnresolved(text, ppos);
195        if (ppos.getErrorIndex() != -1) {
196            assertEquals(ppos.getErrorIndex(), expectedPos);
197        } else {
198            assertTrue(subsequentWidth >= 0);
199            assertEquals(ppos.getIndex(), expectedPos + subsequentWidth);
200            assertEquals(parsed.getLong(DAY_OF_WEEK), expectedValue);
201            assertEquals(parsed.query(TemporalQueries.chronology()), null);
202            assertEquals(parsed.query(TemporalQueries.zoneId()), null);
203        }
204    }
205
206    //-----------------------------------------------------------------------
207    @DataProvider(name="parseSignsStrict")
208    Object[][] provider_parseSignsStrict() {
209        return new Object[][] {
210            // basics
211            {"0", 1, 2, SignStyle.NEVER, 1, 0},
212            {"1", 1, 2, SignStyle.NEVER, 1, 1},
213            {"2", 1, 2, SignStyle.NEVER, 1, 2},
214            {"3", 1, 2, SignStyle.NEVER, 1, 3},
215            {"4", 1, 2, SignStyle.NEVER, 1, 4},
216            {"5", 1, 2, SignStyle.NEVER, 1, 5},
217            {"6", 1, 2, SignStyle.NEVER, 1, 6},
218            {"7", 1, 2, SignStyle.NEVER, 1, 7},
219            {"8", 1, 2, SignStyle.NEVER, 1, 8},
220            {"9", 1, 2, SignStyle.NEVER, 1, 9},
221            {"10", 1, 2, SignStyle.NEVER, 2, 10},
222            {"100", 1, 2, SignStyle.NEVER, 2, 10},
223            {"100", 1, 3, SignStyle.NEVER, 3, 100},
224
225            // never
226            {"0", 1, 2, SignStyle.NEVER, 1, 0},
227            {"5", 1, 2, SignStyle.NEVER, 1, 5},
228            {"50", 1, 2, SignStyle.NEVER, 2, 50},
229            {"500", 1, 2, SignStyle.NEVER, 2, 50},
230            {"-0", 1, 2, SignStyle.NEVER, 0, null},
231            {"-5", 1, 2, SignStyle.NEVER, 0, null},
232            {"-50", 1, 2, SignStyle.NEVER, 0, null},
233            {"-500", 1, 2, SignStyle.NEVER, 0, null},
234            {"-AAA", 1, 2, SignStyle.NEVER, 0, null},
235            {"+0", 1, 2, SignStyle.NEVER, 0, null},
236            {"+5", 1, 2, SignStyle.NEVER, 0, null},
237            {"+50", 1, 2, SignStyle.NEVER, 0, null},
238            {"+500", 1, 2, SignStyle.NEVER, 0, null},
239            {"+AAA", 1, 2, SignStyle.NEVER, 0, null},
240
241            // not negative
242            {"0", 1, 2, SignStyle.NOT_NEGATIVE, 1, 0},
243            {"5", 1, 2, SignStyle.NOT_NEGATIVE, 1, 5},
244            {"50", 1, 2, SignStyle.NOT_NEGATIVE, 2, 50},
245            {"500", 1, 2, SignStyle.NOT_NEGATIVE, 2, 50},
246            {"-0", 1, 2, SignStyle.NOT_NEGATIVE, 0, null},
247            {"-5", 1, 2, SignStyle.NOT_NEGATIVE, 0, null},
248            {"-50", 1, 2, SignStyle.NOT_NEGATIVE, 0, null},
249            {"-500", 1, 2, SignStyle.NOT_NEGATIVE, 0, null},
250            {"-AAA", 1, 2, SignStyle.NOT_NEGATIVE, 0, null},
251            {"+0", 1, 2, SignStyle.NOT_NEGATIVE, 0, null},
252            {"+5", 1, 2, SignStyle.NOT_NEGATIVE, 0, null},
253            {"+50", 1, 2, SignStyle.NOT_NEGATIVE, 0, null},
254            {"+500", 1, 2, SignStyle.NOT_NEGATIVE, 0, null},
255            {"+AAA", 1, 2, SignStyle.NOT_NEGATIVE, 0, null},
256
257            // normal
258            {"0", 1, 2, SignStyle.NORMAL, 1, 0},
259            {"5", 1, 2, SignStyle.NORMAL, 1, 5},
260            {"50", 1, 2, SignStyle.NORMAL, 2, 50},
261            {"500", 1, 2, SignStyle.NORMAL, 2, 50},
262            {"-0", 1, 2, SignStyle.NORMAL, 0, null},
263            {"-5", 1, 2, SignStyle.NORMAL, 2, -5},
264            {"-50", 1, 2, SignStyle.NORMAL, 3, -50},
265            {"-500", 1, 2, SignStyle.NORMAL, 3, -50},
266            {"-AAA", 1, 2, SignStyle.NORMAL, 1, null},
267            {"+0", 1, 2, SignStyle.NORMAL, 0, null},
268            {"+5", 1, 2, SignStyle.NORMAL, 0, null},
269            {"+50", 1, 2, SignStyle.NORMAL, 0, null},
270            {"+500", 1, 2, SignStyle.NORMAL, 0, null},
271            {"+AAA", 1, 2, SignStyle.NORMAL, 0, null},
272
273            // always
274            {"0", 1, 2, SignStyle.ALWAYS, 0, null},
275            {"5", 1, 2, SignStyle.ALWAYS, 0, null},
276            {"50", 1, 2, SignStyle.ALWAYS, 0, null},
277            {"500", 1, 2, SignStyle.ALWAYS, 0, null},
278            {"-0", 1, 2, SignStyle.ALWAYS, 0, null},
279            {"-5", 1, 2, SignStyle.ALWAYS, 2, -5},
280            {"-50", 1, 2, SignStyle.ALWAYS, 3, -50},
281            {"-500", 1, 2, SignStyle.ALWAYS, 3, -50},
282            {"-AAA", 1, 2, SignStyle.ALWAYS, 1, null},
283            {"+0", 1, 2, SignStyle.ALWAYS, 2, 0},
284            {"+5", 1, 2, SignStyle.ALWAYS, 2, 5},
285            {"+50", 1, 2, SignStyle.ALWAYS, 3, 50},
286            {"+500", 1, 2, SignStyle.ALWAYS, 3, 50},
287            {"+AAA", 1, 2, SignStyle.ALWAYS, 1, null},
288
289            // exceeds pad
290            {"0", 1, 2, SignStyle.EXCEEDS_PAD, 1, 0},
291            {"5", 1, 2, SignStyle.EXCEEDS_PAD, 1, 5},
292            {"50", 1, 2, SignStyle.EXCEEDS_PAD, 0, null},
293            {"500", 1, 2, SignStyle.EXCEEDS_PAD, 0, null},
294            {"-0", 1, 2, SignStyle.EXCEEDS_PAD, 0, null},
295            {"-5", 1, 2, SignStyle.EXCEEDS_PAD, 2, -5},
296            {"-50", 1, 2, SignStyle.EXCEEDS_PAD, 3, -50},
297            {"-500", 1, 2, SignStyle.EXCEEDS_PAD, 3, -50},
298            {"-AAA", 1, 2, SignStyle.EXCEEDS_PAD, 1, null},
299            {"+0", 1, 2, SignStyle.EXCEEDS_PAD, 0, null},
300            {"+5", 1, 2, SignStyle.EXCEEDS_PAD, 0, null},
301            {"+50", 1, 2, SignStyle.EXCEEDS_PAD, 3, 50},
302            {"+500", 1, 2, SignStyle.EXCEEDS_PAD, 3, 50},
303            {"+AAA", 1, 2, SignStyle.EXCEEDS_PAD, 1, null},
304       };
305    }
306
307    @Test(dataProvider="parseSignsStrict")
308    public void test_parseSignsStrict(String input, int min, int max, SignStyle style, int parseLen, Integer parseVal) throws Exception {
309        ParsePosition pos = new ParsePosition(0);
310        TemporalAccessor parsed = getFormatter(DAY_OF_MONTH, min, max, style).parseUnresolved(input, pos);
311        if (pos.getErrorIndex() != -1) {
312            assertEquals(pos.getErrorIndex(), parseLen);
313        } else {
314            assertEquals(pos.getIndex(), parseLen);
315            assertEquals(parsed.getLong(DAY_OF_MONTH), (long)parseVal);
316            assertEquals(parsed.query(TemporalQueries.chronology()), null);
317            assertEquals(parsed.query(TemporalQueries.zoneId()), null);
318        }
319    }
320
321    //-----------------------------------------------------------------------
322    @DataProvider(name="parseSignsLenient")
323    Object[][] provider_parseSignsLenient() {
324        return new Object[][] {
325            // never
326            {"0", 1, 2, SignStyle.NEVER, 1, 0},
327            {"5", 1, 2, SignStyle.NEVER, 1, 5},
328            {"50", 1, 2, SignStyle.NEVER, 2, 50},
329            {"500", 1, 2, SignStyle.NEVER, 3, 500},
330            {"-0", 1, 2, SignStyle.NEVER, 2, 0},
331            {"-5", 1, 2, SignStyle.NEVER, 2, -5},
332            {"-50", 1, 2, SignStyle.NEVER, 3, -50},
333            {"-500", 1, 2, SignStyle.NEVER, 4, -500},
334            {"-AAA", 1, 2, SignStyle.NEVER, 1, null},
335            {"+0", 1, 2, SignStyle.NEVER, 2, 0},
336            {"+5", 1, 2, SignStyle.NEVER, 2, 5},
337            {"+50", 1, 2, SignStyle.NEVER, 3, 50},
338            {"+500", 1, 2, SignStyle.NEVER, 4, 500},
339            {"+AAA", 1, 2, SignStyle.NEVER, 1, null},
340            {"50", 2, 2, SignStyle.NEVER, 2, 50},
341            {"-50", 2, 2, SignStyle.NEVER, 0, null},
342            {"+50", 2, 2, SignStyle.NEVER, 0, null},
343
344            // not negative
345            {"0", 1, 2, SignStyle.NOT_NEGATIVE, 1, 0},
346            {"5", 1, 2, SignStyle.NOT_NEGATIVE, 1, 5},
347            {"50", 1, 2, SignStyle.NOT_NEGATIVE, 2, 50},
348            {"500", 1, 2, SignStyle.NOT_NEGATIVE, 3, 500},
349            {"-0", 1, 2, SignStyle.NOT_NEGATIVE, 2, 0},
350            {"-5", 1, 2, SignStyle.NOT_NEGATIVE, 2, -5},
351            {"-50", 1, 2, SignStyle.NOT_NEGATIVE, 3, -50},
352            {"-500", 1, 2, SignStyle.NOT_NEGATIVE, 4, -500},
353            {"-AAA", 1, 2, SignStyle.NOT_NEGATIVE, 1, null},
354            {"+0", 1, 2, SignStyle.NOT_NEGATIVE, 2, 0},
355            {"+5", 1, 2, SignStyle.NOT_NEGATIVE, 2, 5},
356            {"+50", 1, 2, SignStyle.NOT_NEGATIVE, 3, 50},
357            {"+500", 1, 2, SignStyle.NOT_NEGATIVE, 4, 500},
358            {"+AAA", 1, 2, SignStyle.NOT_NEGATIVE, 1, null},
359            {"50", 2, 2, SignStyle.NOT_NEGATIVE, 2, 50},
360            {"-50", 2, 2, SignStyle.NOT_NEGATIVE, 0, null},
361            {"+50", 2, 2, SignStyle.NOT_NEGATIVE, 0, null},
362
363            // normal
364            {"0", 1, 2, SignStyle.NORMAL, 1, 0},
365            {"5", 1, 2, SignStyle.NORMAL, 1, 5},
366            {"50", 1, 2, SignStyle.NORMAL, 2, 50},
367            {"500", 1, 2, SignStyle.NORMAL, 3, 500},
368            {"-0", 1, 2, SignStyle.NORMAL, 2, 0},
369            {"-5", 1, 2, SignStyle.NORMAL, 2, -5},
370            {"-50", 1, 2, SignStyle.NORMAL, 3, -50},
371            {"-500", 1, 2, SignStyle.NORMAL, 4, -500},
372            {"-AAA", 1, 2, SignStyle.NORMAL, 1, null},
373            {"+0", 1, 2, SignStyle.NORMAL, 2, 0},
374            {"+5", 1, 2, SignStyle.NORMAL, 2, 5},
375            {"+50", 1, 2, SignStyle.NORMAL, 3, 50},
376            {"+500", 1, 2, SignStyle.NORMAL, 4, 500},
377            {"+AAA", 1, 2, SignStyle.NORMAL, 1, null},
378            {"50", 2, 2, SignStyle.NORMAL, 2, 50},
379            {"-50", 2, 2, SignStyle.NORMAL, 3, -50},
380            {"+50", 2, 2, SignStyle.NORMAL, 3, 50},
381
382            // always
383            {"0", 1, 2, SignStyle.ALWAYS, 1, 0},
384            {"5", 1, 2, SignStyle.ALWAYS, 1, 5},
385            {"50", 1, 2, SignStyle.ALWAYS, 2, 50},
386            {"500", 1, 2, SignStyle.ALWAYS, 3, 500},
387            {"-0", 1, 2, SignStyle.ALWAYS, 2, 0},
388            {"-5", 1, 2, SignStyle.ALWAYS, 2, -5},
389            {"-50", 1, 2, SignStyle.ALWAYS, 3, -50},
390            {"-500", 1, 2, SignStyle.ALWAYS, 4, -500},
391            {"-AAA", 1, 2, SignStyle.ALWAYS, 1, null},
392            {"+0", 1, 2, SignStyle.ALWAYS, 2, 0},
393            {"+5", 1, 2, SignStyle.ALWAYS, 2, 5},
394            {"+50", 1, 2, SignStyle.ALWAYS, 3, 50},
395            {"+500", 1, 2, SignStyle.ALWAYS, 4, 500},
396            {"+AAA", 1, 2, SignStyle.ALWAYS, 1, null},
397
398            // exceeds pad
399            {"0", 1, 2, SignStyle.EXCEEDS_PAD, 1, 0},
400            {"5", 1, 2, SignStyle.EXCEEDS_PAD, 1, 5},
401            {"50", 1, 2, SignStyle.EXCEEDS_PAD, 2, 50},
402            {"500", 1, 2, SignStyle.EXCEEDS_PAD, 3, 500},
403            {"-0", 1, 2, SignStyle.EXCEEDS_PAD, 2, 0},
404            {"-5", 1, 2, SignStyle.EXCEEDS_PAD, 2, -5},
405            {"-50", 1, 2, SignStyle.EXCEEDS_PAD, 3, -50},
406            {"-500", 1, 2, SignStyle.EXCEEDS_PAD, 4, -500},
407            {"-AAA", 1, 2, SignStyle.EXCEEDS_PAD, 1, null},
408            {"+0", 1, 2, SignStyle.EXCEEDS_PAD, 2, 0},
409            {"+5", 1, 2, SignStyle.EXCEEDS_PAD, 2, 5},
410            {"+50", 1, 2, SignStyle.EXCEEDS_PAD, 3, 50},
411            {"+500", 1, 2, SignStyle.EXCEEDS_PAD, 4, 500},
412            {"+AAA", 1, 2, SignStyle.EXCEEDS_PAD, 1, null},
413       };
414    }
415
416    @Test(dataProvider="parseSignsLenient")
417    public void test_parseSignsLenient(String input, int min, int max, SignStyle style, int parseLen, Integer parseVal) throws Exception {
418        setStrict(false);
419        ParsePosition pos = new ParsePosition(0);
420        TemporalAccessor parsed = getFormatter(DAY_OF_MONTH, min, max, style).parseUnresolved(input, pos);
421        if (pos.getErrorIndex() != -1) {
422            assertEquals(pos.getErrorIndex(), parseLen);
423        } else {
424            assertEquals(pos.getIndex(), parseLen);
425            assertEquals(parsed.getLong(DAY_OF_MONTH), (long)parseVal);
426            assertEquals(parsed.query(TemporalQueries.chronology()), null);
427            assertEquals(parsed.query(TemporalQueries.zoneId()), null);
428        }
429    }
430
431    //-----------------------------------------------------------------------
432    @DataProvider(name="parseDigitsLenient")
433    Object[][] provider_parseDigitsLenient() {
434        return new Object[][] {
435                // never
436                {"5", 1, 2, SignStyle.NEVER, 1, 5},
437                {"5", 2, 2, SignStyle.NEVER, 1, 5},
438                {"54", 1, 3, SignStyle.NEVER, 2, 54},
439                {"54", 2, 3, SignStyle.NEVER, 2, 54},
440                {"54", 3, 3, SignStyle.NEVER, 2, 54},
441                {"543", 1, 3, SignStyle.NEVER, 3, 543},
442                {"543", 2, 3, SignStyle.NEVER, 3, 543},
443                {"543", 3, 3, SignStyle.NEVER, 3, 543},
444                {"5432", 1, 3, SignStyle.NEVER, 4, 5432},
445                {"5432", 2, 3, SignStyle.NEVER, 4, 5432},
446                {"5432", 3, 3, SignStyle.NEVER, 4, 5432},
447                {"5AAA", 2, 3, SignStyle.NEVER, 1, 5},
448
449                // not negative
450                {"5", 1, 2, SignStyle.NOT_NEGATIVE, 1, 5},
451                {"5", 2, 2, SignStyle.NOT_NEGATIVE, 1, 5},
452                {"54", 1, 3, SignStyle.NOT_NEGATIVE, 2, 54},
453                {"54", 2, 3, SignStyle.NOT_NEGATIVE, 2, 54},
454                {"54", 3, 3, SignStyle.NOT_NEGATIVE, 2, 54},
455                {"543", 1, 3, SignStyle.NOT_NEGATIVE, 3, 543},
456                {"543", 2, 3, SignStyle.NOT_NEGATIVE, 3, 543},
457                {"543", 3, 3, SignStyle.NOT_NEGATIVE, 3, 543},
458                {"5432", 1, 3, SignStyle.NOT_NEGATIVE, 4, 5432},
459                {"5432", 2, 3, SignStyle.NOT_NEGATIVE, 4, 5432},
460                {"5432", 3, 3, SignStyle.NOT_NEGATIVE, 4, 5432},
461                {"5AAA", 2, 3, SignStyle.NOT_NEGATIVE, 1, 5},
462
463                // normal
464                {"5", 1, 2, SignStyle.NORMAL, 1, 5},
465                {"5", 2, 2, SignStyle.NORMAL, 1, 5},
466                {"54", 1, 3, SignStyle.NORMAL, 2, 54},
467                {"54", 2, 3, SignStyle.NORMAL, 2, 54},
468                {"54", 3, 3, SignStyle.NORMAL, 2, 54},
469                {"543", 1, 3, SignStyle.NORMAL, 3, 543},
470                {"543", 2, 3, SignStyle.NORMAL, 3, 543},
471                {"543", 3, 3, SignStyle.NORMAL, 3, 543},
472                {"5432", 1, 3, SignStyle.NORMAL, 4, 5432},
473                {"5432", 2, 3, SignStyle.NORMAL, 4, 5432},
474                {"5432", 3, 3, SignStyle.NORMAL, 4, 5432},
475                {"5AAA", 2, 3, SignStyle.NORMAL, 1, 5},
476
477                // always
478                {"5", 1, 2, SignStyle.ALWAYS, 1, 5},
479                {"5", 2, 2, SignStyle.ALWAYS, 1, 5},
480                {"54", 1, 3, SignStyle.ALWAYS, 2, 54},
481                {"54", 2, 3, SignStyle.ALWAYS, 2, 54},
482                {"54", 3, 3, SignStyle.ALWAYS, 2, 54},
483                {"543", 1, 3, SignStyle.ALWAYS, 3, 543},
484                {"543", 2, 3, SignStyle.ALWAYS, 3, 543},
485                {"543", 3, 3, SignStyle.ALWAYS, 3, 543},
486                {"5432", 1, 3, SignStyle.ALWAYS, 4, 5432},
487                {"5432", 2, 3, SignStyle.ALWAYS, 4, 5432},
488                {"5432", 3, 3, SignStyle.ALWAYS, 4, 5432},
489                {"5AAA", 2, 3, SignStyle.ALWAYS, 1, 5},
490
491                // exceeds pad
492                {"5", 1, 2, SignStyle.EXCEEDS_PAD, 1, 5},
493                {"5", 2, 2, SignStyle.EXCEEDS_PAD, 1, 5},
494                {"54", 1, 3, SignStyle.EXCEEDS_PAD, 2, 54},
495                {"54", 2, 3, SignStyle.EXCEEDS_PAD, 2, 54},
496                {"54", 3, 3, SignStyle.EXCEEDS_PAD, 2, 54},
497                {"543", 1, 3, SignStyle.EXCEEDS_PAD, 3, 543},
498                {"543", 2, 3, SignStyle.EXCEEDS_PAD, 3, 543},
499                {"543", 3, 3, SignStyle.EXCEEDS_PAD, 3, 543},
500                {"5432", 1, 3, SignStyle.EXCEEDS_PAD, 4, 5432},
501                {"5432", 2, 3, SignStyle.EXCEEDS_PAD, 4, 5432},
502                {"5432", 3, 3, SignStyle.EXCEEDS_PAD, 4, 5432},
503                {"5AAA", 2, 3, SignStyle.EXCEEDS_PAD, 1, 5},
504        };
505    }
506
507    @Test(dataProvider="parseDigitsLenient")
508    public void test_parseDigitsLenient(String input, int min, int max, SignStyle style, int parseLen, Integer parseVal) throws Exception {
509        setStrict(false);
510        ParsePosition pos = new ParsePosition(0);
511        TemporalAccessor parsed = getFormatter(DAY_OF_MONTH, min, max, style).parseUnresolved(input, pos);
512        if (pos.getErrorIndex() != -1) {
513            assertEquals(pos.getErrorIndex(), parseLen);
514        } else {
515            assertEquals(pos.getIndex(), parseLen);
516            assertEquals(parsed.getLong(DAY_OF_MONTH), (long)parseVal);
517            assertEquals(parsed.query(TemporalQueries.chronology()), null);
518            assertEquals(parsed.query(TemporalQueries.zoneId()), null);
519        }
520    }
521
522    //-----------------------------------------------------------------------
523    @DataProvider(name="parseDigitsAdjacentLenient")
524    Object[][] provider_parseDigitsAdjacentLenient() {
525        return new Object[][] {
526                // never
527                {"5", 1, null, null},
528                {"54", 1, null, null},
529
530                {"543", 3, 5, 43},
531                {"543A", 3, 5, 43},
532
533                {"5432", 4, 54, 32},
534                {"5432A", 4, 54, 32},
535
536                {"54321", 5, 543, 21},
537                {"54321A", 5, 543, 21},
538        };
539    }
540
541    @Test(dataProvider="parseDigitsAdjacentLenient")
542    public void test_parseDigitsAdjacentLenient(String input, int parseLen, Integer parseMonth, Integer parsedDay) throws Exception {
543        setStrict(false);
544        ParsePosition pos = new ParsePosition(0);
545        DateTimeFormatter f = builder
546                .appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL)
547                .appendValue(DAY_OF_MONTH, 2).toFormatter(locale).withDecimalStyle(decimalStyle);
548        TemporalAccessor parsed = f.parseUnresolved(input, pos);
549        if (pos.getErrorIndex() != -1) {
550            assertEquals(pos.getErrorIndex(), parseLen);
551        } else {
552            assertEquals(pos.getIndex(), parseLen);
553            assertEquals(parsed.getLong(MONTH_OF_YEAR), (long) parseMonth);
554            assertEquals(parsed.getLong(DAY_OF_MONTH), (long) parsedDay);
555            assertEquals(parsed.query(TemporalQueries.chronology()), null);
556            assertEquals(parsed.query(TemporalQueries.zoneId()), null);
557        }
558    }
559
560}
561