1 /* DateTimeType.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.Calendar;
41 import java.util.GregorianCalendar;
42 import java.util.TimeZone;
43 import javax.xml.XMLConstants;
44 import javax.xml.namespace.QName;
45 import org.relaxng.datatype.DatatypeException;
46 import org.relaxng.datatype.ValidationContext;
47 
48 /**
49  * The XML Schema dateTime type.
50  *
51  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
52  */
53 final class DateTimeType
54   extends AtomicSimpleType
55 {
56 
57   static final int[] CONSTRAINING_FACETS = {
58     Facet.PATTERN,
59     Facet.ENUMERATION,
60     Facet.WHITESPACE,
61     Facet.MAX_INCLUSIVE,
62     Facet.MAX_EXCLUSIVE,
63     Facet.MIN_INCLUSIVE,
64     Facet.MIN_EXCLUSIVE
65   };
66 
DateTimeType()67   DateTimeType()
68   {
69     super(new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "dateTime"),
70           TypeLibrary.ANY_SIMPLE_TYPE);
71   }
72 
getConstrainingFacets()73   public int[] getConstrainingFacets()
74   {
75     return CONSTRAINING_FACETS;
76   }
77 
checkValue(String value, ValidationContext context)78   public void checkValue(String value, ValidationContext context)
79     throws DatatypeException
80   {
81     super.checkValid(value, context);
82     int len = value.length();
83     int state = 0;
84     int start = 0;
85     for (int i = 0; i < len; i++)
86       {
87         char c = value.charAt(i);
88         if (c == '-' && i == 0)
89           {
90             start++;
91             continue;
92           }
93         if (c >= 0x30 && c <= 0x39)
94           continue;
95         switch (state)
96           {
97           case 0: // year
98             if (c == '-')
99               {
100                 String year = value.substring(start, i);
101                 if ("0000".equals(year) || year.length() < 4)
102                   throw new DatatypeException(i, "invalid dateTime value");
103                 state = 1;
104                 start = i + 1;
105                 continue;
106               }
107             break;
108           case 1: // month
109             if (c == '-')
110               {
111                 if (i - start != 2)
112                   throw new DatatypeException(i, "invalid dateTime value");
113                 state = 2;
114                 start = i + 1;
115                 continue;
116               }
117             break;
118           case 2: // day
119             if (c == 'T')
120               {
121                 if (i - start != 2)
122                   throw new DatatypeException(i, "invalid dateTime value");
123                 state = 3;
124                 start = i + 1;
125                 continue;
126               }
127             break;
128           case 3: // hour
129             if (c == ':')
130               {
131                 if (i - start != 2)
132                   throw new DatatypeException(i, "invalid dateTime value");
133                 state = 4;
134                 start = i + 1;
135                 continue;
136               }
137             break;
138           case 4: // minute
139             if (c == ':')
140               {
141                 if (i - start != 2)
142                   throw new DatatypeException(i, "invalid dateTime value");
143                 state = 5;
144                 start = i + 1;
145                 continue;
146               }
147             break;
148           case 5: // second
149             if (c == '.')
150               {
151                 if (i - start != 2)
152                   throw new DatatypeException(i, "invalid dateTime value");
153                 state = 6;
154                 start = i + 1;
155                 continue;
156               }
157             else if (c == ' ')
158               {
159                 if (i - start != 2)
160                   throw new DatatypeException(i, "invalid dateTime value");
161                 state = 7;
162                 start = i + 1;
163                 continue;
164               }
165             break;
166           case 6: // second fraction
167             if (c == ' ')
168               {
169                 state = 7;
170                 start = i + 1;
171                 continue;
172               }
173             break;
174           case 7: // timezone 1
175             if (start == i)
176               {
177                 if (c == '+' || c == '-')
178                     continue;
179                 else if (c == 'Z')
180                   {
181                     state = 9;
182                     start = i + 1;
183                     continue;
184                   }
185               }
186             if (c == ':')
187               {
188                 if (i - start != 2)
189                   throw new DatatypeException(i, "invalid dateTime value");
190                 state = 8;
191                 start = i + 1;
192                 continue;
193               }
194             break;
195           }
196         throw new DatatypeException(i, "invalid dateTime value");
197       }
198     switch (state)
199       {
200       case 5: // second
201         if (len - start != 2)
202           throw new DatatypeException(len, "invalid dateTime value");
203         break;
204       case 6: // second fraction
205         break;
206       case 8: // timezone 2
207         if (len - start != 2)
208           throw new DatatypeException(len, "invalid dateTime value");
209         break;
210       case 9: // post Z
211         break;
212       default:
213         throw new DatatypeException(len, "invalid dateTime value");
214       }
215   }
216 
createValue(String value, ValidationContext context)217   public Object createValue(String value, ValidationContext context) {
218     int len = value.length();
219     int state = 0;
220     int start = 0;
221     Calendar cal = new GregorianCalendar();
222     try
223       {
224         for (int i = 0; i < len; i++)
225           {
226             char c = value.charAt(i);
227             if (c == '-' && i == 0)
228               {
229                 start++;
230                 continue;
231               }
232             if (c >= 0x30 && c <= 0x39)
233               continue;
234             switch (state)
235               {
236               case 0: // year
237                 if (c == '-')
238                   {
239                     cal.set(Calendar.YEAR,
240                             Integer.parseInt(value.substring(0, i)));
241                     state = 1;
242                     start = i + 1;
243                     continue;
244                   }
245                 break;
246               case 1: // month
247                 if (c == '-')
248                   {
249                     cal.set(Calendar.MONTH,
250                             Integer.parseInt(value.substring(start, i)));
251                     state = 2;
252                     start = i + 1;
253                     continue;
254                   }
255                 break;
256               case 2: // day
257                 if (c == 'T')
258                   {
259                     cal.set(Calendar.DATE,
260                             Integer.parseInt(value.substring(start, i)));
261                     state = 3;
262                     start = i + 1;
263                     continue;
264                   }
265                 break;
266               case 3: // hour
267                 if (c == ':')
268                   {
269                     cal.set(Calendar.HOUR,
270                             Integer.parseInt(value.substring(start, i)));
271                     state = 4;
272                     start = i + 1;
273                     continue;
274                   }
275                 break;
276               case 4: // minute
277                 if (c == ':')
278                   {
279                     cal.set(Calendar.MINUTE,
280                             Integer.parseInt(value.substring(start, i)));
281                     state = 5;
282                     start = i + 1;
283                     continue;
284                   }
285                 break;
286               case 5: // second
287                 if (c == ' ')
288                   {
289                     float second = Float.parseFloat(value.substring(start, i));
290                     // TODO adjust non-integer values
291                     cal.set(Calendar.SECOND, (int) second);
292                     state = 7;
293                     start = i + 1;
294                     continue;
295                   }
296                 break;
297               }
298           }
299         // end of input
300         if (len - start > 0 && state == 7)
301           {
302             // Timezone
303             String timezone = value.substring(len - start);
304             int i = timezone.indexOf(':');
305             if (i == -1)
306               {
307                 if ("Z".equals(timezone))
308                   timezone = "UTC";
309                 TimeZone tz = TimeZone.getTimeZone(timezone);
310                 if (tz == null)
311                   return null;
312                 cal.set(Calendar.ZONE_OFFSET, tz.getRawOffset());
313               }
314             else
315               {
316                 String tzh = timezone.substring(0, i);
317                 String tzm = timezone.substring(i + 1);
318                 int offset = Integer.parseInt(tzh) * 360000;
319                 if (offset < 0)
320                   offset -= Integer.parseInt(tzm) * 60000;
321                 else
322                   offset += Integer.parseInt(tzm) * 60000;
323                 cal.set(Calendar.ZONE_OFFSET, offset);
324               }
325           }
326         return cal.getTime();
327       }
328     catch (NumberFormatException e)
329       {
330         return null;
331       }
332   }
333 
334 }
335