KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.value;
2 import net.sf.saxon.expr.XPathContext;
3 import net.sf.saxon.om.FastStringBuffer;
4 import net.sf.saxon.trans.DynamicError;
5 import net.sf.saxon.trans.XPathException;
6 import net.sf.saxon.type.ItemType;
7 import net.sf.saxon.type.Type;
8
9 import java.util.StringTokenizer JavaDoc;
10
11 /**
12 * A value of type xsd:dayTimeDuration
13 */

14
15 public final class SecondsDurationValue extends DurationValue implements Comparable JavaDoc {
16
17     /**
18     * Private constructor for internal use
19     */

20
21     private SecondsDurationValue() {
22     }
23
24     /**
25     * Constructor: create a duration value from a supplied string, in
26     * ISO 8601 format [+|-]PnDTnHnMnS
27     */

28
29     public SecondsDurationValue(CharSequence JavaDoc s) throws XPathException {
30
31         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(trimWhitespace(s).toString(), "-+.PDTHMS", true);
32         try {
33             if (!tok.hasMoreElements()) badDuration("empty string", s);
34             String JavaDoc part = (String JavaDoc)tok.nextElement();
35             if ("+".equals(part)) {
36                 part = (String JavaDoc)tok.nextElement();
37             } else if ("-".equals(part)) {
38                 negative = true;
39                 part = (String JavaDoc)tok.nextElement();
40             }
41             if (!"P".equals(part)) badDuration("missing 'P'", s);
42             int state = 0;
43             while (tok.hasMoreElements()) {
44                 part = (String JavaDoc)tok.nextElement();
45                 if ("T".equals(part)) {
46                     state = 4;
47                     part = (String JavaDoc)tok.nextElement();
48                 }
49                 int value = Integer.parseInt(part);
50                 if (!tok.hasMoreElements()) badDuration("missing unit letter at end", s);
51                 char delim = ((String JavaDoc)tok.nextElement()).charAt(0);
52                 switch (delim) {
53                     case 'D':
54                         if (state > 2) badDuration("D is out of sequence", s);
55                         days = value;
56                         state = 3;
57                         break;
58                     case 'H':
59                         if (state != 4) badDuration("H is out of sequence", s);
60                         hours = value;
61                         state = 5;
62                         break;
63                     case 'M':
64                         if (state < 4 || state > 5) badDuration("M is out of sequence", s);
65                         minutes = value;
66                         state = 6;
67                         break;
68                     case '.':
69                         if (state < 4 || state > 6) badDuration("misplaced decimal point", s);
70                         seconds = value;
71                         state = 7;
72                         break;
73                     case 'S':
74                         if (state < 4 || state > 7) badDuration("S is out of sequence", s);
75                         if (state==7) {
76                             while (part.length() < 6) part += "0";
77                             if (part.length() > 6) part = part.substring(0, 6);
78                             microseconds = Integer.parseInt(part);
79                         } else {
80                             seconds = value;
81                         }
82                         state = 8;
83                         break;
84                    default:
85                         badDuration("misplaced " + delim, s);
86                 }
87             }
88
89             normalize();
90
91         } catch (NumberFormatException JavaDoc err) {
92             badDuration("non-numeric or out-of-range component", s);
93         }
94     }
95
96     /**
97      * Create a dayTimeDuration given the number of days, hours, minutes, and seconds
98      */

99
100     public SecondsDurationValue(int sign, int days, int hours, int minutes, int seconds, int microseconds) {
101         this.negative = (sign<0);
102         this.years = 0;
103         this.months = 0;
104         this.days = days;
105         this.hours = hours;
106         this.minutes = minutes;
107         this.seconds = seconds;
108         this.microseconds = microseconds;
109     }
110
111     /**
112     * Convert to string
113     * @return ISO 8601 representation.
114     */

115
116     public CharSequence JavaDoc getStringValueCS() {
117
118         // We need to normalize the representation
119

120         double length = getLengthInSeconds();
121         if (length<0) length = -length;
122
123         long secs = (long)Math.floor(length);
124         long micros = (int)((length % 1.0) * 1000000);
125
126         long s = secs % 60;
127         long m = secs / 60;
128         long h = m / 60;
129         m = m % 60;
130         long d = h / 24;
131         h = h % 24;
132
133         FastStringBuffer sb = new FastStringBuffer(32);
134         if (negative) {
135             sb.append('-');
136         }
137         sb.append('P');
138         if (d != 0) {
139             sb.append(d + "D");
140         }
141         if ( d==0 || h!=0 || m!=0 || s!=0 || micros!=0) {
142             sb.append('T');
143         }
144         if (h != 0) {
145             sb.append(h + "H");
146         }
147         if (m != 0) {
148             sb.append(m + "M");
149         }
150         if (s != 0 || micros != 0 || (d==0 && m==0 && h==0)) {
151             if (micros == 0) {
152                 sb.append(s + "S");
153             } else {
154                 long ms = (s * 1000000) + micros;
155                 String JavaDoc mss = ms + "";
156                 if (s == 0) {
157                     mss = "0000000" + mss;
158                     mss = mss.substring(mss.length()-7);
159                 }
160                 sb.append(mss.substring(0, mss.length()-6));
161                 sb.append('.');
162                 int lastSigDigit = mss.length()-1;
163                 while (mss.charAt(lastSigDigit) == '0') {
164                     lastSigDigit--;
165                 }
166                 sb.append(mss.substring(mss.length()-6, lastSigDigit+1));
167                 sb.append('S');
168             }
169         }
170         return sb;
171     }
172
173     /**
174     * Normalize the value, for example 90M becomes 1H30M
175     */

176
177     public void normalize() throws DynamicError {
178         long seconds2 = seconds;
179         long minutes2 = minutes;
180         long hours2 = hours;
181         long days2 = days;
182         if (microseconds >= 1000000) {
183             seconds2 += (microseconds / 1000000);
184             microseconds = microseconds % 1000000;
185         }
186         if (seconds >= 60) {
187             minutes2 += (seconds2 / 60);
188             seconds2 = (int)(seconds2 % 60);
189         }
190         if (minutes2 >= 60) {
191             hours2 += (minutes2 / 60);
192             minutes2 = (int)(minutes2 % 60);
193         }
194         if (hours2 >= 24) {
195             days2 += (hours2 / 24);
196             if (days2 > Integer.MAX_VALUE || days2 < Integer.MIN_VALUE) {
197                 throw new DynamicError("Duration exceeds implementation-defined limits");
198             }
199             hours2 = (int)(hours2 % 24);
200         }
201         days = (int)days2;
202         hours = (int)hours2;
203         minutes = (int)minutes2;
204         seconds = (int)seconds2;
205     }
206
207     /**
208     * Get length of duration in seconds
209     */

210
211     public double getLengthInSeconds() {
212         double a = days;
213         a = a*24 + hours;
214         a = a*60 + minutes;
215         a = a*60 + seconds;
216         a += ((double)microseconds / 1000000);
217         // System.err.println("Duration length " + days + "/" + hours + "/" + minutes + "/" + seconds + " is " + a);
218
return (negative ? -a : a);
219     }
220
221     /**
222      * Get length of duration in milliseconds, as a long
223      */

224
225     public long getLengthInMilliseconds() {
226         long a = days;
227         a = a*24 + hours;
228         a = a*60 + minutes;
229         a = a*60 + seconds;
230         a = a*1000 + (microseconds / 1000);
231         return (negative ? -a : a);
232     }
233
234     /**
235      * Get length of duration in microseconds, as a long
236      */

237
238     public long getLengthInMicroseconds() {
239         long a = days;
240         a = a*24 + hours;
241         a = a*60 + minutes;
242         a = a*60 + seconds;
243         a = a*1000000 + microseconds;
244         return (negative ? -a : a);
245     }
246
247
248     /**
249     * Construct a duration value as a number of seconds.
250     */

251
252     public static SecondsDurationValue fromSeconds(double seconds) throws XPathException {
253         SecondsDurationValue sdv = new SecondsDurationValue();
254         sdv.negative = (seconds<0);
255         sdv.seconds = (int)(seconds<0 ? -seconds : seconds);
256         sdv.microseconds = (int)((seconds % 1.0) * 1000000);
257         sdv.normalize();
258         return sdv;
259     }
260
261     /**
262     * Construct a duration value as a number of milliseconds.
263     */

264
265     public static SecondsDurationValue fromMilliseconds(long milliseconds) throws XPathException {
266         SecondsDurationValue sdv = new SecondsDurationValue();
267         sdv.negative = (milliseconds<0);
268         milliseconds = Math.abs(milliseconds);
269         long seconds = milliseconds/1000;
270         sdv.days = (int)(seconds / (3600*24));
271         sdv.seconds = (int)(seconds % (3600*24));
272         sdv.microseconds = (int)(milliseconds % 1000) * 1000;
273         sdv.normalize();
274         return sdv;
275     }
276
277     /**
278     * Construct a duration value as a number of microseconds.
279     */

280
281     public static SecondsDurationValue fromMicroseconds(long microseconds) throws XPathException {
282         SecondsDurationValue sdv = new SecondsDurationValue();
283         sdv.negative = (microseconds<0);
284         microseconds = Math.abs(microseconds);
285         long seconds = microseconds/1000000L;
286         sdv.days = (int)(seconds / (3600*24));
287         sdv.seconds = (int)(seconds % (3600*24));
288         sdv.microseconds = (int)(microseconds % 1000000L);
289         sdv.normalize();
290         return sdv;
291     }
292
293
294     /**
295     * Multiply duration by a number
296     */

297
298     public DurationValue multiply(double n, XPathContext context) throws XPathException {
299         double m = (double)getLengthInMicroseconds();
300         double product = n*m;
301         if (Double.isInfinite(product) || product > Long.MAX_VALUE || product < Long.MIN_VALUE) {
302             DynamicError err = new DynamicError("Overflow when multiplying/dividing a duration by a number");
303             err.setErrorCode("FODT0002");
304             err.setXPathContext(context);
305             throw err;
306         }
307         return fromMicroseconds((long)product);
308     }
309
310     /**
311      * Find the ratio between two durations
312      * @param other the dividend
313      * @return the ratio, as a double
314      * @throws XPathException
315      */

316     public DoubleValue divide(DurationValue other, XPathContext context) throws XPathException {
317         if (other instanceof SecondsDurationValue) {
318             long v1 = this.getLengthInMicroseconds();
319             long v2 = ((SecondsDurationValue)other).getLengthInMicroseconds();
320             // scale both numbers down by the same amount, to avoid loss of precision when converting to double
321
long mask = (long)1;
322             while ((v1&mask) == 0 && ((v2&mask) == 0 && (v1 != 0) && (v2 != 0))) {
323                 v1 = v1/2;
324                 v2 = v2/2;
325             }
326             return new DoubleValue(
327                     ((double)v1) / (double)v2);
328         } else {
329             throw new DynamicError("Cannot divide two durations of different type");
330         }
331     }
332
333     /**
334     * Add two dayTimeDurations
335     */

336
337     public DurationValue add(DurationValue other, XPathContext context) throws XPathException {
338         if (other instanceof SecondsDurationValue) {
339             return fromMicroseconds(this.getLengthInMicroseconds() +
340                     ((SecondsDurationValue)other).getLengthInMicroseconds());
341         } else {
342             throw new DynamicError("Cannot add two durations of different type");
343         }
344     }
345
346     /**
347     * Subtract two dayTime-durations
348     */

349
350     public DurationValue subtract(DurationValue other, XPathContext context) throws XPathException {
351         if (other instanceof SecondsDurationValue) {
352             return fromMicroseconds(this.getLengthInMicroseconds() -
353                     ((SecondsDurationValue)other).getLengthInMicroseconds());
354         } else {
355             throw new DynamicError("Cannot add two durations of different type");
356         }
357     }
358
359     /**
360     * Compare the value to another duration value
361     * @param other The other dateTime value
362     * @return negative value if this one is the earler, 0 if they are chronologically equal,
363     * positive value if this one is the later. For this purpose, dateTime values with an unknown
364     * timezone are considered to be UTC values (the Comparable interface requires
365     * a total ordering).
366     * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
367     * is declared as Object to satisfy the Comparable interface)
368     */

369
370     public int compareTo(Object JavaDoc other) {
371         if (other instanceof SecondsDurationValue) {
372             long diff = this.getLengthInMicroseconds() - ((SecondsDurationValue)other).getLengthInMicroseconds();
373             if (diff < 0) {
374                 return -1;
375             } else if (diff > 0) {
376                 return +1;
377             } else {
378                 return 0;
379             }
380         } else {
381             throw new ClassCastException JavaDoc("Cannot compare a dayTimeDuration to an object of class "
382                     + other.getClass());
383         }
384     }
385
386
387     /**
388     * Determine the data type of the exprssion
389     * @return Type.DAY_TIME_DURATION,
390     */

391
392     public ItemType getItemType() {
393         return Type.DAY_TIME_DURATION_TYPE;
394     }
395
396     /**
397     * Convert to Java object (for passing to external functions)
398     */

399
400     public Object JavaDoc convertToJava(Class JavaDoc target, XPathContext context) throws XPathException {
401         if (target.isAssignableFrom(DurationValue.class)) {
402             return this;
403         } else if (target==String JavaDoc.class || target==CharSequence JavaDoc.class) {
404             return getStringValue();
405         } else if (target==Object JavaDoc.class) {
406             return getStringValue();
407         } else {
408             throw new DynamicError("Conversion of dayTimeDuration to " + target.getName() +
409                         " is not supported");
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