1 package com.thaiopensource.datatype.xsd; 2 3 import org.relaxng.datatype.ValidationContext; 4 5 import java.math.BigInteger ; 6 import java.math.BigDecimal ; 7 import java.util.Calendar ; 8 import java.util.GregorianCalendar ; 9 10 class DurationDatatype extends RegexDatatype implements OrderRelation { 11 static private final String 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 str) { 19 if (!super.lexicallyAllows(str)) 20 return false; 21 char last = str.charAt(str.length()-1); 22 return last != 'P' && last != 'T'; 25 } 26 27 static private class Duration { 28 private final BigInteger years; 29 private final BigInteger months; 30 private final BigInteger days; 31 private final BigInteger hours; 32 private final BigInteger minutes; 33 private final BigDecimal seconds; 34 35 Duration(boolean negative, 36 BigInteger years, BigInteger months, BigInteger days, 37 BigInteger hours, BigInteger minutes, BigDecimal 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 getYears() { 57 return years; 58 } 59 60 BigInteger getMonths() { 61 return months; 62 } 63 64 BigInteger getDays() { 65 return days; 66 } 67 68 BigInteger getHours() { 69 return hours; 70 } 71 72 BigInteger getMinutes() { 73 return minutes; 74 } 75 76 BigDecimal getSeconds() { 77 return seconds; 78 } 79 80 public boolean equals(Object 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 getValue(String str, ValidationContext vc) { 103 int t = str.indexOf('T'); 104 if (t < 0) 105 t = str.length(); 106 String date = str.substring(0, t); 107 String 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 getIntegerField(String 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 (str.substring(start, end)); 126 } 127 128 static private BigDecimal getDecimalField(String 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 (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 obj1, Object obj2) { 145 Duration d1 = (Duration)obj1; 146 Duration d2 = (Duration)obj2; 147 BigInteger months1 = computeMonths(d1); 148 BigInteger months2 = computeMonths(d2); 149 BigDecimal seconds1 = computeSeconds(d1); 150 BigDecimal 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 total1 = daysPlusSeconds(computeDays(months1, REF_YEAR_MONTHS[i], REF_YEAR_MONTHS[i + 1]), seconds1); 165 BigDecimal 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 176 private static BigInteger computeDays(BigInteger 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 BigInteger [] tem = months.divideAndRemainder(BigInteger.valueOf(400*12)); 185 --refMonth; 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 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 218 private static BigDecimal daysPlusSeconds(BigInteger days, BigDecimal seconds) { 219 return seconds.add(new BigDecimal (days.multiply(BigInteger.valueOf(24*60*60)))); 220 } 221 222 225 private static BigInteger computeMonths(Duration d) { 226 return d.getYears().multiply(BigInteger.valueOf(12)).add(d.getMonths()); 227 } 228 229 233 private static BigDecimal computeSeconds(Duration d) { 234 return d.getSeconds().add(new BigDecimal (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 [] 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 |