001
014
015
044
045 package com.liferay.util.cal;
046
047 import com.liferay.portal.kernel.util.ArrayUtil;
048 import com.liferay.portal.kernel.util.CalendarFactoryUtil;
049 import com.liferay.portal.kernel.util.StringBundler;
050 import com.liferay.portal.kernel.util.StringPool;
051 import com.liferay.portal.kernel.util.TimeZoneUtil;
052
053 import java.io.Serializable;
054
055 import java.util.Calendar;
056 import java.util.Date;
057
058
063 @Deprecated
064 public class Recurrence implements Serializable {
065
066
069 public static final int DAILY = 3;
070
071
074 public static final int MONTHLY = 5;
075
076
079 public static final int NO_RECURRENCE = 7;
080
081
084 public static final int WEEKLY = 4;
085
086
089 public static final int YEARLY = 6;
090
091
094 public Recurrence() {
095 this(null, new Duration(), NO_RECURRENCE);
096 }
097
098
101 public Recurrence(Calendar start, Duration dur) {
102 this(start, dur, NO_RECURRENCE);
103 }
104
105
108 public Recurrence(Calendar start, Duration dur, int freq) {
109 setDtStart(start);
110
111 duration = (Duration)dur.clone();
112 frequency = freq;
113 interval = 1;
114 }
115
116
117
118
123 public DayAndPosition[] getByDay() {
124 if (byDay == null) {
125 return null;
126 }
127
128 DayAndPosition[] b = new DayAndPosition[byDay.length];
129
130
134 for (int i = 0; i < byDay.length; i++) {
135 b[i] = (DayAndPosition)byDay[i].clone();
136 }
137
138 return b;
139 }
140
141
146 public int[] getByMonth() {
147 if (byMonth == null) {
148 return null;
149 }
150
151 int[] b = new int[byMonth.length];
152
153 System.arraycopy(byMonth, 0, b, 0, byMonth.length);
154
155 return b;
156 }
157
158
163 public int[] getByMonthDay() {
164 if (byMonthDay == null) {
165 return null;
166 }
167
168 int[] b = new int[byMonthDay.length];
169
170 System.arraycopy(byMonthDay, 0, b, 0, byMonthDay.length);
171
172 return b;
173 }
174
175
180 public int[] getByWeekNo() {
181 if (byWeekNo == null) {
182 return null;
183 }
184
185 int[] b = new int[byWeekNo.length];
186
187 System.arraycopy(byWeekNo, 0, b, 0, byWeekNo.length);
188
189 return b;
190 }
191
192
197 public int[] getByYearDay() {
198 if (byYearDay == null) {
199 return null;
200 }
201
202 int[] b = new int[byYearDay.length];
203
204 System.arraycopy(byYearDay, 0, b, 0, byYearDay.length);
205
206 return b;
207 }
208
209
215 public Calendar getCandidateStartTime(Calendar current) {
216 if (dtStart.getTime().getTime() > current.getTime().getTime()) {
217 throw new IllegalArgumentException("Current time before DtStart");
218 }
219
220 int minInterval = getMinimumInterval();
221 Calendar candidate = (Calendar)current.clone();
222
223 if (true) {
224
225
226
227 candidate.clear(Calendar.ZONE_OFFSET);
228 candidate.clear(Calendar.DST_OFFSET);
229 candidate.setTimeZone(TimeZoneUtil.getTimeZone(StringPool.UTC));
230 candidate.setMinimalDaysInFirstWeek(4);
231 candidate.setFirstDayOfWeek(dtStart.getFirstDayOfWeek());
232 }
233
234 if (frequency == NO_RECURRENCE) {
235 candidate.setTime(dtStart.getTime());
236
237 return candidate;
238 }
239
240 reduce_constant_length_field(Calendar.SECOND, dtStart, candidate);
241 reduce_constant_length_field(Calendar.MINUTE, dtStart, candidate);
242 reduce_constant_length_field(Calendar.HOUR_OF_DAY, dtStart, candidate);
243
244 switch (minInterval) {
245
246 case DAILY :
247
248
249
250 break;
251
252 case WEEKLY :
253 reduce_constant_length_field(
254 Calendar.DAY_OF_WEEK, dtStart, candidate);
255 break;
256
257 case MONTHLY :
258 reduce_day_of_month(dtStart, candidate);
259 break;
260
261 case YEARLY :
262 reduce_day_of_year(dtStart, candidate);
263 break;
264 }
265
266 return candidate;
267 }
268
269
274 public Calendar getDtEnd() {
275
276
280 Calendar tempEnd = (Calendar)dtStart.clone();
281
282 tempEnd.setTime(
283 new Date(dtStart.getTime().getTime() + duration.getInterval()));
284
285 return tempEnd;
286 }
287
288
293 public Calendar getDtStart() {
294 return (Calendar)dtStart.clone();
295 }
296
297
302 public Duration getDuration() {
303 return (Duration)duration.clone();
304 }
305
306
311 public int getFrequency() {
312 return frequency;
313 }
314
315
320 public int getInterval() {
321 return interval;
322 }
323
324
329 public int getOccurrence() {
330 return occurrence;
331 }
332
333
338 public Calendar getUntil() {
339 return ((until != null) ? (Calendar)until.clone() : null);
340 }
341
342
347 public int getWeekStart() {
348 return dtStart.getFirstDayOfWeek();
349 }
350
351
357 public boolean isInRecurrence(Calendar current) {
358 return isInRecurrence(current, false);
359 }
360
361
368 public boolean isInRecurrence(Calendar current, boolean debug) {
369 Calendar myCurrent = (Calendar)current.clone();
370
371
372
373 myCurrent.clear(Calendar.ZONE_OFFSET);
374 myCurrent.clear(Calendar.DST_OFFSET);
375 myCurrent.setTimeZone(TimeZoneUtil.getTimeZone(StringPool.UTC));
376 myCurrent.setMinimalDaysInFirstWeek(4);
377 myCurrent.setFirstDayOfWeek(dtStart.getFirstDayOfWeek());
378 myCurrent.set(Calendar.SECOND, 0);
379 myCurrent.set(Calendar.MILLISECOND, 0);
380
381 if (myCurrent.getTime().getTime() < dtStart.getTime().getTime()) {
382
383
384
385 if (debug) {
386 System.err.println("current < start");
387 }
388
389 return false;
390 }
391
392 Calendar candidate = getCandidateStartTime(myCurrent);
393
394
395
396 while ((candidate.getTime().getTime() + duration.getInterval()) >
397 myCurrent.getTime().getTime()) {
398
399 if (candidateIsInRecurrence(candidate, debug)) {
400 return true;
401 }
402
403
404
405 candidate.add(Calendar.SECOND, -1);
406
407
408
409 if (candidate.getTime().getTime() < dtStart.getTime().getTime()) {
410 if (debug) {
411 System.err.println("No candidates after dtStart");
412 }
413
414 return false;
415 }
416
417 candidate = getCandidateStartTime(candidate);
418 }
419
420 if (debug) {
421 System.err.println("No matching candidates");
422 }
423
424 return false;
425 }
426
427
430 public void setByDay(DayAndPosition[] b) {
431 if (b == null) {
432 byDay = null;
433
434 return;
435 }
436
437 byDay = new DayAndPosition[b.length];
438
439
443 for (int i = 0; i < b.length; i++) {
444 byDay[i] = (DayAndPosition)b[i].clone();
445 }
446 }
447
448
451 public void setByMonth(int[] b) {
452 if (b == null) {
453 byMonth = null;
454
455 return;
456 }
457
458 byMonth = new int[b.length];
459
460 System.arraycopy(b, 0, byMonth, 0, b.length);
461 }
462
463
466 public void setByMonthDay(int[] b) {
467 if (b == null) {
468 byMonthDay = null;
469
470 return;
471 }
472
473 byMonthDay = new int[b.length];
474
475 System.arraycopy(b, 0, byMonthDay, 0, b.length);
476 }
477
478
481 public void setByWeekNo(int[] b) {
482 if (b == null) {
483 byWeekNo = null;
484
485 return;
486 }
487
488 byWeekNo = new int[b.length];
489
490 System.arraycopy(b, 0, byWeekNo, 0, b.length);
491 }
492
493
496 public void setByYearDay(int[] b) {
497 if (b == null) {
498 byYearDay = null;
499
500 return;
501 }
502
503 byYearDay = new int[b.length];
504
505 System.arraycopy(b, 0, byYearDay, 0, b.length);
506 }
507
508
511 public void setDtEnd(Calendar end) {
512 Calendar tempEnd = (Calendar)end.clone();
513
514 tempEnd.clear(Calendar.ZONE_OFFSET);
515 tempEnd.clear(Calendar.DST_OFFSET);
516 tempEnd.setTimeZone(TimeZoneUtil.getTimeZone(StringPool.UTC));
517 duration.setInterval(
518 tempEnd.getTime().getTime() - dtStart.getTime().getTime());
519 }
520
521
524 public void setDtStart(Calendar start) {
525 int oldStart = 0;
526
527 if (dtStart != null) {
528 oldStart = dtStart.getFirstDayOfWeek();
529 }
530 else {
531 oldStart = Calendar.MONDAY;
532 }
533
534 if (start == null) {
535 dtStart = CalendarFactoryUtil.getCalendar(
536 TimeZoneUtil.getTimeZone(StringPool.UTC));
537
538 dtStart.setTime(new Date(0L));
539 }
540 else {
541 dtStart = (Calendar)start.clone();
542
543 dtStart.clear(Calendar.ZONE_OFFSET);
544 dtStart.clear(Calendar.DST_OFFSET);
545 dtStart.setTimeZone(TimeZoneUtil.getTimeZone(StringPool.UTC));
546 }
547
548 dtStart.setMinimalDaysInFirstWeek(4);
549 dtStart.setFirstDayOfWeek(oldStart);
550 dtStart.set(Calendar.SECOND, 0);
551 dtStart.set(Calendar.MILLISECOND, 0);
552 }
553
554
557 public void setDuration(Duration d) {
558 duration = (Duration)d.clone();
559 }
560
561
564 public void setFrequency(int freq) {
565 if ((frequency != DAILY) && (frequency != WEEKLY) &&
566 (frequency != MONTHLY) && (frequency != YEARLY) &&
567 (frequency != NO_RECURRENCE)) {
568
569 throw new IllegalArgumentException("Invalid frequency");
570 }
571
572 frequency = freq;
573 }
574
575
578 public void setInterval(int intr) {
579 interval = (intr > 0) ? intr : 1;
580 }
581
582
585 public void setOccurrence(int occur) {
586 occurrence = occur;
587 }
588
589
592 public void setUntil(Calendar u) {
593 if (u == null) {
594 until = null;
595
596 return;
597 }
598
599 until = (Calendar)u.clone();
600
601 until.clear(Calendar.ZONE_OFFSET);
602 until.clear(Calendar.DST_OFFSET);
603 until.setTimeZone(TimeZoneUtil.getTimeZone(StringPool.UTC));
604 }
605
606
609 public void setWeekStart(int weekstart) {
610 dtStart.setFirstDayOfWeek(weekstart);
611 }
612
613
618 @Override
619 public String toString() {
620 StringBundler sb = new StringBundler();
621
622 sb.append(getClass().getName());
623 sb.append("[dtStart=");
624 sb.append((dtStart != null) ? dtStart.toString() : "null");
625 sb.append(",duration=");
626 sb.append((duration != null) ? duration.toString() : "null");
627 sb.append(",frequency=");
628 sb.append(frequency);
629 sb.append(",interval=");
630 sb.append(interval);
631 sb.append(",until=");
632 sb.append((until != null) ? until.toString() : "null");
633 sb.append(",byDay=");
634
635 if (byDay == null) {
636 sb.append("null");
637 }
638 else {
639 sb.append("[");
640
641 for (int i = 0; i < byDay.length; i++) {
642 if (i != 0) {
643 sb.append(",");
644 }
645
646 if (byDay[i] != null) {
647 sb.append(byDay[i].toString());
648 }
649 else {
650 sb.append("null");
651 }
652 }
653
654 sb.append("]");
655 }
656
657 sb.append(",byMonthDay=");
658 sb.append(stringizeIntArray(byMonthDay));
659 sb.append(",byYearDay=");
660 sb.append(stringizeIntArray(byYearDay));
661 sb.append(",byWeekNo=");
662 sb.append(stringizeIntArray(byWeekNo));
663 sb.append(",byMonth=");
664 sb.append(stringizeIntArray(byMonth));
665 sb.append(']');
666
667 return sb.toString();
668 }
669
670
675 protected static long getDayNumber(Calendar cal) {
676 Calendar tempCal = (Calendar)cal.clone();
677
678
679
680 tempCal.set(Calendar.MILLISECOND, 0);
681 tempCal.set(Calendar.SECOND, 0);
682 tempCal.set(Calendar.MINUTE, 0);
683 tempCal.set(Calendar.HOUR_OF_DAY, 0);
684
685 return tempCal.getTime().getTime() / (24 * 60 * 60 * 1000);
686 }
687
688
693 protected static long getMonthNumber(Calendar cal) {
694 return
695 ((cal.get(Calendar.YEAR) - 1970) * 12L) +
696 ((cal.get(Calendar.MONTH) - Calendar.JANUARY));
697 }
698
699
704 protected static long getWeekNumber(Calendar cal) {
705 Calendar tempCal = (Calendar)cal.clone();
706
707
708
709 tempCal.set(Calendar.MILLISECOND, 0);
710 tempCal.set(Calendar.SECOND, 0);
711 tempCal.set(Calendar.MINUTE, 0);
712 tempCal.set(Calendar.HOUR_OF_DAY, 0);
713
714
715
716 int delta =
717 tempCal.getFirstDayOfWeek() - tempCal.get(Calendar.DAY_OF_WEEK);
718
719 if (delta > 0) {
720 delta -= 7;
721 }
722
723
724
725
726
727
728 long weekEpoch =
729 (tempCal.getFirstDayOfWeek() - Calendar.THURSDAY) * 24L * 60 * 60 *
730 1000;
731
732 return
733 (tempCal.getTime().getTime() - weekEpoch) /
734 (7 * 24 * 60 * 60 * 1000);
735 }
736
737
740 protected static void reduce_constant_length_field(
741 int field, Calendar start, Calendar candidate) {
742
743 if ((start.getMaximum(field) != start.getLeastMaximum(field)) ||
744 (start.getMinimum(field) != start.getGreatestMinimum(field))) {
745
746 throw new IllegalArgumentException("Not a constant length field");
747 }
748
749 int fieldLength =
750 (start.getMaximum(field) - start.getMinimum(field) + 1);
751 int delta = start.get(field) - candidate.get(field);
752
753 if (delta > 0) {
754 delta -= fieldLength;
755 }
756
757 candidate.add(field, delta);
758 }
759
760
763 protected static void reduce_day_of_month(
764 Calendar start, Calendar candidate) {
765
766 Calendar tempCal = (Calendar)candidate.clone();
767
768 tempCal.add(Calendar.MONTH, -1);
769
770 int delta = start.get(Calendar.DATE) - candidate.get(Calendar.DATE);
771
772 if (delta > 0) {
773 delta -= tempCal.getActualMaximum(Calendar.DATE);
774 }
775
776 candidate.add(Calendar.DATE, delta);
777
778 while (start.get(Calendar.DATE) != candidate.get(Calendar.DATE)) {
779 tempCal.add(Calendar.MONTH, -1);
780 candidate.add(
781 Calendar.DATE, -tempCal.getActualMaximum(Calendar.DATE));
782 }
783 }
784
785
788 protected static void reduce_day_of_year(
789 Calendar start, Calendar candidate) {
790
791 if ((start.get(Calendar.MONTH) > candidate.get(Calendar.MONTH)) ||
792 ((start.get(Calendar.MONTH) == candidate.get(Calendar.MONTH)) &&
793 (start.get(Calendar.DATE) > candidate.get(Calendar.DATE)))) {
794
795 candidate.add(Calendar.YEAR, -1);
796 }
797
798
799
800 candidate.set(Calendar.MONTH, start.get(Calendar.MONTH));
801 candidate.set(Calendar.DATE, start.get(Calendar.DATE));
802
803 while ((start.get(Calendar.MONTH) != candidate.get(Calendar.MONTH)) ||
804 (start.get(Calendar.DATE) != candidate.get(Calendar.DATE))) {
805
806 candidate.add(Calendar.YEAR, -1);
807 candidate.set(Calendar.MONTH, start.get(Calendar.MONTH));
808 candidate.set(Calendar.DATE, start.get(Calendar.DATE));
809 }
810 }
811
812
817 protected boolean candidateIsInRecurrence(
818 Calendar candidate, boolean debug) {
819
820 if ((until != null) &&
821 (candidate.getTime().getTime() > until.getTime().getTime())) {
822
823
824
825 if (debug) {
826 System.err.println("after until");
827 }
828
829 return false;
830 }
831
832 if ((getRecurrenceCount(candidate) % interval) != 0) {
833
834
835
836 if (debug) {
837 System.err.println("not an interval rep");
838 }
839
840 return false;
841 }
842 else if ((occurrence > 0) &&
843 (getRecurrenceCount(candidate) >= occurrence)) {
844
845 return false;
846 }
847
848 if (!matchesByDay(candidate) || !matchesByMonthDay(candidate) ||
849 !matchesByYearDay(candidate) || !matchesByWeekNo(candidate) ||
850 !matchesByMonth(candidate)) {
851
852
853
854 if (debug) {
855 System.err.println("doesn't match a by*");
856 }
857
858 return false;
859 }
860
861 if (debug) {
862 System.err.println("All checks succeeded");
863 }
864
865 return true;
866 }
867
868
873 protected int getMinimumInterval() {
874 if ((frequency == DAILY) || (byDay != null) || (byMonthDay != null) ||
875 (byYearDay != null)) {
876
877 return DAILY;
878 }
879 else if ((frequency == WEEKLY) || (byWeekNo != null)) {
880 return WEEKLY;
881 }
882 else if ((frequency == MONTHLY) || (byMonth != null)) {
883 return MONTHLY;
884 }
885 else if (frequency == YEARLY) {
886 return YEARLY;
887 }
888 else if (frequency == NO_RECURRENCE) {
889 return NO_RECURRENCE;
890 }
891 else {
892
893
894
895 throw new IllegalStateException(
896 "Internal error: Unknown frequency value");
897 }
898 }
899
900
905 protected int getRecurrenceCount(Calendar candidate) {
906 switch (frequency) {
907
908 case NO_RECURRENCE :
909 return 0;
910
911 case DAILY :
912 return (int)(getDayNumber(candidate) - getDayNumber(dtStart));
913
914 case WEEKLY :
915 Calendar tempCand = (Calendar)candidate.clone();
916
917 tempCand.setFirstDayOfWeek(dtStart.getFirstDayOfWeek());
918
919 return (int)(getWeekNumber(tempCand) - getWeekNumber(dtStart));
920
921 case MONTHLY :
922 return
923 (int)(getMonthNumber(candidate) - getMonthNumber(dtStart));
924
925 case YEARLY :
926 return
927 candidate.get(Calendar.YEAR) - dtStart.get(Calendar.YEAR);
928
929 default :
930 throw new IllegalStateException("bad frequency internally...");
931 }
932 }
933
934
939 protected boolean matchesByDay(Calendar candidate) {
940 if (ArrayUtil.isEmpty(byDay)) {
941
942
943
944 return true;
945 }
946
947 for (int i = 0; i < byDay.length; i++) {
948 if (matchesIndividualByDay(candidate, byDay[i])) {
949 return true;
950 }
951 }
952
953 return false;
954 }
955
956
961 protected boolean matchesByField(
962 int[] array, int field, Calendar candidate, boolean allowNegative) {
963
964 if (ArrayUtil.isEmpty(array)) {
965
966
967
968 return true;
969 }
970
971 for (int i = 0; i < array.length; i++) {
972 int val = 0;
973
974 if (allowNegative && (array[i] < 0)) {
975
976
977
978 int max = candidate.getActualMaximum(field);
979
980 val = (max + 1) + array[i];
981 }
982 else {
983 val = array[i];
984 }
985
986 if (val == candidate.get(field)) {
987 return true;
988 }
989 }
990
991 return false;
992 }
993
994
999 protected boolean matchesByMonth(Calendar candidate) {
1000 return matchesByField(byMonth, Calendar.MONTH, candidate, false);
1001 }
1002
1003
1008 protected boolean matchesByMonthDay(Calendar candidate) {
1009 return matchesByField(byMonthDay, Calendar.DATE, candidate, true);
1010 }
1011
1012
1017 protected boolean matchesByWeekNo(Calendar candidate) {
1018 return matchesByField(byWeekNo, Calendar.WEEK_OF_YEAR, candidate, true);
1019 }
1020
1021
1026 protected boolean matchesByYearDay(Calendar candidate) {
1027 return matchesByField(byYearDay, Calendar.DAY_OF_YEAR, candidate, true);
1028 }
1029
1030
1035 protected boolean matchesIndividualByDay(
1036 Calendar candidate, DayAndPosition pos) {
1037
1038 if (pos.getDayOfWeek() != candidate.get(Calendar.DAY_OF_WEEK)) {
1039 return false;
1040 }
1041
1042 int position = pos.getDayPosition();
1043
1044 if (position == 0) {
1045 return true;
1046 }
1047
1048 int field = Calendar.DAY_OF_MONTH;
1049
1050 if (position > 0) {
1051 int candidatePosition = ((candidate.get(field) - 1) / 7) + 1;
1052
1053 return (position == candidatePosition);
1054 }
1055 else {
1056
1057
1058
1059 int negativeCandidatePosition =
1060 ((candidate.getActualMaximum(field) - candidate.get(field)) /
1061 7) + 1;
1062
1063 return (-position == negativeCandidatePosition);
1064 }
1065 }
1066
1067
1072 protected String stringizeIntArray(int[] a) {
1073 if (a == null) {
1074 return "null";
1075 }
1076
1077 StringBundler sb = new StringBundler(2 * a.length + 1);
1078
1079 sb.append("[");
1080
1081 for (int i = 0; i < a.length; i++) {
1082 if (i != 0) {
1083 sb.append(",");
1084 }
1085
1086 sb.append(a[i]);
1087 }
1088
1089 sb.append("]");
1090
1091 return sb.toString();
1092 }
1093
1094
1097 protected DayAndPosition[] byDay;
1098
1099
1102 protected int[] byMonth;
1103
1104
1107 protected int[] byMonthDay;
1108
1109
1112 protected int[] byWeekNo;
1113
1114
1117 protected int[] byYearDay;
1118
1119
1122 protected Calendar dtStart;
1123
1124
1127 protected Duration duration;
1128
1129
1132 protected int frequency;
1133
1134
1137 protected int interval;
1138
1139
1142 protected int occurrence = 0;
1143
1144
1147 protected Calendar until;
1148
1149 }