001 /* 002 * Copyright (c) 2006 Henri Sivonen 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining a 005 * copy of this software and associated documentation files (the "Software"), 006 * to deal in the Software without restriction, including without limitation 007 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 008 * and/or sell copies of the Software, and to permit persons to whom the 009 * Software is furnished to do so, subject to the following conditions: 010 * 011 * The above copyright notice and this permission notice shall be included in 012 * all copies or substantial portions of the Software. 013 * 014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 017 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 019 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 020 * DEALINGS IN THE SOFTWARE. 021 */ 022 023 package org.whattf.datatype; 024 025 import java.util.Date; 026 import java.util.Locale; 027 import java.util.regex.Matcher; 028 import java.util.regex.Pattern; 029 030 import org.relaxng.datatype.DatatypeException; 031 032 import com.ibm.icu.util.Calendar; 033 import com.ibm.icu.util.GregorianCalendar; 034 import com.ibm.icu.util.TimeZone; 035 036 /** 037 * This datatype shall accept strings that conform to the format specified for 038 * <a href='http://whatwg.org/specs/web-forms/current-work/#week'><code>week</code></a> 039 * inputs in Web Forms 2.0. 040 * <p>This datatype must not accept the empty string. 041 * 042 * @version $Id: Week.java,v 1.3 2006/11/18 11:51:44 hsivonen Exp $ 043 * @author hsivonen 044 */ 045 public final class Week extends AbstractDatatype { 046 047 /** 048 * The singleton instance. 049 */ 050 public static final Week THE_INSTANCE = new Week(); 051 052 /** 053 * The rexexp for this datatype. 054 */ 055 private static final Pattern THE_PATTERN = Pattern.compile("^([0-9]{4,})-W([0-9]{2})$"); 056 057 /** 058 * Constructor. 059 */ 060 private Week() { 061 super(); 062 } 063 064 private void checkWeek(String year, String week) 065 throws DatatypeException { 066 checkWeek(Integer.parseInt(year), Integer.parseInt(week)); 067 } 068 069 private void checkWeek(int year, int week) 070 throws DatatypeException { 071 if (year < 1) { 072 throw new DatatypeException("Year cannot be less than 1."); 073 } 074 if (week < 1) { 075 throw new DatatypeException("Week cannot be less than 1."); 076 } 077 if (week == 53) { 078 // TODO still in doubt about the concurrency contract 079 // not using instance variables just in case 080 081 // Can this year have ISO week #53? 082 // Using ICU4J to find out, because the calculation is 083 // non-trivial. 084 TimeZone tz = TimeZone.getTimeZone("GMT"); 085 tz.setRawOffset(0); 086 // Using fixed time zone and locale to make possible 087 // bugs more tractable. 088 GregorianCalendar gc = new GregorianCalendar(tz, Locale.FRANCE); 089 // Say no to the Julian calendar 090 gc.setGregorianChange(new Date(Long.MIN_VALUE)); 091 gc.setLenient(false); 092 gc.setFirstDayOfWeek(Calendar.MONDAY); // ISO week start 093 gc.setMinimalDaysInFirstWeek(4); // ISO week rule 094 gc.set(year, 6, 1); 095 if (gc.getActualMaximum(Calendar.WEEK_OF_YEAR) != 53) { 096 throw new DatatypeException("Week out of range."); 097 } 098 } else if (week > 53) { 099 throw new DatatypeException("Week out of range."); 100 } 101 } 102 103 public final void checkValid(CharSequence literal) 104 throws DatatypeException { 105 Matcher m = THE_PATTERN.matcher(literal); 106 if (m.matches()) { 107 checkWeek(m.group(1), m.group(2)); 108 } else { 109 throw new DatatypeException( 110 "The literal did not satisfy the format for week."); 111 } 112 } 113 114 }