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    }