001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2009-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2015 ForgeRock AS.
016 */
017
018package org.opends.guitools.controlpanel.ui;
019
020import static org.opends.messages.AdminToolMessages.*;
021
022import java.awt.Component;
023import java.awt.Dimension;
024import java.awt.GridBagConstraints;
025import java.awt.GridBagLayout;
026import java.awt.event.ItemEvent;
027import java.awt.event.ItemListener;
028import java.util.ArrayList;
029import java.util.Calendar;
030import java.util.Collection;
031import java.util.Date;
032import java.util.GregorianCalendar;
033
034import javax.swing.Box;
035import javax.swing.DefaultComboBoxModel;
036import javax.swing.JCheckBox;
037import javax.swing.JComboBox;
038import javax.swing.JComponent;
039import javax.swing.JEditorPane;
040import javax.swing.JLabel;
041import javax.swing.JPanel;
042import javax.swing.JTextField;
043import javax.swing.text.PlainDocument;
044
045import org.opends.guitools.controlpanel.datamodel.CategorizedComboBoxElement;
046import org.opends.guitools.controlpanel.datamodel.ScheduleType;
047import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
048import org.opends.guitools.controlpanel.ui.components.
049 NumericLimitedSizeDocumentFilter;
050import org.opends.guitools.controlpanel.ui.components.TimeDocumentFilter;
051import org.opends.guitools.controlpanel.ui.renderer.
052 NoLeftInsetCategoryComboBoxRenderer;
053import org.opends.guitools.controlpanel.util.Utilities;
054import org.forgerock.i18n.LocalizableMessage;
055import org.opends.server.backends.task.RecurringTask;
056
057/**
058 * The panel that allows the user to specify when a task will be launched.
059 *
060 */
061public class TaskToSchedulePanel extends StatusGenericPanel
062{
063  private static final long serialVersionUID = 6855081932432566784L;
064
065  private String taskName;
066
067  private JComboBox scheduleType;
068
069  private JTextField time;
070  private JTextField day;
071  private JComboBox month;
072  private JComboBox year;
073
074  private JLabel lTime;
075  private JLabel lDay;
076  private JLabel lMonth;
077  private JLabel lYear;
078
079  private JLabel lDailyTime;
080  private JTextField dailyTime;
081
082  private JLabel lWeeklyTime;
083  private JLabel lWeeklyDays;
084  private JTextField weeklyTime;
085  private JCheckBox sunday, monday, tuesday, wednesday, thursday, friday,
086  saturday;
087  {
088    sunday =
089      Utilities.createCheckBox(INFO_CTRL_PANEL_TASK_TO_SCHEDULE_SUNDAY.get());
090    monday =
091      Utilities.createCheckBox(INFO_CTRL_PANEL_TASK_TO_SCHEDULE_MONDAY.get());
092    tuesday =
093      Utilities.createCheckBox(INFO_CTRL_PANEL_TASK_TO_SCHEDULE_TUESDAY.get());
094    wednesday =
095      Utilities.createCheckBox(
096          INFO_CTRL_PANEL_TASK_TO_SCHEDULE_WEDNESDAY.get());
097    thursday =
098      Utilities.createCheckBox(INFO_CTRL_PANEL_TASK_TO_SCHEDULE_THURSDAY.get());
099    friday =
100      Utilities.createCheckBox(INFO_CTRL_PANEL_TASK_TO_SCHEDULE_FRIDAY.get());
101    saturday =
102      Utilities.createCheckBox(INFO_CTRL_PANEL_TASK_TO_SCHEDULE_SATURDAY.get());
103  }
104
105  JCheckBox[] weekDays =
106  {
107      sunday, monday, tuesday, wednesday, thursday, friday, saturday
108  };
109
110  private JLabel lMonthlyTime;
111  private JLabel lMonthlyDays;
112  private JTextField monthlyTime;
113  private JCheckBox[] monthDays = new JCheckBox[31];
114
115  private JLabel lCronMinute;
116  private JLabel lCronHour;
117  private JLabel lCronWeekDay;
118  private JLabel lCronMonthDay;
119  private JLabel lCronMonth;
120
121  private JTextField cronMinute;
122  private JTextField cronHour;
123  private JTextField cronWeekDay;
124  private JTextField cronMonthDay;
125  private JTextField cronMonth;
126
127  private Component launchLaterPanel;
128  private Component dailyPanel;
129  private Component weeklyPanel;
130  private Component monthlyPanel;
131  private Component cronPanel;
132
133  private LocalizableMessage LAUNCH_NOW = INFO_CTRL_PANEL_LAUNCH_NOW.get();
134  private LocalizableMessage LAUNCH_LATER = INFO_CTRL_PANEL_LAUNCH_LATER.get();
135  private LocalizableMessage LAUNCH_DAILY = INFO_CTRL_PANEL_TASK_TO_SCHEDULE_DAILY.get();
136  private LocalizableMessage LAUNCH_WEEKLY = INFO_CTRL_PANEL_TASK_TO_SCHEDULE_WEEKLY.get();
137  private LocalizableMessage LAUNCH_MONTHLY =
138    INFO_CTRL_PANEL_TASK_TO_SCHEDULE_MONTHLY.get();
139  private LocalizableMessage CRON = INFO_CTRL_PANEL_TASK_TO_SCHEDULE_CRON.get();
140
141  private ScheduleType schedule;
142
143  /**
144   * Default constructor.
145   * @param taskName the name of the task to be scheduled.
146   */
147  public TaskToSchedulePanel(String taskName)
148  {
149    super();
150    this.taskName = taskName;
151    createLayout();
152  }
153
154  /**
155   * Creates the layout of the panel (but the contents are not populated here).
156   */
157  private void createLayout()
158  {
159    GridBagConstraints gbc = new GridBagConstraints();
160    JEditorPane explanation = Utilities.makeHtmlPane(
161        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_SUMMARY.get(taskName).toString(),
162        ColorAndFontConstants.defaultFont);
163    gbc.gridx = 0;
164    gbc.gridy = 0;
165    gbc.gridwidth = 1;
166    gbc.fill = GridBagConstraints.HORIZONTAL;
167    add(explanation, gbc);
168    gbc.gridy ++;
169    gbc.insets.top = 10;
170    scheduleType = Utilities.createComboBox();
171    scheduleType.setModel(new DefaultComboBoxModel());
172
173    ArrayList<Object> newElements = new ArrayList<>();
174    newElements.add(new CategorizedComboBoxElement(LAUNCH_NOW,
175        CategorizedComboBoxElement.Type.REGULAR));
176    newElements.add(COMBO_SEPARATOR);
177    newElements.add(new CategorizedComboBoxElement(LAUNCH_LATER,
178        CategorizedComboBoxElement.Type.REGULAR));
179    newElements.add(COMBO_SEPARATOR);
180    newElements.add(new CategorizedComboBoxElement(LAUNCH_DAILY,
181        CategorizedComboBoxElement.Type.REGULAR));
182    newElements.add(new CategorizedComboBoxElement(LAUNCH_WEEKLY,
183        CategorizedComboBoxElement.Type.REGULAR));
184    newElements.add(new CategorizedComboBoxElement(LAUNCH_MONTHLY,
185        CategorizedComboBoxElement.Type.REGULAR));
186    newElements.add(new CategorizedComboBoxElement(CRON,
187        CategorizedComboBoxElement.Type.REGULAR));
188    updateComboBoxModel(newElements,
189        (DefaultComboBoxModel)scheduleType.getModel());
190    scheduleType.setRenderer(
191        new NoLeftInsetCategoryComboBoxRenderer(scheduleType));
192    scheduleType.addItemListener(new IgnoreItemListener(scheduleType));
193
194    gbc.weightx = 0.0;
195    gbc.anchor = GridBagConstraints.NORTHWEST;
196    gbc.fill = GridBagConstraints.NONE;
197    add(scheduleType, gbc);
198
199    launchLaterPanel = createLaunchLaterPanel();
200    dailyPanel = createDailyPanel();
201    weeklyPanel = createWeeklyPanel();
202    monthlyPanel = createMonthlyPanel();
203    cronPanel = createCronPanel();
204
205    scheduleType.addItemListener(new ItemListener()
206    {
207      public void itemStateChanged(ItemEvent ev)
208      {
209        Object element = scheduleType.getSelectedItem();
210        boolean launchLaterVisible = false;
211        boolean launchDailyVisible = false;
212        boolean launchWeeklyVisible = false;
213        boolean launchMonthlyVisible = false;
214        boolean cronVisible = false;
215        if (element != null)
216        {
217          if (element instanceof CategorizedComboBoxElement)
218          {
219            element = ((CategorizedComboBoxElement)element).getValue();
220          }
221          launchLaterVisible = element == LAUNCH_LATER;
222          launchDailyVisible = element == LAUNCH_DAILY;
223          launchWeeklyVisible = element == LAUNCH_WEEKLY;
224          launchMonthlyVisible = element == LAUNCH_MONTHLY;
225          cronVisible = element == CRON;
226        }
227        launchLaterPanel.setVisible(launchLaterVisible);
228        dailyPanel.setVisible(launchDailyVisible);
229        weeklyPanel.setVisible(launchWeeklyVisible);
230        monthlyPanel.setVisible(launchMonthlyVisible);
231        cronPanel.setVisible(cronVisible);
232      }
233    });
234    launchLaterPanel.setVisible(false);
235    dailyPanel.setVisible(false);
236    weeklyPanel.setVisible(false);
237    monthlyPanel.setVisible(false);
238    cronPanel.setVisible(false);
239
240    int width = 0;
241    int height = 0;
242    Component[] comps =
243    {launchLaterPanel, dailyPanel, weeklyPanel, monthlyPanel, cronPanel};
244    for (Component comp : comps)
245    {
246      width = Math.max(width, comp.getPreferredSize().width);
247      height = Math.max(height, comp.getPreferredSize().height);
248    }
249
250    gbc.gridy ++;
251    gbc.gridwidth = 1;
252    gbc.gridx = 0;
253    gbc.weightx = 0.0;
254    gbc.insets.left = 30;
255    add(launchLaterPanel, gbc);
256    add(dailyPanel, gbc);
257    add(weeklyPanel, gbc);
258    add(monthlyPanel, gbc);
259    add(cronPanel, gbc);
260    add(Box.createRigidArea(new Dimension(width, height)), gbc);
261    gbc.gridy ++;
262    gbc.gridx = 0;
263    gbc.fill = GridBagConstraints.VERTICAL;
264    gbc.weighty = 1.0;
265    add(Box.createVerticalGlue(), gbc);
266  }
267
268  /** {@inheritDoc} */
269  public void toBeDisplayed(boolean visible)
270  {
271    // Reset the schedule and the labels
272    if (visible)
273    {
274      schedule = null;
275      setPrimaryValid(lTime);
276      setPrimaryValid(lDay);
277      setPrimaryValid(lMonth);
278      setPrimaryValid(lYear);
279      setPrimaryValid(lWeeklyTime);
280      setPrimaryValid(lWeeklyDays);
281      setPrimaryValid(lMonthlyTime);
282      setPrimaryValid(lMonthlyDays);
283      setPrimaryValid(lCronMinute);
284      setPrimaryValid(lCronHour);
285      setPrimaryValid(lCronMonthDay);
286      setPrimaryValid(lCronMonth);
287      setPrimaryValid(lCronWeekDay);
288    }
289  }
290
291  /** {@inheritDoc} */
292  public LocalizableMessage getTitle()
293  {
294    return INFO_CTRL_PANEL_TASK_TO_SCHEDULE_TITLE.get(taskName);
295  }
296
297  /** {@inheritDoc} */
298  public void okClicked()
299  {
300    schedule = null;
301    ArrayList<LocalizableMessage> errorMessages = new ArrayList<>();
302
303    updateErrorMessages(errorMessages);
304
305    if (!errorMessages.isEmpty())
306    {
307      displayErrorDialog(errorMessages);
308    }
309    else
310    {
311      schedule = createSchedule();
312      Utilities.getParentDialog(this).setVisible(false);
313    }
314  }
315
316  /**
317   * Checks the validity of the provided information and updates the provided
318   * collection of messages with the errors that have been found.
319   * @param errorMessages the collection of messages to be updated.
320   */
321  private void updateErrorMessages(Collection<LocalizableMessage> errorMessages)
322  {
323    Object type =
324      ((CategorizedComboBoxElement)scheduleType.getSelectedItem()).getValue();
325    if (type == LAUNCH_LATER)
326    {
327      updateLaunchLaterErrorMessages(errorMessages);
328    }
329    else if (type == LAUNCH_DAILY)
330    {
331      updateLaunchDailyErrorMessages(errorMessages);
332    }
333    else if (type == LAUNCH_WEEKLY)
334    {
335      updateLaunchWeeklyErrorMessages(errorMessages);
336    }
337    else if (type == LAUNCH_MONTHLY)
338    {
339      updateLaunchMonthlyErrorMessages(errorMessages);
340    }
341    else if (type == CRON)
342    {
343      updateCronErrorMessages(errorMessages);
344    }
345  }
346
347  /**
348   * Checks the validity of the launch later information and updates
349   * the provided collection of messages with the errors that have been found.
350   * The associated labels are also updated.
351   * @param errorMessages the collection of messages to be updated.
352   */
353  private void updateLaunchLaterErrorMessages(Collection<LocalizableMessage> errorMessages)
354  {
355    setPrimaryValid(lTime);
356    setPrimaryValid(lDay);
357    setPrimaryValid(lMonth);
358    setPrimaryValid(lYear);
359
360    int previousErrorNumber = errorMessages.size();
361
362    int y = Integer.parseInt(year.getSelectedItem().toString());
363    int d = -1;
364    int m = month.getSelectedIndex();
365    int[] h = {-1};
366    int[] min = {-1};
367    checkTime(time, lTime, h, min, errorMessages);
368    try
369    {
370      d = Integer.parseInt(day.getText().trim());
371      if (d < 0 || d > 31)
372      {
373        errorMessages.add(ERR_CTRL_PANEL_INVALID_DAY.get());
374        setPrimaryInvalid(lDay);
375      }
376    }
377    catch (Exception ex)
378    {
379      errorMessages.add(ERR_CTRL_PANEL_INVALID_DAY.get());
380      setPrimaryInvalid(lDay);
381    }
382
383    if (errorMessages.size() == previousErrorNumber)
384    {
385      GregorianCalendar calendar = new GregorianCalendar(y, m, d, h[0], min[0]);
386      Date date = calendar.getTime();
387      // Check that the actual date's month date corresponds to a valid day
388      // (for instance if user specifies 30th of February, the resulting date
389      // is 2nd (or 1st depending of the year) of Mars.
390      if (calendar.get(Calendar.MONTH) != m)
391      {
392        errorMessages.add(ERR_CTRL_PANEL_INVALID_DAY_IN_MONTH.get(d, month.getSelectedItem()));
393        setPrimaryInvalid(lDay);
394        setPrimaryInvalid(lMonth);
395      }
396      else if (date.before(new Date()))
397      {
398        errorMessages.add(ERR_CTRL_PANEL_DATE_ALREADY_PASSED.get());
399        setPrimaryInvalid(lTime);
400        setPrimaryInvalid(lDay);
401        setPrimaryInvalid(lMonth);
402        setPrimaryInvalid(lYear);
403      }
404    }
405  }
406
407  /**
408   * Checks the validity of the launch daily information and updates
409   * the provided collection of messages with the errors that have been found.
410   * The associated labels are also updated.
411   * @param errorMessages the collection of messages to be updated.
412   */
413  private void updateLaunchDailyErrorMessages(Collection<LocalizableMessage> errorMessages)
414  {
415    setPrimaryValid(lDailyTime);
416
417    int[] h = {-1};
418    int[] min = {-1};
419    checkTime(dailyTime, lDailyTime, h, min, errorMessages);
420  }
421
422  /**
423   * Checks the validity of the launch weekly information and updates
424   * the provided collection of messages with the errors that have been found.
425   * The associated labels are also updated.
426   * @param errorMessages the collection of messages to be updated.
427   */
428  private void updateLaunchWeeklyErrorMessages(
429      Collection<LocalizableMessage> errorMessages)
430  {
431    setPrimaryValid(lWeeklyTime);
432    setPrimaryValid(lWeeklyDays);
433
434    int[] h = {-1};
435    int[] min = {-1};
436    checkTime(weeklyTime, lWeeklyTime, h, min, errorMessages);
437
438    boolean oneSelected = false;
439    for (JCheckBox cb : weekDays)
440    {
441      if (cb.isSelected())
442      {
443        oneSelected = true;
444        break;
445      }
446    }
447    if (!oneSelected)
448    {
449      errorMessages.add(ERR_CTRL_PANEL_NO_WEEK_DAY_SELECTED.get());
450      setPrimaryInvalid(lWeeklyDays);
451    }
452  }
453
454  /**
455   * Checks the validity of the launch monthly information and updates
456   * the provided collection of messages with the errors that have been found.
457   * The associated labels are also updated.
458   * @param errorMessages the collection of messages to be updated.
459   */
460  private void updateLaunchMonthlyErrorMessages(
461      Collection<LocalizableMessage> errorMessages)
462  {
463    setPrimaryValid(lMonthlyTime);
464    setPrimaryValid(lMonthlyDays);
465
466    int[] h = {-1};
467    int[] min = {-1};
468    checkTime(monthlyTime, lMonthlyTime, h, min, errorMessages);
469
470    boolean oneSelected = false;
471    for (JCheckBox cb : monthDays)
472    {
473      if (cb.isSelected())
474      {
475        oneSelected = true;
476        break;
477      }
478    }
479    if (!oneSelected)
480    {
481      errorMessages.add(ERR_CTRL_PANEL_NO_MONTH_DAY_SELECTED.get());
482      setPrimaryInvalid(lMonthlyDays);
483    }
484  }
485
486  /**
487   * Checks the validity of the cron schedule information and updates
488   * the provided collection of messages with the errors that have been found.
489   * The associated labels are also updated.
490   * @param errorMessages the collection of messages to be updated.
491   */
492  private void updateCronErrorMessages(Collection<LocalizableMessage> errorMessages)
493  {
494    setPrimaryValid(lCronMinute);
495    setPrimaryValid(lCronHour);
496    setPrimaryValid(lCronMonthDay);
497    setPrimaryValid(lCronMonth);
498    setPrimaryValid(lCronWeekDay);
499
500    String minute = cronMinute.getText().trim();
501    String hour = cronHour.getText().trim();
502    String monthDay = cronMonthDay.getText().trim();
503    String month = cronMonth.getText().trim();
504    String weekDay = cronWeekDay.getText().trim();
505
506    updateCronErrorMessages(minute, lCronMinute,
507        ERR_CTRL_PANEL_NO_CRON_MINUTE_PROVIDED.get(),
508        ERR_CTRL_PANEL_NOT_VALID_CRON_MINUTE_PROVIDED.get(),
509        0, 59,
510        errorMessages);
511    updateCronErrorMessages(hour, lCronHour,
512        ERR_CTRL_PANEL_NO_CRON_HOUR_PROVIDED.get(),
513        ERR_CTRL_PANEL_NOT_VALID_CRON_HOUR_PROVIDED.get(),
514        0, 23,
515        errorMessages);
516    updateCronErrorMessages(weekDay, lCronWeekDay,
517        ERR_CTRL_PANEL_NO_CRON_WEEK_DAY_PROVIDED.get(),
518        ERR_CTRL_PANEL_NOT_VALID_CRON_WEEK_DAY_PROVIDED.get(),
519        0, 6,
520        errorMessages);
521    updateCronErrorMessages(monthDay, lCronMonthDay,
522        ERR_CTRL_PANEL_NO_CRON_MONTH_DAY_PROVIDED.get(),
523        ERR_CTRL_PANEL_NOT_VALID_CRON_MONTH_DAY_PROVIDED.get(),
524        1, 31,
525        errorMessages);
526    updateCronErrorMessages(month, lCronMonth,
527        ERR_CTRL_PANEL_NO_CRON_MONTH_PROVIDED.get(),
528        ERR_CTRL_PANEL_NOT_VALID_CRON_MONTH_PROVIDED.get(),
529        1, 12,
530        errorMessages);
531  }
532
533  /**
534   * Checks the validity of the cron schedule information tab and updates
535   * the provided collection of messages with the errors that have been found.
536   * The associated labels are also updated.
537   * @param value the value of the cron schedule tab.
538   * @param label the label associated with the cron schedule tab.
539   * @param errorIfEmpty the message to be displayed if the value tab is empty.
540   * @param contentError the message to be displayed if the value tab is not
541   * valid.
542   * @param minValue the minimum value accepted.
543   * @param maxValue the maximum value accepted.
544   * @param errorMessages the collection of messages to be updated.
545   */
546  private void updateCronErrorMessages(String value, JLabel label,
547      LocalizableMessage errorIfEmpty, LocalizableMessage contentError, int minValue, int maxValue,
548      Collection<LocalizableMessage> errorMessages)
549  {
550    if (value.length() == 0)
551    {
552      errorMessages.add(errorIfEmpty);
553      setPrimaryInvalid(label);
554    }
555    else
556    {
557      try
558      {
559        RecurringTask.parseTaskTabField(value, minValue, maxValue);
560      }
561      catch (Exception ex)
562      {
563        errorMessages.add(contentError);
564        setPrimaryInvalid(label);
565      }
566    }
567  }
568
569  /**
570   * Returns the schedule type corresponding to the input provided by user.
571   * This method assumes that all the date provided by the user has been
572   * validated.
573   * @return the schedule type corresponding to the input provided by user.
574   */
575  private ScheduleType createSchedule()
576  {
577    ScheduleType schedule;
578    Object type =
579      ((CategorizedComboBoxElement)scheduleType.getSelectedItem()).getValue();
580    if (type == LAUNCH_NOW)
581    {
582      schedule = ScheduleType.createLaunchNow();
583    }
584    else if (type == LAUNCH_LATER)
585    {
586      int y = Integer.parseInt(year.getSelectedItem().toString());
587      int d = Integer.parseInt(day.getText().trim());
588      int m = month.getSelectedIndex();
589      String sTime = time.getText().trim();
590      int index = sTime.indexOf(':');
591      int h = Integer.parseInt(sTime.substring(0, index).trim());
592      int min = Integer.parseInt(sTime.substring(index+1).trim());
593      GregorianCalendar calendar = new GregorianCalendar(y, m, d, h, min);
594      schedule = ScheduleType.createLaunchLater(calendar.getTime());
595    }
596    else if (type == LAUNCH_DAILY)
597    {
598      String sTime = dailyTime.getText().trim();
599      int index = sTime.indexOf(':');
600      int h = Integer.parseInt(sTime.substring(0, index).trim());
601      int m = Integer.parseInt(sTime.substring(index+1).trim());
602      String cron = m+" "+h+" * * *";
603      schedule = ScheduleType.createCron(cron);
604    }
605    else if (type == LAUNCH_WEEKLY)
606    {
607      String sTime = weeklyTime.getText().trim();
608      int index = sTime.indexOf(':');
609      int h = Integer.parseInt(sTime.substring(0, index).trim());
610      int m = Integer.parseInt(sTime.substring(index+1).trim());
611      StringBuilder sb = new StringBuilder();
612      sb.append(m).append(" ").append(h).append(" * * ");
613
614      boolean oneDayAdded = false;
615      for (int i=0; i<weekDays.length; i++)
616      {
617        if (weekDays[i].isSelected())
618        {
619          if (oneDayAdded)
620          {
621            sb.append(',');
622          }
623          sb.append(i);
624          oneDayAdded = true;
625        }
626      }
627      schedule = ScheduleType.createCron(sb.toString());
628    }
629    else if (type == LAUNCH_MONTHLY)
630    {
631      String sTime = monthlyTime.getText().trim();
632      int index = sTime.indexOf(':');
633      int h = Integer.parseInt(sTime.substring(0, index).trim());
634      int m = Integer.parseInt(sTime.substring(index+1).trim());
635      StringBuilder sb = new StringBuilder();
636      sb.append(m).append(" ").append(h).append(" ");
637      boolean oneDayAdded = false;
638      for (int i=0; i<monthDays.length; i++)
639      {
640        if (monthDays[i].isSelected())
641        {
642          if (oneDayAdded)
643          {
644            sb.append(',');
645          }
646          sb.append(i+1);
647          oneDayAdded = true;
648        }
649      }
650      sb.append(" * *");
651      schedule = ScheduleType.createCron(sb.toString());
652    }
653    else if (type == CRON)
654    {
655      String cron = cronMinute.getText().trim() + " "+
656      cronHour.getText().trim() + " "+
657      cronMonthDay.getText().trim() + " "+
658      cronMonth.getText().trim() + " "+
659      cronWeekDay.getText().trim();
660      schedule = ScheduleType.createCron(cron);
661    }
662    else
663    {
664      throw new RuntimeException("Unknown schedule type: "+type);
665    }
666    return schedule;
667  }
668
669  /**
670   * Convenience method to retrieve the time specified by the user.
671   * @param time the text field where the user specifies time.
672   * @param lTime the label associated with the text field.
673   * @param h an integer array of size 1 where the value of the hour specified
674   * by the user will be set.
675   * @param m an integer array of size 1 where the value of the minute specified
676   * by the user will be set.
677   * @param errorMessages the collection of error messages that will be updated
678   * with the encountered problems.
679   */
680  private void checkTime(JTextField time, JLabel lTime, int[] h, int[] m,
681      Collection<LocalizableMessage> errorMessages)
682  {
683    String sTime = time.getText().trim();
684    int index = sTime.indexOf(':');
685    try
686    {
687      h[0] = Integer.parseInt(sTime.substring(0, index).trim());
688      m[0] = Integer.parseInt(sTime.substring(index+1).trim());
689      if (h[0] < 0 || h[0] > 23)
690      {
691        errorMessages.add(ERR_CTRL_PANEL_INVALID_HOUR.get());
692        setPrimaryInvalid(lTime);
693      }
694      if (m[0] < 0 || m[0] > 59)
695      {
696        errorMessages.add(ERR_CTRL_PANEL_INVALID_MINUTE.get());
697        setPrimaryInvalid(lTime);
698      }
699    }
700    catch (Exception ex)
701    {
702      errorMessages.add(ERR_CTRL_PANEL_INVALID_TIME.get());
703      setPrimaryInvalid(lTime);
704    }
705  }
706
707  /**
708   * Tells whether the user chose to close the dialog discarding the provided
709   * input.
710   * @return <CODE>true</CODE> if the user chose to close the dialog discarding
711   * the provided input and <CODE>false</CODE> otherwise.
712   */
713  public boolean isCanceled()
714  {
715    return schedule == null;
716  }
717
718  /** {@inheritDoc} */
719  public void configurationChanged(ConfigurationChangeEvent ev)
720  {
721  }
722
723  /** {@inheritDoc} */
724  public Component getPreferredFocusComponent()
725  {
726    return scheduleType;
727  }
728
729  /**
730   * Returns the schedule provided by the user.
731   * @return the schedule provided by the user.
732   */
733  public ScheduleType getSchedule()
734  {
735    return schedule;
736  }
737
738  private Component createLaunchLaterPanel()
739  {
740    JPanel panel = new JPanel(new GridBagLayout());
741    panel.setOpaque(false);
742    GridBagConstraints gbc = new GridBagConstraints();
743    Calendar calendar = Calendar.getInstance();
744
745    int currentYear = calendar.get(Calendar.YEAR);
746    int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
747    int currentMinute = calendar.get(Calendar.MINUTE);
748    int currentDay = calendar.get(Calendar.DAY_OF_MONTH);
749    int currentMonth = calendar.get(Calendar.MONTH);
750
751    time = Utilities.createShortTextField();
752    PlainDocument plainTextDocument = new PlainDocument();
753    time.setDocument(plainTextDocument);
754    String sHour = currentHour >= 10 ?
755        String.valueOf(currentHour) : "0"+currentHour;
756    String sMinute = currentMinute >= 10 ?
757        String.valueOf(currentMinute) : "0"+currentMinute;
758    time.setText(sHour+":"+sMinute);
759    plainTextDocument.setDocumentFilter(new TimeDocumentFilter(time));
760
761
762    day = Utilities.createShortTextField();
763    day.setColumns(4);
764    plainTextDocument = new PlainDocument();
765    day.setDocument(plainTextDocument);
766    day.setText(String.valueOf(currentDay));
767    plainTextDocument.setDocumentFilter(
768        new NumericLimitedSizeDocumentFilter(day, 2));
769    month = Utilities.createComboBox();
770    year = Utilities.createComboBox();
771
772    int[][] maxMin =
773    {
774        {currentYear, currentYear + 5}
775    };
776
777    JComboBox[] numericBoxes =
778    {
779        year
780    };
781
782    int[] currentValues =
783    {
784        currentYear
785    };
786
787    for (int i=0; i<maxMin.length; i++)
788    {
789      int min = maxMin[i][0];
790      int max = maxMin[i][1];
791
792      DefaultComboBoxModel model = new DefaultComboBoxModel();
793
794      int selectedIndex = 0;
795
796      int index = 0;
797      for (int j=min; j<=max; j++)
798      {
799        String s;
800        if (j < 10)
801        {
802          s = "0"+j;
803        }
804        else
805        {
806          s = String.valueOf(j);
807        }
808        model.addElement(s);
809
810        if (j == currentValues[i])
811        {
812          selectedIndex= index;
813        }
814        index++;
815      }
816
817      numericBoxes[i].setModel(model);
818
819      if (selectedIndex != 0)
820      {
821        numericBoxes[i].setSelectedIndex(selectedIndex);
822      }
823    }
824
825    DefaultComboBoxModel model = new DefaultComboBoxModel();
826    month.setModel(model);
827
828    LocalizableMessage[] monthMessages =
829    {
830        INFO_CTRL_PANEL_JANUARY.get(),
831        INFO_CTRL_PANEL_FEBRUARY.get(),
832        INFO_CTRL_PANEL_MARCH.get(),
833        INFO_CTRL_PANEL_APRIL.get(),
834        INFO_CTRL_PANEL_MAY.get(),
835        INFO_CTRL_PANEL_JUNE.get(),
836        INFO_CTRL_PANEL_JULY.get(),
837        INFO_CTRL_PANEL_AUGUST.get(),
838        INFO_CTRL_PANEL_SEPTEMBER.get(),
839        INFO_CTRL_PANEL_OCTOBER.get(),
840        INFO_CTRL_PANEL_NOVEMBER.get(),
841        INFO_CTRL_PANEL_DECEMBER.get(),
842    };
843    for (LocalizableMessage msg : monthMessages)
844    {
845      model.addElement(msg.toString());
846    }
847
848    month.setSelectedIndex(currentMonth);
849
850    lTime = Utilities.createPrimaryLabel(
851        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_TIME.get());
852    lDay = Utilities.createPrimaryLabel(
853        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_DAY.get());
854    lMonth = Utilities.createPrimaryLabel(
855        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_MONTH.get());
856    lYear = Utilities.createPrimaryLabel(
857        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_YEAR.get());
858
859    gbc.gridy = 0;
860
861    JLabel[] labels = {lTime, lDay, lMonth, lYear};
862    JComponent[] comps = {time, day, month, year};
863    LocalizableMessage[] inlineHelp =
864    {
865        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_TIME_TOOLTIP.get(),
866        null,
867        null,
868        null
869    };
870
871    gbc.gridwidth = 1;
872    gbc.fill = GridBagConstraints.HORIZONTAL;
873
874    for (int i=0; i<labels.length; i++)
875    {
876      gbc.gridx = 0;
877      gbc.weightx = 0.0;
878      gbc.gridwidth = 1;
879
880      panel.add(labels[i], gbc);
881      gbc.gridx = 1;
882      gbc.insets.left = 10;
883      panel.add(comps[i], gbc);
884      gbc.gridx = 2;
885      gbc.weightx = 1.0;
886      gbc.insets.left = 0;
887      panel.add(Box.createHorizontalGlue(), gbc);
888
889      if (inlineHelp[i] != null)
890      {
891        gbc.gridwidth = 2;
892        gbc.insets.top = 3;
893        gbc.insets.left = 10;
894        gbc.gridx = 1;
895        gbc.gridy ++;
896        panel.add(Utilities.createInlineHelpLabel(inlineHelp[i]), gbc);
897      }
898
899      gbc.insets.top = 10;
900      gbc.gridy ++;
901    }
902
903    gbc.insets.top = 0;
904    gbc.weighty = 1.0;
905    gbc.fill = GridBagConstraints.VERTICAL;
906    panel.add(Box.createVerticalGlue(), gbc);
907
908    return panel;
909  }
910
911  private Component createDailyPanel()
912  {
913    JPanel panel = new JPanel(new GridBagLayout());
914    panel.setOpaque(false);
915    GridBagConstraints gbc = new GridBagConstraints();
916    gbc.gridx = 0;
917    gbc.gridy = 0;
918    gbc.weightx = 0.0;
919
920    lDailyTime =
921      Utilities.createPrimaryLabel(INFO_CTRL_PANEL_TASK_TO_SCHEDULE_TIME.get());
922
923    dailyTime = Utilities.createShortTextField();
924    PlainDocument plainTextDocument = new PlainDocument();
925    dailyTime.setDocument(plainTextDocument);
926    dailyTime.setColumns(4);
927    dailyTime.setText("00:00");
928    plainTextDocument.setDocumentFilter(new TimeDocumentFilter(dailyTime));
929
930    panel.add(lDailyTime, gbc);
931    gbc.gridx = 1;
932    gbc.insets.left = 10;
933    gbc.fill = GridBagConstraints.NONE;
934    panel.add(dailyTime, gbc);
935    gbc.gridx = 2;
936    gbc.weightx = 1.0;
937    gbc.insets.left = 0;
938    gbc.fill = GridBagConstraints.HORIZONTAL;
939    panel.add(Box.createHorizontalGlue(), gbc);
940
941    gbc.gridy ++;
942    gbc.gridwidth = 2;
943    gbc.insets.top = 3;
944    gbc.insets.left = 10;
945    gbc.gridx = 1;
946    panel.add(Utilities.createInlineHelpLabel(
947        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_TIME_TOOLTIP.get()), gbc);
948
949    return panel;
950  }
951
952  private Component createWeeklyPanel()
953  {
954    JPanel panel = new JPanel(new GridBagLayout());
955    panel.setOpaque(false);
956    GridBagConstraints gbc = new GridBagConstraints();
957    gbc.gridx = 0;
958    gbc.gridy = 0;
959    gbc.weightx = 0.0;
960
961    lWeeklyTime =
962      Utilities.createPrimaryLabel(INFO_CTRL_PANEL_TASK_TO_SCHEDULE_TIME.get());
963
964    weeklyTime = Utilities.createShortTextField();
965    PlainDocument plainTextDocument = new PlainDocument();
966    weeklyTime.setDocument(plainTextDocument);
967    weeklyTime.setColumns(4);
968    weeklyTime.setText("00:00");
969    plainTextDocument.setDocumentFilter(new TimeDocumentFilter(weeklyTime));
970
971    lWeeklyDays = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_DAYS.get());
972    for (JCheckBox cb : weekDays)
973    {
974      cb.setFont(ColorAndFontConstants.inlineHelpFont);
975    }
976    sunday.setSelected(true);
977    wednesday.setSelected(true);
978
979    gbc.anchor = GridBagConstraints.WEST;
980    panel.add(lWeeklyTime, gbc);
981    gbc.gridx = 1;
982    gbc.insets.left = 10;
983    gbc.gridwidth = weekDays.length;
984    gbc.fill = GridBagConstraints.NONE;
985    panel.add(weeklyTime, gbc);
986    gbc.gridx = 2;
987    gbc.weightx = 1.0;
988    gbc.insets.left = 0;
989    gbc.gridwidth = 1;
990    gbc.fill = GridBagConstraints.HORIZONTAL;
991    panel.add(Box.createHorizontalGlue(), gbc);
992    gbc.gridy ++;
993    gbc.gridwidth = weekDays.length + 1;
994    gbc.insets.top = 3;
995    gbc.insets.left = 10;
996    gbc.gridx = 1;
997    panel.add(Utilities.createInlineHelpLabel(
998        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_TIME_TOOLTIP.get()), gbc);
999
1000    gbc.gridx = 0;
1001    gbc.gridy ++;
1002    gbc.insets.top = 10;
1003    gbc.weightx = 1.0;
1004    panel.add(lWeeklyDays, gbc);
1005    gbc.insets.left = 10;
1006    gbc.gridwidth = 1;
1007    for (JCheckBox cb : weekDays)
1008    {
1009      gbc.gridx ++;
1010      panel.add(cb, gbc);
1011    }
1012    gbc.weightx = 1.0;
1013    gbc.insets.left = 0;
1014    gbc.gridwidth = 1;
1015    gbc.gridx ++;
1016    panel.add(Box.createHorizontalGlue(), gbc);
1017
1018    return panel;
1019  }
1020
1021  private Component createMonthlyPanel()
1022  {
1023    JPanel panel = new JPanel(new GridBagLayout());
1024    panel.setOpaque(false);
1025    GridBagConstraints gbc = new GridBagConstraints();
1026    gbc.gridx = 0;
1027    gbc.gridy = 0;
1028    gbc.weightx = 0.0;
1029
1030    lMonthlyTime =
1031      Utilities.createPrimaryLabel(INFO_CTRL_PANEL_TASK_TO_SCHEDULE_TIME.get());
1032
1033    monthlyTime = Utilities.createShortTextField();
1034    PlainDocument plainTextDocument = new PlainDocument();
1035    monthlyTime.setDocument(plainTextDocument);
1036    monthlyTime.setColumns(4);
1037    monthlyTime.setText("00:00");
1038    plainTextDocument.setDocumentFilter(new TimeDocumentFilter(monthlyTime));
1039
1040    lMonthlyDays = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_DAYS.get());
1041
1042    gbc.anchor = GridBagConstraints.WEST;
1043    panel.add(lMonthlyTime, gbc);
1044    gbc.gridx = 1;
1045    gbc.insets.left = 10;
1046    gbc.fill = GridBagConstraints.NONE;
1047    gbc.gridwidth = 7;
1048    panel.add(monthlyTime, gbc);
1049    gbc.gridx = 2;
1050    gbc.weightx = 1.0;
1051    gbc.insets.left = 0;
1052    gbc.gridwidth = 1;
1053    gbc.fill = GridBagConstraints.HORIZONTAL;
1054    panel.add(Box.createHorizontalGlue(), gbc);
1055    gbc.gridy ++;
1056    gbc.gridwidth = 8;
1057    gbc.insets.top = 3;
1058    gbc.insets.left = 10;
1059    gbc.gridx = 1;
1060    panel.add(Utilities.createInlineHelpLabel(
1061        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_TIME_TOOLTIP.get()), gbc);
1062
1063    gbc.gridx = 0;
1064    gbc.gridy ++;
1065    gbc.insets.top = 10;
1066    gbc.weightx = 1.0;
1067    gbc.gridwidth = 1;
1068    panel.add(lMonthlyDays, gbc);
1069    gbc.insets.left = 10;
1070    gbc.gridwidth = 1;
1071    for (int i=0 ; i<monthDays.length; i++)
1072    {
1073      monthDays[i] = Utilities.createCheckBox(LocalizableMessage.raw(String.valueOf(i+1)));
1074      monthDays[i].setFont(ColorAndFontConstants.inlineHelpFont);
1075      int x = i % 7;
1076      if (x == 0 && i != 0)
1077      {
1078        gbc.gridy ++;
1079        gbc.insets.top = 5;
1080      }
1081      if (x != 0)
1082      {
1083        gbc.insets.left = 5;
1084      }
1085      else
1086      {
1087        gbc.insets.left = 10;
1088      }
1089      gbc.gridx = x + 1;
1090      panel.add(monthDays[i], gbc);
1091    }
1092    monthDays[0].setSelected(true);
1093    gbc.weightx = 1.0;
1094    gbc.insets.left = 0;
1095    gbc.gridwidth = 1;
1096    gbc.gridx ++;
1097    panel.add(Box.createHorizontalGlue(), gbc);
1098
1099    return panel;
1100  }
1101
1102  private Component createCronPanel()
1103  {
1104    JPanel panel = new JPanel(new GridBagLayout());
1105    panel.setOpaque(false);
1106    GridBagConstraints gbc = new GridBagConstraints();
1107
1108    JEditorPane explanation = Utilities.makeHtmlPane(
1109        INFO_CTRL_PANEL_CRON_HELP.get(),
1110        ColorAndFontConstants.inlineHelpFont);
1111    gbc.gridx = 0;
1112    gbc.gridy = 0;
1113    gbc.gridwidth = 2;
1114    gbc.fill = GridBagConstraints.HORIZONTAL;
1115    panel.add(explanation, gbc);
1116    gbc.gridy ++;
1117    gbc.insets.top = 10;
1118
1119    gbc.gridwidth = 1;
1120    lCronMinute = Utilities.createPrimaryLabel(
1121        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_CRON_MINUTE.get());
1122    lCronHour = Utilities.createPrimaryLabel(
1123        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_CRON_HOUR.get());
1124    lCronWeekDay = Utilities.createPrimaryLabel(
1125        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_CRON_WEEK_DAY.get());
1126    lCronMonthDay = Utilities.createPrimaryLabel(
1127        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_CRON_MONTH_DAY.get());
1128    lCronMonth = Utilities.createPrimaryLabel(
1129        INFO_CTRL_PANEL_TASK_TO_SCHEDULE_CRON_MONTH.get());
1130
1131    cronMinute = Utilities.createShortTextField();
1132    cronMinute.setText("*");
1133
1134    cronHour = Utilities.createShortTextField();
1135    cronHour.setText("*");
1136
1137    cronWeekDay = Utilities.createShortTextField();
1138    cronWeekDay.setText("*");
1139
1140    cronMonthDay = Utilities.createShortTextField();
1141    cronMonthDay.setText("*");
1142
1143    cronMonth = Utilities.createShortTextField();
1144    cronMonth.setText("*");
1145
1146    JLabel[] labels = {lCronMinute, lCronHour, lCronWeekDay, lCronMonthDay,
1147        lCronMonth};
1148    Component[] comps = {cronMinute, cronHour, cronWeekDay, cronMonthDay,
1149        cronMonth};
1150    LocalizableMessage[] help =
1151    {
1152      INFO_CTRL_PANEL_CRON_MINUTE_HELP.get(),
1153      INFO_CTRL_PANEL_CRON_HOUR_HELP.get(),
1154      INFO_CTRL_PANEL_CRON_WEEK_DAY_HELP.get(),
1155      INFO_CTRL_PANEL_CRON_MONTH_DAY_HELP.get(),
1156      INFO_CTRL_PANEL_CRON_MONTH_HELP.get(),
1157    };
1158
1159    gbc.gridwidth = 1;
1160    gbc.fill = GridBagConstraints.HORIZONTAL;
1161
1162    for (int i=0; i<labels.length; i++)
1163    {
1164      gbc.gridx = 0;
1165      gbc.weightx = 0.0;
1166
1167      gbc.insets.left = 0;
1168      panel.add(labels[i], gbc);
1169      gbc.gridx = 1;
1170      gbc.insets.left = 10;
1171      panel.add(comps[i], gbc);
1172      gbc.gridx = 2;
1173      gbc.weightx = 1.0;
1174      gbc.insets.left = 0;
1175      panel.add(Box.createHorizontalGlue(), gbc);
1176      if (help[i] != null)
1177      {
1178        gbc.insets.top = 3;
1179        gbc.insets.left = 10;
1180        gbc.gridy ++;
1181        gbc.gridx = 1;
1182        panel.add(Utilities.createInlineHelpLabel(help[i]), gbc);
1183      }
1184
1185      gbc.insets.top = 10;
1186      gbc.gridy ++;
1187    }
1188
1189    gbc.insets.top = 0;
1190    gbc.weighty = 1.0;
1191    gbc.fill = GridBagConstraints.VERTICAL;
1192    panel.add(Box.createVerticalGlue(), gbc);
1193
1194    return panel;
1195  }
1196
1197  /**
1198   * The main method to test this panel.
1199   * @param args the arguments.
1200   */
1201  public static void main(String[] args)
1202  {
1203    while (true)
1204    {
1205      TaskToSchedulePanel p = new TaskToSchedulePanel("TEST TASK");
1206      GenericDialog dlg = new GenericDialog(Utilities.createFrame(), p);
1207      dlg.setModal(true);
1208      dlg.setVisible(true);
1209    }
1210  }
1211}