View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 2000 (C) Intalio, Inc. All Rights Reserved.
32   *
33   * $Id$ Date Author Changes 10/26/2000 Arnaud Blandin add TimeDuration(long l) 10/26/2000 Arnaud
34   * Blandin change parse method 10/23/2000 Arnaud Blandin Created
35   */
36  package org.exolab.castor.types;
37  
38  import java.text.ParseException;
39  
40  /**
41   * Represents the timeDuration XML Schema type.
42   * <p>
43   * This representation does not support the decimal fraction for the lowest order item. Besides
44   * setting TimeDuration to '0' is not possible thus there is no distinction between '0' and 'P0Y'
45   * <p>
46   * Note: This datatype is not included in any recommendation. It was introduced in
47   * http://www.w3.org/TR/1999/WD-xmlschema-2-19990924/ and was last in
48   * http://www.w3.org/TR/2000/CR-xmlschema-2-20001024/ and was removed by
49   * http://www.w3.org/TR/2001/PR-xmlschema-2-20010316/. It was not in the final approved
50   * recommendation: http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/
51   *
52   * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
53   * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
54   * @deprecated since Castor 1.0.6 since this type is not in any recommendation.
55   */
56  public class TimeDuration implements java.io.Serializable {
57    /** SerialVersionUID */
58    private static final long serialVersionUID = -3080457339689062021L;
59  
60    /** Set to true and recompile to include debugging code in class. */
61    private static final boolean DEBUG = false;
62    /** the flag representing the 'T' position */
63    private static final int TIME_FLAG = 8;
64    // Private variables
65  
66    /**
67     * the number of years
68     */
69    private short _year = 0;
70    /**
71     * the number of months
72     */
73    private short _month = 0;
74    /**
75     * the number of days
76     */
77    private short _day = 0;
78    /**
79     * the number of hours
80     */
81    private short _hour = 0;
82    /**
83     * the number of minutes
84     */
85    private short _minute = 0;
86    /**
87     * the number of seconds
88     */
89    private short _second = 0;
90  
91    /**
92     * the potential number of milliseconds
93     */
94    private short _millisecond = 0;
95  
96    /**
97     * true if the Time Duration is negative
98     */
99    private boolean _isNegative = false;
100 
101   /**
102    * default constructor
103    */
104   public TimeDuration() {}
105 
106   /**
107    * <p>
108    * This constructor fills in the time duration fields according to the value of the long by
109    * calling setValue
110    * 
111    * @see #setValue
112    * @param l the long value of the Time Duration
113    */
114   public TimeDuration(long l) {
115 
116     long refSecond = 1000;
117     long refMinute = 60 * refSecond;
118     long refHour = 60 * refMinute;
119     long refDay = 24 * refHour;
120     long refMonth = (long) (30.42 * refDay);
121     long refYear = 12 * refMonth;
122 
123     if (DEBUG) {
124       System.out.println("In time duration Constructor");
125       System.out.println("long : " + l);
126     }
127 
128     if (l < 0) {
129       this.setNegative();
130       l = -l;
131     }
132 
133     short year = (short) (l / refYear);
134     l = l % refYear;
135     if (DEBUG) {
136       System.out.println("nb years:" + year);
137       System.out.println("New long : " + l);
138     }
139 
140     short month = (short) (l / refMonth);
141     l = l % refMonth;
142     if (DEBUG) {
143       System.out.println("nb months:" + month);
144       System.out.println("New long : " + l);
145       System.out.println(refDay);
146     }
147 
148     short day = (short) (l / refDay);
149     l = l % refDay;
150     if (DEBUG) {
151       System.out.println("nb days:" + day);
152       System.out.println("New long : " + l);
153     }
154 
155     short hour = (short) (l / refHour);
156     l = l % refHour;
157     if (DEBUG) {
158       System.out.println("nb hours:" + hour);
159       System.out.println("New long : " + l);
160     }
161 
162     short minute = (short) (l / refMinute);
163     l = l % refMinute;
164     if (DEBUG) {
165       System.out.println("nb minutes:" + minute);
166       System.out.println("New long : " + l);
167     }
168 
169     short seconds = (short) (l / refSecond);
170     l = l % refSecond;
171     if (DEBUG) {
172       System.out.println("nb seconds:" + seconds);
173     }
174 
175     short milliseconds = (short) (l);
176     if (DEBUG) {
177       System.out.println("nb milliseconds:" + milliseconds);
178     }
179 
180     this.setValue(year, month, day, hour, minute, seconds, milliseconds);
181   }
182 
183   // Set methods
184   public void setYear(short year) {
185     _year = year;
186   }
187 
188   public void setMonth(short month) {
189     _month = month;
190   }
191 
192   public void setDay(short day) {
193     _day = day;
194   }
195 
196   public void setHour(short hour) {
197     _hour = hour;
198   }
199 
200   public void setMinute(short minute) {
201     _minute = minute;
202   }
203 
204   public void setSeconds(short second) {
205     _second = second;
206   }
207 
208   public void setMilli(short milli) {
209     _millisecond = milli;
210   }
211 
212   public void setNegative() {
213     _isNegative = true;
214   }
215 
216   /**
217    * Fill in the fields of the TimeDuration with the given values
218    * 
219    * @param year the year value
220    * @param month the month value
221    * @param day the day value
222    * @param hour the hour value
223    * @param minute the minute value
224    * @param second the second value
225    */
226   public void setValue(short year, short month, short day, short hour, short minute, short second,
227       short millisecond) {
228     this.setYear(year);
229     this.setMonth(month);
230     this.setDay(day);
231     this.setHour(hour);
232     this.setMinute(minute);
233     this.setSeconds(second);
234     this.setMilli(millisecond);
235   }
236 
237 
238   // Get methods
239   public short getYear() {
240     return (_year);
241   }
242 
243   public short getMonth() {
244     return (_month);
245   }
246 
247   public short getDay() {
248     return (_day);
249   }
250 
251   public short getHour() {
252     return (_hour);
253   }
254 
255   public short getMinute() {
256     return (_minute);
257   }
258 
259   public short getSeconds() {
260     return (_second);
261   }
262 
263   public short getMilli() {
264     return (_millisecond);
265   }
266 
267   public boolean isNegative() {
268     return _isNegative;
269   }
270 
271   /**
272    * <p>
273    * Convert a timeDuration into a long This long represents the duration in milliseconds
274    * 
275    * @return a long representing the duration
276    */
277   public long toLong() {
278     long result = 0;
279     // 30.42 days in a month (365/12)
280     // Horner method
281     result =
282         ((long) (((((((_year * 12L) + _month) * 30.42 + _day) * 24L + _hour) * 60L + _minute) * 60L
283             + _second) * 1000L + _millisecond));
284 
285     result = isNegative() ? -result : result;
286     return result;
287   }
288 
289   /**
290    * <p>
291    * Convert a timeDuration into a String conforming to ISO8601 and
292    * <a href="http://www.w3.org/TR/2000/WD-xmlschema-2-20000922/#timeDuration"> XML Schema
293    * specs </a>
294    * 
295    * @return a string representing the time duration
296    */
297   public String toString() {
298     StringBuilder result = new StringBuilder();
299     result.append('P');
300     if (_year != 0) {
301       result.append(_year).append('Y');
302     }
303     if (_month != 0) {
304       result.append(_month).append('M');
305     }
306     if (_day != 0) {
307       result.append(_day).append('D');
308     }
309     boolean isThereTime = ((_hour != 0) || (_minute != 0) || (_second != 0));
310     if (isThereTime) {
311       result.append('T');
312       if (_hour != 0) {
313         result.append(_hour).append('H');
314       }
315       if (_minute != 0) {
316         result.append(_minute).append('M');
317       }
318       if (_second != 0) {
319         result.append(_second);
320         if (_millisecond != 0) {
321           result.append('.');
322           if (_millisecond < 100) {
323             result.append('0');
324             if (_millisecond < 10)
325               result.append('0');
326           }
327           result.append(_millisecond);
328         }
329         result.append('S');
330       }
331     }
332     if (_isNegative)
333       result.insert(0, '-');
334     return result.toString();
335   } // toString
336 
337   /**
338    * parse a String and convert it into a java.lang.Object
339    * 
340    * @param str the string to parse
341    * @return the java.lang.Object represented by the string
342    * @throws ParseException a parse exception is thrown if the string to parse does not follow the
343    *         rigth format (see the description of this class)
344    */
345   public static Object parse(String str) throws ParseException {
346     return parseTimeDuration(str);
347   }
348 
349   /**
350    * <p>
351    * Parse the given string and return a time duration which represents this string
352    * 
353    * @param str the string to parse
354    * @return a TimeDuration instance which represent the string
355    * @throws ParseException thrown when the string is not valid
356    */
357   public static TimeDuration parseTimeDuration(String str) throws ParseException {
358 
359 
360     if (str == null) {
361       throw new IllegalArgumentException("the string to be parsed must not" + " be null");
362 
363     }
364 
365     TimeDuration result = new TimeDuration();
366     char[] chars = str.toCharArray();
367     int idx = 0;
368 
369 
370     if (chars.length == 0) {
371       // str = "" means a null TimeDuration
372       return null;
373     }
374 
375     if (chars[idx] == '-') {
376       ++idx;
377       result.setNegative();
378       if (idx >= chars.length) {
379         throw new ParseException("'-' is wrong placed", 0);
380       }
381     }
382 
383     // -- make sure we start with 'P'
384     if (chars[idx] != 'P') {
385       throw new ParseException("Missing 'P' delimiter", idx);
386     }
387     ++idx;
388 
389 
390     int number = 0;
391     boolean hasNumber = false;
392 
393     // -- parse flags
394     // YMDTHMS = b1111111 (127)
395     // Year = 64, Month = 32, etc
396     int flags = 0;
397 
398     while (idx < chars.length) {
399 
400       char ch = chars[idx++];
401 
402       switch (ch) {
403 
404         // -- Year
405         case 'Y':
406           // -- check for error
407           if (flags > 0) {
408             String err = str + ":Syntax error, 'Y' must " + "proceed all other delimiters.";
409             throw new ParseException(err, idx);
410           }
411           // --set flags
412           flags = 64;
413           if (hasNumber) {
414             result.setYear((short) number);
415             hasNumber = false;
416           } else {
417             String err = str + ":missing number of years before 'Y'";
418             throw new ParseException(err, idx);
419           }
420 
421           break;
422         // -- Month or Minute
423         case 'M':
424           // -- Either month or minute, check for T flag,
425           // -- if present then Minute, otherwise Month
426           if ((flags & TIME_FLAG) == 8) {
427 
428             // make sure no existing minute or second
429             // flags have been set.
430             if ((flags & 3) > 0) {
431               throw new ParseException(str + ": Syntax Error...", idx);
432             }
433             flags = flags | 2;
434             if (hasNumber) {
435               result.setMinute((short) number);
436               hasNumber = false;
437             } else {
438               String err = str + ": missing number of minutes " + "before 'M'";
439               throw new ParseException(err, idx);
440             }
441           }
442           // -- Month
443           else {
444             // make sure no existing month, day or time
445             // flags have been set
446             if ((flags & 63) > 0) {
447               throw new ParseException(str + ":Syntax Error...", idx);
448             }
449             flags = flags | 32;
450             if (hasNumber) {
451               result.setMonth((short) number);
452               hasNumber = false;
453             } else {
454               String err = str + ":missing number of months before 'M'";
455               throw new ParseException(err, idx);
456             }
457           }
458           break;
459         // -- Day
460         case 'D':
461           // make sure no day or time flags have been set
462           if ((flags & 31) > 0) {
463             throw new ParseException(str + ":Syntax Error...", idx);
464           }
465           flags = flags | 16;
466           if (hasNumber) {
467             result.setDay((short) number);
468             hasNumber = false;
469           } else {
470             String err = str + ":missing number of days before 'D'";
471             throw new ParseException(err, idx);
472           }
473           break;
474         // -- Time
475         case 'T':
476           // make sure no T flag already exists
477           if ((flags & TIME_FLAG) == 8) {
478             String err = str + ":Syntax error, 'T' may not " + "exist more than once.";
479             throw new ParseException(err, idx);
480           }
481           flags = flags | 8;
482           break;
483         // -- Hour
484         case 'H':
485           // make sure no time flags have been set, but
486           // that T exists
487           if ((flags & 15) != 8) {
488             String err = null;
489             if ((flags & 8) != 8)
490               err = str + ": Missing 'T' before 'H'";
491             else
492               err = str + ": Syntax Error, 'H' must appear for 'M' or 'S'";
493             throw new ParseException(err, idx);
494           }
495           flags = flags | 4;
496           if (hasNumber) {
497             result.setHour((short) number);
498             hasNumber = false;
499           } else {
500             String err = str + ":missing number of hours before 'H'";
501             throw new ParseException(err, idx);
502           }
503           break;
504         case 'S':
505           if (flags != 0) {
506             // make sure T exists, but no 'S'
507             if ((flags & 8) != 8) {
508               String err = str + ": Missing 'T' before 'S'";
509               throw new ParseException(err, idx);
510             }
511             if ((flags & 1) == 1) {
512               String err = str + ": Syntax error 'S' may not exist more than once.";
513               throw new ParseException(err, idx);
514             }
515 
516             flags = flags | 1;
517             if (hasNumber) {
518               result.setSeconds((short) number);
519               hasNumber = false;
520             } else {
521               String err = str + ": missing number of seconds before 'S'";
522               throw new ParseException(err, idx);
523             }
524           } else {
525             if (hasNumber) {
526               String numb = Integer.toString(number);
527               if (numb.length() < 3) {
528                 if (numb.length() < 2)
529                   number = number * 10;
530                 number = number * 10;
531               }
532               result.setMilli((short) number);
533               hasNumber = false;
534             } else {
535               String err = str + ": missing number of milliseconds before 'S'";
536               throw new ParseException(err, idx);
537             }
538           }
539 
540           break;
541 
542         case '.':
543           // make sure T exists, but no 'S'
544           if ((flags | 1) == 1) {
545             String err = str + ": Syntax error '.' may not exist more than once.";
546             throw new ParseException(err, idx);
547           }
548 
549           if ((flags & 8) != 8) {
550             String err = str + ": Missing 'T' before 'S'";
551             throw new ParseException(err, idx);
552           }
553 
554 
555           flags = 0;
556 
557           if (hasNumber) {
558             result.setSeconds((short) number);
559             hasNumber = false;
560           }
561 
562           else {
563             String err = str + ": missing number of seconds before 'S'";
564             throw new ParseException(err, idx);
565           }
566           break;
567 
568         default:
569           // make sure ch is a digit...
570           if (('0' <= ch) && (ch <= '9')) {
571 
572             if (hasNumber)
573               number = (number * 10) + (ch - 48);
574             else {
575               hasNumber = true;
576               number = (ch - 48);
577             }
578           } else
579             throw new ParseException(str + ":Invalid character: " + ch, idx);
580           break;
581       }
582     }
583     // -- check for T, but no HMS
584     if ((flags & 15) == 8) {
585       throw new ParseException(str + ": T must be omitted", idx);
586     }
587 
588     if (hasNumber) {
589       throw new ParseException(str + ": expecting ending delimiter", idx);
590     }
591 
592     return result;
593 
594   } // parse
595 
596   /**
597    * Override the java.lang.equals method
598    * 
599    * @see #equal
600    */
601   public boolean equals(Object object) {
602     if (object instanceof TimeDuration)
603       return equal((TimeDuration) object);
604     return false;
605   }
606 
607   /**
608    * Returns true if the instance of TimeDuration has the same fields of the parameter
609    * 
610    * @param timeD the time duration to compare
611    * @return true if equal, false if not
612    */
613   public boolean equal(TimeDuration timeD) {
614     boolean result = false;
615     if (timeD == null)
616       return result;
617     result = (_year == timeD.getYear());
618     result = result && (_month == timeD.getMonth());
619     result = result && (_day == timeD.getDay());
620     result = result && (_hour == timeD.getHour());
621     result = result && (_minute == timeD.getMinute());
622     result = result && (_second == timeD.getSeconds());
623     result = result && (this.isNegative() == timeD.isNegative());
624     return result;
625   } // equals
626 
627   /**
628    * <p>
629    * Returns true if the present instance of TimeDuration is greater than the parameter
630    * <p>
631    * Note This definition does not follow the XML SCHEMA DRAFT 20001024 the following orger relation
632    * is used : t1,t2 timeDuration types t1>t2 iff t1.toLong()>t2.toLong()
633    * 
634    * @param timeD the time duration to compare with the present instance
635    * @return true if the present instance is the greatest, false if not
636    */
637   public boolean isGreater(TimeDuration timeD) {
638     boolean result = false;
639     // to be optimized ??
640     result = this.toLong() > timeD.toLong();
641     return result;
642   } // isGreater
643 }// TimeDuration