KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > xquark > schema > datatypes > Duration


1 /*
2  * This file belongs to the XQuark distribution.
3  * Copyright (C) 2003 Universite de Versailles Saint-Quentin.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307.
18  * You can also get it at http://www.gnu.org/licenses/lgpl.html
19  *
20  * For more information on this software, see http://www.xquark.org.
21  */

22
23 package org.xquark.schema.datatypes;
24
25 import java.text.ParseException JavaDoc;
26
27 public class Duration implements Comparable JavaDoc {
28
29     private static final String JavaDoc RCSRevision = "$Revision: 1.1 $";
30     private static final String JavaDoc RCSName = "$Name: $";
31
32     private static long MILLISINMIN = 60000L;
33     private static long MILLISINHOUR = MILLISINMIN * 60;
34     private static long MILLISINDAY = MILLISINHOUR * 24;
35     private static int MONTHSINYEAR = 12;
36     private static int[] MINDAYSINMONTHS = { 0, 28, 59, 89, 120, 150, 181, 212, 242, 273, 303, 334 };
37     private static int[] MAXDAYSINMONTHS = { 0, 31, 62, 92, 123, 153, 184, 215, 245, 276, 306, 337 };
38     private static int[] POWERSOFTEN = { 1, 10, 100, 1000 };
39     private static int MILLISECOND_FIELD = 1 << 0;
40     private static int MINUTE_FIELD = 1 << 1;
41     private static int HOUR_FIELD = 1 << 2;
42     private static int DAY_FIELD = 1 << 3;
43     private static int MONTH_FIELD = 1 << 4;
44     private static int YEAR_FIELD = 1 << 5;
45     private static int SIGN_FIELD = 1 << 6;
46     
47     private int fields = 0;
48     private int sign = 1;
49     private int year = 0;
50     private int month = 0;
51     private int day = 0;
52     private int hour = 0;
53     private int minute = 0;
54     private long millisecond = 0;
55
56     public Duration(String JavaDoc value) throws ParseException JavaDoc {
57         parseDuration(value); // get YY, MM, DD, HH, MM, SS value from string value
58
}
59     
60     public Duration(int sign, int year, int month, int day, int hour, int minute, long millisecond) {
61         this.sign = sign;
62         this.year = year;
63         this.month = month;
64         this.day = day;
65         this.hour = hour;
66         this.minute = minute;
67         this.millisecond = millisecond;
68     }
69
70     /**
71      * Implements the compareTo method of interface Comparable
72      * @param obj The object to be compared to this object. It must be of the same
73      * class or of a subclass.
74      * @return -1 if this object is smaller than obj, 0 if it is equal, and 1 if it is greater
75      * @exception ClassCastException if the object to be compared is not castable to a Duration
76      * @exception IllegalArgumentException if the comparison between the two Duration objects is indeterminate
77      * (see XML Schema Datatypes specification for details)
78      */

79     public int compareTo(Object JavaDoc obj) throws ClassCastException JavaDoc, IllegalArgumentException JavaDoc {
80         return compareTo((Duration) obj);
81     }
82
83     private int compareTo(Duration other) throws IllegalArgumentException JavaDoc {
84         if (this.sign < other.sign) {
85             return -1;
86         } else if (this.sign > other.sign) {
87             return 1;
88         } else {
89             long deltaMillis = this.getDayTime()-other.getDayTime();
90             int deltaMonths = this.getYearMonth()-other.getYearMonth();
91             if (deltaMonths == 0) {
92                 return (deltaMillis == 0) ? 0 : ((deltaMillis < 0) ? -this.sign : this.sign);
93             } else if (deltaMillis == 0) {
94                 return (deltaMonths < 0) ? -this.sign : this.sign;
95             } else {
96                 long min = 0;
97                 long max = 0;
98                 if (deltaMonths > 0) {
99                     min = getMinDaysFromMonths(deltaMonths) * MILLISINDAY;
100                     max = getMaxDaysFromMonths(deltaMonths) * MILLISINDAY;
101                 } else {
102                     max = -getMinDaysFromMonths(-deltaMonths) * MILLISINDAY;
103                     min = -getMaxDaysFromMonths(-deltaMonths) * MILLISINDAY;
104                 }
105                 if (max+deltaMillis < 0)
106                     return -this.sign;
107                 else if (min+deltaMillis > 0)
108                     return this.sign;
109                 else
110                     throw new IllegalArgumentException JavaDoc("Indeterminate comparison result for these arguments");
111             }
112         }
113     }
114
115     private int getYearMonth() {
116         return year * MONTHSINYEAR + month;
117     }
118
119     private long getDayTime() {
120         return day * MILLISINDAY + hour * MILLISINHOUR + minute * MILLISINMIN + millisecond;
121     }
122
123     private static int getMinDaysFromMonths(int nMonths) {
124         int m = nMonths % MONTHSINYEAR;
125         int y = nMonths / MONTHSINYEAR;
126         return y * 365 + MINDAYSINMONTHS[m] + getMinLeapYears(y + ((m > 0) ? 1 : 0));
127     }
128
129     private static int getMaxDaysFromMonths(int nMonths) {
130         int m = nMonths % MONTHSINYEAR;
131         int y = nMonths / MONTHSINYEAR;
132         return y * 365 + MAXDAYSINMONTHS[m] + getMaxLeapYears(y);
133     }
134
135     private static int getMinLeapYears(int y) {
136         if (y < 8)
137             return 0;
138         else if (y < 300)
139             return y / 4 - (y / 100 + 1);
140         else if (y < 400)
141             return y / 4 - y / 100;
142         else
143             return y / 400 * 97 + getMinLeapYears(y % 400);
144     }
145
146     private static int getMaxLeapYears(int y) {
147         int part = (y % 4 == 0) ? 0 : 1;
148         if (y < 200)
149             return y / 4 + part;
150         else if (y < 400)
151             return y / 4 + part - (y / 100 - 1);
152         else
153             return y / 400 * 97 + getMaxLeapYears(y % 400);
154     }
155
156     public boolean equals(Object JavaDoc obj) {
157         if (this == obj)
158             return true;
159         if (!(obj instanceof Duration))
160             return false;
161         Duration other = (Duration) obj;
162         return this.sign == other.sign
163             && this.getYearMonth() == other.getYearMonth()
164             && this.getDayTime() == other.getDayTime();
165     }
166
167     public int hashCode() {
168         long m = getDayTime();
169         int highMillis = (int) (m >> 32);
170         int lowMillis = (int) (m & 0x00000000ffffffffL);
171         return sign ^ getYearMonth() ^ highMillis ^ lowMillis;
172     }
173
174     private void setField(int field) {
175         fields |= field;
176     }
177     
178     private boolean hasField(int field) {
179         return (fields & field) != 0;
180     }
181     
182     public String JavaDoc toString() {
183         StringBuffer JavaDoc print = new StringBuffer JavaDoc(20);
184         if (sign == -1)
185             print.append('-');
186         print.append('P');
187         if (hasField(YEAR_FIELD)) {
188             print.append(year);
189             print.append('Y');
190         }
191         if (hasField(MONTH_FIELD)) {
192             print.append(month);
193             print.append('M');
194         }
195         if (hasField(DAY_FIELD)) {
196             print.append(day);
197             print.append('D');
198         }
199         boolean inTime = false;
200         if (hasField(HOUR_FIELD)) {
201             print.append('T');
202             print.append(hour);
203             print.append('H');
204             inTime = true;
205         }
206         if (hasField(MINUTE_FIELD)) {
207             if (!inTime)
208                 print.append('T');
209             print.append(minute);
210             print.append('M');
211             inTime = true;
212         }
213         if (hasField(MILLISECOND_FIELD)) {
214             if (!inTime)
215                 print.append('T');
216             print.append(millisecond / 1000);
217             int millis = (int) millisecond % 1000;
218             if (millis > 0) {
219                 print.append('.');
220                 if (millis < 10) {
221                     print.append("00");
222                     print.append(millis);
223                 } else if (millis < 100) {
224                     print.append("0");
225                     print.append(millis);
226                 } else {
227                     print.append(millis);
228                 }
229                 int len = print.length();
230                 while (print.charAt(len-1) == '0') {
231                     print.setLength(len-1);
232                     len--;
233                 }
234             }
235             print.append('S');
236         }
237         return print.toString();
238     }
239
240     private void parseDuration(String JavaDoc value) throws ParseException JavaDoc {
241         int len = value.length();
242         int index = 0;
243         int fieldIndex = 0;
244         if (len <= 2)
245             throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
246         if (value.charAt(index) == '-') {
247             sign = -1;
248             index++;
249             setField(SIGN_FIELD);
250         }
251         if (value.charAt(index) != 'P')
252             throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
253         index++;
254         while (index < len && value.charAt(index) != 'T') {
255             int val = 0;
256             int second = -1;
257             int count = 0;
258             char c = 0;
259             while (index < len) {
260                 c = value.charAt(index++);
261                 if (!Character.isDigit(c)) break;
262                 val *= 10;
263                 val += ((int) c - (int) '0');
264                 count++;
265             }
266             if (c == 'Y') {
267                 if (count == 0 || fieldIndex > 0)
268                     throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
269                 year = val;
270                 fieldIndex = 1;
271                 setField(YEAR_FIELD);
272             } else if (c == 'M') {
273                 if (count == 0 || fieldIndex > 1)
274                     throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
275                 month = val;
276                 fieldIndex = 2;
277                 setField(MONTH_FIELD);
278             } else if (count == 0 || c == 'D') {
279                 if (fieldIndex > 2)
280                     throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
281                 day = val;
282                 fieldIndex = 3;
283                 setField(DAY_FIELD);
284             } else {
285                 throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
286             }
287         }
288         if (index < len) {
289             index++;
290             while (index < len) {
291                 int val = 0;
292                 int second = -1;
293                 int count = 0;
294                 char c = 0;
295                 while (index < len) {
296                     c = value.charAt(index++);
297                     if (!Character.isDigit(c)) break;
298                     val *= 10;
299                     val += ((int) c - (int) '0');
300                     count++;
301                 }
302                 if (c == 'H') {
303                     if (count == 0 || fieldIndex > 3)
304                         throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
305                     hour = val;
306                     fieldIndex = 4;
307                     setField(HOUR_FIELD);
308                 } else if (c == 'M') {
309                     if (count == 0 || fieldIndex > 4)
310                         throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
311                     minute = val;
312                     fieldIndex = 5;
313                     setField(MINUTE_FIELD);
314                 } else if (c == '.') {
315                     if (count == 0 || fieldIndex > 5)
316                         throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
317                     second = val;
318                     fieldIndex = 6;
319                 } else if (c == 'S') {
320                     if (count == 0 || fieldIndex > 6)
321                         throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
322                     if (second != -1) {
323                         if (count > 3) {
324                             // truncate milliseconds digits after the third
325
val = (int)(val/Math.pow(10, count-3));
326                         } else {
327                             val *= POWERSOFTEN[3 - count];
328                         }
329                         millisecond = 1000*second+val;
330                     } else {
331                         millisecond = 1000*val;
332                     }
333                     fieldIndex = 7;
334                     setField(MILLISECOND_FIELD);
335                 } else {
336                     throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
337                 }
338             }
339             if (fieldIndex < 4 || fieldIndex == 6)
340                 throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
341         } else if (fieldIndex == 0) {
342             throw new ParseException JavaDoc("Invalid duration specification: " + value, index);
343         }
344     }
345 }
346
Popular Tags