1 57 58 package org.enhydra.apache.xerces.validators.datatype; 59 60 import java.util.Hashtable ; 61 import java.util.Vector ; 62 63 64 74 75 public abstract class DateTimeValidator extends AbstractNumericFacetValidator { 76 77 private static final boolean DEBUG=false; 79 80 82 protected final static int CY = 0, M = 1, D = 2, h = 3, 84 m = 4, s = 5, ms = 6, utc=7, hh=0, mm=1; 85 86 protected static final short LESS_THAN=-1; 88 protected static final short EQUAL=0; 89 protected static final short GREATER_THAN=1; 90 91 protected final static int TOTAL_SIZE = 8; 94 95 protected final static int MONTH_SIZE = 6; 97 98 private final static int YEARMONTH_SIZE = 7; 100 101 protected final static int YEAR=2000; 104 protected final static int MONTH=01; 105 protected final static int DAY = 15; 106 107 protected int[] timeZone; 109 110 protected int fEnumSize; 112 113 protected int fEnd; 115 protected int fStart; 116 117 protected StringBuffer fBuffer; 119 120 protected int[] fDateValue; 123 private int[] fTempDate; 124 125 protected StringBuffer message; 127 131 132 134 public DateTimeValidator () throws InvalidDatatypeFacetException { 135 super( null, null, false ); 137 } 138 139 public DateTimeValidator (DatatypeValidator base, Hashtable facets, boolean derivedByList ) 140 throws InvalidDatatypeFacetException { 141 super (base, facets, derivedByList); 142 } 143 144 protected void initializeValues(){ 145 fDateValue = new int[TOTAL_SIZE]; 146 fTempDate = new int[TOTAL_SIZE]; 147 fEnd = 30; 148 fStart = 0; 149 message = new StringBuffer (TOTAL_SIZE); 150 fBuffer = new StringBuffer (fEnd); 151 timeZone = new int[2]; 152 } 153 154 protected void assignAdditionalFacets(String key, Hashtable facets ) throws InvalidDatatypeFacetException{ 155 throw new InvalidDatatypeFacetException( getErrorString(DatatypeMessageProvider.ILLEGAL_DATETIME_FACET, 156 DatatypeMessageProvider.MSG_NONE, new Object [] { key })); 157 } 158 159 protected int compareValues (Object value1, Object value2) { 160 return compareDates((int[])value1, (int[])value2, true); 161 } 162 163 protected void setMaxInclusive (String value) { 164 fMaxInclusive = parse(value, null); 165 } 166 protected void setMinInclusive (String value) { 167 fMinInclusive = parse(value, null); 168 } 169 170 protected void setMaxExclusive (String value) { 171 fMaxExclusive = parse(value, null); 172 173 } 174 protected void setMinExclusive (String value) { 175 fMinExclusive = parse(value, null); 176 177 } 178 protected void setEnumeration (Vector enumeration) throws InvalidDatatypeValueException{ 179 180 if ( enumeration != null ) { 181 182 fEnumSize = enumeration.size(); 183 fEnumeration = new int[fEnumSize][]; 184 for ( int i=0; i<fEnumSize; i++ ) { 185 try { 186 fEnumeration[i] = parse((String )enumeration.elementAt(i), null); 187 } 188 catch ( RuntimeException e ) { 189 throw new InvalidDatatypeValueException(e.getMessage()); 190 } 191 } 192 } 193 } 194 195 196 protected String getMaxInclusive (boolean isBase) { 197 return (isBase)?(dateToString((int[]) ((DateTimeValidator)fBaseValidator).fMaxInclusive)) 198 :dateToString((int[])fMaxInclusive); 199 } 200 protected String getMinInclusive (boolean isBase) { 201 return (isBase)?(dateToString((int[]) ((DateTimeValidator)fBaseValidator).fMinInclusive)) 202 :dateToString((int[])fMinInclusive); 203 } 204 protected String getMaxExclusive (boolean isBase) { 205 return (isBase)?(dateToString((int[]) ((DateTimeValidator)fBaseValidator).fMaxExclusive)) 206 :dateToString((int[])fMaxExclusive); 207 } 208 protected String getMinExclusive (boolean isBase) { 209 return (isBase)?(dateToString((int[]) ((DateTimeValidator)fBaseValidator).fMinExclusive)) 210 :dateToString((int[])fMinExclusive); 211 } 212 213 protected void checkContent( String content, Object State, Vector enumer, boolean asBase) 214 throws InvalidDatatypeValueException{ 215 } 216 217 228 abstract protected int[] parse (String content, int[] date) throws SchemaDateTimeException; 229 230 238 public Object validate(String content, Object state) throws InvalidDatatypeValueException{ 239 240 try { 241 resetDateObj(fDateValue); 242 parse(content, fDateValue); 243 } 244 catch ( RuntimeException e ) { 245 throw new InvalidDatatypeValueException("Value '"+content+ 246 "' is not legal value for current datatype. " +e.getMessage() ); 247 } 248 validateDate (fDateValue, content); 249 return null; 250 } 251 252 259 protected void validateDate (int[] date, String content) throws InvalidDatatypeValueException{ 260 261 if ( this.fBaseValidator != null ) { if ( (fFacetsDefined & DatatypeValidator.FACET_PATTERN ) != 0 ) { 263 if ( fRegex == null || fRegex.matches( content) == false ) 264 throw new InvalidDatatypeValueException("Value'"+content+ 265 "' does not match regular expression facet " + fRegex.getPattern() ); 266 } 267 ((DateTimeValidator)this.fBaseValidator).validateDate( date, content); 269 if ( (fFacetsDefined & DatatypeValidator.FACET_ENUMERATION ) != 0 ) { 270 int count=0; 271 boolean valid = false; 272 while ( count < fEnumSize ) { 273 if ( compareDates(date, (int[])fEnumeration[count], false) == EQUAL ) { 274 valid = true; 275 break; 276 } 277 count++; 278 } 279 if ( !valid ) { 280 throw new InvalidDatatypeValueException("Value'"+content+ 281 "' does not match enumeration values" ); 282 } 283 } 284 285 short c; 287 if ( fMinInclusive != null ) { 288 289 c = compareDates(date, (int[])fMinInclusive, false); 290 if ( c == LESS_THAN || c == INDETERMINATE ) { 291 throw new InvalidDatatypeValueException("Value '"+content+ 292 "' is less than minInclusive: " +dateToString((int[])fMinInclusive) ); 293 } 294 } 295 if ( fMinExclusive != null ) { 296 297 if ( compareDates(date, (int[])fMinExclusive, true) != GREATER_THAN ) { 298 throw new InvalidDatatypeValueException("Value '"+content+ 299 "' is less than or equal to minExclusive: " +dateToString((int[])fMinExclusive)); 300 } 301 } 302 303 if ( fMaxInclusive != null ) { 304 305 c = compareDates(date, (int[])fMaxInclusive, false ); 306 if ( c == GREATER_THAN || c == INDETERMINATE ) { 307 throw new InvalidDatatypeValueException("Value '"+content+ 308 "' is greater than maxInclusive: " +dateToString((int[])fMaxInclusive) ); 309 } 310 } 311 312 if ( fMaxExclusive != null ) { 313 314 if ( compareDates(date, (int[])fMaxExclusive, true ) != LESS_THAN ) { 315 throw new InvalidDatatypeValueException("Value '"+content+ 316 "' is greater than or equal to maxExlusive: " +dateToString((int[])fMaxExclusive) ); 317 } 318 } 319 } 320 else { 321 return; 322 } 323 324 } 325 public int compare( String content1, String content2) { 326 try{ 328 parse(content1, fDateValue); 329 parse(content2,fTempDate); 330 int result = compareDates(fDateValue, fTempDate, true); 331 return (result==INDETERMINATE)?-1:result; 332 } 333 catch ( RuntimeException e ) { 334 return -1; 335 336 } 337 } 338 339 340 public Object clone() throws CloneNotSupportedException { 341 throw new CloneNotSupportedException ("clone() is not supported in "+this.getClass().getName()); 342 } 343 344 345 346 355 protected short compareDates(int[] date1, int[] date2, boolean strict) { 356 if ( date1[utc]==date2[utc] ) { 357 return compareOrder(date1, date2); 358 } 359 short c1, c2; 360 361 if ( date1[utc]=='Z' ) { 362 363 cloneDate(date2); timeZone[hh]=14; 367 timeZone[mm]=0; 368 fTempDate[utc]='+'; 369 normalize(fTempDate); 370 c1 = compareOrder(date1, fTempDate); 371 372 cloneDate(date2); timeZone[hh]=14; 376 timeZone[mm]=0; 377 fTempDate[utc]='-'; 378 normalize(fTempDate); 379 c2 = compareOrder(date1, fTempDate); 380 381 if ( (c1==LESS_THAN && c2==GREATER_THAN) || 382 (c1==GREATER_THAN && c2==LESS_THAN) ) { 383 return INDETERMINATE; 384 } 385 return(c1!=INDETERMINATE)?c1:c2; 387 } 388 else if ( date2[utc]=='Z' ) { 389 390 cloneDate(date1); timeZone[hh]=14; 394 timeZone[mm]=0; 395 396 fTempDate[utc]='-'; 397 if (DEBUG) { 398 System.out.println("fTempDate=" + dateToString(fTempDate)); 399 } 400 normalize(fTempDate); 401 c1 = compareOrder(fTempDate, date2); 402 if (DEBUG) { 403 System.out.println("date=" + dateToString(date2)); 404 System.out.println("fTempDate=" + dateToString(fTempDate)); 405 } 406 cloneDate(date1); timeZone[hh]=14; 410 timeZone[mm]=0; 411 fTempDate[utc]='+'; 412 normalize(fTempDate); 413 c2 = compareOrder(fTempDate, date2); 414 if (DEBUG) { 415 System.out.println("fTempDate=" + dateToString(fTempDate)); 416 } 417 if ( (c1==LESS_THAN && c2==GREATER_THAN) || 418 (c1==GREATER_THAN && c2==LESS_THAN) ) { 419 return INDETERMINATE; 420 } 421 return(c1!=INDETERMINATE)?c1:c2; 423 } 424 return INDETERMINATE; 425 426 } 427 428 429 437 protected short compareOrder (int[] date1, int[] date2) { 438 439 for ( int i=0;i<TOTAL_SIZE;i++ ) { 440 if ( date1[i]<date2[i] ) { 441 return LESS_THAN; 442 } 443 else if ( date1[i]>date2[i] ) { 444 return GREATER_THAN; 445 } 446 } 447 return EQUAL; 448 } 449 450 451 460 protected void getTime (int start, int end, int[] data) throws RuntimeException { 461 462 int stop = start+2; 463 464 data[h]=parseInt(start,stop); 466 467 469 if (fBuffer.charAt(stop++)!=':') { 470 throw new RuntimeException ("Error in parsing time zone" ); 471 } 472 start = stop; 473 stop = stop+2; 474 data[m]=parseInt(start,stop); 475 476 if (fBuffer.charAt(stop++)!=':') { 478 throw new RuntimeException ("Error in parsing time zone" ); 479 } 480 start = stop; 481 stop = stop+2; 482 data[s]=parseInt(start,stop); 483 484 int milisec = indexOf(start, end, '.'); 486 487 int sign = findUTCSign((milisec!=-1)?milisec:start, end); 489 490 if ( milisec != -1 ) { 492 493 if ( sign<0 ) { 494 495 data[ms]=parseInt(milisec+1,fEnd); 497 } 498 else { 499 500 data[ms]=parseInt(milisec+1,sign); 502 } 503 504 } 505 506 if ( sign>0 ) { 508 getTimeZone(data,sign); 509 } 510 } 511 512 513 522 protected void getDate (int start, int end, int[] date) throws RuntimeException { 523 524 getYearMonth(start, end, date); 525 526 if (fBuffer.charAt(fStart++) !='-') { 527 throw new RuntimeException ("CCYY-MM must be followed by '-' sign"); 528 } 529 int stop = fStart + 2; 530 date[D]=parseInt(fStart, stop); 531 fStart = stop; } 533 534 543 protected void getYearMonth (int start, int end, int[] date) throws RuntimeException { 544 545 if ( fBuffer.charAt(0)=='-' ) { 546 start++; 550 } 551 int i = indexOf(start, end, '-'); 552 if ( i==-1 ) throw new RuntimeException ("Year separator is missing or misplaced"); 553 date[CY]= parseIntYear(i); 554 if (fBuffer.charAt(i)!='-') { 555 throw new RuntimeException ("CCYY must be followed by '-' sign"); 556 } 557 start = ++i; 558 i = start +2; 559 date[M]=parseInt(start, i); 560 fStart = i; } 562 563 564 565 574 protected void parseTimeZone (int end, int[] date) throws RuntimeException { 575 576 578 if ( fStart<fEnd ) { 579 int sign = findUTCSign(fStart, fEnd); 580 if ( sign<0 ) { 581 throw new RuntimeException ("Error in month parsing"); 582 } 583 else { 584 getTimeZone(date, sign); 585 } 586 } 587 } 588 589 596 protected void getTimeZone (int[] data, int sign) throws RuntimeException { 597 data[utc]=fBuffer.charAt(sign); 598 599 if ( fBuffer.charAt(sign) == 'Z' ) { 600 if (fEnd>(++sign)) { 601 throw new RuntimeException ("Error in parsing time zone"); 602 } 603 return; 604 } 605 if ( sign<=(fEnd-6) ) { 606 607 int stop = ++sign+2; 609 timeZone[hh]=parseInt(sign, stop); 610 if (fBuffer.charAt(stop++)!=':') { 611 throw new RuntimeException ("Error in parsing time zone" ); 612 } 613 614 timeZone[mm]=parseInt(stop, stop+2); 616 617 if ( stop+2!=fEnd ) { 618 throw new RuntimeException ("Error in parsing time zone"); 619 } 620 621 } 622 else { 623 throw new RuntimeException ("Error in parsing time zone"); 624 } 625 if ( DEBUG ) { 626 System.out.println("time[hh]="+timeZone[hh] + " time[mm]=" +timeZone[mm]); 627 } 628 } 629 630 631 632 640 protected int indexOf (int start, int end, char ch) { 641 for ( int i=start;i<end;i++ ) { 642 if ( fBuffer.charAt(i) == ch ) { 643 return i; 644 } 645 } 646 return -1; 647 } 648 649 650 657 protected void validateDateTime (int[] data) { 658 659 if ( data[CY]==0 ) { 662 throw new RuntimeException ("The year \"0000\" is an illegal year value"); 663 664 } 665 666 if ( data[M]<1 || data[M]>12 ) { 667 throw new RuntimeException ("The month must have values 1 to 12"); 668 669 } 670 671 if ( data[D]>maxDayInMonthFor(data[CY], data[M]) || data[D]==0 ) { 673 throw new RuntimeException ("The day must have values 1 to 31"); 674 } 675 676 if ( data[h]>24 || data[h]<0 || 678 (data[h] == 24 && (data[m]!=0 || data[s]!=0 || data[ms]!=0)) ) { 679 throw new RuntimeException ("Hour must have values 0-24. If hour is 24, minutes and seconds must both have the value 0."); 680 } 681 682 if ( data[m]>59 || data[m]<0 ) { 684 throw new RuntimeException ("Minute must have values 0-59"); 685 } 686 687 if ( data[s]>60 || data[s]<0 ) { 689 throw new RuntimeException ("Second must have values 0-60"); 690 691 } 692 693 if ( Math.abs(timeZone[hh])>14 || 695 (Math.abs(timeZone[hh]) == 14 && timeZone[mm] != 0) ) { 696 throw new RuntimeException ("Time zone should have range -14..+14"); 697 } 698 699 if ( Math.abs(timeZone[mm]) > 59 ) { 701 throw new RuntimeException ("Minute must have values 0-59"); 702 } 703 } 704 705 706 713 protected int findUTCSign (int start, int end) { 714 int c; 715 for ( int i=start;i<end;i++ ) { 716 c=fBuffer.charAt(i); 717 if ( c == 'Z' || c=='+' || c=='-' ) { 718 return i; 719 } 720 721 } 722 return -1; 723 } 724 725 726 734 protected int parseInt (int start, int end) 735 throws NumberFormatException { 736 int radix=10; 738 int result = 0; 739 int digit=0; 740 int limit = -Integer.MAX_VALUE; 741 int multmin = limit / radix; 742 int i = start; 743 do { 744 digit = Character.digit(fBuffer.charAt(i),radix); 745 if ( digit < 0 ) throw new NumberFormatException ("'"+fBuffer.toString()+"' has wrong format"); 746 if ( result < multmin ) throw new NumberFormatException ("'"+fBuffer.toString()+"' has wrong format"); 747 result *= radix; 748 if ( result < limit + digit ) throw new NumberFormatException ("'"+fBuffer.toString()+"' has wrong format"); 749 result -= digit; 750 751 }while ( ++i < end ); 752 return -result; 753 } 754 755 protected int parseIntYear (int end){ 757 int radix=10; 758 int result = 0; 759 boolean negative = false; 760 int i=0; 761 int limit; 762 int multmin; 763 int digit=0; 764 765 if (fBuffer.charAt(0) == '-'){ 766 negative = true; 767 limit = Integer.MIN_VALUE; 768 i++; 769 } 770 else{ 771 limit = -Integer.MAX_VALUE; 772 } 773 774 int length = end-i; 775 if (length<4 || 776 (length > 4 && fBuffer.charAt(i)=='0')) { 777 throw new RuntimeException ("Leading zeros are required if the year value would otherwise have fewer than four digits; otherwise they are forbidden."); 778 } 779 780 multmin = limit / radix; 781 while (i < end) 782 { 783 digit = Character.digit(fBuffer.charAt(i++),radix); 784 if (digit < 0) throw new NumberFormatException ("'"+fBuffer.toString()+"' has wrong format"); 785 if (result < multmin) throw new NumberFormatException ("'"+fBuffer.toString()+"' has wrong format"); 786 result *= radix; 787 if (result < limit + digit) throw new NumberFormatException ("'"+fBuffer.toString()+"' has wrong format"); 788 result -= digit; 789 } 790 791 if (negative) 792 { 793 if (i > 1) return result; 794 else throw new NumberFormatException ("'"+fBuffer.toString()+"' has wrong format"); 795 } 796 return -result; 797 798 799 800 } 801 802 810 protected void normalize (int[] date) { 811 812 816 int negate = 1; 818 if (date[utc]=='+') { 819 negate = -1; 820 } 821 if ( DEBUG ) { 822 System.out.println("==>date[m]"+date[m]); 823 System.out.println("==>timeZone[mm]" +timeZone[mm]); 824 } 825 int temp = date[m] + negate*timeZone[mm]; 826 int carry = fQuotient (temp, 60); 827 date[m]= mod(temp, 60, carry); 828 829 if ( DEBUG ) { 830 System.out.println("==>carry: " + carry); 831 } 832 temp = date[h] + negate*timeZone[hh] + carry; 834 carry = fQuotient(temp, 24); 835 date[h]=mod(temp, 24, carry); 836 if ( DEBUG ) { 837 System.out.println("==>date[h]"+date[h]); 838 System.out.println("==>carry: " + carry); 839 } 840 841 date[D]=date[D]+carry; 842 843 while ( true ) { 844 temp=maxDayInMonthFor(date[CY], date[M]); 845 if (date[D]<1) { 846 date[D] = date[D] + maxDayInMonthFor(date[CY], date[M]-1); 847 carry=-1; 848 } 849 else if ( date[D]>temp ) { 850 date[D]=date[D]-temp; 851 carry=1; 852 } 853 else { 854 break; 855 } 856 temp=date[M]+carry; 857 date[M]=modulo(temp, 1, 13); 858 date[CY]=date[CY]+fQuotient(temp, 1, 13); 859 } 860 861 if (date[utc] != 0) { 862 date[utc]='Z'; 863 } 864 } 865 866 867 873 protected void resetBuffer (String str) { 874 fBuffer.setLength(0); 875 fStart=fEnd=0; 876 timeZone[hh]=timeZone[mm]=0; 877 fBuffer.append(str); 878 fEnd = fBuffer.length(); 879 880 } 881 882 883 888 protected void resetDateObj (int[] data) { 889 for ( int i=0;i<TOTAL_SIZE;i++ ) { 890 data[i]=0; 891 } 892 } 893 894 895 903 protected int maxDayInMonthFor(int year, int month) { 904 if ( month==4 || month==6 || month==9 || month==11 ) { 906 return 30; 907 } 908 else if ( month==2 ) { 909 if ( isLeapYear(year) ) { 910 return 29; 911 } 912 else { 913 return 28; 914 } 915 } 916 else { 917 return 31; 918 } 919 } 920 921 922 private boolean isLeapYear(int year) { 923 return((year%4 == 0) && ((year%100 != 0) || (year%400 == 0))); 924 } 925 926 protected int mod (int a, int b, int quotient) { 930 return (a - quotient*b) ; 932 } 933 934 protected int fQuotient (int a, int b) { 938 939 return (int)Math.floor((float)a/b); 941 } 942 943 protected int modulo (int temp, int low, int high) { 947 int a = temp - low; 949 int b = high - low; 950 return (mod (a, b, fQuotient(a, b)) + low) ; 951 } 952 953 protected int fQuotient (int temp, int low, int high) { 957 959 return fQuotient(temp - low, high - low); 960 } 961 962 963 protected String dateToString(int[] date) { 964 message.setLength(0); 965 message.append(date[CY]); 966 message.append('-'); 967 message.append(date[M]); 968 message.append('-'); 969 message.append(date[D]); 970 message.append('T'); 971 message.append(date[h]); 972 message.append(':'); 973 message.append(date[m]); 974 message.append(':'); 975 message.append(date[s]); 976 message.append('.'); 977 message.append(date[ms]); 978 message.append((char)date[utc]); 979 return message.toString(); 980 } 981 982 983 989 protected void reportError(String msg, String value) { 990 System.err.println("[Error]: " +msg+": Value '"+value+"' is not legal for current datatype"); 991 } 992 993 994 998 private void cloneDate (int[] finalValue) { 999 resetDateObj(fTempDate); 1000 for ( int i=0;i<TOTAL_SIZE;i++ ) { 1001 fTempDate[i]=finalValue[i]; 1002 } 1003 } 1004 1005} 1006 | Popular Tags |