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 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()));
    }
  }
  protected Widget createStartTimePanel() {
    CaptionPanel startTimeGB = new CaptionPanel(MSGS.startTime());
    startTimeGB.setStyleName(SCHEDULE_EDITOR_CAPTION_PANEL);

    startTimeGB.add(getStartTimePicker());

    return startTimeGB;
  }
  /**
   * 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());
  }
  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);
    }
  }
 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()));
   }
 }
/** @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;
  }
}
  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();
  }
  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 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;
  }
  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;
  }