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.regex.Matcher; 026 import java.util.regex.Pattern; 027 028 import org.relaxng.datatype.DatatypeException; 029 030 /** 031 * Superclass for various datetime datatypes. 032 * 033 * @version $Id: AbstractDatetime.java,v 1.8 2006/11/26 14:22:54 hsivonen Exp $ 034 * @author hsivonen 035 */ 036 abstract class AbstractDatetime extends AbstractDatatype { 037 /** 038 * Days in monts on non-leap years. 039 */ 040 private static int[] DAYS_IN_MONTHS = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 041 31, 30, 31 }; 042 043 /** 044 * Constructor. 045 */ 046 AbstractDatetime() { 047 super(); 048 } 049 050 private void checkDate(String year, String month, String day) 051 throws DatatypeException { 052 checkDate(Integer.parseInt(year), Integer.parseInt(month), 053 Integer.parseInt(day)); 054 } 055 056 private void checkDate(int year, int month, int day) 057 throws DatatypeException { 058 if (year < 1) { 059 throw new DatatypeException("Year cannot be less than 1."); 060 } 061 if (month < 1) { 062 throw new DatatypeException("Month cannot be less than 1."); 063 } 064 if (month > 12) { 065 throw new DatatypeException("Month cannot be greater than 12."); 066 } 067 if (day < 1) { 068 throw new DatatypeException("Day cannot be less than 1."); 069 } 070 if (day > DAYS_IN_MONTHS[month - 1]) { 071 if (!(day == 29 && month == 2 && isLeapYear(year))) { 072 throw new DatatypeException("Day out of range."); 073 } 074 } 075 076 } 077 078 private boolean isLeapYear(int year) { 079 return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0)); 080 } 081 082 protected final void checkHour(String hour) throws DatatypeException { 083 checkHour(Integer.parseInt(hour)); 084 } 085 086 private void checkHour(int hour) throws DatatypeException { 087 if (hour > 23) { 088 throw new DatatypeException("Hour cannot be greater than 23."); 089 } 090 } 091 092 protected final void checkMinute(String minute) throws DatatypeException { 093 checkMinute(Integer.parseInt(minute)); 094 } 095 096 private void checkMinute(int minute) throws DatatypeException { 097 if (minute > 59) { 098 throw new DatatypeException("Minute cannot be greater than 59."); 099 } 100 } 101 102 protected final void checkSecond(String second) throws DatatypeException { 103 checkSecond(Integer.parseInt(second)); 104 } 105 106 private void checkSecond(int second) throws DatatypeException { 107 if (second > 59) { 108 throw new DatatypeException("Second cannot be greater than 59."); 109 } 110 } 111 112 private void checkTzd(String hours, String minutes) throws DatatypeException { 113 if (hours.charAt(0) == '+') { 114 hours = hours.substring(1); 115 } 116 checkTzd(Integer.parseInt(hours), Integer.parseInt(minutes)); 117 } 118 119 private void checkTzd(int hours, int minutes) throws DatatypeException { 120 if (hours < -23 || hours > 23) { 121 throw new DatatypeException("Hours out of range in time zone designator."); 122 } 123 if (minutes > 59) { 124 throw new DatatypeException("Minutes out of range in time zone designator."); 125 } 126 } 127 128 protected abstract Pattern getPattern(); 129 130 public void checkValid(CharSequence literal) 131 throws DatatypeException { 132 Matcher m = getPattern().matcher(literal); 133 if (m.matches()) { 134 // int count = m.groupCount(); 135 // checkDate(m.group(1), m.group(2), m.group(3)); 136 // if (count > 3) { 137 // checkHour(m.group(4)); 138 // checkMinute(m.group(5)); 139 // String seconds = m.group(6); 140 // if (seconds != null) { 141 // checkSecond(seconds); 142 // } 143 // if (count > 6) { 144 // String tzdHours = m.group(7); 145 // if (tzdHours != null) { 146 // checkTzd(tzdHours, m.group(8)); 147 // } 148 // } 149 // } 150 int count = m.groupCount(); 151 String year = m.group(1); 152 String month = m.group(2); 153 String day = m.group(3); 154 if (year != null) { 155 checkDate(year, month, day); 156 } 157 if (count == 3) { 158 return; 159 } 160 String hour = m.group(4); 161 String minute = m.group(5); 162 if (hour != null) { 163 checkHour(hour); 164 checkMinute(minute); 165 } 166 String seconds = m.group(6); 167 if (seconds != null) { 168 checkSecond(seconds); 169 } 170 if (count == 6) { 171 return; 172 } 173 String tzdHours = m.group(7); 174 String tzdMinutes = m.group(8); 175 if (tzdHours != null) { 176 checkTzd(tzdHours, tzdMinutes); 177 } 178 if (count == 8) { 179 return; 180 } 181 hour = m.group(9); 182 minute = m.group(10); 183 if (hour != null) { 184 checkHour(hour); 185 checkMinute(minute); 186 } 187 seconds = m.group(11); 188 if (seconds != null) { 189 checkSecond(seconds); 190 } 191 if (count == 11) { 192 return; 193 } 194 tzdHours = m.group(12); 195 tzdMinutes = m.group(13); 196 if (tzdHours != null) { 197 checkTzd(tzdHours, tzdMinutes); 198 } 199 } else { 200 throw new DatatypeException( 201 "The literal did not satisfy the date format."); 202 } 203 } 204 205 }