KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > value > TimeValue


1 package net.sf.saxon.value;
2
3 import net.sf.saxon.expr.XPathContext;
4 import net.sf.saxon.functions.Component;
5 import net.sf.saxon.om.FastStringBuffer;
6 import net.sf.saxon.trans.DynamicError;
7 import net.sf.saxon.trans.XPathException;
8 import net.sf.saxon.type.BuiltInAtomicType;
9 import net.sf.saxon.type.ItemType;
10 import net.sf.saxon.type.Type;
11 import net.sf.saxon.type.ValidationException;
12 import net.sf.saxon.ConversionContext;
13
14 import java.util.*;
15
16 /**
17  * A value of type xs:time
18  */

19
20 public final class TimeValue extends CalendarValue {
21
22
23     /**
24      * Constructor: create a time value given a Java calendar object
25      * @param calendar holds the date and time
26      * @param tzSpecified indicates whether the timezone is specified
27      */

28
29     public TimeValue(GregorianCalendar calendar, boolean tzSpecified) {
30         this.calendar = calendar;
31         zoneSpecified = tzSpecified;
32     }
33
34     /**
35      * Constructor: create a dateTime value from a supplied string, in
36      * ISO 8601 format
37      */

38
39     public TimeValue(CharSequence JavaDoc s) throws XPathException {
40         // input must have format hh:mm:ss[.fff*][([+|-]hh:mm | Z)]
41

42         zoneSpecified = false;
43         StringTokenizer tok = new StringTokenizer(trimWhitespace(s).toString(), "-:.+Z", true);
44         try {
45             if (!tok.hasMoreElements()) badTime("too short");
46             String JavaDoc part = (String JavaDoc) tok.nextElement();
47
48             int hour = Integer.parseInt(part);
49             if (part.length() != 2) badTime("hour must be two digits");
50             if (hour > 24) badTime("hour is out of range");
51             if (!tok.hasMoreElements()) badTime("too short");
52             if (!":".equals(tok.nextElement())) badTime("wrong delimiter after hour");
53
54             if (!tok.hasMoreElements()) badTime("too short");
55             part = (String JavaDoc) tok.nextElement();
56             int minute = Integer.parseInt(part);
57             if (part.length() != 2) badTime("minute must be two digits");
58             if (minute > 59) badTime("minute is out of range");
59             if (hour == 24 && minute != 0) badTime("If hour is 24, minute must be 00");
60             if (!tok.hasMoreElements()) badTime("too short");
61             if (!":".equals(tok.nextElement())) badTime("wrong delimiter after minute");
62
63             if (!tok.hasMoreElements()) badTime("too short");
64             part = (String JavaDoc) tok.nextElement();
65             int second = Integer.parseInt(part);
66             if (part.length() != 2) badTime("second must be two digits");
67             if (second > 61) badTime("second is out of range");
68             if (hour == 24 && second != 0) badTime("If hour is 24, second must be 00");
69
70             int millisecond = 0;
71             int tz = 0;
72
73             int state = 0;
74             while (tok.hasMoreElements()) {
75                 if (state == 9) {
76                     badTime("characters after the end");
77                 }
78                 String JavaDoc delim = (String JavaDoc) tok.nextElement();
79                 if (".".equals(delim)) {
80                     if (state != 0) {
81                         badTime("decimal separator occurs twice");
82                     }
83                     part = (String JavaDoc) tok.nextElement();
84                     double fractionalSeconds = Double.parseDouble('.' + part);
85                     millisecond = (int) (Math.round(fractionalSeconds * 1000));
86                     if (hour == 24 && millisecond != 0) {
87                         badTime("If hour is 24, milliseconds must be 0");
88                     }
89                     state = 1;
90                 } else if ("Z".equals(delim)) {
91                     if (state > 1) {
92                         badTime("Z cannot occur here");
93                     }
94                     zoneSpecified = true;
95                     tz = 0;
96                     state = 9; // we've finished
97
} else if ("+".equals(delim) || "-".equals(delim)) {
98                     if (state > 1) {
99                         badTime(delim + " cannot occur here");
100                     }
101                     state = 2;
102                     zoneSpecified = true;
103                     if (!tok.hasMoreElements()) badTime("missing timezone");
104                     part = (String JavaDoc) tok.nextElement();
105                     if (part.length() != 2) badTime("timezone hour must be two digits");
106                     tz = Integer.parseInt(part) * 60;
107                     if (tz > 14 * 60) badTime("timezone hour is out of range");
108                     if (tz > 12 * 60) badTime("Because of Java limitations, Saxon currently limits the timezone to +/- 12 hours");
109                     if ("-".equals(delim)) tz = -tz;
110
111                 } else if (":".equals(delim)) {
112                     if (state != 2) {
113                         badTime("colon cannot occur here");
114                     }
115                     state = 9;
116                     part = (String JavaDoc) tok.nextElement();
117                     int tzminute = Integer.parseInt(part);
118                     if (part.length() != 2) badTime("timezone minute must be two digits");
119                     if (tzminute > 59) badTime("timezone minute is out of range");
120                     if (tz < 0) tzminute = -tzminute;
121                     tz += tzminute;
122                 } else {
123                     badTime("timezone format is incorrect");
124                 }
125             }
126
127             if (state == 2 || state == 3) {
128                 badTime("timezone incomplete");
129             }
130
131             // create a calendar using the specified timezone
132
TimeZone zone = new SimpleTimeZone(tz * 60000, "LLL");
133             calendar = new GregorianCalendar(zone);
134             calendar.setLenient(false);
135
136             // use a reference date of 1972-12-31
137
int year = 1972;
138             int month = 11;
139             int day = 31;
140             if (hour == 24) {
141                 hour = 0;
142             }
143
144             calendar.set(year, month, day, hour, minute, second);
145             calendar.set(Calendar.MILLISECOND, millisecond);
146             calendar.set(Calendar.ZONE_OFFSET, tz * 60000);
147             calendar.set(Calendar.DST_OFFSET, 0);
148
149             try {
150                 calendar.getTime();
151             } catch (IllegalArgumentException JavaDoc err) {
152                 badTime("time components out of range");
153             }
154
155
156         } catch (NumberFormatException JavaDoc err) {
157             badTime("non-numeric component");
158         }
159     }
160
161     private void badTime(String JavaDoc msg) throws XPathException {
162         throw new DynamicError("Invalid time value (" + msg + ')');
163     }
164
165     /**
166      * Convert to target data type
167      * @param requiredType an integer identifying the required atomic type
168      * @param conversion
169      * @return an AtomicValue, a value of the required type; or an ErrorValue
170      */

171
172     public AtomicValue convertPrimitive(BuiltInAtomicType requiredType, boolean validate, ConversionContext conversion) {
173         switch (requiredType.getPrimitiveType()) {
174             case Type.TIME:
175             case Type.ATOMIC:
176             case Type.ITEM:
177                 return this;
178             case Type.STRING:
179                 return new StringValue(getStringValue());
180             case Type.UNTYPED_ATOMIC:
181                 return new UntypedAtomicValue(getStringValue());
182 // case Type.DATE_TIME:
183
// return new DateTimeValue(calendar, zoneSpecified);
184
default:
185                 ValidationException err = new ValidationException("Cannot convert time to " +
186                         requiredType.getDisplayName());
187                 //err.setXPathContext(context);
188
err.setErrorCode("FORG0001");
189                 return new ValidationErrorValue(err);
190         }
191     }
192
193     /**
194      * Convert to string
195      * @return ISO 8601 representation, in the localized timezone
196      * (the timezone held within the value).
197      */

198
199     public String JavaDoc getStringValue() {
200
201         FastStringBuffer sb = new FastStringBuffer(16);
202
203         DateTimeValue.appendString(sb, calendar.get(Calendar.HOUR_OF_DAY), 2);
204         sb.append(':');
205         DateTimeValue.appendString(sb, calendar.get(Calendar.MINUTE), 2);
206         sb.append(':');
207         DateTimeValue.appendSeconds(calendar, sb);
208
209         if (zoneSpecified) {
210             DateTimeValue.appendTimezone(calendar, sb);
211         }
212
213         return sb.toString();
214
215     }
216
217     /**
218      * Convert to a DateTime value (used internally only: the date components should be ignored)
219      */

220
221     public DateTimeValue toDateTime() {
222         return new DateTimeValue(calendar, zoneSpecified);
223     }
224
225     /**
226      * Determine the data type of the exprssion
227      * @return Type.TIME,
228      */

229
230     public ItemType getItemType() {
231         return Type.TIME_TYPE;
232     }
233
234     /**
235      * Return a dateTime with the same localized value, but
236      * without the timezone component
237      * @return the result of removing the timezone
238      */

239
240     public CalendarValue removeTimezone() throws XPathException {
241         return (CalendarValue)
242                 new DateTimeValue(calendar, zoneSpecified)
243                 .removeTimezone()
244                 .convert(Type.TIME, null);
245     }
246
247     /**
248      * Return a date, time, or dateTime with the same normalized value, but
249      * in a different timezone
250      * @return the date/time in the new timezone
251      * @throws XPathException
252      */

253
254     public CalendarValue setTimezone(SecondsDurationValue tz) throws XPathException {
255         return (CalendarValue)
256                 new DateTimeValue(calendar, zoneSpecified)
257                 .setTimezone(tz)
258                 .convert(Type.TIME, null);
259     }
260
261     /**
262      * Convert to Java object (for passing to external functions)
263      */

264
265     public Object JavaDoc convertToJava(Class JavaDoc target, XPathContext context) throws XPathException {
266         if (target.isAssignableFrom(TimeValue.class)) {
267             return this;
268         } else if (target == String JavaDoc.class) {
269             return getStringValue();
270         } else if (target == Object JavaDoc.class) {
271             return getStringValue();
272         } else {
273             Object JavaDoc o = super.convertToJava(target, context);
274             if (o == null) {
275                 throw new DynamicError("Conversion of time to " + target.getName() +
276                         " is not supported");
277             }
278             return o;
279         }
280     }
281
282     /**
283      * Get a component of the value. Returns null if the timezone component is
284      * requested and is not present.
285      */

286
287     public AtomicValue getComponent(int component) throws XPathException {
288         switch (component) {
289             case Component.HOURS:
290                 return new IntegerValue(calendar.get(Calendar.HOUR_OF_DAY));
291             case Component.MINUTES:
292                 return new IntegerValue(calendar.get(Calendar.MINUTE));
293             case Component.SECONDS:
294                 FastStringBuffer sb = new FastStringBuffer(10);
295                 DateTimeValue.appendSeconds(calendar, sb);
296                 return DecimalValue.makeDecimalValue(sb, false);
297             case Component.TIMEZONE:
298                 if (zoneSpecified) {
299                     int tzsecs = (calendar.get(Calendar.ZONE_OFFSET) +
300                             calendar.get(Calendar.DST_OFFSET));
301                     return SecondsDurationValue.fromMilliseconds(tzsecs);
302                 } else {
303                     return null;
304                 }
305             default:
306                 throw new IllegalArgumentException JavaDoc("Unknown component for time: " + component);
307         }
308     }
309
310     /**
311      * Compare the value to another dateTime value
312      * @param other The other dateTime value
313      * @return negative value if this one is the earler, 0 if they are chronologically equal,
314      * positive value if this one is the later. For this purpose, dateTime values with an unknown
315      * timezone are considered to be UTC values (the Comparable interface requires
316      * a total ordering).
317      * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
318      * is declared as Object to satisfy the Comparable interface)
319      */

320
321     public int compareTo(Object JavaDoc other) {
322         if (!(other instanceof TimeValue)) {
323             throw new ClassCastException JavaDoc("Time values are not comparable to " + other.getClass());
324         }
325         TimeValue otherTime = (TimeValue)other;
326         if (zoneSpecified == otherTime.zoneSpecified) {
327             GregorianCalendar cal2 = otherTime.calendar;
328             return calendar.getTime().compareTo(cal2.getTime());
329         } else {
330             return new DateTimeValue(calendar, zoneSpecified).compareTo(
331                     new DateTimeValue(otherTime.calendar, otherTime.zoneSpecified));
332         }
333     }
334
335     /**
336      * Compare the value to another dateTime value
337      * @param other The other dateTime value
338      * @return negative value if this one is the earler, 0 if they are chronologically equal,
339      * positive value if this one is the later. For this purpose, dateTime values with an unknown
340      * timezone are considered to be UTC values (the Comparable interface requires
341      * a total ordering).
342      * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
343      * is declared as Object to satisfy the Comparable interface)
344      */

345
346     public int compareTo(CalendarValue other, ConversionContext conversion) {
347         if (!(other instanceof TimeValue)) {
348             throw new ClassCastException JavaDoc("Time values are not comparable to " + other.getClass());
349         }
350         TimeValue otherTime = (TimeValue)other;
351         if (zoneSpecified == otherTime.zoneSpecified) {
352             GregorianCalendar cal2 = otherTime.calendar;
353             return calendar.getTime().compareTo(cal2.getTime());
354         } else {
355             return new DateTimeValue(calendar, zoneSpecified).compareTo(
356                     new DateTimeValue(otherTime.calendar, otherTime.zoneSpecified),
357                     conversion);
358         }
359     }
360
361
362     public boolean equals(Object JavaDoc other) {
363         return compareTo(other) == 0;
364     }
365
366     public int hashCode() {
367         return new DateTimeValue(calendar, zoneSpecified).hashCode();
368     }
369
370     /**
371      * Add a duration to a dateTime
372      * @param duration the duration to be added (may be negative)
373      * @return the new date
374      * @throws XPathException if the duration is an xs:duration, as distinct from
375      * a subclass thereof
376      */

377
378     public CalendarValue add(DurationValue duration) throws XPathException {
379         if (duration instanceof SecondsDurationValue) {
380             double seconds = duration.getLengthInSeconds();
381             GregorianCalendar cal2 = (GregorianCalendar) calendar.clone();
382             cal2.add(Calendar.SECOND, (int) seconds);
383             cal2.add(Calendar.MILLISECOND, (int) ((seconds % 1) * 1000));
384             return new TimeValue(cal2, zoneSpecified);
385         } else {
386             DynamicError err = new DynamicError(
387                     "Time+Duration arithmetic is supported only for xdt:dayTimeDuration");
388             err.setIsTypeError(true);
389             throw err;
390         }
391     }
392
393     /**
394      * Determine the difference between two points in time, as a duration
395      * @param other the other point in time
396      * @param context
397      * @return the duration as an xdt:dayTimeDuration
398      * @throws XPathException for example if one value is a date and the other is a time
399      */

400
401     public SecondsDurationValue subtract(CalendarValue other, ConversionContext context) throws XPathException {
402         if (!(other instanceof TimeValue)) {
403             DynamicError err = new DynamicError(
404                     "First operand of '-' is a time, but the second is not");
405             err.setIsTypeError(true);
406             throw err;
407         }
408         return super.subtract(other, context);
409     }
410
411
412
413 }
414
415 //
416
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
417
// you may not use this file except in compliance with the License. You may obtain a copy of the
418
// License at http://www.mozilla.org/MPL/
419
//
420
// Software distributed under the License is distributed on an "AS IS" basis,
421
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
422
// See the License for the specific language governing rights and limitations under the License.
423
//
424
// The Original Code is: all this file.
425
//
426
// The Initial Developer of the Original Code is Michael H. Kay
427
//
428
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
429
//
430
// Contributor(s): none.
431
//
432

433
Popular Tags