1 /*
   2  * Copyright (c) 2012, 2016, 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) 2010-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  */
  60 package tck.java.time.format;
  61 
  62 import static java.time.temporal.ChronoField.YEAR_OF_ERA;
  63 import static org.testng.Assert.assertEquals;
  64 
  65 import java.text.ParsePosition;
  66 import java.time.LocalDate;
  67 import java.time.format.DateTimeFormatter;
  68 import java.time.format.DateTimeFormatterBuilder;
  69 import java.time.format.DateTimeParseException;
  70 import java.time.temporal.TemporalAccessor;
  71 import java.time.temporal.TemporalField;
  72 import java.time.temporal.WeekFields;
  73 import java.util.Locale;
  74 
  75 import org.testng.annotations.DataProvider;
  76 import org.testng.annotations.Test;
  77 import test.java.time.format.AbstractTestPrinterParser;
  78 
  79 /**
  80  * Test TCKLocalizedFieldParser.
  81  */
  82 @Test
  83 public class TCKLocalizedFieldParser extends AbstractTestPrinterParser {
  84     WeekFields weekDef = WeekFields.of(Locale.US);
  85     TemporalField Y = weekDef.weekBasedYear();
  86     TemporalField w = weekDef.weekOfWeekBasedYear();
  87     TemporalField c = weekDef.dayOfWeek();
  88     //-----------------------------------------------------------------------
  89     @DataProvider(name="FieldPatterns")
  90     Object[][] provider_fieldPatterns() {
  91         return new Object[][] {
  92             {"e", "6", 0, 1, 6},
  93             {"e", "06", 0, 2, 6},
  94             {"ee", "06", 0, 2, 6},
  95             {"c",  "6", 0, 1 , 6},
  96             {"W",  "3", 0, 1, 3},
  97             {"w",  "29", 0, 2, 29},
  98             {"ww", "29", 0, 2, 29},
  99             {"Y", "2013", 0, 4, 2013},
 100             {"YY", "13", 0, 2, 2013},
 101             {"YYYY", "2013", 0, 4, 2013},
 102         };
 103     }
 104 
 105     @Test(dataProvider="FieldPatterns")
 106     public void test_parse_textField(String pattern, String text, int pos, int expectedPos, long expectedValue) {
 107         WeekFields weekDef = WeekFields.of(locale);
 108         TemporalField field = null;
 109         switch(pattern.charAt(0)) {
 110             case 'c' :
 111             case 'e' :
 112                 field = weekDef.dayOfWeek();
 113                 break;
 114             case 'w':
 115                 field = weekDef.weekOfWeekBasedYear();
 116                 break;
 117             case 'W':
 118                 field = weekDef.weekOfMonth();
 119                 break;
 120             case 'Y':
 121                 field = weekDef.weekBasedYear();
 122                 break;
 123             default:
 124                 throw new IllegalStateException("bad format letter from pattern");
 125         }
 126         ParsePosition ppos = new ParsePosition(pos);
 127         DateTimeFormatterBuilder b
 128                 = new DateTimeFormatterBuilder().appendPattern(pattern);
 129         DateTimeFormatter dtf = b.toFormatter(locale);
 130         TemporalAccessor parsed = dtf.parseUnresolved(text, ppos);
 131         if (ppos.getErrorIndex() != -1) {
 132             assertEquals(ppos.getErrorIndex(), expectedPos);
 133         } else {
 134             assertEquals(ppos.getIndex(), expectedPos, "Incorrect ending parse position");
 135             long value = parsed.getLong(field);
 136             assertEquals(value, expectedValue, "Value incorrect for " + field);
 137         }
 138     }
 139 
 140     //-----------------------------------------------------------------------
 141     @DataProvider(name="LocalWeekMonthYearPatterns")
 142     Object[][] provider_patternLocalDate() {
 143         return new Object[][] {
 144             {"e W M y",  "1 1 1 2012", 0, 10, LocalDate.of(2012, 1, 1)},
 145             {"e W M y",  "1 2 1 2012", 0, 10, LocalDate.of(2012, 1, 8)},
 146             {"e W M y",  "2 2 1 2012", 0, 10, LocalDate.of(2012, 1, 9)},
 147             {"e W M y",  "3 2 1 2012", 0, 10, LocalDate.of(2012, 1, 10)},
 148             {"e W M y",  "1 3 1 2012", 0, 10, LocalDate.of(2012, 1, 15)},
 149             {"e W M y",  "2 3 1 2012", 0, 10, LocalDate.of(2012, 1, 16)},
 150             {"e W M y",  "6 2 1 2012", 0, 10, LocalDate.of(2012, 1, 13)},
 151             {"e W M y",  "6 2 7 2012", 0, 10, LocalDate.of(2012, 7, 13)},
 152             {"'Date: 'y-MM', day-of-week: 'e', week-of-month: 'W",
 153                 "Date: 2012-07, day-of-week: 6, week-of-month: 3", 0, 47, LocalDate.of(2012, 7, 20)},
 154         };
 155     }
 156 
 157    @Test(dataProvider="LocalWeekMonthYearPatterns")
 158     public void test_parse_textLocalDate(String pattern, String text, int pos, int expectedPos, LocalDate expectedValue) {
 159         ParsePosition ppos = new ParsePosition(pos);
 160         DateTimeFormatterBuilder b = new DateTimeFormatterBuilder().appendPattern(pattern);
 161         DateTimeFormatter dtf = b.toFormatter(locale);
 162         TemporalAccessor parsed = dtf.parseUnresolved(text, ppos);
 163         if (ppos.getErrorIndex() != -1) {
 164             assertEquals(ppos.getErrorIndex(), expectedPos);
 165         } else {
 166             assertEquals(ppos.getIndex(), expectedPos, "Incorrect ending parse position");
 167             assertEquals(parsed.isSupported(YEAR_OF_ERA), true);
 168             assertEquals(parsed.isSupported(WeekFields.of(locale).dayOfWeek()), true);
 169             assertEquals(parsed.isSupported(WeekFields.of(locale).weekOfMonth()) ||
 170                     parsed.isSupported(WeekFields.of(locale).weekOfYear()), true);
 171             // ensure combination resolves into a date
 172             LocalDate result = LocalDate.parse(text, dtf);
 173             assertEquals(result, expectedValue, "LocalDate incorrect for " + pattern);
 174         }
 175     }
 176 
 177     //-----------------------------------------------------------------------
 178     @DataProvider(name="LocalWeekBasedYearPatterns")
 179     Object[][] provider_patternLocalWeekBasedYearDate() {
 180         return new Object[][] {
 181             //{"w Y",  "29 2012", 0, 7, LocalDate.of(2012, 7, 20)},  // Default lenient dayOfWeek not supported
 182             {"e w Y",  "6 29 2012", 0, 9, LocalDate.of(2012, 7, 20)},
 183             {"'Date: 'Y', day-of-week: 'e', week-of-year: 'w",
 184                 "Date: 2012, day-of-week: 6, week-of-year: 29", 0, 44, LocalDate.of(2012, 7, 20)},
 185             {"Y-w-e",  "2008-01-1", 0, 9, LocalDate.of(2007, 12, 30)},
 186             {"Y-w-e",  "2008-52-1", 0, 9, LocalDate.of(2008, 12, 21)},
 187             {"Y-w-e",  "2008-52-7", 0, 9, LocalDate.of(2008, 12, 27)},
 188             {"Y-w-e",  "2009-01-01", 0, 10, LocalDate.of(2008, 12, 28)},
 189             {"Y-w-e",  "2009-01-04", 0, 10, LocalDate.of(2008, 12, 31)},
 190             {"Y-w-e",  "2009-01-05", 0, 10, LocalDate.of(2009, 1, 1)},
 191        };
 192     }
 193 
 194    @Test(dataProvider="LocalWeekBasedYearPatterns")
 195     public void test_parse_WeekBasedYear(String pattern, String text, int pos, int expectedPos, LocalDate expectedValue) {
 196         ParsePosition ppos = new ParsePosition(pos);
 197         DateTimeFormatterBuilder b = new DateTimeFormatterBuilder().appendPattern(pattern);
 198         DateTimeFormatter dtf = b.toFormatter(locale);
 199         TemporalAccessor parsed = dtf.parseUnresolved(text, ppos);
 200         if (ppos.getErrorIndex() != -1) {
 201             assertEquals(ppos.getErrorIndex(), expectedPos);
 202         } else {
 203             WeekFields weekDef = WeekFields.of(locale);
 204             assertEquals(ppos.getIndex(), expectedPos, "Incorrect ending parse position");
 205             assertEquals(parsed.isSupported(weekDef.dayOfWeek()), pattern.indexOf('e') >= 0);
 206             assertEquals(parsed.isSupported(weekDef.weekOfWeekBasedYear()), pattern.indexOf('w') >= 0);
 207             assertEquals(parsed.isSupported(weekDef.weekBasedYear()), pattern.indexOf('Y') >= 0);
 208             // ensure combination resolves into a date
 209             LocalDate result = LocalDate.parse(text, dtf);
 210             assertEquals(result, expectedValue, "LocalDate incorrect for " + pattern + ", weekDef: " + weekDef);
 211         }
 212     }
 213 
 214     //-----------------------------------------------------------------------
 215     @DataProvider(name = "adjacentValuePatterns1")
 216     Object[][] provider_adjacentValuePatterns1() {
 217         return new Object[][] {
 218                 {"YYww", Y, w, "1612", 2016, 12},
 219                 {"YYYYww",Y, w, "201612", 2016, 12},
 220         };
 221     }
 222 
 223     @Test(dataProvider = "adjacentValuePatterns1")
 224     public void test_adjacentValuePatterns1(String pattern, TemporalField field1, TemporalField field2,
 225             String text, int expected1, int expected2) {
 226         DateTimeFormatter df = new DateTimeFormatterBuilder()
 227                 .appendPattern(pattern).toFormatter(Locale.US);
 228         ParsePosition ppos = new ParsePosition(0);
 229         TemporalAccessor parsed = df.parseUnresolved(text, ppos);
 230         assertEquals(parsed.get(field1), expected1);
 231         assertEquals(parsed.get(field2), expected2);      
 232     }
 233 
 234     @DataProvider(name = "adjacentValuePatterns2")
 235     Object[][] provider_adjacentValuePatterns2() {
 236         return new Object[][] {
 237                 {"YYYYwwc", Y, w, c, "2016121", 2016, 12, 1},
 238                 {"YYYYwwee", Y, w, c, "20161201", 2016, 12, 1},
 239         };
 240     }
 241 
 242     @Test(dataProvider = "adjacentValuePatterns2")
 243     public void test_adjacentValuePatterns2(String pattern, TemporalField field1, TemporalField field2,
 244             TemporalField field3, String text, int expected1, int expected2, int expected3) {
 245         DateTimeFormatter df = new DateTimeFormatterBuilder()
 246                 .appendPattern(pattern).toFormatter(Locale.US);
 247         ParsePosition ppos = new ParsePosition(0);
 248         TemporalAccessor parsed = df.parseUnresolved(text, ppos);
 249         assertEquals(parsed.get(field1), expected1);
 250         assertEquals(parsed.get(field2), expected2);
 251         assertEquals(parsed.get(field3), expected3);
 252     }
 253 
 254     @Test
 255     public void test_adjacentValuePatterns3() {
 256         String pattern = "yyyyMMddwwc";
 257         String text =  "20120720296";
 258         DateTimeFormatter df = new DateTimeFormatterBuilder()
 259                 .appendPattern(pattern).toFormatter(Locale.US);
 260         ParsePosition ppos = new ParsePosition(0);
 261         TemporalAccessor parsed = df.parseUnresolved(text, ppos);
 262         assertEquals(parsed.get(c), 6);
 263         assertEquals(parsed.get(w), 29);
 264         LocalDate result = LocalDate.parse(text, df);
 265         LocalDate expectedValue = LocalDate.of(2012, 07, 20);
 266         assertEquals(result, expectedValue, "LocalDate incorrect for " + pattern);
 267     }
 268 
 269     @DataProvider(name = "invalidPatterns")
 270     Object[][] provider_invalidPatterns() {
 271         return new Object[][] {
 272             {"W", "01"},
 273             {"c", "01"},
 274             {"YYYYwwe", "2016121"}, //e can have variable width(1 or 2) and must come first
 275             {"yyyyMMddwwc", "201207202906"}, //  1 extra digit in the input 
 276         };
 277     }
 278 
 279     @Test(dataProvider = "invalidPatterns", expectedExceptions = DateTimeParseException.class)
 280     public void test_invalidPatterns(String pattern, String value) {
 281         DateTimeFormatter.ofPattern(pattern).parse(value);
 282     }
 283 }