KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > scalagent > scheduler > event > PeriodicEvent


1 /*
2  * Copyright (C) 2001 - 2005 ScalAgent Distributed Technologies
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  * USA.
18  *
19  * Initial developer(s): ScalAgent Distributed Technologies
20  * Contributor(s):
21  */

22 package com.scalagent.scheduler.event;
23
24 import java.util.Date JavaDoc;
25 import java.util.Calendar JavaDoc;
26 import java.util.TimeZone JavaDoc;
27 import java.text.DateFormat JavaDoc;
28
29 import fr.dyade.aaa.util.Strings;
30
31 /**
32  * A <code>PeriodicEvent</code> object implements a repetitive event defined
33  * by a reference date and a time period. The time period is qualified with
34  * a time unit, so that user meaningful periods may be defined, even though
35  * they are variable in length (such as a day, a month, and so on).
36  * <p>
37  * The reference date is used internally to keep track of the last computed
38  * event occurrence.
39  */

40 public class PeriodicEvent implements DiaryEvent {
41   /**
42    * Provides an internal computation facility to others, unrelated to
43    * any specific <code>PeriodicEvent</code> object.
44    *
45    * @param refDate reference date from which the result date is computed
46    * @param period period definition to add to <code>refDate</code>
47    * @return <code>refDate + period</code>
48    */

49   public static long getDate(long refDate, DiaryPeriod period) {
50     if ((refDate == -1) ||
51     (period.value == 0))
52       return -1;
53     PeriodicEvent pevt = new PeriodicEvent(refDate, period);
54     return pevt.getNextDate(refDate);
55   }
56
57   /**
58    * Checks a field value of a calendar against a reference value.
59    * Handles some special -1 cases.
60    *
61    * @param calendar reference date to check
62    * @param field field identifier, as a <code>Calendar</code> constant
63    * @param value reference value, may be negative
64    * @return <code>true</code> if values match
65    */

66   public static boolean checkField(
67     Calendar JavaDoc calendar, int field, int value) {
68
69     if (value >= 0) {
70       if (calendar.get(field) != value)
71     return false;
72       return true;
73     }
74
75     // keeps the parameter calendar as it is
76
Calendar JavaDoc clone = (Calendar JavaDoc) calendar.clone();
77     switch (field) {
78     case Calendar.DAY_OF_MONTH:
79       clone.add(Calendar.DAY_OF_MONTH, -value);
80       if (clone.get(Calendar.DAY_OF_MONTH) != 1)
81     return false;
82       return true;
83     case Calendar.DAY_OF_WEEK_IN_MONTH:
84       clone.add(Calendar.DAY_OF_MONTH, -7 * value);
85       if (clone.get(Calendar.DAY_OF_MONTH) >= 7)
86     return false;
87       return true;
88     default:
89       // not accepted
90
break;
91     }
92
93     return false;
94   }
95
96   /**
97    * ID of the time zone to be used when computing dates.
98    * A <code>null</code> value stands for the local time zone.
99    * <p>
100    * Defaults to <code>null</code>.
101    */

102   protected String JavaDoc timeZoneId = null;
103
104   /**
105    * Reference date from which event dates are computed by adding a multiple
106    * of the period length. A <code>-1</code> value means that no reference date
107    * is set.
108    * <p>
109    * Defaults to <code>-1</code>.
110    */

111   protected long refDate = -1;
112   
113   /**
114    * Period definition. A <code>null</code> value stands for a no repetitive
115    * event.
116    * <p>
117    * Defaults to <code>null</code>.
118    */

119   protected DiaryPeriod period = null;
120
121   /**
122    * Mask of fields to match event dates, as <code>Calendar</code> constants.
123    * <p>
124    * Some values for the <code>period</code> field accept a logical definition
125    * of the event date, in addition to the reference date. There are only a
126    * small set of <code>(period/maskField)</code> legal combinations that may
127    * be validated using {@link validateMask}.
128    * <p>
129    * The fields are ordered according to the following:
130    * <code>YEAR, MONTH, WEEK_OF_YEAR, WEEK_OF_MONTH,
131    * DAY_OF_YEAR, DAY_OF_MONTH, DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK</code>.
132    */

133   protected int[] maskField = null;
134
135   /**
136    * Values of corresponding fields in <code>maskField</code>.
137    * May be set from <code>refDate</code> using {@link validateMask}.
138    * <p>
139    * Positive values require an exact match, while negative values are
140    * interpreted either by the <code>Calendar</code> class or directly by
141    * this class. Negative values are accepted for the <code>DAY_OF_MONTH</code>
142    * and <code>DAY_OF_WEEK_IN_MONTH</code> fields.
143    */

144   protected int[] maskValue = null;
145
146   /**
147    * Object used internally to compute dates. It is created from the
148    * <code>timeZoneId</code> field when this object is created, and initialized
149    * with the reference date.
150    * <p>
151    * A <code>Calendar</code> object holds two descriptions of a date, as a
152    * <code>long</code> value, and as a number of fields. Those two descriptions
153    * are always synced in <code>calendar</code>.
154    */

155   protected Calendar JavaDoc calendar;
156
157   /**
158    * Constructor setting all fields. May need a call to {@link validateMask}
159    * if <code>maskField</code> is not <code>null</code>.
160    *
161    * @param timeZoneId
162    * id of the time zone to be used when computing dates
163    * @param refDate
164    * reference date from which event dates are computed
165    * @param period
166    * period definition
167    * @param maskField
168    * mask of fields to match event dates, as <code>Calendar</code> constants
169    * @param maskValue
170    * values of corresponding fields in <code>maskField</code>
171    */

172   public PeriodicEvent(String JavaDoc timeZoneId, long refDate, DiaryPeriod period,
173                int[] maskField, int[] maskValue) {
174     this.timeZoneId = timeZoneId;
175     this.refDate = refDate;
176     this.period = period;
177     this.maskField = maskField;
178     this.maskValue = maskValue;
179
180     if (timeZoneId == null) {
181       calendar = Calendar.getInstance();
182     } else {
183       TimeZone JavaDoc tz = TimeZone.getTimeZone(timeZoneId);
184       if (tz == null)
185     calendar = Calendar.getInstance();
186       else
187     calendar = Calendar.getInstance(tz);
188     }
189
190     if (refDate != -1)
191       calendar.setTime(new Date JavaDoc(refDate));
192   }
193
194   /**
195    * Constructor with no mask.
196    *
197    * @param timeZoneId id of the time zone to be used when computing dates
198    * @param refDate reference date from which event dates are computed
199    * @param period period definition
200    */

201   public PeriodicEvent(String JavaDoc timeZoneId, long refDate, DiaryPeriod period) {
202     this(timeZoneId, refDate, period, null, null);
203   }
204
205   /**
206    * Constructor with default time zone.
207    *
208    * @param refDate reference date from which event dates are computed
209    * @param period period definition
210    */

211   public PeriodicEvent(long refDate, DiaryPeriod period) {
212     this(null, refDate, period);
213   }
214
215   /**
216    * Default constructor.
217    * Creates an event with a null date and no repeat period.
218    */

219   public PeriodicEvent() {
220     this(-1, null);
221   }
222
223   /**
224    * Field accessor.
225    *
226    * @param timeZoneId id of the time zone to be used when computing dates
227    */

228   public void setTimeZoneId(String JavaDoc timeZoneId) {
229     this.timeZoneId = timeZoneId;
230   }
231
232   /**
233    * Field accessor.
234    *
235    * @return id of the time zone to be used when computing dates
236    */

237   public String JavaDoc getTimeZoneId() {
238     return timeZoneId;
239   }
240
241   /**
242    * Field accessor. May need an additional call to {@link validateMask}.
243    *
244    * @param refDate reference date from which event dates are computed
245    */

246   public void setRefDate(long refDate) {
247     this.refDate = refDate;
248     if (refDate != -1)
249       calendar.setTime(new Date JavaDoc(refDate));
250   }
251
252   /**
253    * Field accessor.
254    *
255    * @return reference date from which event dates are computed
256    */

257   public long getRefDate() {
258     return refDate;
259   }
260
261   /**
262    * Field accessor.
263    *
264    * @param period period definition
265    */

266   public void setPeriod(DiaryPeriod period) {
267     this.period = period;
268   }
269
270   /**
271    * Field accessor.
272    *
273    * @return period definition
274    */

275   public DiaryPeriod getPeriod() {
276     return period;
277   }
278
279   /**
280    * Field accessor.
281    *
282    * @param maskField
283    * mask of fields to match event dates, as <code>Calendar</code> constants
284    */

285   public void setMaskField(int[] maskField) {
286     if ((maskField != null) && (maskField.length == 0))
287       this.maskField = null;
288     else
289       this.maskField = maskField;
290   }
291
292   /**
293    * Field accessor.
294    *
295    * @return mask of fields to match event dates
296    */

297   public int[] getMaskField() {
298     return maskField;
299   }
300
301   /**
302    * Field accessor.
303    *
304    * @param maskValue
305    * values of corresponding fields in <code>maskField</code>
306    */

307   public void setMaskValue(int[] maskValue) {
308     if ((maskValue != null) && (maskValue.length == 0))
309       this.maskValue = null;
310     else
311       this.maskValue = maskValue;
312   }
313
314   /**
315    * Field accessor.
316    *
317    * @return values of corresponding fields in <code>maskField</code>
318    */

319   public int[] getMaskValue() {
320     return maskValue;
321   }
322
323   /**
324    * Checks the consistency of the four fields <code>refDate</code>,
325    * <code>period</code>, <code>maskField</code>, and <code>maskValue</code>.
326    * Updates <code>maskValue</code> when <code>null</code>.
327    * <p>
328    * Legal <code>(period/maskField)</code> combinations are
329    * <code>(YEAR/DAY_OF_YEAR)</code>,
330    * <code>(YEAR/WEEK_OF_YEAR(&DAY_OF_WEEK))</code>,
331    * <code>(YEAR/MONTH&DAY_OF_MONTH)</code>,
332    * <code>(YEAR/MONTH&WEEK_OF_MONTH(&DAY_OF_WEEK))</code>,
333    * <code>(YEAR/MONTH&DAY_OF_WEEK_IN_MONTH(&DAY_OF_WEEK))</code>,
334    * <code>(MONTH/DAY_OF_MONTH)</code>,
335    * <code>(MONTH/WEEK_OF_MONTH(&DAY_OF_WEEK))</code>,
336    * <code>(MONTH/DAY_OF_WEEK_IN_MONTH(&DAY_OF_WEEK))</code>.
337    * The optional <code>DAY_OF_WEEK</code> mask value may be retrieved from
338    * the reference date.
339    *
340    * @return
341    * <code>true</code> when all three values are consistent,
342    * <code>false</code> otherwise.
343    */

344   public boolean validateMask() {
345     if ((maskField == null) ||
346     (maskField.length == 0))
347       return true;
348
349     if (period == null) {
350       // a sensible configuration should also set maskField to null
351
return false;
352     }
353
354     boolean fillValue = false;
355     if (maskValue == null) {
356       fillValue = true;
357       maskValue = new int[maskField.length];
358     }
359
360     // checks the reference date
361
if (refDate != -1)
362       calendar.setTime(new Date JavaDoc(refDate));
363
364     if (maskField[0] == Calendar.YEAR)
365       return false;
366     int index = 0;
367     switch (period.unit) {
368     case Calendar.YEAR:
369       switch (maskField[index]) {
370       case Calendar.MONTH:
371     if (fillValue) {
372       if (refDate == -1)
373         return false;
374       maskValue[index] = calendar.get(Calendar.MONTH);
375     } else if (refDate != -1) {
376       if (! checkField(calendar, Calendar.MONTH, maskValue[index]))
377         return false;
378     }
379     index ++;
380     if (index >= maskField.length)
381       return false;
382     switch (maskField[index]) {
383     case Calendar.WEEK_OF_MONTH:
384       if (fillValue) {
385         if (refDate == -1)
386           return false;
387         maskValue[index] = calendar.get(Calendar.WEEK_OF_MONTH);
388       } else if (refDate != -1) {
389         if (! checkField(calendar,
390                  Calendar.WEEK_OF_MONTH,
391                  maskValue[index]))
392           return false;
393       }
394       index ++;
395       if (index < maskField.length) {
396         switch (maskField[index]) {
397         case Calendar.DAY_OF_WEEK:
398           if (fillValue) {
399         if (refDate == -1)
400           return false;
401         maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK);
402           } else if (refDate != -1) {
403         if (! checkField(calendar, Calendar.DAY_OF_WEEK, maskValue[index]))
404           return false;
405           }
406           index ++;
407           if (index >= maskField.length)
408         return true;
409           return false;
410         default:
411           return false;
412         }
413       } else {
414         // gets the DAY_OF_WEEK value from the reference date
415
if (refDate == -1)
416           return false;
417         int mask[] = new int[index];
418         System.arraycopy(maskField, 0, mask, 0, maskField.length);
419         maskField = mask;
420         maskField[index] = Calendar.DAY_OF_WEEK;
421         mask = new int[index];
422         System.arraycopy(maskValue, 0, mask, 0, maskValue.length);
423         maskValue = mask;
424         maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK);
425         return true;
426       }
427     case Calendar.DAY_OF_MONTH:
428       if (fillValue) {
429         if (refDate == -1)
430           return false;
431         maskValue[index] = calendar.get(Calendar.DAY_OF_MONTH);
432       } else if (refDate != -1) {
433         if (! checkField(calendar, Calendar.DAY_OF_MONTH, maskValue[index]))
434           return false;
435       }
436       index ++;
437       if (index >= maskField.length)
438         return true;
439       return false;
440     case Calendar.DAY_OF_WEEK_IN_MONTH:
441       if (fillValue) {
442         if (refDate == -1)
443           return false;
444         maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH);
445       } else if (refDate != -1) {
446         if (! checkField(calendar, Calendar.DAY_OF_WEEK_IN_MONTH, maskValue[index]))
447           return false;
448       }
449       index ++;
450       if (index < maskField.length) {
451         switch (maskField[index]) {
452         case Calendar.DAY_OF_WEEK:
453           if (fillValue) {
454         if (refDate == -1)
455           return false;
456         maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK);
457           } else if (refDate != -1) {
458         if (! checkField(calendar, Calendar.DAY_OF_WEEK, maskValue[index]))
459           return false;
460           }
461           index ++;
462           if (index >= maskField.length)
463         return true;
464           return false;
465         default:
466           return false;
467         }
468       } else {
469         // gets the DAY_OF_WEEK value from the reference date
470
if (refDate == -1)
471           return false;
472         int mask[] = new int[index];
473         System.arraycopy(maskField, 0, mask, 0, maskField.length);
474         maskField = mask;
475         maskField[index] = Calendar.DAY_OF_WEEK;
476         mask = new int[index];
477         System.arraycopy(maskValue, 0, mask, 0, maskValue.length);
478         maskValue = mask;
479         maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK);
480         return true;
481       }
482     default:
483       return false;
484     }
485       case Calendar.WEEK_OF_YEAR:
486     if (fillValue) {
487       if (refDate == -1)
488         return false;
489       maskValue[index] = calendar.get(Calendar.WEEK_OF_YEAR);
490     } else if (refDate != -1) {
491       if (! checkField(calendar, Calendar.WEEK_OF_YEAR, maskValue[index]))
492         return false;
493     }
494     index ++;
495     if (index < maskField.length) {
496       switch (maskField[index]) {
497       case Calendar.DAY_OF_WEEK:
498         if (fillValue) {
499           if (refDate == -1)
500         return false;
501           maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK);
502         } else if (refDate != -1) {
503           if (! checkField(calendar, Calendar.DAY_OF_WEEK, maskValue[index]))
504         return false;
505         }
506         index ++;
507         if (index >= maskField.length)
508           return true;
509         return false;
510       default:
511         return false;
512       }
513     } else {
514       // gets the DAY_OF_WEEK value from the reference date
515
if (refDate == -1)
516         return false;
517       int mask[] = new int[index];
518       System.arraycopy(maskField, 0, mask, 0, maskField.length);
519       maskField = mask;
520       maskField[index] = Calendar.DAY_OF_WEEK;
521       mask = new int[index];
522       System.arraycopy(maskValue, 0, mask, 0, maskValue.length);
523       maskValue = mask;
524       maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK);
525       return true;
526     }
527       case Calendar.DAY_OF_YEAR:
528     if (fillValue) {
529       if (refDate == -1)
530         return false;
531       maskValue[index] = calendar.get(Calendar.DAY_OF_YEAR);
532     } else if (refDate != -1) {
533       if (! checkField(calendar, Calendar.DAY_OF_YEAR, maskValue[index]))
534         return false;
535     }
536     index ++;
537     if (index >= maskField.length)
538       return true;
539     return false;
540       }
541       break;
542     case Calendar.MONTH:
543       switch (maskField[index]) {
544       case Calendar.WEEK_OF_MONTH:
545     if (fillValue) {
546       if (refDate == -1)
547         return false;
548       maskValue[index] = calendar.get(Calendar.WEEK_OF_MONTH);
549     } else if (refDate != -1) {
550       if (! checkField(calendar, Calendar.WEEK_OF_MONTH, maskValue[index]))
551         return false;
552     }
553     index ++;
554     if (index < maskField.length) {
555       switch (maskField[index]) {
556       case Calendar.DAY_OF_WEEK:
557         if (fillValue) {
558           if (refDate == -1)
559         return false;
560           maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK);
561         } else if (refDate != -1) {
562           if (! checkField(calendar, Calendar.DAY_OF_WEEK, maskValue[index]))
563         return false;
564         }
565         index ++;
566         if (index >= maskField.length)
567           return true;
568         return false;
569       default:
570         return false;
571       }
572     } else {
573       // gets the DAY_OF_WEEK value from the reference date
574
if (refDate == -1)
575         return false;
576       int mask[] = new int[index];
577       System.arraycopy(maskField, 0, mask, 0, maskField.length);
578       maskField = mask;
579       maskField[index] = Calendar.DAY_OF_WEEK;
580       mask = new int[index];
581       System.arraycopy(maskValue, 0, mask, 0, maskValue.length);
582       maskValue = mask;
583       maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK);
584       return true;
585     }
586       case Calendar.DAY_OF_MONTH:
587     if (fillValue) {
588       if (refDate == -1)
589         return false;
590       maskValue[index] = calendar.get(Calendar.DAY_OF_MONTH);
591     } else if (refDate != -1) {
592       if (! checkField(calendar, Calendar.DAY_OF_MONTH, maskValue[index]))
593         return false;
594     }
595     index ++;
596     if (index >= maskField.length)
597       return true;
598     return false;
599       case Calendar.DAY_OF_WEEK_IN_MONTH:
600     if (fillValue) {
601       if (refDate == -1)
602         return false;
603       maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH);
604     } else if (refDate != -1) {
605       if (! checkField(calendar, Calendar.DAY_OF_WEEK_IN_MONTH, maskValue[index]))
606         return false;
607     }
608     index ++;
609     if (index < maskField.length) {
610       switch (maskField[index]) {
611       case Calendar.DAY_OF_WEEK:
612         if (fillValue) {
613           if (refDate == -1)
614         return false;
615           maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK);
616         } else if (refDate != -1) {
617           if (! checkField(calendar, Calendar.DAY_OF_WEEK, maskValue[index]))
618         return false;
619         }
620         index ++;
621         if (index >= maskField.length)
622           return true;
623         return false;
624       default:
625         return false;
626       }
627     } else {
628       // gets the DAY_OF_WEEK value from the reference date
629
if (refDate == -1)
630         return false;
631       int mask[] = new int[index];
632       System.arraycopy(maskField, 0, mask, 0, maskField.length);
633       maskField = mask;
634       maskField[index] = Calendar.DAY_OF_WEEK;
635       mask = new int[index];
636       System.arraycopy(maskValue, 0, mask, 0, maskValue.length);
637       maskValue = mask;
638       maskValue[index] = calendar.get(Calendar.DAY_OF_WEEK);
639       return true;
640     }
641       default:
642     return false;
643       }
644     default:
645       // when period is the day or lower, no mask is allowed
646
return false;
647     }
648
649     // not reached
650
return false;
651   }
652
653   /**
654    * Provides a string image for this object.
655    *
656    * @return string image for this object
657    */

658   public String JavaDoc toString() {
659     StringBuffer JavaDoc output = new StringBuffer JavaDoc();
660     output.append("(");
661     output.append(super.toString());
662     output.append(",timeZoneId=");
663     Strings.toString(output, timeZoneId);
664     output.append(",refDate=");
665     output.append(refDate);
666     if (refDate > 0) {
667       output.append("(");
668       output.append(DateFormat.getDateTimeInstance().format(
669     new Date JavaDoc(refDate)));
670       output.append(")");
671     }
672     output.append(",period=");
673     output.append(period);
674     output.append(",maskField=");
675     Strings.toStringArray(output, maskField);
676     output.append(",maskValue=");
677     Strings.toStringArray(output, maskValue);
678     output.append(")");
679     return output.toString();
680   }
681
682   /**
683    * Applies the mask on the calendar.
684    */

685   protected void applyMask() {
686     if (maskField == null)
687       return;
688
689     int lastDayInMonth = 0;
690
691     for (int i = 0; i < maskField.length; i ++) {
692       // negative values for DAY_OF_MONTH are not handled by the Calendar
693
if ((maskField[i] == Calendar.DAY_OF_MONTH) &&
694       (maskValue[i] < 0)) {
695     lastDayInMonth = maskValue[i];
696     calendar.set(Calendar.DAY_OF_MONTH, 33);
697       } else {
698     calendar.set(maskField[i], maskValue[i]);
699       }
700     }
701
702     if (lastDayInMonth != 0) {
703       // resolve the calendar date.
704
// calendar.getTime();
705
// Work-around calendar jdk1.4 bug (Id: 4685354).
706
calendar.get(Calendar.MONTH);
707       calendar.set(Calendar.DAY_OF_MONTH, lastDayInMonth + 1);
708     }
709   }
710
711   /**
712    * Checks that the calendar conforms to the mask.
713    */

714   protected boolean checkMask() {
715     if (maskField == null)
716       return true;
717
718     for (int i = 0; i < maskField.length; i ++) {
719       if ((maskValue[i] > 0) &&
720       (calendar.get(maskField[i]) != maskValue[i]))
721     return false;
722     }
723
724     return true;
725   }
726
727   /**
728    * Gets the next event date after the parameter date. Returns
729    * <code>-1</code> if there is no such date.
730    *
731    * @param now
732    * starting date for the lookup
733    * @param inclusive
734    * if <code>true</code> checks <code>now</code> as an answer
735    * @return
736    * the next event date after now,
737    * <code>-1</code> if there is no such date
738    */

739   public long getNextDate(long now, boolean inclusive) {
740     return getNextDate(now, 1, inclusive);
741   }
742
743   /**
744    * Gets the next event date after the parameter date. Returns
745    * <code>-1</code> if there is no such date.
746    * Calls {@link getNextDate(long, boolean) getNextDate} with the
747    * <code>inclusive</code> parameter set to <code>false</code>.
748    *
749    * @param now
750    * starting date for the lookup
751    * @return
752    * the next event date after now,
753    * <code>-1</code> if there is no such date
754    */

755   public long getNextDate(long now) {
756     return getNextDate(now, false);
757   }
758
759   /**
760    * Gets the last event date before the parameter date. Returns
761    * <code>-1</code> if there is no such date.
762    *
763    * @param now
764    * starting date for the backward lookup
765    * @param inclusive
766    * if <code>true</code> checks <code>now</code> as an answer
767    * @return
768    * the last event date before now,
769    * <code>-1</code> if there is no such date
770    */

771   public long getLastDate(long now, boolean inclusive) {
772     return getNextDate(now, -1, inclusive);
773   }
774
775   /**
776    * Gets the last event date before the parameter date. Returns
777    * <code>-1</code> if there is no such date.
778    * Calls {@link getLastDate(long, boolean) getLastDate} with the
779    * <code>inclusive</code> parameter set to <code>false</code>.
780    *
781    * @param now
782    * starting date for the backward lookup
783    * @return
784    * the last event date before now,
785    * <code>-1</code> if there is no such date
786    */

787   public long getLastDate(long now) {
788     return getLastDate(now, false);
789   }
790
791   /**
792    * Gets the next event date before or after the parameter date. Returns
793    * <code>-1</code> if there is no such date.
794    *
795    * @param now
796    * starting date for the lookup
797    * @param dir
798    * direction of lookup, forward when positive, backward when negative
799    * @param inclusive
800    * if <code>true</code> checks <code>now</code> as an answer
801    * @return
802    * the next event date before/after now,
803    * <code>-1</code> if there is no such date
804    */

805   private long getNextDate(long now, int dir, boolean inclusive) {
806     if (dir == 0)
807       return -1;
808     if (dir > 0)
809       dir = 1;
810     else
811       dir = -1;
812
813     if (refDate == -1)
814       return -1;
815
816     // the calendar date is always set to the reference date, which is
817
// a valid event date, or to the last valid event date returned
818
long date = calendar.getTime().getTime();
819     if (inclusive && (date == now))
820       return date;
821
822     if (period.value == 0)
823       return -1;
824
825     Calendar JavaDoc clone = (Calendar JavaDoc) calendar.clone();
826
827     // gets a rough estimate of the number of periods between date and now
828
long pnum = now - date;
829     int sign = 1;
830     if (pnum > 0) {
831       sign = dir;
832     } else {
833       pnum = -pnum;
834       sign = -dir;
835     }
836     switch (period.unit) {
837     case Calendar.YEAR:
838       pnum /= 12;
839     case Calendar.MONTH:
840       pnum /= 30;
841     case Calendar.DAY_OF_MONTH:
842       pnum /= 24;
843     case Calendar.HOUR_OF_DAY:
844       pnum /= 60;
845     case Calendar.MINUTE:
846       pnum /= 60;
847     case Calendar.SECOND:
848       pnum /= 1000;
849     }
850     pnum /= period.value;
851
852     // tries to skip a large period
853
pnum_loop:
854     while (pnum > 3) {
855       calendar.add(period.unit, (int) (pnum * period.value * dir * sign));
856
857       // sets all fields before evaluating the calendar
858
applyMask();
859       if (! checkMask()) {
860     // this is not a valid event date
861
calendar = clone;
862     clone = (Calendar JavaDoc) calendar.clone();
863     pnum --;
864     continue pnum_loop;
865       }
866
867       // this is a valid event date
868
clone = (Calendar JavaDoc) calendar.clone();
869       break pnum_loop;
870     }
871
872     date = calendar.getTime().getTime();
873     if (now == date) {
874       if (inclusive)
875     return date;
876       sign = 1;
877     } else if (now > date) {
878       sign = dir;
879     } else {
880       sign = -dir;
881     }
882     pnum = 1;
883
884     main_loop:
885     while (true) {
886       calendar.add(period.unit, (int) (pnum * period.value * dir * sign));
887
888       applyMask();
889
890       date = calendar.getTime().getTime();
891       if ((sign == -1) &&
892       ((((date - now) * dir) < 0) ||
893        (!inclusive && (date == now)))) {
894     calendar = clone;
895     return calendar.getTime().getTime();
896       }
897
898       if (! checkMask()) {
899     // this is not a valid event date
900
calendar = clone;
901     clone = (Calendar JavaDoc) calendar.clone();
902     pnum ++;
903     continue main_loop;
904       }
905
906       // this is a valid event date
907
if ((sign == 1) &&
908       ((((date - now) * dir) > 0) ||
909        (inclusive && (date == now)))) {
910     return date;
911       }
912
913       clone = (Calendar JavaDoc) calendar.clone();
914       pnum = 1;
915     }
916
917     // never reached
918
}
919 }
920
Popular Tags