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 }