KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > apache > xerces > validators > datatype > DateTimeValidator


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999, 2001 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 2001, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58 package org.enhydra.apache.xerces.validators.datatype;
59
60 import java.util.Hashtable JavaDoc;
61 import java.util.Vector JavaDoc;
62
63
64 /**
65  * This is the base class of all date/time datatype validators.
66  * It implements common code for parsing, validating and comparing datatypes.
67  * Classes that extend this class, must implement parse() method.
68  *
69  * @author Elena Litani
70  * @author Len Berman
71  *
72  * @version $Id: DateTimeValidator.java,v 1.2 2005/01/26 08:28:44 jkjome Exp $
73  */

74
75 public abstract class DateTimeValidator extends AbstractNumericFacetValidator {
76
77     //debugging
78
private static final boolean DEBUG=false;
79     
80     //define shared variables for date/time
81

82     //define constants
83
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     //comparison
87
protected static final short LESS_THAN=-1;
88     protected static final short EQUAL=0;
89     protected static final short GREATER_THAN=1;
90
91     //size for all objects must have the same fields:
92
//CCYY, MM, DD, h, m, s, ms + timeZone
93
protected final static int TOTAL_SIZE = 8;
94
95     //date obj size for gMonth datatype (without time zone): --09--
96
protected final static int MONTH_SIZE = 6;
97
98     //date obj must have at least 6 chars after year (without time zone): "-MM-DD"
99
private final static int YEARMONTH_SIZE = 7;
100
101     //define constants to be used in assigning default values for
102
//all date/time excluding duration
103
protected final static int YEAR=2000;
104     protected final static int MONTH=01;
105     protected final static int DAY = 15;
106
107     //obj to store timeZone for date/time object excluding duration
108
protected int[] timeZone;
109
110     //size of enumeration if any
111
protected int fEnumSize;
112
113     //size of string buffer
114
protected int fEnd;
115     protected int fStart;
116
117     //storage for string value of date/time object
118
protected StringBuffer JavaDoc fBuffer;
119
120     //obj to store all date/time objects with fields:
121
// {CY, M, D, h, m, s, ms, utc}
122
protected int[] fDateValue;
123     private int[] fTempDate;
124
125     //error message buffer
126
protected StringBuffer JavaDoc message;
127     //
128
//REVISIT: more error checking, general debuging/common code clean up
129
//
130

131
132     //default constractor
133

134     public DateTimeValidator () throws InvalidDatatypeFacetException {
135         super( null, null, false ); // Native, No Facets defined, Restriction
136

137     }
138
139     public DateTimeValidator (DatatypeValidator base, Hashtable JavaDoc 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 JavaDoc(TOTAL_SIZE);
150         fBuffer = new StringBuffer JavaDoc(fEnd);
151         timeZone = new int[2];
152     }
153
154     protected void assignAdditionalFacets(String JavaDoc key, Hashtable JavaDoc facets ) throws InvalidDatatypeFacetException{
155         throw new InvalidDatatypeFacetException( getErrorString(DatatypeMessageProvider.ILLEGAL_DATETIME_FACET,
156                                                                 DatatypeMessageProvider.MSG_NONE, new Object JavaDoc[] { key }));
157     }
158     
159     protected int compareValues (Object JavaDoc value1, Object JavaDoc value2) {
160             return compareDates((int[])value1, (int[])value2, true);
161     }
162
163     protected void setMaxInclusive (String JavaDoc value) {
164         fMaxInclusive = parse(value, null);
165     }
166     protected void setMinInclusive (String JavaDoc value) {
167         fMinInclusive = parse(value, null);
168     }
169     
170     protected void setMaxExclusive (String JavaDoc value) {
171         fMaxExclusive = parse(value, null);
172
173     }
174     protected void setMinExclusive (String JavaDoc value) {
175         fMinExclusive = parse(value, null);
176
177     }
178     protected void setEnumeration (Vector JavaDoc 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 JavaDoc)enumeration.elementAt(i), null);
187             }
188             catch ( RuntimeException JavaDoc e ) {
189                 throw new InvalidDatatypeValueException(e.getMessage());
190             }
191         }
192     }
193 }
194
195
196     protected String JavaDoc getMaxInclusive (boolean isBase) {
197         return (isBase)?(dateToString((int[]) ((DateTimeValidator)fBaseValidator).fMaxInclusive))
198         :dateToString((int[])fMaxInclusive);
199     }
200     protected String JavaDoc getMinInclusive (boolean isBase) {
201         return (isBase)?(dateToString((int[]) ((DateTimeValidator)fBaseValidator).fMinInclusive))
202         :dateToString((int[])fMinInclusive);
203     }
204     protected String JavaDoc getMaxExclusive (boolean isBase) {
205         return (isBase)?(dateToString((int[]) ((DateTimeValidator)fBaseValidator).fMaxExclusive))
206         :dateToString((int[])fMaxExclusive);
207     }
208     protected String JavaDoc getMinExclusive (boolean isBase) {
209         return (isBase)?(dateToString((int[]) ((DateTimeValidator)fBaseValidator).fMinExclusive))
210         :dateToString((int[])fMinExclusive);
211     }
212
213     protected void checkContent( String JavaDoc content, Object JavaDoc State, Vector JavaDoc enumer, boolean asBase)
214                                     throws InvalidDatatypeValueException{
215     }
216
217     /**
218      * Implemented by each subtype, calling appropriate function to parse
219      * given date/time
220      *
221      * @param content String value of the date/time
222      * @param date Storage to represent date/time object.
223      * If null - new object will be created, otherwise
224      * date will be reset and reused
225      * @return updated date/time object
226      * @exception Exception
227      */

228     abstract protected int[] parse (String JavaDoc content, int[] date) throws SchemaDateTimeException;
229
230     /**
231      * Validate that a string is a W3C date/time type
232      *
233      * @param content string value of date/time
234      * @param state
235      * @return
236      * @exception InvalidDatatypeValueException
237      */

238     public Object JavaDoc validate(String JavaDoc content, Object JavaDoc state) throws InvalidDatatypeValueException{
239
240         try {
241             resetDateObj(fDateValue);
242             parse(content, fDateValue);
243         }
244         catch ( RuntimeException JavaDoc 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     /**
253      * Validates date object against facet and base datatype
254      *
255      * @param date represents date/time obj
256      * @param content lexical representation of date/time obj
257      * @exception InvalidDatatypeValueException
258      */

259     protected void validateDate (int[] date, String JavaDoc content) throws InvalidDatatypeValueException{
260
261         if ( this.fBaseValidator != null ) {//validate against parent type if any
262
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             //validate against base type
268
((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             // REVISIT: output values for facets in error message
286
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 JavaDoc content1, String JavaDoc content2) {
326         //implement compareDates using the compare() method
327
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 JavaDoc e ) {
334             return -1;
335         
336         }
337     }
338
339
340     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
341         throw new CloneNotSupportedException JavaDoc("clone() is not supported in "+this.getClass().getName());
342     }
343
344
345
346     /**
347      * Compare algorithm described in dateDime (3.2.7).
348      * Duration datatype overwrites this method
349      *
350      * @param date1 normalized date representation of the first value
351      * @param date2 normalized date representation of the second value
352      * @param strict
353      * @return less, greater, less_equal, greater_equal, equal
354      */

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             //compare date1<=date1<=(date2 with time zone -14)
364
//
365
cloneDate(date2); //clones date1 value to global temporary storage: fTempDate
366
timeZone[hh]=14;
367             timeZone[mm]=0;
368             fTempDate[utc]='+';
369             normalize(fTempDate);
370             c1 = compareOrder(date1, fTempDate);
371
372             //compare date1>=(date2 with time zone +14)
373
//
374
cloneDate(date2); //clones date1 value to global temporary storage: fTempDate
375
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             //REVISIT: wait for clarification on this case from schema
386
return(c1!=INDETERMINATE)?c1:c2;
387         }
388         else if ( date2[utc]=='Z' ) {
389
390             //compare (date1 with time zone -14)<=date2
391
//
392
cloneDate(date1); //clones date1 value to global temporary storage: fTempDate
393
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             //compare (date1 with time zone +14)<=date2
407
//
408
cloneDate(date1); //clones date1 value to global temporary storage: fTempDate
409
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             //REVISIT: wait for clarification on this case from schema
422
return(c1!=INDETERMINATE)?c1:c2;
423         }
424         return INDETERMINATE;
425
426     }
427
428
429     /**
430      * Given normalized values, determines order-relation
431      * between give date/time objects.
432      *
433      * @param date1 date/time object
434      * @param date2 date/time object
435      * @return
436      */

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     /**
452      * Parses time hh:mm:ss.sss and time zone if any
453      *
454      * @param start
455      * @param end
456      * @param data
457      * @return
458      * @exception Exception
459      */

460     protected void getTime (int start, int end, int[] data) throws RuntimeException JavaDoc{
461         
462         int stop = start+2;
463         
464         //get hours (hh)
465
data[h]=parseInt(start,stop);
466
467         //get minutes (mm)
468

469         if (fBuffer.charAt(stop++)!=':') {
470                 throw new RuntimeException JavaDoc("Error in parsing time zone" );
471         }
472         start = stop;
473         stop = stop+2;
474         data[m]=parseInt(start,stop);
475
476         //get seconds (ss)
477
if (fBuffer.charAt(stop++)!=':') {
478                 throw new RuntimeException JavaDoc("Error in parsing time zone" );
479         }
480         start = stop;
481         stop = stop+2;
482         data[s]=parseInt(start,stop);
483
484         //get miliseconds (ms)
485
int milisec = indexOf(start, end, '.');
486
487         //find UTC sign if any
488
int sign = findUTCSign((milisec!=-1)?milisec:start, end);
489
490         //parse miliseconds
491
if ( milisec != -1 ) {
492
493             if ( sign<0 ) {
494
495                 //get all digits after "."
496
data[ms]=parseInt(milisec+1,fEnd);
497             }
498             else {
499
500                 //get ms before UTC sign
501
data[ms]=parseInt(milisec+1,sign);
502             }
503
504         }
505
506         //parse UTC time zone (hh:mm)
507
if ( sign>0 ) {
508             getTimeZone(data,sign);
509         }
510     }
511
512
513     /**
514      * Parses date CCYY-MM-DD
515      *
516      * @param start
517      * @param end
518      * @param data
519      * @return
520      * @exception Exception
521      */

522     protected void getDate (int start, int end, int[] date) throws RuntimeException JavaDoc{
523
524         getYearMonth(start, end, date);
525
526         if (fBuffer.charAt(fStart++) !='-') {
527             throw new RuntimeException JavaDoc("CCYY-MM must be followed by '-' sign");
528         }
529         int stop = fStart + 2;
530         date[D]=parseInt(fStart, stop);
531         fStart = stop; //fStart points right after the Day
532
}
533
534     /**
535      * Parses date CCYY-MM
536      *
537      * @param start
538      * @param end
539      * @param data
540      * @return
541      * @exception Exception
542      */

543     protected void getYearMonth (int start, int end, int[] date) throws RuntimeException JavaDoc{
544
545         if ( fBuffer.charAt(0)=='-' ) {
546             // REVISIT: date starts with preceding '-' sign
547
// do we have to do anything with it?
548
//
549
start++;
550         }
551         int i = indexOf(start, end, '-');
552         if ( i==-1 ) throw new RuntimeException JavaDoc("Year separator is missing or misplaced");
553         date[CY]= parseIntYear(i);
554         if (fBuffer.charAt(i)!='-') {
555             throw new RuntimeException JavaDoc("CCYY must be followed by '-' sign");
556         }
557         start = ++i;
558         i = start +2;
559         date[M]=parseInt(start, i);
560         fStart = i; //fStart points right after the MONTH
561
}
562
563
564
565     /**
566      * Shared code from Date and YearMonth datatypes.
567      * Finds if time zone sign is present
568      *
569      * @param end
570      * @param date
571      * @return
572      * @exception Exception
573      */

574     protected void parseTimeZone (int end, int[] date) throws RuntimeException JavaDoc{
575
576         //fStart points right after the date
577

578         if ( fStart<fEnd ) {
579             int sign = findUTCSign(fStart, fEnd);
580             if ( sign<0 ) {
581                 throw new RuntimeException JavaDoc ("Error in month parsing");
582             }
583             else {
584                 getTimeZone(date, sign);
585             }
586         }
587     }
588
589     /**
590      * Parses time zone: 'Z' or {+,-} followed by hh:mm
591      *
592      * @param data
593      * @param sign
594      * @return
595      */

596     protected void getTimeZone (int[] data, int sign) throws RuntimeException JavaDoc{
597         data[utc]=fBuffer.charAt(sign);
598
599         if ( fBuffer.charAt(sign) == 'Z' ) {
600             if (fEnd>(++sign)) {
601                 throw new RuntimeException JavaDoc("Error in parsing time zone");
602             }
603             return;
604         }
605         if ( sign<=(fEnd-6) ) {
606              
607             //parse [hh]
608
int stop = ++sign+2;
609             timeZone[hh]=parseInt(sign, stop);
610             if (fBuffer.charAt(stop++)!=':') {
611                 throw new RuntimeException JavaDoc("Error in parsing time zone" );
612             }
613             
614             //parse [ss]
615
timeZone[mm]=parseInt(stop, stop+2);
616             
617             if ( stop+2!=fEnd ) {
618                 throw new RuntimeException JavaDoc("Error in parsing time zone");
619             }
620             
621         }
622         else {
623             throw new RuntimeException JavaDoc("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     /**
633      * Computes index of given char within StringBuffer
634      *
635      * @param start
636      * @param end
637      * @param ch character to look for in StringBuffer
638      * @return index of ch within StringBuffer
639      */

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     /**
651      * Validates given date/time object accoring to W3C PR Schema
652      * [D.1 ISO 8601 Conventions]
653      *
654      * @param data
655      * @return
656      */

657     protected void validateDateTime (int[] data) {
658
659         //REVISIT: should we throw an exception for not valid dates
660
// or reporting an error message should be sufficient?
661
if ( data[CY]==0 ) {
662             throw new RuntimeException JavaDoc("The year \"0000\" is an illegal year value");
663
664         }
665
666         if ( data[M]<1 || data[M]>12 ) {
667             throw new RuntimeException JavaDoc("The month must have values 1 to 12");
668
669         }
670
671         //validate days
672
if ( data[D]>maxDayInMonthFor(data[CY], data[M]) || data[D]==0 ) {
673             throw new RuntimeException JavaDoc("The day must have values 1 to 31");
674         }
675
676         //validate hours
677
if ( data[h]>24 || data[h]<0 ||
678              (data[h] == 24 && (data[m]!=0 || data[s]!=0 || data[ms]!=0)) ) {
679             throw new RuntimeException JavaDoc("Hour must have values 0-24. If hour is 24, minutes and seconds must both have the value 0.");
680         }
681
682         //validate minutes
683
if ( data[m]>59 || data[m]<0 ) {
684             throw new RuntimeException JavaDoc("Minute must have values 0-59");
685         }
686
687         //validate seconds
688
if ( data[s]>60 || data[s]<0 ) {
689             throw new RuntimeException JavaDoc("Second must have values 0-60");
690
691         }
692
693         //validate time-zone hours
694
if ( Math.abs(timeZone[hh])>14 ||
695              (Math.abs(timeZone[hh]) == 14 && timeZone[mm] != 0) ) {
696             throw new RuntimeException JavaDoc("Time zone should have range -14..+14");
697         }
698
699         //validate time-zone minutes
700
if ( Math.abs(timeZone[mm]) > 59 ) {
701             throw new RuntimeException JavaDoc("Minute must have values 0-59");
702         }
703     }
704
705
706     /**
707      * Return index of UTC char: 'Z', '+', '-'
708      *
709      * @param start
710      * @param end
711      * @return
712      */

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     /**
727      * Given start and end position, parses string value
728      *
729      * @param value string to parse
730      * @param start Start position
731      * @param end end position
732      * @return return integer representation of characters
733      */

734     protected int parseInt (int start, int end)
735     throws NumberFormatException JavaDoc{
736         //REVISIT: more testing on this parsing needs to be done.
737
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 JavaDoc("'"+fBuffer.toString()+"' has wrong format");
746             if ( result < multmin ) throw new NumberFormatException JavaDoc("'"+fBuffer.toString()+"' has wrong format");
747             result *= radix;
748             if ( result < limit + digit ) throw new NumberFormatException JavaDoc("'"+fBuffer.toString()+"' has wrong format");
749             result -= digit;
750
751         }while ( ++i < end );
752         return -result;
753     }
754
755     // parse Year differently to support negative value.
756
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 JavaDoc("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 JavaDoc("'"+fBuffer.toString()+"' has wrong format");
785             if (result < multmin) throw new NumberFormatException JavaDoc("'"+fBuffer.toString()+"' has wrong format");
786             result *= radix;
787             if (result < limit + digit) throw new NumberFormatException JavaDoc("'"+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 JavaDoc("'"+fBuffer.toString()+"' has wrong format");
795         }
796         return -result;
797
798
799
800     }
801
802     /**
803      * normalize dateTime [E Adding durations to dateTimes]
804      * If timezone present - normalize to UTC
805      * If hour is 24 - normalize to start of following day
806      *
807      * @param date CCYY-MM-DDThh:mm:ss+03
808      * @return CCYY-MM-DDThh:mm:ssZ
809      */

810     protected void normalize (int[] date) {
811
812         // REVISIT: we have common code in addDuration() for durations
813
// should consider reorganizing it.
814
//
815

816         //add minutes (from time zone)
817
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         //add hours
833
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     /**
868      * Resets fBuffer to store string representation of
869      * date/time
870      *
871      * @param str Lexical representation of date/time
872      */

873     protected void resetBuffer (String JavaDoc 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     /**
884      * Resets object representation of date/time
885      *
886      * @param data date/time object
887      */

888     protected void resetDateObj (int[] data) {
889         for ( int i=0;i<TOTAL_SIZE;i++ ) {
890             data[i]=0;
891         }
892     }
893
894
895     /**
896      * Given {year,month} computes maximum
897      * number of days for given month
898      *
899      * @param year
900      * @param month
901      * @return
902      */

903     protected int maxDayInMonthFor(int year, int month) {
904         //validate days
905
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     //
927
// help function described in W3C PR Schema [E Adding durations to dateTimes]
928
//
929
protected int mod (int a, int b, int quotient) {
930         //modulo(a, b) = a - fQuotient(a,b)*b
931
return (a - quotient*b) ;
932     }
933     
934     //
935
// help function described in W3C PR Schema [E Adding durations to dateTimes]
936
//
937
protected int fQuotient (int a, int b) {
938         
939         //fQuotient(a, b) = the greatest integer less than or equal to a/b
940
return (int)Math.floor((float)a/b);
941     }
942
943     //
944
// help function described in W3C PR Schema [E Adding durations to dateTimes]
945
//
946
protected int modulo (int temp, int low, int high) {
947         //modulo(a - low, high - low) + low
948
int a = temp - low;
949         int b = high - low;
950         return (mod (a, b, fQuotient(a, b)) + low) ;
951     }
952
953     //
954
// help function described in W3C PR Schema [E Adding durations to dateTimes]
955
//
956
protected int fQuotient (int temp, int low, int high) {
957         //fQuotient(a - low, high - low)
958

959         return fQuotient(temp - low, high - low);
960     }
961
962
963     protected String JavaDoc 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     /**
984      * Use this function to report errors in constructor
985      *
986      * @param msg
987      * @param value
988      */

989     protected void reportError(String JavaDoc msg, String JavaDoc value) {
990         System.err.println("[Error]: " +msg+": Value '"+value+"' is not legal for current datatype");
991     }
992
993
994     //
995
//Private help functions
996
//
997

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