KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > emf > ecore > xml > type > internal > XMLCalendar


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

74
75 package org.eclipse.emf.ecore.xml.type.internal;
76
77
78 import java.text.DateFormat JavaDoc;
79 import java.text.FieldPosition JavaDoc;
80 import java.text.ParseException JavaDoc;
81 import java.text.SimpleDateFormat JavaDoc;
82 import java.util.Date JavaDoc;
83 import java.util.TimeZone JavaDoc;
84
85 import org.eclipse.emf.common.util.WrappedException;
86 import org.eclipse.emf.ecore.xml.type.InvalidDatatypeValueException;
87 import org.eclipse.emf.ecore.xml.type.internal.DataValue.TypeValidator;
88
89 /**
90  * Representation for the <a HREF="http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/">W3C XML Schema 1.0</a>
91  * dateTime, time, date, gYearMonth, gYear, gMonthDay, gDay, gMonth datatypes.
92  *
93  * NOTE: this class is for internal use only.
94  * Later this class will be replaced by JAXP 1.3 javax.xml.datatype.XMLGregorianCalendar class.
95  * This class is based on Apache Xerces2 2.6.2 parser implementation of date/time validation.
96  */

97 public final class XMLCalendar
98 {
99   public final static short DATETIME = 0;
100
101   public final static short TIME = 1;
102
103   public final static short DATE = 2;
104
105   public final static short GYEARMONTH = 3;
106
107   public final static short GYEAR = 4;
108
109   public final static short GMONTHDAY = 5;
110
111   public final static short GDAY = 6;
112
113   public final static short GMONTH = 7;
114   
115   public final static int EQUALS = 0;
116   public final static int LESS_THAN = -1;
117   public final static int GREATER_THAN = 1;
118   public final static int INDETERMINATE = 2;
119   
120
121   //define shared variables for date/time
122

123   //define constants
124
protected final static int CY = 0, M = 1, D = 2, h = 3, m = 4, s = 5, ms = 6, utc = 7, hh = 0, mm = 1;
125
126   //size for all objects must have the same fields:
127
//CCYY, MM, DD, h, m, s, ms + timeZone
128
protected final static int TOTAL_SIZE = 8;
129
130   //size without time zone: ---09
131
private final static int DAY_SIZE = 5;
132
133   //size without time zone: --MM-DD
134
private final static int MONTHDAY_SIZE = 7;
135
136   //define constants to be used in assigning default values for
137
//all date/time excluding duration
138
protected final static int YEAR = 2000;
139
140   protected final static int MONTH = 01;
141
142   protected final static int DAY = 15;
143
144   private int[] dateValue;
145
146   final short dataType;
147
148   private Date JavaDoc date;
149
150   protected static final DateFormat JavaDoc [] EDATE_FORMATS =
151   {
152     new SafeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'.'S'Z'"),
153     new SafeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"),
154     new SafeSimpleDateFormat("yyyy-MM-dd")
155   };
156
157   {
158     EDATE_FORMATS[0].setTimeZone(TimeZone.getTimeZone("GMT"));
159   }
160
161   public XMLCalendar(String JavaDoc value, short datatype)
162   {
163     if (value == null || value.length() == 0)
164     {
165       throw new InvalidDatatypeValueException("Incomplete value");
166     }
167
168     this.dataType = datatype;
169     switch (dataType)
170     {
171       case DATETIME:
172         {
173           this.dateValue = parseDateTime(value);
174           break;
175         }
176       case TIME:
177         {
178           this.dateValue = parseTime(value);
179           break;
180         }
181       case DATE:
182         {
183           this.dateValue = parseDate(value);
184           break;
185         }
186       case GYEAR:
187         {
188           this.dateValue = parseYear(value);
189           break;
190         }
191       case GYEARMONTH:
192         {
193           this.dateValue = parseYearMonth(value);
194           break;
195         }
196       case GMONTHDAY:
197         {
198           this.dateValue = parseMonthDay(value);
199           break;
200         }
201       case GMONTH:
202         {
203           this.dateValue = parseMonth(value);
204           break;
205         }
206       case GDAY:
207         {
208           this.dateValue = parseDay(value);
209           break;
210         }
211       default:
212         throw new IllegalArgumentException JavaDoc("Illegal datatype value");
213     }
214   }
215
216   public XMLCalendar(Date JavaDoc date, short dataType)
217   {
218     this.date = date;
219     this.dataType = dataType;
220   }
221
222   public boolean equals(Object JavaDoc obj)
223   {
224     if (!(obj instanceof XMLCalendar))
225     {
226       return false;
227     }
228
229     XMLCalendar xmlCalendarObj = (XMLCalendar)obj;
230     if (dataType != xmlCalendarObj.dataType)
231     {
232       return false;
233     }
234
235     return compare(this, (XMLCalendar)obj) == EQUALS;
236   }
237
238   public int hashCode()
239   {
240     int hashCode = dataType;
241     int [] dateValue = getDateValue();
242     for (int i=0;i<TOTAL_SIZE;i++)
243     {
244       hashCode^=dateValue[i];
245     }
246     return hashCode;
247   }
248   
249   public String JavaDoc toString()
250   {
251     switch (dataType)
252     {
253       case DATETIME: return dateTimeToString();
254       case TIME: return timeToString();
255       case DATE: return dateToString();
256       case GYEAR: return yearToString();
257       case GYEARMONTH: return yearMonthToString();
258       case GMONTHDAY: return monthDayToString();
259       case GMONTH: return monthToString();
260       case GDAY: return dayToString();
261     }
262     return null;
263   }
264
265   // the parameters are in compiled form (from getActualValue)
266
public static int compare(XMLCalendar value1, XMLCalendar value2)
267   {
268     return (value1.dataType != value2.dataType) ? INDETERMINATE : compareDates(value1.getDateValue(), value2.getDateValue(), true);
269   }//compare()
270

271   protected int[] getDateValue()
272   {
273     if (dateValue == null)
274     {
275       dateValue = parseDateTime(XMLCalendar.EDATE_FORMATS[1].format(date));
276     }
277     return dateValue;
278   }
279
280   public Date JavaDoc getDate()
281   {
282     if (date == null)
283     {
284       try
285       {
286         if (dataType == XMLCalendar.DATETIME)
287         {
288           date = XMLCalendar.EDATE_FORMATS[0].parse(dateTimeToString());
289         }
290         else if (dataType == XMLCalendar.DATE)
291         {
292           date = XMLCalendar.EDATE_FORMATS[2].parse(dateToString());
293         }
294       }
295       catch (Exception JavaDoc e)
296       {
297         throw new WrappedException(e);
298       }
299     }
300     return date;
301   }
302
303   /**
304    * Compare algorithm described in dateDime (3.2.7).
305    * Duration datatype overwrites this method
306    *
307    * @param date1 normalized date representation of the first value
308    * @param date2 normalized date representation of the second value
309    * @param strict
310    * @return less, greater, less_equal, greater_equal, equal
311    */

312   private static int compareDates(int[] date1, int[] date2, boolean strict)
313   {
314     if (date1[utc] == date2[utc])
315     {
316       return compareOrder(date1, date2);
317     }
318     short c1, c2;
319
320     int[] tempDate = new int [TOTAL_SIZE];
321     int[] timeZone = new int [2];
322
323     if (date1[utc] == 'Z')
324     {
325
326       //compare date1<=date1<=(date2 with time zone -14)
327
//
328
cloneDate(date2, tempDate); //clones date1 value to global temporary storage: fTempDate
329
timeZone[hh] = 14;
330       timeZone[mm] = 0;
331       tempDate[utc] = '+';
332       normalize(tempDate, timeZone);
333       c1 = compareOrder(date1, tempDate);
334       if (c1 == TypeValidator.LESS_THAN)
335         return c1;
336
337       //compare date1>=(date2 with time zone +14)
338
//
339
cloneDate(date2, tempDate); //clones date1 value to global temporary storage: tempDate
340
timeZone[hh] = 14;
341       timeZone[mm] = 0;
342       tempDate[utc] = '-';
343       normalize(tempDate, timeZone);
344       c2 = compareOrder(date1, tempDate);
345       if (c2 == TypeValidator.GREATER_THAN)
346         return c2;
347
348       return TypeValidator.INDETERMINATE;
349     }
350     else if (date2[utc] == 'Z')
351     {
352
353       //compare (date1 with time zone -14)<=date2
354
//
355
cloneDate(date1, tempDate); //clones date1 value to global temporary storage: tempDate
356
timeZone[hh] = 14;
357       timeZone[mm] = 0;
358       tempDate[utc] = '-';
359       
360       normalize(tempDate, timeZone);
361       c1 = compareOrder(tempDate, date2);
362
363       if (c1 == TypeValidator.LESS_THAN)
364         return c1;
365
366       //compare (date1 with time zone +14)<=date2
367
//
368
cloneDate(date1, tempDate); //clones date1 value to global temporary storage: tempDate
369
timeZone[hh] = 14;
370       timeZone[mm] = 0;
371       tempDate[utc] = '+';
372       normalize(tempDate, timeZone);
373       c2 = compareOrder(tempDate, date2);
374
375       if (c2 == TypeValidator.GREATER_THAN)
376         return c2;
377
378       return TypeValidator.INDETERMINATE;
379     }
380     return TypeValidator.INDETERMINATE;
381
382   }
383
384   /**
385    * Given normalized values, determines order-relation
386    * between give date/time objects.
387    *
388    * @param date1 date/time object
389    * @param date2 date/time object
390    * @return 0 if date1 and date2 are equal, a value less than 0 if date1 is less than date2, a value greater than 0 if date1 is greater than date2
391    */

392   protected static short compareOrder(int[] date1, int[] date2)
393   {
394
395     for (int i = 0; i < TOTAL_SIZE; i++)
396     {
397       if (date1[i] < date2[i])
398       {
399         return -1;
400       }
401       else if (date1[i] > date2[i])
402       {
403         return 1;
404       }
405     }
406     return 0;
407   }
408
409   /**
410    * Parses time hh:mm:ss.sss and time zone if any
411    */

412   protected static void getTime(String JavaDoc buffer, int start, int end, int[] data, int[] timeZone)
413   {
414
415     int stop = start + 2;
416
417     //get hours (hh)
418
data[h] = parseInt(buffer, start, stop);
419
420     //get minutes (mm)
421

422     if (buffer.charAt(stop++) != ':')
423     {
424       throw new InvalidDatatypeValueException("Error in parsing time zone");
425     }
426     start = stop;
427     stop = stop + 2;
428     data[m] = parseInt(buffer, start, stop);
429
430     //get seconds (ss)
431
if (buffer.charAt(stop++) != ':')
432     {
433       throw new InvalidDatatypeValueException("Error in parsing time zone");
434     }
435     start = stop;
436     stop = stop + 2;
437     data[s] = parseInt(buffer, start, stop);
438
439     if (stop == end)
440       return;
441
442     //get miliseconds (ms)
443
start = stop;
444     int milisec = buffer.charAt(start) == '.' ? start : -1;
445
446     //find UTC sign if any
447
int sign = findUTCSign(buffer, start, end);
448
449     //parse miliseconds
450
if (milisec != -1)
451     {
452       // The end of millisecond part is between . and
453
// either the end of the UTC sign
454
start = sign < 0 ? end : sign;
455       data[ms] = parseInt(buffer, milisec + 1, start);
456     }
457
458     //parse UTC time zone (hh:mm)
459
if (sign > 0)
460     {
461       if (start != sign)
462         throw new InvalidDatatypeValueException("Error in parsing time zone");
463       getTimeZone(buffer, data, sign, end, timeZone);
464     }
465     else if (start != end)
466     {
467       throw new InvalidDatatypeValueException("Error in parsing time zone");
468     }
469   }
470
471   /**
472    * Parses date CCYY-MM-DD
473    *
474    * @param start
475    * @param end
476    * @param date
477    */

478   protected static int getDate(String JavaDoc buffer, int start, int end, int[] date)
479   {
480
481     start = getYearMonth(buffer, start, end, date);
482
483     if (buffer.charAt(start++) != '-')
484     {
485       throw new InvalidDatatypeValueException("CCYY-MM must be followed by '-' sign");
486     }
487     int stop = start + 2;
488     date[D] = parseInt(buffer, start, stop);
489     return stop;
490   }
491
492   /**
493    * Parses date CCYY-MM
494    *
495    * @param start
496    * @param end
497    * @param date
498    */

499   protected static int getYearMonth(String JavaDoc buffer, int start, int end, int[] date)
500   {
501
502     if (buffer.charAt(0) == '-')
503     {
504       // REVISIT: date starts with preceding '-' sign
505
// do we have to do anything with it?
506
//
507
start++;
508     }
509     int i = indexOf(buffer, start, end, '-');
510     if (i == -1)
511       throw new InvalidDatatypeValueException("Year separator is missing or misplaced");
512     int length = i - start;
513     if (length < 4)
514     {
515       throw new InvalidDatatypeValueException("Year must have 'CCYY' format");
516     }
517     else if (length > 4 && buffer.charAt(start) == '0')
518     {
519       throw new InvalidDatatypeValueException(
520           "Leading zeros are required if the year value would otherwise have fewer than four digits; otherwise they are forbidden");
521     }
522     date[CY] = parseIntYear(buffer, i);
523     if (buffer.charAt(i) != '-')
524     {
525       throw new InvalidDatatypeValueException("CCYY must be followed by '-' sign");
526     }
527     start = ++i;
528     i = start + 2;
529     date[M] = parseInt(buffer, start, i);
530     return i; //fStart points right after the MONTH
531
}
532
533   /**
534    * Shared code from Date and YearMonth datatypes.
535    * Finds if time zone sign is present
536    *
537    * @param end
538    * @param date
539    */

540   protected static void parseTimeZone(String JavaDoc buffer, int start, int end, int[] date, int[] timeZone)
541   {
542
543     //fStart points right after the date
544

545     if (start < end)
546     {
547       int sign = findUTCSign(buffer, start, end);
548       if (sign < 0)
549       {
550         throw new InvalidDatatypeValueException("Error in month parsing");
551       }
552       else
553       {
554         getTimeZone(buffer, date, sign, end, timeZone);
555       }
556     }
557   }
558
559   /**
560    * Parses time zone: 'Z' or {+,-} followed by hh:mm
561    *
562    * @param data
563    * @param sign
564    */

565   protected static void getTimeZone(String JavaDoc buffer, int[] data, int sign, int end, int[] timeZone)
566   {
567     data[utc] = buffer.charAt(sign);
568
569     if (buffer.charAt(sign) == 'Z')
570     {
571       if (end > (++sign))
572       {
573         throw new InvalidDatatypeValueException("Error in parsing time zone");
574       }
575       return;
576     }
577     if (sign <= (end - 6))
578     {
579
580       //parse [hh]
581
int stop = ++sign + 2;
582       timeZone[hh] = parseInt(buffer, sign, stop);
583       if (buffer.charAt(stop++) != ':')
584       {
585         throw new InvalidDatatypeValueException("Error in parsing time zone");
586       }
587
588       //parse [ss]
589
timeZone[mm] = parseInt(buffer, stop, stop + 2);
590
591       if (stop + 2 != end)
592       {
593         throw new InvalidDatatypeValueException("Error in parsing time zone");
594       }
595
596     }
597     else
598     {
599       throw new InvalidDatatypeValueException("Error in parsing time zone");
600     }
601   }
602
603   /**
604    * Computes index of given char within StringBuffer
605    *
606    * @param start
607    * @param end
608    * @param ch character to look for in StringBuffer
609    * @return index of ch within StringBuffer
610    */

611   protected static int indexOf(String JavaDoc buffer, int start, int end, char ch)
612   {
613     for (int i = start; i < end; i++)
614     {
615       if (buffer.charAt(i) == ch)
616       {
617         return i;
618       }
619     }
620     return -1;
621   }
622
623   /**
624    * Validates given date/time object accoring to W3C PR Schema
625    * [D.1 ISO 8601 Conventions]
626    *
627    * @param data
628    */

629   protected static void validateDateTime(int[] data, int[] timeZone)
630   {
631
632     //REVISIT: should we throw an exception for not valid dates
633
// or reporting an error message should be sufficient?
634
if (data[CY] == 0)
635     {
636       throw new InvalidDatatypeValueException("The year \"0000\" is an illegal year value");
637
638     }
639
640     if (data[M] < 1 || data[M] > 12)
641     {
642       throw new InvalidDatatypeValueException("The month must have values 1 to 12");
643
644     }
645
646     //validate days
647
if (data[D] > maxDayInMonthFor(data[CY], data[M]) || data[D] < 1)
648     {
649       throw new InvalidDatatypeValueException("The day must have values 1 to 31");
650     }
651
652     //validate hours
653
if (data[h] > 23 || data[h] < 0)
654     {
655       if (data[h] == 24 && data[m] == 0 && data[s] == 0 && data[ms] == 0)
656       {
657         data[h] = 0;
658         if (++data[D] > maxDayInMonthFor(data[CY], data[M]))
659         {
660           data[D] = 1;
661           if (++data[M] > 12)
662           {
663             data[M] = 1;
664             if (++data[CY] == 0)
665               data[CY] = 1;
666           }
667         }
668       }
669       else
670       {
671         throw new InvalidDatatypeValueException("Hour must have values 0-23, unless 24:00:00");
672       }
673     }
674
675     //validate
676
if (data[m] > 59 || data[m] < 0)
677     {
678       throw new InvalidDatatypeValueException("Minute must have values 0-59");
679     }
680
681     //validate
682
if (data[s] > 60 || data[s] < 0)
683     {
684       throw new InvalidDatatypeValueException("Second must have values 0-60");
685
686     }
687
688     //validate
689
if (timeZone[hh] > 14 || timeZone[hh] < -14)
690     {
691       throw new InvalidDatatypeValueException("Time zone should have range -14..+14");
692     }
693
694     //validate
695
if (timeZone[mm] > 59 || timeZone[mm] < -59)
696     {
697       throw new InvalidDatatypeValueException("Minute must have values 0-59");
698     }
699   }
700
701   /**
702    * Return index of UTC char: 'Z', '+', '-'
703    *
704    * @param start
705    * @param end
706    * @return index of the UTC character that was found
707    */

708   private static int findUTCSign(String JavaDoc buffer, int start, int end)
709   {
710     int c;
711     for (int i = start; i < end; i++)
712     {
713       c = buffer.charAt(i);
714       if (c == 'Z' || c == '+' || c == '-')
715       {
716         return i;
717       }
718
719     }
720     return -1;
721   }
722
723   /**
724    * Given start and end position, parses string value
725    *
726    * @param buffer string to parse
727    * @param start Start position
728    * @param end end position
729    * @return return integer representation of characters
730    */

731   protected static int parseInt(String JavaDoc buffer, int start, int end) throws NumberFormatException JavaDoc
732   {
733     //REVISIT: more testing on this parsing needs to be done.
734
int radix = 10;
735     int result = 0;
736     int digit = 0;
737     int limit = -Integer.MAX_VALUE;
738     int multmin = limit / radix;
739     int i = start;
740     do
741     {
742       digit = TypeValidator.getDigit(buffer.charAt(i));
743       if (digit < 0)
744         throw new NumberFormatException JavaDoc("'" + buffer.toString() + "' has wrong format");
745       if (result < multmin)
746         throw new NumberFormatException JavaDoc("'" + buffer.toString() + "' has wrong format");
747       result *= radix;
748       if (result < limit + digit)
749         throw new NumberFormatException JavaDoc("'" + buffer.toString() + "' has wrong format");
750       result -= digit;
751
752     }
753     while (++i < end);
754     return -result;
755   }
756
757   // parse Year differently to support negative value.
758
protected static int parseIntYear(String JavaDoc buffer, int end)
759   {
760     int radix = 10;
761     int result = 0;
762     boolean negative = false;
763     int i = 0;
764     int limit;
765     int multmin;
766     int digit = 0;
767
768     if (buffer.charAt(0) == '-')
769     {
770       negative = true;
771       limit = Integer.MIN_VALUE;
772       i++;
773
774     }
775     else
776     {
777       limit = -Integer.MAX_VALUE;
778     }
779     multmin = limit / radix;
780     while (i < end)
781     {
782       digit = TypeValidator.getDigit(buffer.charAt(i++));
783       if (digit < 0)
784         throw new NumberFormatException JavaDoc("'" + buffer.toString() + "' has wrong format");
785       if (result < multmin)
786         throw new NumberFormatException JavaDoc("'" + buffer.toString() + "' has wrong format");
787       result *= radix;
788       if (result < limit + digit)
789         throw new NumberFormatException JavaDoc("'" + buffer.toString() + "' has wrong format");
790       result -= digit;
791     }
792
793     if (negative)
794     {
795       if (i > 1)
796         return result;
797       else
798         throw new NumberFormatException JavaDoc("'" + buffer.toString() + "' has wrong format");
799     }
800     return -result;
801
802   }
803
804   /**
805    * If timezone present - normalize dateTime [E Adding durations to dateTimes]
806    *
807    * @param date CCYY-MM-DDThh:mm:ss+03
808    * @return CCYY-MM-DDThh:mm:ssZ
809    */

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

817     //add minutes (from time zone)
818
int negate = 1;
819     if (date[utc] == '+')
820     {
821       negate = -1;
822     }
823     int temp = date[m] + negate * timeZone[mm];
824     int carry = fQuotient(temp, 60);
825     date[m] = mod(temp, 60, carry);
826
827     //add hours
828
temp = date[h] + negate * timeZone[hh] + carry;
829     carry = fQuotient(temp, 24);
830     date[h] = mod(temp, 24, carry);
831
832     date[D] = date[D] + carry;
833
834     while (true)
835     {
836       temp = maxDayInMonthFor(date[CY], date[M]);
837       if (date[D] < 1)
838       {
839         date[D] = date[D] + maxDayInMonthFor(date[CY], date[M] - 1);
840         carry = -1;
841       }
842       else if (date[D] > temp)
843       {
844         date[D] = date[D] - temp;
845         carry = 1;
846       }
847       else
848       {
849         break;
850       }
851       temp = date[M] + carry;
852       date[M] = modulo(temp, 1, 13);
853       date[CY] = date[CY] + fQuotient(temp, 1, 13);
854     }
855     date[utc] = 'Z';
856   }
857
858   /**
859    * Resets object representation of date/time
860    *
861    * @param data date/time object
862    */

863   protected static void resetDateObj(int[] data)
864   {
865     for (int i = 0; i < TOTAL_SIZE; i++)
866     {
867       data[i] = 0;
868     }
869   }
870
871   /**
872    * Given {year,month} computes maximum
873    * number of days for given month
874    *
875    * @param year
876    * @param month
877    * @return integer containg the number of days in a given month
878    */

879   protected static int maxDayInMonthFor(int year, int month)
880   {
881     //validate days
882
if (month == 4 || month == 6 || month == 9 || month == 11)
883     {
884       return 30;
885     }
886     else if (month == 2)
887     {
888       if (isLeapYear(year))
889       {
890         return 29;
891       }
892       else
893       {
894         return 28;
895       }
896     }
897     else
898     {
899       return 31;
900     }
901   }
902
903   private static boolean isLeapYear(int year)
904   {
905
906     //REVISIT: should we take care about Julian calendar?
907
return ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)));
908   }
909
910   //
911
// help function described in W3C PR Schema [E Adding durations to dateTimes]
912
//
913
protected static int mod(int a, int b, int quotient)
914   {
915     //modulo(a, b) = a - fQuotient(a,b)*b
916
return (a - quotient * b);
917   }
918
919   //
920
// help function described in W3C PR Schema [E Adding durations to dateTimes]
921
//
922
protected static int fQuotient(int a, int b)
923   {
924
925     //fQuotient(a, b) = the greatest integer less than or equal to a/b
926
return (int)Math.floor((float)a / b);
927   }
928
929   //
930
// help function described in W3C PR Schema [E Adding durations to dateTimes]
931
//
932
protected static int modulo(int temp, int low, int high)
933   {
934     //modulo(a - low, high - low) + low
935
int a = temp - low;
936     int b = high - low;
937     return (mod(a, b, fQuotient(a, b)) + low);
938   }
939
940   //
941
// help function described in W3C PR Schema [E Adding durations to dateTimes]
942
//
943
protected static int fQuotient(int temp, int low, int high)
944   {
945     //fQuotient(a - low, high - low)
946
return fQuotient(temp - low, high - low);
947   }
948
949   private String JavaDoc dateTimeToString()
950   {
951     if (date != null)
952     {
953       return XMLCalendar.EDATE_FORMATS[1].format(date);
954     }
955     StringBuffer JavaDoc message = new StringBuffer JavaDoc(25);
956     append(message, dateValue[CY], 4);
957     message.append('-');
958     append(message, dateValue[M], 2);
959     message.append('-');
960     append(message, dateValue[D], 2);
961     message.append('T');
962     append(message, dateValue[h], 2);
963     message.append(':');
964     append(message, dateValue[m], 2);
965     message.append(':');
966     append(message, dateValue[s], 2);
967     message.append('.');
968     message.append(dateValue[ms]);
969     append(message, (char)dateValue[utc], 0);
970     return message.toString();
971   }
972
973   private String JavaDoc dateToString()
974   {
975     StringBuffer JavaDoc message = new StringBuffer JavaDoc(25);
976     int[] dateValue = getDateValue();
977     append(message, dateValue[CY], 4);
978     message.append('-');
979     append(message, dateValue[M], 2);
980     message.append('-');
981     append(message, dateValue[D], 2);
982     append(message, (char)dateValue[utc], 0);
983     return message.toString();
984   }
985
986   private String JavaDoc dayToString()
987   {
988     StringBuffer JavaDoc message = new StringBuffer JavaDoc(6);
989     message.append('-');
990     message.append('-');
991     message.append('-');
992     append(message, dateValue[D], 2);
993     append(message, (char)dateValue[utc], 0);
994     return message.toString();
995   }
996
997   private String JavaDoc monthDayToString()
998   {
999     StringBuffer JavaDoc message = new StringBuffer JavaDoc(8);
1000    message.append('-');
1001    message.append('-');
1002    append(message, dateValue[M], 2);
1003    message.append('-');
1004    append(message, dateValue[D], 2);
1005    append(message, (char)dateValue[utc], 0);
1006    return message.toString();
1007  }
1008
1009  private String JavaDoc monthToString()
1010  {
1011    StringBuffer JavaDoc message = new StringBuffer JavaDoc(5);
1012    message.append('-');
1013    message.append('-');
1014    append(message, dateValue[M], 2);
1015    append(message, (char)dateValue[utc], 0);
1016    return message.toString();
1017  }
1018
1019  private String JavaDoc timeToString()
1020  {
1021    StringBuffer JavaDoc message = new StringBuffer JavaDoc(16);
1022    append(message, dateValue[h], 2);
1023    message.append(':');
1024    append(message, dateValue[m], 2);
1025    message.append(':');
1026    append(message, dateValue[s], 2);
1027    message.append('.');
1028    message.append(dateValue[ms]);
1029    append(message, (char)dateValue[utc], 0);
1030    return message.toString();
1031  }
1032
1033  private String JavaDoc yearToString()
1034  {
1035    StringBuffer JavaDoc message = new StringBuffer JavaDoc(5);
1036    append(message, dateValue[CY], 4);
1037    append(message, (char)dateValue[utc], 0);
1038    return message.toString();
1039  }
1040
1041  private String JavaDoc yearMonthToString()
1042  {
1043    StringBuffer JavaDoc message = new StringBuffer JavaDoc(25);
1044    append(message, dateValue[CY], 4);
1045    message.append('-');
1046    append(message, dateValue[M], 2);
1047    append(message, (char)dateValue[utc], 0);
1048    return message.toString();
1049  }
1050
1051  private static void append(StringBuffer JavaDoc message, int value, int nch)
1052  {
1053    if (value < 0)
1054    {
1055      message.append('-');
1056      value = -value;
1057    }
1058    if (nch == 4)
1059    {
1060      if (value < 10)
1061        message.append("000");
1062      else if (value < 100)
1063        message.append("00");
1064      else if (value < 1000)
1065        message.append("0");
1066      message.append(value);
1067    }
1068    else if (nch == 2)
1069    {
1070      if (value < 10)
1071        message.append('0');
1072      message.append(value);
1073    }
1074    else
1075    {
1076      if (value != 0)
1077        message.append((char)value);
1078    }
1079  }
1080
1081  //
1082
//Private help functions
1083
//
1084

1085  private static void cloneDate(int[] finalValue, int[] tempDate)
1086  {
1087    System.arraycopy(finalValue, 0, tempDate, 0, TOTAL_SIZE);
1088  }
1089
1090  private int[] parseDateTime(String JavaDoc str) throws InvalidDatatypeValueException
1091  {
1092    int len = str.length();
1093    int[] date = new int [TOTAL_SIZE];
1094    int[] timeZone = new int [2];
1095
1096    int end = indexOf(str, 0, len, 'T');
1097
1098    // both time and date
1099
getDate(str, 0, end, date);
1100    getTime(str, end + 1, len, date, timeZone);
1101
1102    //validate and normalize
1103
validateDateTime(date, timeZone);
1104
1105    if (date[utc] != 0 && date[utc] != 'Z')
1106    {
1107      normalize(date, timeZone);
1108    }
1109    return date;
1110  }
1111
1112  private int[] parseDate(String JavaDoc str) throws InvalidDatatypeValueException
1113  {
1114    int len = str.length();
1115    int[] date = new int [TOTAL_SIZE];
1116    int[] timeZone = new int [2];
1117
1118    int end = getDate(str, 0, len, date);
1119    parseTimeZone(str, end, len, date, timeZone);
1120
1121    //validate and normalize
1122
validateDateTime(date, timeZone);
1123
1124    if (date[utc] != 0 && date[utc] != 'Z')
1125    {
1126      normalize(date, timeZone);
1127    }
1128    return date;
1129  }
1130
1131  private int[] parseDay(String JavaDoc str) throws InvalidDatatypeValueException
1132  {
1133    int len = str.length();
1134    int[] date = new int [TOTAL_SIZE];
1135    int[] timeZone = new int [2];
1136
1137    if (str.charAt(0) != '-' || str.charAt(1) != '-' || str.charAt(2) != '-')
1138    {
1139      throw new InvalidDatatypeValueException("Error in day parsing");
1140    }
1141
1142    //initialize values
1143
date[CY] = YEAR;
1144    date[M] = MONTH;
1145
1146    date[D] = parseInt(str, 3, 5);
1147
1148    if (DAY_SIZE < len)
1149    {
1150      int sign = findUTCSign(str, DAY_SIZE, len);
1151      if (sign < 0)
1152      {
1153        throw new InvalidDatatypeValueException("Error in day parsing");
1154      }
1155      else
1156      {
1157        getTimeZone(str, date, sign, len, timeZone);
1158      }
1159    }
1160
1161    //validate and normalize
1162
validateDateTime(date, timeZone);
1163
1164    if (date[utc] != 0 && date[utc] != 'Z')
1165    {
1166      normalize(date, timeZone);
1167    }
1168    return date;
1169  }
1170
1171  private int[] parseMonthDay(String JavaDoc str) throws InvalidDatatypeValueException
1172  {
1173    int len = str.length();
1174    int[] date = new int [TOTAL_SIZE];
1175    int[] timeZone = new int [2];
1176
1177    //initialize
1178
date[CY] = YEAR;
1179
1180    if (str.charAt(0) != '-' || str.charAt(1) != '-')
1181    {
1182      throw new InvalidDatatypeValueException("Invalid format for gMonthDay: " + str);
1183    }
1184    date[M] = parseInt(str, 2, 4);
1185    int start = 4;
1186
1187    if (str.charAt(start++) != '-')
1188    {
1189      throw new InvalidDatatypeValueException("Invalid format for gMonthDay: " + str);
1190    }
1191
1192    date[D] = parseInt(str, start, start + 2);
1193
1194    if (MONTHDAY_SIZE < len)
1195    {
1196      int sign = findUTCSign(str, MONTHDAY_SIZE, len);
1197      if (sign < 0)
1198      {
1199        throw new InvalidDatatypeValueException("Error in month parsing:" + str);
1200      }
1201      else
1202      {
1203        getTimeZone(str, date, sign, len, timeZone);
1204      }
1205    }
1206    //validate and normalize
1207

1208    validateDateTime(date, timeZone);
1209
1210    if (date[utc] != 0 && date[utc] != 'Z')
1211    {
1212      normalize(date, timeZone);
1213    }
1214    return date;
1215  }
1216
1217  private int[] parseMonth(String JavaDoc str) throws InvalidDatatypeValueException
1218  {
1219    int len = str.length();
1220    int[] date = new int [TOTAL_SIZE];
1221    int[] timeZone = new int [2];
1222
1223    //set constants
1224
date[CY] = YEAR;
1225    date[D] = DAY;
1226    if (str.charAt(0) != '-' || str.charAt(1) != '-')
1227    {
1228      throw new InvalidDatatypeValueException("Invalid format for gMonth: " + str);
1229    }
1230    int stop = 4;
1231    date[M] = parseInt(str, 2, stop);
1232
1233    // REVISIT: allow both --MM and --MM-- now.
1234
// need to remove the following 4 lines to disallow --MM--
1235
// when the errata is offically in the rec.
1236
if (str.length() >= stop + 2 && str.charAt(stop) == '-' && str.charAt(stop + 1) == '-')
1237    {
1238      stop += 2;
1239    }
1240    if (stop < len)
1241    {
1242      int sign = findUTCSign(str, stop, len);
1243      if (sign < 0)
1244      {
1245        throw new InvalidDatatypeValueException("Error in month parsing: " + str);
1246      }
1247      else
1248      {
1249        getTimeZone(str, date, sign, len, timeZone);
1250      }
1251    }
1252    //validate and normalize
1253
validateDateTime(date, timeZone);
1254
1255    if (date[utc] != 0 && date[utc] != 'Z')
1256    {
1257      normalize(date, timeZone);
1258    }
1259    return date;
1260  }
1261
1262  private int[] parseYear(String JavaDoc str) throws InvalidDatatypeValueException
1263  {
1264    int len = str.length();
1265    int[] date = new int [TOTAL_SIZE];
1266    int[] timeZone = new int [2];
1267
1268    // check for preceding '-' sign
1269
int start = 0;
1270    if (str.charAt(0) == '-')
1271    {
1272      start = 1;
1273    }
1274    int sign = findUTCSign(str, start, len);
1275    if (sign == -1)
1276    {
1277      date[CY] = parseIntYear(str, len);
1278    }
1279    else
1280    {
1281      date[CY] = parseIntYear(str, sign);
1282      getTimeZone(str, date, sign, len, timeZone);
1283    }
1284
1285    //initialize values
1286
date[M] = MONTH;
1287    date[D] = 1;
1288
1289    //validate and normalize
1290
validateDateTime(date, timeZone);
1291
1292    if (date[utc] != 0 && date[utc] != 'Z')
1293    {
1294      normalize(date, timeZone);
1295    }
1296    return date;
1297  }
1298
1299  private int[] parseYearMonth(String JavaDoc str) throws InvalidDatatypeValueException
1300  {
1301    int len = str.length();
1302    int[] date = new int [TOTAL_SIZE];
1303    int[] timeZone = new int [2];
1304
1305    // get date
1306
int end = getYearMonth(str, 0, len, date);
1307    date[D] = DAY;
1308    parseTimeZone(str, end, len, date, timeZone);
1309
1310    //validate and normalize
1311
validateDateTime(date, timeZone);
1312
1313    if (date[utc] != 0 && date[utc] != 'Z')
1314    {
1315      normalize(date, timeZone);
1316    }
1317    return date;
1318  }
1319
1320  private int[] parseTime(String JavaDoc str) throws InvalidDatatypeValueException
1321  {
1322    int len = str.length();
1323    int[] date = new int [TOTAL_SIZE];
1324    int[] timeZone = new int [2];
1325
1326    // time
1327
// initialize to default values
1328
date[CY] = YEAR;
1329    date[M] = MONTH;
1330    date[D] = DAY;
1331    getTime(str, 0, len, date, timeZone);
1332
1333    //validate and normalize
1334

1335    validateDateTime(date, timeZone);
1336
1337    if (date[utc] != 0)
1338    {
1339      normalize(date, timeZone);
1340    }
1341    return date;
1342  }
1343
1344  private static class SafeSimpleDateFormat extends SimpleDateFormat JavaDoc
1345  {
1346    public SafeSimpleDateFormat(String JavaDoc pattern)
1347    {
1348      super(pattern);
1349    }
1350
1351    public synchronized Date JavaDoc parse(String JavaDoc source) throws ParseException JavaDoc
1352    {
1353      return super.parse(source);
1354    }
1355
1356    public StringBuffer JavaDoc format(Date JavaDoc date, StringBuffer JavaDoc toAppendTo, FieldPosition JavaDoc pos)
1357    {
1358      StringBuffer JavaDoc result = super.format(date, toAppendTo, pos);
1359      result.insert(result.length() - 2, ":");
1360      return result;
1361    }
1362  }
1363
1364}
1365
Popular Tags