1 /* TimeType.java --
2    Copyright (C) 2006  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package gnu.xml.validation.datatype;
39 
40 import java.util.TimeZone;
41 import javax.xml.XMLConstants;
42 import javax.xml.namespace.QName;
43 import org.relaxng.datatype.DatatypeException;
44 import org.relaxng.datatype.ValidationContext;
45 
46 /**
47  * The XML Schema time type.
48  *
49  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
50  */
51 final class TimeType
52   extends AtomicSimpleType
53 {
54 
55   static class Time
56     implements Comparable
57   {
58     int minutes;
59     float seconds;
60 
hashCode()61     public int hashCode()
62     {
63       return minutes * 31 + new Float(seconds).hashCode();
64     }
65 
equals(Object other)66     public boolean equals(Object other)
67     {
68       if (other instanceof Time)
69         {
70           Time time = (Time) other;
71           return time.minutes == minutes && time.seconds == seconds;
72         }
73       return false;
74     }
75 
compareTo(Object other)76     public int compareTo(Object other)
77     {
78       if (other instanceof Time)
79         {
80           Time time = (Time) other;
81           if (time.minutes != minutes)
82             return minutes - time.minutes;
83           if (time.seconds == seconds)
84             return 0;
85           return (seconds < time.seconds) ? -1 : 1;
86         }
87       return 0;
88     }
89 
90   }
91 
92   static final int[] CONSTRAINING_FACETS = {
93     Facet.PATTERN,
94     Facet.ENUMERATION,
95     Facet.WHITESPACE,
96     Facet.MAX_INCLUSIVE,
97     Facet.MAX_EXCLUSIVE,
98     Facet.MIN_INCLUSIVE,
99     Facet.MIN_EXCLUSIVE
100   };
101 
TimeType()102   TimeType()
103   {
104     super(new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "time"),
105           TypeLibrary.ANY_SIMPLE_TYPE);
106   }
107 
getConstrainingFacets()108   public int[] getConstrainingFacets()
109   {
110     return CONSTRAINING_FACETS;
111   }
112 
checkValid(String value, ValidationContext context)113   public void checkValid(String value, ValidationContext context)
114     throws DatatypeException
115   {
116     super.checkValid(value, context);
117     int len = value.length();
118     int state = 3;
119     int start = 0;
120     for (int i = 0; i < len; i++)
121       {
122         char c = value.charAt(i);
123         if (c == '-' && state == 0)
124           {
125             start++;
126             continue;
127           }
128         if (c >= 0x30 && c <= 0x39)
129           continue;
130         switch (state)
131           {
132           case 3: // hour
133             if (c == ':')
134               {
135                 if (i - start != 2)
136                   throw new DatatypeException(i, "invalid time value");
137                 state = 4;
138                 start = i + 1;
139                 continue;
140               }
141             break;
142           case 4: // minute
143             if (c == ':')
144               {
145                 if (i - start != 2)
146                   throw new DatatypeException(i, "invalid time value");
147                 state = 5;
148                 start = i + 1;
149                 continue;
150               }
151             break;
152           case 5: // second
153             if (c == '.')
154               {
155                 if (i - start != 2)
156                   throw new DatatypeException(i, "invalid time value");
157                 state = 6;
158                 start = i + 1;
159                 continue;
160               }
161             else if (c == ' ')
162               {
163                 if (i - start != 2)
164                   throw new DatatypeException(i, "invalid time value");
165                 state = 7;
166                 start = i + 1;
167                 continue;
168               }
169             break;
170           case 6: // second fraction
171             if (c == ' ')
172               {
173                 state = 7;
174                 start = i + 1;
175                 continue;
176               }
177             break;
178           case 7: // timezone 1
179             if (start == i)
180               {
181                 if (c == '+' || c == '-')
182                   continue;
183                 else if (c == 'Z')
184                   {
185                     state = 9;
186                     start = i + 1;
187                     continue;
188                   }
189               }
190             if (c == ':')
191               {
192                 if (i - start != 2)
193                   throw new DatatypeException(i, "invalid time value");
194                 state = 8;
195                 start = i + 1;
196                 continue;
197               }
198             break;
199           }
200         throw new DatatypeException(i, "invalid time value");
201       }
202     switch (state)
203       {
204       case 5: // second
205         if (len - start != 2)
206           throw new DatatypeException(len, "invalid time value");
207         break;
208       case 6: // second fraction
209         break;
210       case 8: // timezone 2
211         if (len - start != 2)
212           throw new DatatypeException(len, "invalid time value");
213         break;
214       case 9: // post Z
215         break;
216       default:
217         throw new DatatypeException(len, "invalid time value");
218       }
219   }
220 
createValue(String value, ValidationContext context)221   public Object createValue(String value, ValidationContext context) {
222     int len = value.length();
223     int state = 3;
224     int start = 0;
225     Time time = new Time();
226     try
227       {
228         for (int i = 0; i < len; i++)
229           {
230             char c = value.charAt(i);
231             if (c >= 0x30 && c <= 0x39)
232               continue;
233             switch (state)
234               {
235               case 3: // hour
236                 if (c == ':')
237                   {
238                     time.minutes =
239                       Integer.parseInt(value.substring(start, i)) * 60;
240                     state = 4;
241                     start = i + 1;
242                     continue;
243                   }
244                 break;
245               case 4: // minute
246                 if (c == ':')
247                   {
248                     time.minutes +=
249                       Integer.parseInt(value.substring(start, i));
250                     state = 5;
251                     start = i + 1;
252                     continue;
253                   }
254                 break;
255               case 5: // second
256                 if (c == ' ')
257                   {
258                     time.seconds =
259                       Float.parseFloat(value.substring(start, i));
260                     state = 7;
261                     start = i + 1;
262                     continue;
263                   }
264                 break;
265               }
266           }
267         // end of input
268         if (len - start > 0 && state == 7)
269           {
270             // Timezone
271             String timezone = value.substring(len - start);
272             int i = timezone.indexOf(':');
273             if (i == -1)
274               {
275                 if ("Z".equals(timezone))
276                   timezone = "UTC";
277                 TimeZone tz = TimeZone.getTimeZone(timezone);
278                 if (tz == null)
279                   return null;
280                 time.minutes += tz.getRawOffset();
281               }
282             else
283               {
284                 String tzh = timezone.substring(0, i);
285                 String tzm = timezone.substring(i + 1);
286                 int offset = Integer.parseInt(tzh) * 60;
287                 if (offset < 0)
288                   offset -= Integer.parseInt(tzm);
289                 else
290                   offset += Integer.parseInt(tzm);
291                 time.minutes += offset;
292               }
293           }
294         return time;
295       }
296     catch (NumberFormatException e)
297       {
298         return null;
299       }
300   }
301 
302 }
303