/** * Provides a parser and evaluator for unix-like cron expressions. Cron expressions provide the * ability to specify complex time combinations such as "At 8:00am every Monday through * Friday" or "At 1:30am every last Friday of the month". * * <p>Cron expressions are comprised of 6 required fields and one optional field separated by white * space. The fields respectively are described as follows: * * <table cellspacing="8"> * <tr> * <th align="left">Field Name</th> * <th align="left"> </th> * <th align="left">Allowed Values</th> * <th align="left"> </th> * <th align="left">Allowed Special Characters</th> * </tr> * <tr> * <td align="left"><code>Seconds</code></td> * <td align="left"> </th> * <td align="left"><code>0-59</code></td> * <td align="left"> </th> * <td align="left"><code>, - * /</code></td> * </tr> * <tr> * <td align="left"><code>Minutes</code></td> * <td align="left"> </th> * <td align="left"><code>0-59</code></td> * <td align="left"> </th> * <td align="left"><code>, - * /</code></td> * </tr> * <tr> * <td align="left"><code>Hours</code></td> * <td align="left"> </th> * <td align="left"><code>0-23</code></td> * <td align="left"> </th> * <td align="left"><code>, - * /</code></td> * </tr> * <tr> * <td align="left"><code>Day-of-month</code></td> * <td align="left"> </th> * <td align="left"><code>1-31</code></td> * <td align="left"> </th> * <td align="left"><code>, - * ? / L W C</code></td> * </tr> * <tr> * <td align="left"><code>Month</code></td> * <td align="left"> </th> * <td align="left"><code>1-12 or JAN-DEC</code></td> * <td align="left"> </th> * <td align="left"><code>, - * /</code></td> * </tr> * <tr> * <td align="left"><code>Day-of-Week</code></td> * <td align="left"> </th> * <td align="left"><code>1-7 or SUN-SAT</code></td> * <td align="left"> </th> * <td align="left"><code>, - * ? / L #</code></td> * </tr> * <tr> * <td align="left"><code>Year (Optional)</code></td> * <td align="left"> </th> * <td align="left"><code>empty, 1970-2099</code></td> * <td align="left"> </th> * <td align="left"><code>, - * /</code></td> * </tr> * </table> * * <p>The '*' character is used to specify all values. For example, "*" in the minute * field means "every minute". * * <p>The '?' character is allowed for the day-of-month and day-of-week fields. It is used to * specify 'no specific value'. This is useful when you need to specify something in one of the two * fileds, but not the other. * * <p>The '-' character is used to specify ranges For example "10-12" in the hour field * means "the hours 10, 11 and 12". * * <p>The ',' character is used to specify additional values. For example "MON,WED,FRI" in * the day-of-week field means "the days Monday, Wednesday, and Friday". * * <p>The '/' character is used to specify increments. For example "0/15" in the seconds * field means "the seconds 0, 15, 30, and 45". And "5/15" in the seconds field * means "the seconds 5, 20, 35, and 50". Specifying '*' before the '/' is equivalent to * specifying 0 is the value to start with. Essentially, for each field in the expression, there is * a set of numbers that can be turned on or off. For seconds and minutes, the numbers range from 0 * to 59. For hours 0 to 23, for days of the month 0 to 31, and for months 1 to 12. The * "/" character simply helps you turn on every "nth" value in the given set. * Thus "7/6" in the month field only turns on month "7", it does NOT mean every * 6th month, please note that subtlety. * * <p>The 'L' character is allowed for the day-of-month and day-of-week fields. This character is * short-hand for "last", but it has different meaning in each of the two fields. For * example, the value "L" in the day-of-month field means "the last day of the * month" - day 31 for January, day 28 for February on non-leap years. If used in the * day-of-week field by itself, it simply means "7" or "SAT". But if used in the * day-of-week field after another value, it means "the last xxx day of the month" - for * example "6L" means "the last friday of the month". When using the 'L' option, * it is important not to specify lists, or ranges of values, as you'll get confusing results. * * <p>The 'W' character is allowed for the day-of-month field. This character is used to specify the * weekday (Monday-Friday) nearest the given day. As an example, if you were to specify * "15W" as the value for the day-of-month field, the meaning is: "the nearest * weekday to the 15th of the month". So if the 15th is a Saturday, the trigger will fire on * Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the 16th. If the 15th * is a Tuesday, then it will fire on Tuesday the 15th. However if you specify "1W" as the * value for day-of-month, and the 1st is a Saturday, the trigger will fire on Monday the 3rd, as it * will not 'jump' over the boundary of a month's days. The 'W' character can only be specified when * the day-of-month is a single day, not a range or list of days. * * <p>The 'L' and 'W' characters can also be combined for the day-of-month expression to yield 'LW', * which translates to "last weekday of the month". * * <p>The '#' character is allowed for the day-of-week field. This character is used to specify * "the nth" XXX day of the month. For example, the value of "6#3" in the * day-of-week field means the third Friday of the month (day 6 = Friday and "#3" = the * 3rd one in the month). Other examples: "2#1" = the first Monday of the month and * "4#5" = the fifth Wednesday of the month. Note that if you specify "#5" and * there is not 5 of the given day-of-week in the month, then no firing will occur that month. * * <p> * <!--The 'C' character is allowed for the day-of-month and day-of-week fields. * This character is short-hand for "calendar". This means values are * calculated against the associated calendar, if any. If no calendar is * associated, then it is equivalent to having an all-inclusive calendar. A * value of "5C" in the day-of-month field means "the first day included by the * calendar on or after the 5th". A value of "1C" in the day-of-week field * means "the first day included by the calendar on or after sunday".--> * * <p>The legal characters and the names of months and days of the week are not case sensitive. * * <p><b>NOTES:</b> * * <ul> * <li>Support for specifying both a day-of-week and a day-of-month value is not complete (you'll * need to use the '?' character in on of these fields). * </ul> * * @author Sharada Jambula, James House * @author Contributions from Mads Henderson * @author Refactoring from CronTrigger to CronExpression by Aaron Craven */ public class CronExpression { private static final WidgetsLocalizedMessages MSGS = WidgetsLocalizedMessagesSingleton.getInstance().getMessages(); private static final long serialVersionUID = 12423409423L; protected static final int SECOND = 0; protected static final int MINUTE = 1; protected static final int HOUR = 2; protected static final int DAY_OF_MONTH = 3; protected static final int MONTH = 4; protected static final int DAY_OF_WEEK = 5; protected static final int YEAR = 6; protected static final int ALL_SPEC_INT = 99; // '*' protected static final int NO_SPEC_INT = 98; // '?' protected static final Integer ALL_SPEC = new Integer(ALL_SPEC_INT); protected static final Integer NO_SPEC = new Integer(NO_SPEC_INT); protected static Map monthMap = new HashMap(20); protected static Map dayMap = new HashMap(60); static { monthMap.put("JAN", new Integer(0)); monthMap.put("FEB", new Integer(1)); monthMap.put("MAR", new Integer(2)); monthMap.put("APR", new Integer(3)); monthMap.put("MAY", new Integer(4)); monthMap.put("JUN", new Integer(5)); monthMap.put("JUL", new Integer(6)); monthMap.put("AUG", new Integer(7)); monthMap.put("SEP", new Integer(8)); monthMap.put("OCT", new Integer(9)); monthMap.put("NOV", new Integer(10)); monthMap.put("DEC", new Integer(11)); dayMap.put("SUN", new Integer(1)); dayMap.put("MON", new Integer(2)); dayMap.put("TUE", new Integer(3)); dayMap.put("WED", new Integer(4)); dayMap.put("THU", new Integer(5)); dayMap.put("FRI", new Integer(6)); dayMap.put("SAT", new Integer(7)); } private String cronExpression = null; protected transient TreeSet seconds; protected transient TreeSet minutes; protected transient TreeSet hours; protected transient TreeSet daysOfMonth; protected transient TreeSet months; protected transient TreeSet daysOfWeek; protected transient TreeSet years; protected transient boolean lastdayOfWeek = false; protected transient int nthdayOfWeek = 0; protected transient boolean lastdayOfMonth = false; protected transient boolean nearestWeekday = false; protected transient boolean calendardayOfWeek = false; protected transient boolean calendardayOfMonth = false; protected transient boolean expressionParsed = false; /** * Constructs a new <CODE>CronExpression</CODE> based on the specified parameter. * * @param cronExpression String representation of the cron expression the new object should * represent * @throws java.text.ParseException if the string expression cannot be parsed into a valid <CODE> * CronExpression</CODE> */ public CronExpression(String cronExpression) throws ParseException { if (cronExpression == null) { throw new IllegalArgumentException(MSGS.cronExpressionNull()); } this.cronExpression = cronExpression; buildExpression(cronExpression.toUpperCase()); } /** * Returns the string representation of the <CODE>CronExpression</CODE> * * @return a string representation of the <CODE>CronExpression</CODE> */ public String toString() { return cronExpression; } /** * Indicates whether the specified cron expression can be parsed into a valid cron expression * * @param cronExpression the expression to evaluate * @return a boolean indicating whether the given expression is a valid cron expression */ public static boolean isValidExpression(String cronExpression) { try { new CronExpression(cronExpression); } catch (ParseException pe) { return false; } return true; } //////////////////////////////////////////////////////////////////////////// // // Expression Parsing Functions // //////////////////////////////////////////////////////////////////////////// protected void buildExpression(String expression) throws ParseException { expressionParsed = true; try { if (seconds == null) seconds = new TreeSet(); if (minutes == null) minutes = new TreeSet(); if (hours == null) hours = new TreeSet(); if (daysOfMonth == null) daysOfMonth = new TreeSet(); if (months == null) months = new TreeSet(); if (daysOfWeek == null) daysOfWeek = new TreeSet(); if (years == null) years = new TreeSet(); int exprOn = SECOND; String[] exprsTok = expression.split(" |\\t"); for (int i = 0; i < exprsTok.length; i++) { if (exprOn > YEAR) { break; } String expr = exprsTok[i]; String[] vtok = expr.split(","); for (int j = 0; j < vtok.length; j++) { String v = vtok[j]; storeExpressionVals(0, v, exprOn); } exprOn++; } if (exprOn <= DAY_OF_WEEK) throw new ParseException(MSGS.cronUnexpectedEndOfExpression(), expression.length()); if (exprOn <= YEAR) storeExpressionVals(0, "*", YEAR); } catch (ParseException pe) { throw pe; } catch (Exception e) { throw new ParseException(MSGS.cronIllegalExpressionFormat(e.toString()), 0); } } protected int storeExpressionVals(int pos, String s, int type) throws ParseException { int incr = 0; int i = skipWhiteSpace(pos, s); if (i >= s.length()) return i; char c = s.charAt(i); if ((c >= 'A') && (c <= 'Z') && (!s.equals("L")) && (!s.equals("LW"))) { String sub = s.substring(i, i + 3); int sval = -1; int eval = -1; if (type == MONTH) { sval = getMonthNumber(sub) + 1; if (sval < 0) { throw new ParseException(MSGS.cronInvalidMonthValue(sub), i); } if (s.length() > i + 3) { c = s.charAt(i + 3); if (c == '-') { i += 4; sub = s.substring(i, i + 3); eval = getMonthNumber(sub) + 1; if (eval < 0) { throw new ParseException(MSGS.cronInvalidMonthValue(sub), i); } } } } else if (type == DAY_OF_WEEK) { sval = getDayOfWeekNumber(sub); if (sval < 0) { throw new ParseException(MSGS.cronInvalidDOWValue(sub), i); } if (s.length() > i + 3) { c = s.charAt(i + 3); if (c == '-') { i += 4; sub = s.substring(i, i + 3); eval = getDayOfWeekNumber(sub); if (eval < 0) { throw new ParseException(MSGS.cronInvalidDOWValue(sub), i); } if (sval > eval) { throw new ParseException( MSGS.cronInvalidDOWSequence(Integer.toString(sval), Integer.toString(eval)), i); } } else if (c == '#') { try { i += 4; nthdayOfWeek = Integer.parseInt(s.substring(i)); if (nthdayOfWeek < 1 || nthdayOfWeek > 5) throw new Exception(); } catch (Exception e) { throw new ParseException(MSGS.cronIllegalHashFollowingNumeric(), i); } } else if (c == 'L') { lastdayOfWeek = true; i++; } } } else { throw new ParseException(MSGS.cronIllegalCharactersForPosition(sub), i); } if (eval != -1) { incr = 1; } addToSet(sval, eval, incr, type); return (i + 3); } if (c == '?') { i++; if ((i + 1) < s.length() && (s.charAt(i) != ' ' && s.charAt(i + 1) != '\t')) { throw new ParseException( MSGS.cronIllegalCharacterAfter("?", String.valueOf(s.charAt(i))), i); } if (type != DAY_OF_WEEK && type != DAY_OF_MONTH) { throw new ParseException(MSGS.cronIllegalQuestionMark(), i); } if (type == DAY_OF_WEEK && !lastdayOfMonth) { int val = ((Integer) daysOfMonth.last()).intValue(); if (val == NO_SPEC_INT) { throw new ParseException(MSGS.cronIllegalQuestionMark(), i); } } addToSet(NO_SPEC_INT, -1, 0, type); return i; } if (c == '*' || c == '/') { if (c == '*' && (i + 1) >= s.length()) { addToSet(ALL_SPEC_INT, -1, incr, type); return i + 1; } else if (c == '/' && ((i + 1) >= s.length() || s.charAt(i + 1) == ' ' || s.charAt(i + 1) == '\t')) { throw new ParseException(MSGS.cronIllegalSlash(), i); } else if (c == '*') { i++; } c = s.charAt(i); if (c == '/') { // is an increment specified? i++; if (i >= s.length()) { throw new ParseException(MSGS.cronUnexpectedEndOfString(), i); } incr = getNumericValue(s, i); i++; if (incr > 10) i++; if (incr > 59 && (type == SECOND || type == MINUTE)) { throw new ParseException(MSGS.cronIllegalIncrement("60", Integer.toString(incr)), i); } else if (incr > 23 && (type == HOUR)) { throw new ParseException(MSGS.cronIllegalIncrement("24", Integer.toString(incr)), i); } else if (incr > 31 && (type == DAY_OF_MONTH)) { throw new ParseException(MSGS.cronIllegalIncrement("31", Integer.toString(incr)), i); } else if (incr > 7 && (type == DAY_OF_WEEK)) { throw new ParseException(MSGS.cronIllegalIncrement("7", Integer.toString(incr)), i); } else if (incr > 12 && (type == MONTH)) { throw new ParseException(MSGS.cronIllegalIncrement("12", Integer.toString(incr)), i); } } else incr = 1; addToSet(ALL_SPEC_INT, -1, incr, type); return i; } else if (c == 'L') { i++; if (type == DAY_OF_MONTH) lastdayOfMonth = true; if (type == DAY_OF_WEEK) addToSet(7, 7, 0, type); if (type == DAY_OF_MONTH && s.length() > i) { c = s.charAt(i); if (c == 'W') { nearestWeekday = true; i++; } } return i; } else if (c >= '0' && c <= '9') { int val = Integer.parseInt(String.valueOf(c)); i++; if (i >= s.length()) { addToSet(val, -1, -1, type); } else { c = s.charAt(i); if (c >= '0' && c <= '9') { ValueSet vs = getValue(val, s, i); val = vs.value; i = vs.pos; } i = checkNext(i, s, val, type); return i; } } else { throw new ParseException(MSGS.cronUnexpectedCharacter(String.valueOf(c)), i); } return i; } protected int checkNext(int pos, String s, int val, int type) throws ParseException { int end = -1; int i = pos; if (i >= s.length()) { addToSet(val, end, -1, type); return i; } char c = s.charAt(pos); if (c == 'L') { if (type == DAY_OF_WEEK) { lastdayOfWeek = true; } else { throw new ParseException(MSGS.cronOptionIsNotValidHere("L", Integer.toString(i)), i); } TreeSet set = getSet(type); set.add(new Integer(val)); i++; return i; } if (c == 'W') { if (type == DAY_OF_MONTH) { nearestWeekday = true; } else { throw new ParseException(MSGS.cronOptionIsNotValidHere("W", Integer.toString(i)), i); } TreeSet set = getSet(type); set.add(new Integer(val)); i++; return i; } if (c == '#') { if (type != DAY_OF_WEEK) { throw new ParseException(MSGS.cronOptionIsNotValidHere("#", Integer.toString(i)), i); } i++; try { nthdayOfWeek = Integer.parseInt(s.substring(i)); if (nthdayOfWeek < 1 || nthdayOfWeek > 5) { throw new Exception(); } } catch (Exception e) { throw new ParseException(MSGS.cronIllegalHashFollowingNumeric(), i); } TreeSet set = getSet(type); set.add(new Integer(val)); i++; return i; } if (c == 'C') { if (type == DAY_OF_WEEK) { calendardayOfWeek = true; } else if (type == DAY_OF_MONTH) { calendardayOfMonth = true; } else { throw new ParseException(MSGS.cronOptionIsNotValidHere("C", Integer.toString(i)), i); } TreeSet set = getSet(type); set.add(new Integer(val)); i++; return i; } if (c == '-') { i++; c = s.charAt(i); int v = Integer.parseInt(String.valueOf(c)); end = v; i++; if (i >= s.length()) { addToSet(val, end, 1, type); return i; } c = s.charAt(i); if (c >= '0' && c <= '9') { ValueSet vs = getValue(v, s, i); int v1 = vs.value; end = v1; i = vs.pos; } if (i < s.length() && ((c = s.charAt(i)) == '/')) { i++; c = s.charAt(i); int v2 = Integer.parseInt(String.valueOf(c)); i++; if (i >= s.length()) { addToSet(val, end, v2, type); return i; } c = s.charAt(i); if (c >= '0' && c <= '9') { ValueSet vs = getValue(v2, s, i); int v3 = vs.value; addToSet(val, end, v3, type); i = vs.pos; return i; } else { addToSet(val, end, v2, type); return i; } } else { addToSet(val, end, 1, type); return i; } } if (c == '/') { i++; c = s.charAt(i); int v2 = Integer.parseInt(String.valueOf(c)); i++; if (i >= s.length()) { addToSet(val, end, v2, type); return i; } c = s.charAt(i); if (c >= '0' && c <= '9') { ValueSet vs = getValue(v2, s, i); int v3 = vs.value; addToSet(val, end, v3, type); i = vs.pos; return i; } else { throw new ParseException(MSGS.cronUnexpectedCharacterAfterSlash(String.valueOf(c)), i); } } addToSet(val, end, 0, type); i++; return i; } public String getCronExpression() { return cronExpression; } protected int skipWhiteSpace(int i, String s) { for (; i < s.length() && (s.charAt(i) == ' ' || s.charAt(i) == '\t'); i++) ; return i; } protected int findNextWhiteSpace(int i, String s) { for (; i < s.length() && (s.charAt(i) != ' ' || s.charAt(i) != '\t'); i++) ; return i; } protected void addToSet(int val, int end, int incr, int type) throws ParseException { TreeSet set = getSet(type); if (type == SECOND || type == MINUTE) { if ((val < 0 || val > 59 || end > 59) && (val != ALL_SPEC_INT)) { throw new ParseException(MSGS.cronInvalidMinuteSecondValue(), -1); } } else if (type == HOUR) { if ((val < 0 || val > 23 || end > 23) && (val != ALL_SPEC_INT)) { throw new ParseException(MSGS.cronInvalidHourValue(), -1); } } else if (type == DAY_OF_MONTH) { if ((val < 1 || val > 31 || end > 31) && (val != ALL_SPEC_INT) && (val != NO_SPEC_INT)) { throw new ParseException(MSGS.cronInvalidDayOfMonthValue(), -1); } } else if (type == MONTH) { if ((val < 1 || val > 12 || end > 12) && (val != ALL_SPEC_INT)) { throw new ParseException(MSGS.cronInvalidMonthValueGeneral(), -1); } } else if (type == DAY_OF_WEEK) { if ((val == 0 || val > 7 || end > 7) && (val != ALL_SPEC_INT) && (val != NO_SPEC_INT)) { throw new ParseException(MSGS.cronInvalidDayOfWeekValue(), -1); } } if ((incr == 0 || incr == -1) && val != ALL_SPEC_INT) { if (val != -1) { set.add(new Integer(val)); } else { set.add(NO_SPEC); } return; } int startAt = val; int stopAt = end; if (val == ALL_SPEC_INT && incr <= 0) { incr = 1; set.add(ALL_SPEC); // put in a marker, but also fill values } if (type == SECOND || type == MINUTE) { if (stopAt == -1) { stopAt = 59; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 0; } } else if (type == HOUR) { if (stopAt == -1) { stopAt = 23; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 0; } } else if (type == DAY_OF_MONTH) { if (stopAt == -1) { stopAt = 31; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 1; } } else if (type == MONTH) { if (stopAt == -1) { stopAt = 12; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 1; } } else if (type == DAY_OF_WEEK) { if (stopAt == -1) { stopAt = 7; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 1; } } else if (type == YEAR) { if (stopAt == -1) { stopAt = 2099; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 1970; } } for (int i = startAt; i <= stopAt; i += incr) { set.add(new Integer(i)); } } protected TreeSet getSet(int type) { switch (type) { case SECOND: return seconds; case MINUTE: return minutes; case HOUR: return hours; case DAY_OF_MONTH: return daysOfMonth; case MONTH: return months; case DAY_OF_WEEK: return daysOfWeek; case YEAR: return years; default: return null; } } protected ValueSet getValue(int v, String s, int i) { char c = s.charAt(i); String s1 = String.valueOf(v); while (c >= '0' && c <= '9') { s1 += c; i++; if (i >= s.length()) { break; } c = s.charAt(i); } ValueSet val = new ValueSet(); if (i < s.length()) { val.pos = i; } else { val.pos = i + 1; } val.value = Integer.parseInt(s1); return val; } protected int getNumericValue(String s, int i) { int endOfVal = findNextWhiteSpace(i, s); String val = s.substring(i, endOfVal); return Integer.parseInt(val); } protected int getMonthNumber(String s) { Integer integer = (Integer) monthMap.get(s); if (integer == null) { return -1; } return integer.intValue(); } protected int getDayOfWeekNumber(String s) { Integer integer = (Integer) dayMap.get(s); if (integer == null) { return -1; } return integer.intValue(); } }
/** @author Steven Barkdull */ public class ScheduleEditor extends VerticalPanel implements IChangeHandler { public static enum ENDS_TYPE { TIME, DURATION } public static class DurationValues { public int days = 0; public int hours = 0; public int minutes = 0; } public static enum TIME { MILLISECOND(1), SECOND(MILLISECOND.time * 1000), MINUTE(SECOND.time * 60), HOUR(MINUTE.time * 60), DAY(HOUR.time * 24); private long time; TIME(long time) { this.time = time; } public long getTime() { return this.time; } } private static final WidgetsLocalizedMessages MSGS = WidgetsLocalizedMessagesSingleton.getInstance().getMessages(); protected static final String SCHEDULE_LABEL = "schedule-label"; // $NON-NLS-1$ protected static final String SCHEDULE_EDITOR_CAPTION_PANEL = "schedule-editor-caption-panel"; //$NON-NLS-1$ public enum ScheduleType { RUN_ONCE(0, MSGS.runOnce()), SECONDS(1, MSGS.seconds()), MINUTES(2, MSGS.minutes()), HOURS(3, MSGS.hours()), DAILY(4, MSGS.daily()), WEEKLY(5, MSGS.weekly()), MONTHLY(6, MSGS.monthly()), YEARLY(7, MSGS.yearly()), CRON(8, MSGS.cron()); private ScheduleType(int value, String name) { this.value = value; this.name = name; } private final int value; private final String name; private static ScheduleType[] scheduleValue = { RUN_ONCE, SECONDS, MINUTES, HOURS, DAILY, WEEKLY, MONTHLY, YEARLY, CRON }; public int value() { return value; } public String toString() { return name; } public static ScheduleType get(int idx) { return scheduleValue[idx]; } public static int length() { return scheduleValue.length; } public static ScheduleType stringToScheduleType(String strSchedule) throws EnumException { for (ScheduleType v : EnumSet.range(ScheduleType.RUN_ONCE, ScheduleType.CRON)) { if (v.toString().equals(strSchedule)) { return v; } } throw new EnumException(MSGS.invalidTemporalValue(scheduleValue.toString())); } } /* end enum */ private RunOnceEditor runOnceEditor = null; private RecurrenceEditor recurrenceEditor = null; private CronEditor cronEditor = null; // TODO sbarkdull, can this be static? private Map<ScheduleType, Panel> scheduleTypeMap = new HashMap<ScheduleType, Panel>(); private Map<TemporalValue, ScheduleType> temporalValueToScheduleTypeMap = createTemporalValueToScheduleTypeMap(); private Map<ScheduleType, TemporalValue> scheduleTypeToTemporalValueMap = createScheduleTypeMapToTemporalValue(); private ListBox scheduleCombo = null; private ICallback<IChangeHandler> onChangeHandler = null; private boolean isBlockoutDialog = false; private TimePicker startTimePicker = null; private TimePicker blockoutEndTimePicker = null; private Widget startTimePanel = null; private RadioButton endTimeRadioButton = null; private RadioButton durationRadioButton = null; private ListBox daysListBox = null; private ListBox hoursListBox = null; private ListBox minutesListBox = null; protected Button blockoutCheckButton = new Button(MSGS.viewBlockoutTimes()); protected ListBox timeZonePicker = null; public ScheduleEditor(ScheduleDialogType type) { super(); isBlockoutDialog = (type == ScheduleDialogType.BLOCKOUT); startTimePicker = new TimePicker(); setStylePrimaryName("scheduleEditor"); // $NON-NLS-1$ scheduleCombo = createScheduleCombo(); Label l = new Label(MSGS.recurrenceColon()); l.setStyleName(SCHEDULE_LABEL); add(l); add(scheduleCombo); SimplePanel hspacer = new SimplePanel(); hspacer.setWidth("100px"); // $NON-NLS-1$ if (!isBlockoutDialog) { startTimePanel = createStartTimePanel(); add(startTimePanel); } else { // Blockout End TimePicker blockoutEndTimePicker = new TimePicker(); blockoutEndTimePicker.setHour("01"); // $NON-NLS-1$ blockoutEndTimePicker.setMinute("00"); // $NON-NLS-1$ blockoutEndTimePicker.setTimeOfDay(TimeUtil.TimeOfDay.PM); // Blockout End Caption Panel blockoutEndTimePicker.getElement().getStyle().setDisplay(Display.NONE); final String[] daysList = new String[365]; final String[] hoursList = new String[24]; final String[] minutesList = new String[60]; // Populate list for (Integer i = 0; i < 365; i++) { String iStr = i.toString(); daysList[i] = iStr; if (i < 60) { minutesList[i] = iStr; if (i < 24) { hoursList[i] = iStr; } } } // Units of time Drop Down daysListBox = new ListBox(); daysListBox.getElement().setId("daysListBox"); // $NON-NLS-1$ populateListItems(daysListBox, daysList, 0, 365); final Label daysLabel = new Label(MSGS.dayOrDays()); daysLabel.getElement().setAttribute("for", daysListBox.getElement().getId()); // $NON-NLS-1$ hoursListBox = new ListBox(); hoursListBox.getElement().setId("hoursListBox"); // $NON-NLS-1$ populateListItems(hoursListBox, hoursList, 0, 24); final Label hoursLabel = new Label(MSGS.hourOrHours()); hoursLabel.getElement().setAttribute("for", hoursListBox.getElement().getId()); // $NON-NLS-1$ minutesListBox = new ListBox(); minutesListBox.getElement().setId("minutesListBox"); // $NON-NLS-1$ populateListItems(minutesListBox, minutesList, 0, 60); final Label minutesLabel = new Label(MSGS.minuteOrMinutes()); minutesLabel .getElement() .setAttribute("for", minutesListBox.getElement().getId()); // $NON-NLS-1$ final HorizontalPanel durationPanel = new HorizontalPanel(); durationPanel.setVerticalAlignment(VerticalPanel.ALIGN_MIDDLE); durationPanel.setSpacing(blockoutEndTimePicker.getSpacing()); durationPanel.add(daysListBox); durationPanel.add(daysLabel); durationPanel.add(hoursListBox); durationPanel.add(hoursLabel); durationPanel.add(minutesListBox); durationPanel.add(minutesLabel); // Bind change handler this.scheduleCombo.addChangeHandler( new ChangeHandler() { @Override public void onChange(ChangeEvent event) { String scheduleType = scheduleCombo.getItemText(scheduleCombo.getSelectedIndex()); if (ScheduleType.RUN_ONCE.toString().equals(scheduleType)) { show( true, daysListBox, daysLabel, hoursListBox, hoursLabel, minutesListBox, minutesLabel); populateListItems(daysListBox, daysList, 0, 365); populateListItems(hoursListBox, hoursList, 0, 24); populateListItems(minutesListBox, minutesList, 0, 60); } else if (ScheduleType.HOURS.toString().equals(scheduleType)) { hide(true, daysListBox, daysLabel, hoursListBox, hoursLabel); show(true, minutesListBox, minutesLabel); populateListItems(minutesListBox, minutesList, 0, 60); } else if (ScheduleType.DAILY.toString().equals(scheduleType)) { hide(true, daysListBox, daysLabel); show(true, hoursListBox, hoursLabel, minutesListBox, minutesLabel); populateListItems(hoursListBox, hoursList, 0, 24); populateListItems(minutesListBox, minutesList, 0, 60); } else if (ScheduleType.WEEKLY.toString().equals(scheduleType)) { show( true, daysListBox, daysLabel, hoursListBox, hoursLabel, minutesListBox, minutesLabel); populateListItems(daysListBox, daysList, 0, 7); populateListItems(hoursListBox, hoursList, 0, 24); populateListItems(minutesListBox, minutesList, 0, 60); } else if (ScheduleType.MONTHLY.toString().equals(scheduleType)) { show( true, daysListBox, daysLabel, hoursListBox, hoursLabel, minutesListBox, minutesLabel); populateListItems(daysListBox, daysList, 0, 28); populateListItems(hoursListBox, hoursList, 0, 24); populateListItems(minutesListBox, minutesList, 0, 60); } else if (ScheduleType.YEARLY.toString().equals(scheduleType)) { show( true, daysListBox, daysLabel, hoursListBox, hoursLabel, minutesListBox, minutesLabel); populateListItems(daysListBox, daysList, 0, 365); populateListItems(hoursListBox, hoursList, 0, 24); populateListItems(minutesListBox, minutesList, 0, 60); } } }); /* * Radio Buttons for duration */ this.durationRadioButton = new RadioButton("durationRadioGroup", "durationRadioButton"); // $NON-NLS-1$ //$NON-NLS-2$ this.durationRadioButton.setText(MSGS.duration()); this.durationRadioButton.setValue(Boolean.TRUE); this.durationRadioButton.addClickHandler( new ClickHandler() { @Override public void onClick(ClickEvent event) { blockoutEndTimePicker.getElement().getStyle().setDisplay(Display.NONE); durationPanel.getElement().getStyle().clearDisplay(); } }); this.endTimeRadioButton = new RadioButton("durationRadioGroup", "endTimeRadioButton"); // $NON-NLS-1$ //$NON-NLS-2$ this.endTimeRadioButton.setText(MSGS.endTime()); this.endTimeRadioButton.addClickHandler( new ClickHandler() { @Override public void onClick(ClickEvent event) { blockoutEndTimePicker.getElement().getStyle().clearDisplay(); durationPanel.getElement().getStyle().setDisplay(Display.NONE); } }); // Radio Buttons Panel HorizontalPanel radioButtonsPanel = new HorizontalPanel(); radioButtonsPanel.setVerticalAlignment(VerticalPanel.ALIGN_MIDDLE); radioButtonsPanel.add(this.durationRadioButton); radioButtonsPanel.add(this.endTimeRadioButton); // Ends Panel VerticalPanel endsPanel = new VerticalPanel(); endsPanel.add(radioButtonsPanel); endsPanel.add(blockoutEndTimePicker); endsPanel.add(durationPanel); // Blockout period CaptionPanel blockoutStartCaptionPanel = new CaptionPanel(MSGS.startTime()); HorizontalPanel blockoutStartPanel = new HorizontalPanel(); blockoutStartPanel.add(getStartTimePicker()); timeZonePicker = new ListBox(); timeZonePicker.setStyleName("timeZonePicker"); timeZonePicker.setVisibleItemCount(1); blockoutStartPanel.add(timeZonePicker); timeZonePicker.getElement().getParentElement().getStyle().setPaddingTop(5, Unit.PX); blockoutStartCaptionPanel.add(blockoutStartPanel); populateTimeZonePicker(); // Ends Caption Panel CaptionPanel endCaptionPanel = new CaptionPanel(MSGS.endsCaptionTitle()); endCaptionPanel.add(endsPanel); VerticalPanel blockoutPanel = new VerticalPanel(); blockoutPanel.setWidth("100%"); // $NON-NLS-1$ blockoutPanel.add(blockoutStartCaptionPanel); blockoutPanel.add(endCaptionPanel); add(blockoutPanel); } VerticalPanel vp = new VerticalPanel(); vp.setWidth("100%"); // $NON-NLS-1$ add(vp); setCellHeight(vp, "100%"); // $NON-NLS-1$ runOnceEditor = new RunOnceEditor(startTimePicker); vp.add(runOnceEditor); scheduleTypeMap.put(ScheduleType.RUN_ONCE, runOnceEditor); runOnceEditor.setVisible(true); recurrenceEditor = new RecurrenceEditor(startTimePicker); vp.add(recurrenceEditor); scheduleTypeMap.put(ScheduleType.SECONDS, recurrenceEditor); scheduleTypeMap.put(ScheduleType.MINUTES, recurrenceEditor); scheduleTypeMap.put(ScheduleType.HOURS, recurrenceEditor); scheduleTypeMap.put(ScheduleType.DAILY, recurrenceEditor); scheduleTypeMap.put(ScheduleType.WEEKLY, recurrenceEditor); scheduleTypeMap.put(ScheduleType.MONTHLY, recurrenceEditor); scheduleTypeMap.put(ScheduleType.YEARLY, recurrenceEditor); recurrenceEditor.setVisible(false); cronEditor = new CronEditor(); scheduleTypeMap.put(ScheduleType.CRON, cronEditor); cronEditor.setVisible(false); if (!isBlockoutDialog) { vp.add(cronEditor); VerticalPanel blockoutButtonPanel = new VerticalPanel(); blockoutButtonPanel.setWidth("100%"); // $NON-NLS-1$ // blockoutButtonPanel.setHeight("30%"); blockoutButtonPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER); blockoutButtonPanel.setVerticalAlignment(VerticalPanel.ALIGN_MIDDLE); // We want to add a button to check for blockout conflicts blockoutCheckButton.setStyleName("pentaho-button"); // $NON-NLS-1$ blockoutCheckButton.getElement().setId("blockout-check-button"); // $NON-NLS-1$ blockoutCheckButton.setVisible(false); hspacer.setHeight("50px"); // $NON-NLS-1$ blockoutButtonPanel.add(hspacer); blockoutButtonPanel.add(blockoutCheckButton); vp.add(hspacer); add(blockoutButtonPanel); } configureOnChangeHandler(); } private void show(boolean applyToParent, UIObject... objs) { for (UIObject obj : objs) { Element ele = obj.getElement(); if (applyToParent) { ele = ele.getParentElement(); } ele.getStyle().clearDisplay(); } } private void hide(boolean applyToParent, UIObject... objs) { for (UIObject obj : objs) { Element ele = obj.getElement(); if (applyToParent) { ele = ele.getParentElement(); } ele.getStyle().setDisplay(Display.NONE); } } private void populateListItems(ListBox listBox, String[] arr, int startIndex, int howMany) { // Clear items listBox.clear(); // Add itesm int endIndex = startIndex + howMany; for (int i = startIndex; i < endIndex; i++) { listBox.addItem(arr[i]); } } private void populateTimeZonePicker() { String url = GWT.getHostPageBaseURL() + "api/system/timezones"; // $NON-NLS-1$ RequestBuilder timeZonesRequest = new RequestBuilder(RequestBuilder.GET, url); timeZonesRequest.setHeader("accept", "application/json"); // $NON-NLS-1$ //$NON-NLS-2$ timeZonesRequest.setHeader("If-Modified-Since", "01 Jan 1970 00:00:00 GMT"); try { timeZonesRequest.sendRequest( null, new RequestCallback() { @Override public void onResponseReceived(Request request, Response response) { timeZonePicker.clear(); String responseText = response.getText(); JSONValue value = JSONParser.parseLenient(responseText); JSONObject object = value.isObject(); value = object.get("timeZones"); JSONValue serverTZvalue = object.get("serverTzId"); JSONString serverTZIdString = serverTZvalue.isString(); String serverTZId = serverTZIdString.stringValue(); object = value.isObject(); value = object.get("entry"); JSONArray timeZonesJSONArray = value.isArray(); for (int i = 0; i < timeZonesJSONArray.size(); i++) { JSONValue entryValue = timeZonesJSONArray.get(i); JSONObject entryObject = entryValue.isObject(); JSONValue keyValue = entryObject.get("key"); JSONValue theValue = entryObject.get("value"); String key = keyValue.isString().stringValue(); String valueForKey = theValue.isString().stringValue(); timeZonePicker.addItem(valueForKey, key); } for (int i = 0; i < timeZonePicker.getItemCount(); i++) { if (timeZonePicker.getValue(i).equalsIgnoreCase(serverTZId)) { timeZonePicker.setSelectedIndex(i); break; } } } @Override public void onError(Request request, Throwable exception) { // TODO Auto-generated method stub } }); } catch (RequestException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public ListBox getTimeZonePicker() { return timeZonePicker; } public void setBlockoutButtonHandler(final ClickHandler handler) { blockoutCheckButton.addClickHandler(handler); } public Button getBlockoutCheckButton() { return blockoutCheckButton; } public TimePicker getStartTimePicker() { return startTimePicker; } public TimePicker getBlockoutEndTimePicker() { return blockoutEndTimePicker; } public ENDS_TYPE getBlockoutEndsType() { return this.durationRadioButton.getValue() ? ENDS_TYPE.DURATION : ENDS_TYPE.TIME; } public DurationValues getDurationValues() { DurationValues vals = new DurationValues(); String displayNone = Display.NONE.getCssName(); // Days if (!displayNone.equals(this.daysListBox.getElement().getStyle().getDisplay())) { vals.days = Integer.parseInt(this.daysListBox.getItemText(this.daysListBox.getSelectedIndex())); } // Hours if (!displayNone.equals(this.hoursListBox.getElement().getStyle().getDisplay())) { vals.hours = Integer.parseInt(this.hoursListBox.getItemText(this.hoursListBox.getSelectedIndex())); } // Minutes if (!displayNone.equals(this.minutesListBox.getElement().getStyle().getDisplay())) { vals.minutes = Integer.parseInt(this.minutesListBox.getItemText(this.minutesListBox.getSelectedIndex())); } return vals; } public void setDurationFields(long duration) { long remainder = duration; long days = remainder / TIME.DAY.getTime(); remainder -= days * TIME.DAY.getTime(); long hours = remainder / TIME.HOUR.getTime(); remainder -= hours * TIME.HOUR.getTime(); long minutes = remainder / TIME.MINUTE.getTime(); this.daysListBox.setSelectedIndex(new Long(days).intValue()); this.hoursListBox.setSelectedIndex(new Long(hours).intValue()); this.minutesListBox.setSelectedIndex(new Long(minutes).intValue()); // Set valid end time if range is within 24hrs if (duration < TIME.DAY.getTime()) { boolean isPM = hours >= 12; this.blockoutEndTimePicker.setHour(new Long(hours + (isPM ? -12 : 0)).toString()); this.blockoutEndTimePicker.setMinute(new Long(minutes).toString()); this.blockoutEndTimePicker.setTimeOfDay(isPM ? TimeOfDay.PM : TimeOfDay.AM); } } protected Widget createStartTimePanel() { CaptionPanel startTimeGB = new CaptionPanel(MSGS.startTime()); startTimeGB.setStyleName(SCHEDULE_EDITOR_CAPTION_PANEL); startTimeGB.add(getStartTimePicker()); return startTimeGB; } public void reset(Date now) { runOnceEditor.reset(now); recurrenceEditor.reset(now); cronEditor.reset(now); setScheduleType(ScheduleType.RUN_ONCE); } public String getCronString() { switch (getScheduleType()) { case RUN_ONCE: return null; case SECONDS: // fall through case MINUTES: // fall through case HOURS: // fall through case DAILY: // fall through case WEEKLY: // fall through case MONTHLY: // fall through case YEARLY: return recurrenceEditor.getCronString(); case CRON: return cronEditor.getCronString(); default: throw new RuntimeException(MSGS.invalidRunType(getScheduleType().toString())); } } /** * @param cronStr * @throws CronParseException if cronStr is not a valid CRON string. */ public void setCronString(String cronStr) throws CronParseException { // Try original simplistic parser... CronParser cp = new CronParser(cronStr); String recurrenceStr = null; try { recurrenceStr = cp.parseToRecurrenceString(); // throws CronParseException } catch (CronParseException e) { if (!CronExpression.isValidExpression(cronStr)) { // Parse with proper expression parser throw e; } recurrenceStr = null; // valid cronstring, not parse-able to recurrence string } if (null != recurrenceStr) { recurrenceEditor.inititalizeWithRecurrenceString(recurrenceStr); TemporalValue tv = recurrenceEditor.getTemporalState(); ScheduleType rt = temporalValueToScheduleType(tv); setScheduleType(rt); } else { // its a cron string that cannot be parsed into a recurrence string, switch to cron string // editor. setScheduleType(ScheduleType.CRON); } cronEditor.setCronString(cronStr); } /** * @return null if the selected schedule does not support repeat-in-seconds, otherwise return the * number of seconds between schedule execution. * @throws RuntimeException if the temporal value is invalid. This condition occurs as a result of * programmer error. */ public Long getRepeatInSecs() throws RuntimeException { return recurrenceEditor.getRepeatInSecs(); } public void setRepeatInSecs(Integer repeatInSecs) { recurrenceEditor.inititalizeWithRepeatInSecs(repeatInSecs); TemporalValue tv = recurrenceEditor.getTemporalState(); ScheduleType rt = temporalValueToScheduleType(tv); setScheduleType(rt); } private ListBox createScheduleCombo() { final ScheduleEditor localThis = this; ListBox lb = new ListBox(); lb.setVisibleItemCount(1); // lb.setStyleName("scheduleCombo"); //$NON-NLS-1$ lb.addChangeHandler( new ChangeHandler() { @Override public void onChange(ChangeEvent event) { localThis.handleScheduleChange(); } }); // add all schedule types to the combobox for (ScheduleType schedType : EnumSet.range(ScheduleType.RUN_ONCE, ScheduleType.CRON)) { if (!isBlockoutDialog || (schedType != ScheduleType.CRON && schedType != ScheduleType.SECONDS && schedType != ScheduleType.MINUTES && schedType != ScheduleType.HOURS)) { lb.addItem(schedType.toString()); } } lb.setItemSelected(0, true); return lb; } public ScheduleType getScheduleType() { String selectedValue = scheduleCombo.getValue(scheduleCombo.getSelectedIndex()); return ScheduleType.stringToScheduleType(selectedValue); } public void setScheduleType(ScheduleType scheduleType) { int itemCount = scheduleCombo.getItemCount(); for (int i = 0; i < itemCount; i++) { String itemText = scheduleCombo.getItemText(i); if (itemText.equals(scheduleType.toString())) { scheduleCombo.setSelectedIndex(i); } } selectScheduleTypeEditor(scheduleType); } /** * NOTE: should only ever be used by validators. This is a backdoor into this class that shouldn't * be here, do not use this method unless you are validating. * * @return DateRangeEditor */ public RecurrenceEditor getRecurrenceEditor() { return recurrenceEditor; } /** * NOTE: should only ever be used by validators. This is a backdoor into this class that shouldn't * be here, do not use this method unless you are validating. * * @return DateRangeEditor */ public CronEditor getCronEditor() { return cronEditor; } /** * NOTE: should only ever be used by validators. This is a backdoor into this class that shouldn't * be here, do not use this method unless you are validating. * * @return DateRangeEditor */ public RunOnceEditor getRunOnceEditor() { return runOnceEditor; } public void setStartTime(String startTime) { runOnceEditor.setStartTime(startTime); recurrenceEditor.setStartTime(startTime); } public void setBlockoutEndTime(String endTime) { blockoutEndTimePicker.setTime(endTime); } public String getStartTime() { switch (getScheduleType()) { case RUN_ONCE: return runOnceEditor.getStartTime(); case SECONDS: // fall through case MINUTES: // fall through case HOURS: // fall through case DAILY: // fall through case WEEKLY: // fall through case MONTHLY: // fall through case YEARLY: return recurrenceEditor.getStartTime(); case CRON: return cronEditor.getStartTime(); default: throw new RuntimeException(MSGS.invalidRunType(getScheduleType().toString())); } } public void setStartDate(Date startDate) { runOnceEditor.setStartDate(startDate); recurrenceEditor.setStartDate(startDate); cronEditor.setStartDate(startDate); } public Date getStartDate() { switch (getScheduleType()) { case RUN_ONCE: Date startDate = runOnceEditor.getStartDate(); String startTime = runOnceEditor.getStartTime(); String[] times = startTime.split(":"); // $NON-NLS-1$ int hour = Integer.parseInt(times[0]); int minute = Integer.parseInt(times[1]); if (startTime.indexOf("PM") >= 0) { // $NON-NLS-1$ hour += 12; } startDate.setHours(hour); startDate.setMinutes(minute); startDate.setSeconds(0); return startDate; case SECONDS: // fall through case MINUTES: // fall through case HOURS: // fall through case DAILY: // fall through case WEEKLY: // fall through case MONTHLY: // fall through case YEARLY: return recurrenceEditor.getStartDate(); case CRON: return cronEditor.getStartDate(); default: throw new RuntimeException(MSGS.invalidRunType(getScheduleType().toString())); } } public void setEndDate(Date endDate) { recurrenceEditor.setEndDate(endDate); cronEditor.setEndDate(endDate); } public Date getEndDate() { switch (getScheduleType()) { case RUN_ONCE: return null; case SECONDS: // fall through case MINUTES: // fall through case HOURS: // fall through case DAILY: // fall through case WEEKLY: // fall through case MONTHLY: // fall through case YEARLY: return recurrenceEditor.getEndDate(); case CRON: return cronEditor.getEndDate(); default: throw new RuntimeException(MSGS.invalidRunType(getScheduleType().toString())); } } public void setNoEndDate() { recurrenceEditor.setNoEndDate(); cronEditor.setNoEndDate(); } public void setEndBy() { cronEditor.setEndBy(); recurrenceEditor.setEndBy(); } private void handleScheduleChange() throws EnumException { ScheduleType schedType = getScheduleType(); selectScheduleTypeEditor(schedType); } private void selectScheduleTypeEditor(ScheduleType scheduleType) { // if we are switching to cron type, then hide the start time panel if ((isBlockoutDialog == false) && (startTimePanel != null)) { if (scheduleType == ScheduleType.CRON) { startTimePanel.setVisible(false); } else { startTimePanel.setVisible(true); } } // hide all panels for (Map.Entry<ScheduleType, Panel> me : scheduleTypeMap.entrySet()) { me.getValue().setVisible(false); } // show the selected panel Panel p = scheduleTypeMap.get(scheduleType); p.setVisible(true); TemporalValue tv = scheduleTypeToTemporalValue(scheduleType); if (null != tv) { // force the recurrence editor to display the appropriate ui recurrenceEditor.setTemporalState(tv); } } private static Map<TemporalValue, ScheduleType> createTemporalValueToScheduleTypeMap() { Map<TemporalValue, ScheduleType> m = new HashMap<TemporalValue, ScheduleType>(); m.put(TemporalValue.SECONDS, ScheduleType.SECONDS); m.put(TemporalValue.MINUTES, ScheduleType.MINUTES); m.put(TemporalValue.HOURS, ScheduleType.HOURS); m.put(TemporalValue.DAILY, ScheduleType.DAILY); m.put(TemporalValue.WEEKLY, ScheduleType.WEEKLY); m.put(TemporalValue.MONTHLY, ScheduleType.MONTHLY); m.put(TemporalValue.YEARLY, ScheduleType.YEARLY); return m; } private static Map<ScheduleType, TemporalValue> createScheduleTypeMapToTemporalValue() { Map<ScheduleType, TemporalValue> m = new HashMap<ScheduleType, TemporalValue>(); m.put(ScheduleType.SECONDS, TemporalValue.SECONDS); m.put(ScheduleType.MINUTES, TemporalValue.MINUTES); m.put(ScheduleType.HOURS, TemporalValue.HOURS); m.put(ScheduleType.DAILY, TemporalValue.DAILY); m.put(ScheduleType.WEEKLY, TemporalValue.WEEKLY); m.put(ScheduleType.MONTHLY, TemporalValue.MONTHLY); m.put(ScheduleType.YEARLY, TemporalValue.YEARLY); return m; } private ScheduleType temporalValueToScheduleType(TemporalValue tv) { return temporalValueToScheduleTypeMap.get(tv); } private TemporalValue scheduleTypeToTemporalValue(ScheduleType st) { return scheduleTypeToTemporalValueMap.get(st); } public void setOnChangeHandler(ICallback<IChangeHandler> handler) { this.onChangeHandler = handler; } protected void changeHandler() { if (null != onChangeHandler) { onChangeHandler.onHandle(this); } } private void configureOnChangeHandler() { final ScheduleEditor localThis = this; ICallback<IChangeHandler> handler = new ICallback<IChangeHandler>() { @Override public void onHandle(IChangeHandler o) { localThis.changeHandler(); } }; ChangeHandler changeHandler = new ChangeHandler() { @Override public void onChange(ChangeEvent event) { localThis.changeHandler(); } }; ClickHandler clickHandler = new ClickHandler() { @Override public void onClick(ClickEvent event) { localThis.changeHandler(); } }; scheduleCombo.addChangeHandler(changeHandler); runOnceEditor.setOnChangeHandler(handler); recurrenceEditor.setOnChangeHandler(handler); cronEditor.setOnChangeHandler(handler); if (daysListBox != null) { this.daysListBox.addChangeHandler(changeHandler); } if (hoursListBox != null) { this.hoursListBox.addChangeHandler(changeHandler); } if (minutesListBox != null) { this.minutesListBox.addChangeHandler(changeHandler); } if (this.startTimePicker != null) { startTimePicker.setOnChangeHandler(handler); } if (this.blockoutEndTimePicker != null) { this.blockoutEndTimePicker.setOnChangeHandler(handler); } if (this.durationRadioButton != null) { this.durationRadioButton.addClickHandler(clickHandler); } if (this.endTimeRadioButton != null) { this.endTimeRadioButton.addClickHandler(clickHandler); } } public boolean isBlockoutDialog() { return isBlockoutDialog; } }
/** @author Steven Barkdull */ @SuppressWarnings("deprecation") public class DateRangeEditor extends CaptionPanel implements IChangeHandler { private static final WidgetsLocalizedMessages MSGS = WidgetsLocalizedMessagesSingleton.getInstance().getMessages(); private static final String SCHEDULE_EDITOR_CAPTION_PANEL = "schedule-editor-caption-panel"; //$NON-NLS-1$ private static final String END_DATE_RB_GROUP = "end-date-group"; // $NON-NLS-1$ private static final String END_DATE_PICKER = "end-date-picker"; // $NON-NLS-1$ private static final String START_DATE_PICKER = "start-date-picker"; // $NON-NLS-1$ private DatePickerEx startDatePicker = null; private EndDatePanel endDatePanel = null; private ErrorLabel startLabel = null; private ICallback<IChangeHandler> onChangeHandler = null; private static int uniqueInstanceNumber = 0; public DateRangeEditor(Date date) { super(MSGS.rangeOfRecurrence()); this.addStyleName(SCHEDULE_EDITOR_CAPTION_PANEL); uniqueInstanceNumber += 1; HorizontalPanel outerHP = new HorizontalPanel(); add(outerHP); HorizontalPanel hp = new HorizontalPanel(); Label l = new Label(MSGS.startLabel()); l.setStyleName("startLabel"); // $NON-NLS-1$ hp.add(l); DefaultFormat format = new DefaultFormat(DateTimeFormat.getShortDateFormat()); startDatePicker = new DatePickerEx(format); startDatePicker.getDatePicker().setStyleName(START_DATE_PICKER); hp.add(startDatePicker.getDatePicker()); startLabel = new ErrorLabel(hp); outerHP.add(startLabel); endDatePanel = new EndDatePanel(date); outerHP.add(endDatePanel); reset(date); configureOnChangeHandler(); } public void setStartDateError(String errorMsg) { startLabel.setErrorMsg(errorMsg); } public Date getStartDate() { return startDatePicker.getSelectedDate(); } public void setStartDate(Date d) { startDatePicker.getDatePicker().setValue(d); } public Date getEndDate() { return endDatePanel.getDate(); } public void setEndDate(Date d) { endDatePanel.setDate(d); } public void reset(Date d) { startDatePicker.getDatePicker().setValue(d); endDatePanel.reset(d); } public void setNoEndDate() { endDatePanel.setNoEndDate(); } public boolean isEndBy() { return endDatePanel.isEndBy(); } public void setEndBy() { endDatePanel.setEndBy(); } public boolean isNoEndDate() { return endDatePanel.isNoEndDate(); } public void setEndByError(String errorMsg) { endDatePanel.setEndByError(errorMsg); } public void setOnChangeHandler(ICallback<IChangeHandler> handler) { this.onChangeHandler = handler; } private void changeHandler() { if (null != onChangeHandler) { onChangeHandler.onHandle(this); } } private void configureOnChangeHandler() { final DateRangeEditor localThis = this; ICallback<IChangeHandler> handler = new ICallback<IChangeHandler>() { public void onHandle(IChangeHandler o) { localThis.changeHandler(); } }; startDatePicker.setOnChangeHandler(handler); endDatePanel.setOnChangeHandler(handler); } private class EndDatePanel extends VerticalPanel implements IChangeHandler { private DatePickerEx endDatePicker = null; private RadioButton noEndDateRb = null; private RadioButton endByRb = null; private ErrorLabel endByLabel = null; private ICallback<IChangeHandler> onChangeHandler = null; public EndDatePanel(Date date) { final EndDatePanel localThis = this; noEndDateRb = new RadioButton(END_DATE_RB_GROUP + uniqueInstanceNumber, MSGS.noEndDateLabel()); noEndDateRb.setStyleName("recurrenceRadioButton"); // $NON-NLS-1$ noEndDateRb.setValue(true); add(noEndDateRb); HorizontalPanel hp = new HorizontalPanel(); add(hp); HorizontalPanel endByPanel = new HorizontalPanel(); endByRb = new RadioButton(END_DATE_RB_GROUP + uniqueInstanceNumber, MSGS.endByLabel()); endByRb.setStyleName("recurrenceRadioButton"); // $NON-NLS-1$ endByPanel.add(endByRb); DefaultFormat format = new DefaultFormat(DateTimeFormat.getShortDateFormat()); endDatePicker = new DatePickerEx(format); endDatePicker.getDatePicker().setStyleName(END_DATE_PICKER); endDatePicker.getDatePicker().setEnabled(false); endByPanel.add(endDatePicker.getDatePicker()); endByLabel = new ErrorLabel(endByPanel); hp.add(endByLabel); noEndDateRb.addClickListener( new ClickListener() { public void onClick(Widget sender) { localThis.endDatePicker.getDatePicker().setEnabled(false); } }); endByRb.addClickListener( new ClickListener() { public void onClick(Widget sender) { localThis.endDatePicker.getDatePicker().setEnabled(true); } }); reset(date); configureOnChangeHandler(); } public void reset(Date d) { setNoEndDate(); endDatePicker.getDatePicker().setValue(d); } @SuppressWarnings("unused") public DatePickerEx getEndDatePicker() { return endDatePicker; } public void setNoEndDate() { endByRb.setValue(false); noEndDateRb.setValue(true); endDatePicker.getDatePicker().setEnabled(false); } public boolean isEndBy() { return endByRb.getValue(); } public void setEndBy() { noEndDateRb.setValue(false); endByRb.setValue(true); endDatePicker.getDatePicker().setEnabled(true); } public boolean isNoEndDate() { return noEndDateRb.getValue(); } public Date getDate() { return isEndBy() ? endDatePicker.getSelectedDate() : null; } public void setDate(Date d) { endDatePicker.getDatePicker().setValue(d); } public void setEndByError(String errorMsg) { endByLabel.setErrorMsg(errorMsg); } public void setOnChangeHandler(ICallback<IChangeHandler> handler) { this.onChangeHandler = handler; } private void changeHandler() { if (null != onChangeHandler) { onChangeHandler.onHandle(this); } } private void configureOnChangeHandler() { final EndDatePanel localThis = this; ICallback<IChangeHandler> handler = new ICallback<IChangeHandler>() { public void onHandle(IChangeHandler o) { localThis.changeHandler(); } }; KeyboardListener keyboardListener = new KeyboardListener() { public void onKeyDown(Widget sender, char keyCode, int modifiers) {} public void onKeyPress(Widget sender, char keyCode, int modifiers) {} public void onKeyUp(Widget sender, char keyCode, int modifiers) { localThis.changeHandler(); } }; ClickListener clickListener = new ClickListener() { public void onClick(Widget sender) { localThis.changeHandler(); } }; endDatePicker.setOnChangeHandler(handler); noEndDateRb.addClickListener(clickListener); noEndDateRb.addKeyboardListener(keyboardListener); endByRb.addClickListener(clickListener); endByRb.addKeyboardListener(keyboardListener); } } // end EndDatePanel }