KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > thaiopensource > datatype > xsd > DurationDatatype


1 package com.thaiopensource.datatype.xsd;
2
3 import org.relaxng.datatype.ValidationContext;
4
5 import java.math.BigInteger JavaDoc;
6 import java.math.BigDecimal JavaDoc;
7 import java.util.Calendar JavaDoc;
8 import java.util.GregorianCalendar JavaDoc;
9
10 class DurationDatatype extends RegexDatatype implements OrderRelation {
11   static private final String JavaDoc PATTERN =
12     "-?P([0-9]+Y)?([0-9]+M)?([0-9]+D)?(T([0-9]+H)?([0-9]+M)?(([0-9]+(\\.[0-9]*)?|\\.[0-9]+)S)?)?";
13
14   DurationDatatype() {
15     super(PATTERN);
16   }
17
18   public boolean lexicallyAllows(String JavaDoc str) {
19     if (!super.lexicallyAllows(str))
20       return false;
21     char last = str.charAt(str.length()-1);
22     // This enforces that there must be at least one component
23
// and that T is omitted if all time components are omitted
24
return last != 'P' && last != 'T';
25   }
26
27   static private class Duration {
28     private final BigInteger JavaDoc years;
29     private final BigInteger JavaDoc months;
30     private final BigInteger JavaDoc days;
31     private final BigInteger JavaDoc hours;
32     private final BigInteger JavaDoc minutes;
33     private final BigDecimal JavaDoc seconds;
34
35     Duration(boolean negative,
36              BigInteger JavaDoc years, BigInteger JavaDoc months, BigInteger JavaDoc days,
37              BigInteger JavaDoc hours, BigInteger JavaDoc minutes, BigDecimal JavaDoc seconds) {
38       if (negative) {
39         this.years = years.negate();
40         this.months = months.negate();
41         this.days = days.negate();
42         this.hours = hours.negate();
43         this.minutes = minutes.negate();
44         this.seconds = seconds.negate();
45       }
46       else {
47         this.years = years;
48         this.months = months;
49         this.days = days;
50         this.hours = hours;
51         this.minutes = minutes;
52         this.seconds = seconds;
53       }
54     }
55
56     BigInteger JavaDoc getYears() {
57       return years;
58     }
59
60     BigInteger JavaDoc getMonths() {
61       return months;
62     }
63
64     BigInteger JavaDoc getDays() {
65       return days;
66     }
67
68     BigInteger JavaDoc getHours() {
69       return hours;
70     }
71
72     BigInteger JavaDoc getMinutes() {
73       return minutes;
74     }
75
76     BigDecimal JavaDoc getSeconds() {
77       return seconds;
78     }
79
80     public boolean equals(Object JavaDoc obj) {
81       if (!(obj instanceof Duration))
82         return false;
83       Duration other = (Duration)obj;
84       return (this.years.equals(other.years)
85               && this.months.equals(other.months)
86               && this.days.equals(other.days)
87               && this.hours.equals(other.hours)
88               && this.minutes.equals(other.minutes)
89               && this.seconds.compareTo(other.seconds) == 0);
90     }
91
92     public int hashCode() {
93       return (years.hashCode()
94               ^ months.hashCode()
95               ^ days.hashCode()
96               ^ hours.hashCode()
97               ^ minutes.hashCode()
98               ^ seconds.hashCode());
99     }
100   }
101
102   Object JavaDoc getValue(String JavaDoc str, ValidationContext vc) {
103     int t = str.indexOf('T');
104     if (t < 0)
105       t = str.length();
106     String JavaDoc date = str.substring(0, t);
107     String JavaDoc time = str.substring(t);
108     return new Duration(str.charAt(0) == '-',
109                         getIntegerField(date, 'Y'),
110                         getIntegerField(date, 'M'),
111                         getIntegerField(date, 'D'),
112                         getIntegerField(time, 'H'),
113                         getIntegerField(time, 'M'),
114                         getDecimalField(time, 'S'));
115
116   }
117
118   static private BigInteger JavaDoc getIntegerField(String JavaDoc str, char code) {
119     int end = str.indexOf(code);
120     if (end < 0)
121       return BigInteger.valueOf(0);
122     int start = end;
123     while (Character.isDigit(str.charAt(start - 1)))
124       --start;
125     return new BigInteger JavaDoc(str.substring(start, end));
126   }
127
128   static private BigDecimal JavaDoc getDecimalField(String JavaDoc str, char code) {
129     int end = str.indexOf(code);
130     if (end < 0)
131       return BigDecimal.valueOf(0);
132     int start = end;
133     while (!Character.isLetter(str.charAt(start - 1)))
134       --start;
135     return new BigDecimal JavaDoc(str.substring(start, end));
136   }
137
138   OrderRelation getOrderRelation() {
139     return this;
140   }
141
142   private static final int[] REF_YEAR_MONTHS = { 1696, 9, 1697, 2, 1903, 3, 1903, 7 };
143
144   public boolean isLessThan(Object JavaDoc obj1, Object JavaDoc obj2) {
145     Duration d1 = (Duration)obj1;
146     Duration d2 = (Duration)obj2;
147     BigInteger JavaDoc months1 = computeMonths(d1);
148     BigInteger JavaDoc months2 = computeMonths(d2);
149     BigDecimal JavaDoc seconds1 = computeSeconds(d1);
150     BigDecimal JavaDoc seconds2 = computeSeconds(d2);
151     switch (months1.compareTo(months2)) {
152     case -1:
153       if (seconds1.compareTo(seconds2) <= 0)
154         return true;
155       break;
156     case 0:
157       return seconds1.compareTo(seconds2) < 0;
158     case 1:
159       if (seconds1.compareTo(seconds2) >= 0)
160         return false;
161       break;
162     }
163     for (int i = 0; i < REF_YEAR_MONTHS.length; i += 2) {
164       BigDecimal JavaDoc total1 = daysPlusSeconds(computeDays(months1, REF_YEAR_MONTHS[i], REF_YEAR_MONTHS[i + 1]), seconds1);
165       BigDecimal JavaDoc total2 = daysPlusSeconds(computeDays(months2, REF_YEAR_MONTHS[i], REF_YEAR_MONTHS[i + 1]), seconds2);
166       if (total1.compareTo(total2) >= 0)
167         return false;
168     }
169     return true;
170   }
171
172   /**
173    * Returns the number of days spanned by a period of months starting with a particular
174    * reference year and month.
175    */

176   private static BigInteger JavaDoc computeDays(BigInteger JavaDoc months, int refYear, int refMonth) {
177     switch (months.signum()) {
178     case 0:
179       return BigInteger.valueOf(0);
180     case -1:
181       return computeDays(months.negate(), refYear, refMonth).negate();
182     }
183     // Complete cycle of Gregorian calendar is 400 years
184
BigInteger JavaDoc[] tem = months.divideAndRemainder(BigInteger.valueOf(400*12));
185     --refMonth; // use 0 base to match Java
186
int total = 0;
187     for (int rem = tem[1].intValue(); rem > 0; rem--) {
188       total += daysInMonth(refYear, refMonth);
189       if (++refMonth == 12) {
190         refMonth = 0;
191         refYear++;
192       }
193     }
194     // In the Gregorian calendar, there are 97 (= 100 + 4 - 1) leap years every 400 years.
195
return tem[0].multiply(BigInteger.valueOf(365*400 + 97)).add(BigInteger.valueOf(total));
196   }
197
198   private static int daysInMonth(int year, int month) {
199     switch (month) {
200     case Calendar.SEPTEMBER:
201     case Calendar.APRIL:
202     case Calendar.JUNE:
203     case Calendar.NOVEMBER:
204       return 30;
205     case Calendar.FEBRUARY:
206       return isLeapYear(year) ? 29 : 28;
207     }
208     return 31;
209   }
210
211   private static boolean isLeapYear(int year) {
212     return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
213   }
214
215   /**
216    * Returns the total number of seconds from a specified number of days and seconds.
217    */

218   private static BigDecimal JavaDoc daysPlusSeconds(BigInteger JavaDoc days, BigDecimal JavaDoc seconds) {
219     return seconds.add(new BigDecimal JavaDoc(days.multiply(BigInteger.valueOf(24*60*60))));
220   }
221
222   /**
223    * Returns the total number of months specified by the year and month fields of the duration
224    */

225   private static BigInteger JavaDoc computeMonths(Duration d) {
226     return d.getYears().multiply(BigInteger.valueOf(12)).add(d.getMonths());
227   }
228
229   /**
230    * Returns the total number of seconds specified by the days, hours, minuts and seconds fields of
231    * the duration.
232    */

233   private static BigDecimal JavaDoc computeSeconds(Duration d) {
234     return d.getSeconds().add(new BigDecimal JavaDoc(d.getDays().multiply(BigInteger.valueOf(24))
235                                              .add(d.getHours()).multiply(BigInteger.valueOf(60))
236                                              .add(d.getMinutes()).multiply(BigInteger.valueOf(60))));
237   }
238
239   public static void main(String JavaDoc[] args) {
240     DurationDatatype dt = new DurationDatatype();
241     System.err.println(dt.isLessThan(dt.getValue(args[0], null), dt.getValue(args[1], null)));
242   }
243 }
244
Popular Tags