1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 1999-2002,2004,2005 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xerces.internal.impl.dv.xs; 22 23 import java.math.BigDecimal; 24 import java.math.BigInteger; 25 26 import javax.xml.datatype.DatatypeConstants; 27 import javax.xml.datatype.Duration; 28 29 import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException; 30 import com.sun.org.apache.xerces.internal.impl.dv.ValidationContext; 31 32 /** 33 * Validator for <duration> datatype (W3C Schema Datatypes) 34 * 35 * @xerces.internal 36 * 37 * @author Elena Litani 38 * @author Gopal Sharma, SUN Microsystem Inc. 39 * @version $Id: DurationDV.java,v 1.7 2010-11-01 04:39:47 joehw Exp $ 40 */ 41 public class DurationDV extends AbstractDateTimeDV { 42 43 public static final int DURATION_TYPE = 0; 44 public static final int YEARMONTHDURATION_TYPE = 1; 45 public static final int DAYTIMEDURATION_TYPE = 2; 46 // order-relation on duration is a partial order. The dates below are used to 47 // for comparison of 2 durations, based on the fact that 48 // duration x and y is x<=y iff s+x<=s+y 49 // see 3.2.6 duration W3C schema datatype specs 50 // 51 // the dates are in format: {CCYY,MM,DD, H, S, M, MS, timezone} 52 private final static DateTimeData[] DATETIMES= { 53 new DateTimeData(1696, 9, 1, 0, 0, 0, 'Z', null, true, null), 54 new DateTimeData(1697, 2, 1, 0, 0, 0, 'Z', null, true, null), 55 new DateTimeData(1903, 3, 1, 0, 0, 0, 'Z', null, true, null), 56 new DateTimeData(1903, 7, 1, 0, 0, 0, 'Z', null, true, null)}; 57 58 public Object getActualValue(String content, ValidationContext context) throws InvalidDatatypeValueException{ 59 try{ 60 return parse(content, DURATION_TYPE); 61 } catch (Exception ex) { 62 throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.1", new Object[]{content, "duration"}); 63 } 64 } 65 66 /** 67 * Parses, validates and computes normalized version of duration object 68 * 69 * @param str The lexical representation of duration object PnYn MnDTnH nMnS 70 * @param durationType 71 * @return normalized date representation 72 * @exception SchemaDateTimeException Invalid lexical representation 73 */ 74 protected DateTimeData parse(String str, int durationType) throws SchemaDateTimeException{ 75 int len = str.length(); 76 DateTimeData date= new DateTimeData(str, this); 77 78 int start = 0; 79 char c=str.charAt(start++); 80 if ( c!='P' && c!='-' ) { 81 throw new SchemaDateTimeException(); 82 } 83 else { 84 date.utc=(c=='-')?'-':0; 85 if ( c=='-' && str.charAt(start++)!='P' ) { 86 throw new SchemaDateTimeException(); 87 } 88 } 89 90 int negate = 1; 91 //negative duration 92 if ( date.utc=='-' ) { 93 negate = -1; 94 95 } 96 //at least one number and designator must be seen after P 97 boolean designator = false; 98 99 int endDate = indexOf (str, start, len, 'T'); 100 if ( endDate == -1 ) { 101 endDate = len; 102 } 103 else if (durationType == YEARMONTHDURATION_TYPE) { 104 throw new SchemaDateTimeException(); 105 } 106 107 //find 'Y' 108 int end = indexOf (str, start, endDate, 'Y'); 109 if ( end!=-1 ) { 110 111 if (durationType == DAYTIMEDURATION_TYPE) { 112 throw new SchemaDateTimeException(); 113 } 114 115 //scan year 116 date.year=negate * parseInt(str,start,end); 117 start = end+1; 118 designator = true; 119 } 120 121 end = indexOf (str, start, endDate, 'M'); 122 if ( end!=-1 ) { 123 124 if (durationType == DAYTIMEDURATION_TYPE) { 125 throw new SchemaDateTimeException(); 126 } 127 128 //scan month 129 date.month=negate * parseInt(str,start,end); 130 start = end+1; 131 designator = true; 132 } 133 134 end = indexOf (str, start, endDate, 'D'); 135 if ( end!=-1 ) { 136 137 if(durationType == YEARMONTHDURATION_TYPE) { 138 throw new SchemaDateTimeException(); 139 } 140 141 //scan day 142 date.day=negate * parseInt(str,start,end); 143 start = end+1; 144 designator = true; 145 } 146 147 if ( len == endDate && start!=len ) { 148 throw new SchemaDateTimeException(); 149 } 150 if ( len !=endDate ) { 151 152 //scan hours, minutes, seconds 153 //REVISIT: can any item include a decimal fraction or only seconds? 154 // 155 156 end = indexOf (str, ++start, len, 'H'); 157 if ( end!=-1 ) { 158 //scan hours 159 date.hour=negate * parseInt(str,start,end); 160 start=end+1; 161 designator = true; 162 } 163 164 end = indexOf (str, start, len, 'M'); 165 if ( end!=-1 ) { 166 //scan min 167 date.minute=negate * parseInt(str,start,end); 168 start=end+1; 169 designator = true; 170 } 171 172 end = indexOf (str, start, len, 'S'); 173 if ( end!=-1 ) { 174 //scan seconds 175 date.second = negate * parseSecond(str, start, end); 176 start=end+1; 177 designator = true; 178 } 179 // no additional data shouls appear after last item 180 // P1Y1M1DT is illigal value as well 181 if ( start != len || str.charAt(--start)=='T' ) { 182 throw new SchemaDateTimeException(); 183 } 184 } 185 186 if ( !designator ) { 187 throw new SchemaDateTimeException(); 188 } 189 190 return date; 191 } 192 193 /** 194 * Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration") 195 * 196 * @param date1 Unnormalized duration 197 * @param date2 Unnormalized duration 198 * @param strict (min/max)Exclusive strict == true ( LESS_THAN ) or ( GREATER_THAN ) 199 * (min/max)Inclusive strict == false (LESS_EQUAL) or (GREATER_EQUAL) 200 * @return INDETERMINATE if the order relationship between date1 and date2 is indeterminate. 201 * EQUAL if the order relation between date1 and date2 is EQUAL. 202 * If the strict parameter is true, return LESS_THAN if date1 is less than date2 and 203 * return GREATER_THAN if date1 is greater than date2. 204 * If the strict parameter is false, return LESS_THAN if date1 is less than OR equal to date2 and 205 * return GREATER_THAN if date1 is greater than OR equal to date2 206 */ 207 protected short compareDates(DateTimeData date1, DateTimeData date2, boolean strict) { 208 209 //REVISIT: this is unoptimazed vs of comparing 2 durations 210 // Algorithm is described in 3.2.6.2 W3C Schema Datatype specs 211 // 212 213 //add constA to both durations 214 short resultA, resultB= INDETERMINATE; 215 //try and see if the objects are equal 216 resultA = compareOrder (date1, date2); 217 if ( resultA == 0 ) { 218 return 0; 219 } 220 221 DateTimeData[] result = new DateTimeData[2]; 222 result[0] = new DateTimeData(null, this); 223 result[1] = new DateTimeData(null, this); 224 225 //long comparison algorithm is required 226 DateTimeData tempA = addDuration (date1, DATETIMES[0], result[0]); 227 DateTimeData tempB = addDuration (date2, DATETIMES[0], result[1]); 228 resultA = compareOrder(tempA, tempB); 229 if ( resultA == INDETERMINATE ) { 230 return INDETERMINATE; 231 } 232 233 tempA = addDuration(date1, DATETIMES[1], result[0]); 234 tempB = addDuration(date2, DATETIMES[1], result[1]); 235 resultB = compareOrder(tempA, tempB); 236 resultA = compareResults(resultA, resultB, strict); 237 if (resultA == INDETERMINATE) { 238 return INDETERMINATE; 239 } 240 241 tempA = addDuration(date1, DATETIMES[2], result[0]); 242 tempB = addDuration(date2, DATETIMES[2], result[1]); 243 resultB = compareOrder(tempA, tempB); 244 resultA = compareResults(resultA, resultB, strict); 245 if (resultA == INDETERMINATE) { 246 return INDETERMINATE; 247 } 248 249 tempA = addDuration(date1, DATETIMES[3], result[0]); 250 tempB = addDuration(date2, DATETIMES[3], result[1]); 251 resultB = compareOrder(tempA, tempB); 252 resultA = compareResults(resultA, resultB, strict); 253 254 return resultA; 255 } 256 257 private short compareResults(short resultA, short resultB, boolean strict){ 258 259 if ( resultB == INDETERMINATE ) { 260 return INDETERMINATE; 261 } 262 else if ( resultA!=resultB && strict ) { 263 return INDETERMINATE; 264 } 265 else if ( resultA!=resultB && !strict ) { 266 if ( resultA!=0 && resultB!=0 ) { 267 return INDETERMINATE; 268 } 269 else { 270 return (resultA!=0)?resultA:resultB; 271 } 272 } 273 return resultA; 274 } 275 276 private DateTimeData addDuration(DateTimeData date, DateTimeData addto, DateTimeData duration) { 277 278 //REVISIT: some code could be shared between normalize() and this method, 279 // however is it worth moving it? The structures are different... 280 // 281 282 resetDateObj(duration); 283 //add months (may be modified additionaly below) 284 int temp = addto.month + date.month; 285 duration.month = modulo (temp, 1, 13); 286 int carry = fQuotient (temp, 1, 13); 287 288 //add years (may be modified additionaly below) 289 duration.year=addto.year + date.year + carry; 290 291 //add seconds 292 double dtemp = addto.second + date.second; 293 carry = (int)Math.floor(dtemp/60); 294 duration.second = dtemp - carry*60; 295 296 //add minutes 297 temp = addto.minute +date.minute + carry; 298 carry = fQuotient (temp, 60); 299 duration.minute= mod(temp, 60, carry); 300 301 //add hours 302 temp = addto.hour + date.hour + carry; 303 carry = fQuotient(temp, 24); 304 duration.hour = mod(temp, 24, carry); 305 306 307 duration.day=addto.day + date.day + carry; 308 309 while ( true ) { 310 311 temp=maxDayInMonthFor(duration.year, duration.month); 312 if ( duration.day < 1 ) { //original duration was negative 313 duration.day = duration.day + maxDayInMonthFor(duration.year, duration.month-1); 314 carry=-1; 315 } 316 else if ( duration.day > temp ) { 317 duration.day = duration.day - temp; 318 carry=1; 319 } 320 else { 321 break; 322 } 323 temp = duration.month+carry; 324 duration.month = modulo(temp, 1, 13); 325 duration.year = duration.year+fQuotient(temp, 1, 13); 326 } 327 328 duration.utc='Z'; 329 return duration; 330 } 331 332 protected double parseSecond(String buffer, int start, int end) 333 throws NumberFormatException { 334 int dot = -1; 335 for (int i = start; i < end; i++) { 336 char ch = buffer.charAt(i); 337 if (ch == '.') 338 dot = i; 339 else if (ch > '9' || ch < '0') 340 throw new NumberFormatException("'" + buffer + "' has wrong format"); 341 } 342 if (dot+1 == end) { 343 throw new NumberFormatException("'" + buffer + "' has wrong format"); 344 } 345 double value = Double.parseDouble(buffer.substring(start, end)); 346 if (value == Double.POSITIVE_INFINITY) { 347 throw new NumberFormatException("'" + buffer + "' has wrong format"); 348 } 349 return value; 350 } 351 352 protected String dateToString(DateTimeData date) { 353 StringBuffer message = new StringBuffer(30); 354 if ( date.year<0 || date.month<0 || date.day<0 355 || date.hour<0 || date.minute<0 || date.second<0) { 356 message.append('-'); 357 } 358 message.append('P'); 359 message.append((date.year < 0?-1:1) * date.year); 360 message.append('Y'); 361 message.append((date.month < 0?-1:1) * date.month); 362 message.append('M'); 363 message.append((date.day < 0?-1:1) * date.day); 364 message.append('D'); 365 message.append('T'); 366 message.append((date.hour < 0?-1:1) * date.hour); 367 message.append('H'); 368 message.append((date.minute < 0?-1:1) * date.minute); 369 message.append('M'); 370 append2(message, (date.second < 0?-1:1) * date.second); 371 message.append('S'); 372 373 return message.toString(); 374 } 375 376 protected Duration getDuration(DateTimeData date) { 377 int sign = 1; 378 if ( date.year<0 || date.month<0 || date.day<0 379 || date.hour<0 || date.minute<0 || date.second<0) { 380 sign = -1; 381 } 382 return datatypeFactory.newDuration(sign == 1, 383 date.year != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.year):null, 384 date.month != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.month):null, 385 date.day != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.day):null, 386 date.hour != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.hour):null, 387 date.minute != DatatypeConstants.FIELD_UNDEFINED?BigInteger.valueOf(sign*date.minute):null, 388 date.second != DatatypeConstants.FIELD_UNDEFINED?new BigDecimal(String.valueOf(sign*date.second)):null); 389 } 390 }