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