1   package com.atlassian.core.cron;
2   
3   import com.atlassian.core.util.DateUtils;
4   import org.apache.commons.lang.StringUtils;
5   
6   import java.util.*;
7   
8   /**
9    * Represents the form state for the CronEditorWebComponent.
10   */
11  public class CronEditorBean
12  {
13  
14      // Defines the modes
15      public static final String DAILY_SPEC_MODE = "daily";
16      public static final String DAYS_OF_WEEK_SPEC_MODE = "daysOfWeek";
17      public static final String DAYS_OF_MONTH_SPEC_MODE = "daysOfMonth";
18      public static final String ADVANCED_MODE = "advanced";
19      public static final String DOT = ".";
20  
21      // Defines the params that get submitted via the cron editor form
22      private static final String CRON_STRING = "cronString";
23      private static final String DAILY_WEEKLY_MONTHLY = "dailyWeeklyMonthly";
24      private static final String RUN_ONCE_MINS = "runOnceMins";
25      private static final String RUN_ONCE_HOURS = "runOnceHours";
26      private static final String RUN_ONCE_MERIDIAN = "runOnceMeridian";
27      private static final String RUN_FROM_HOURS = "runFromHours";
28      private static final String RUN_FROM_MERIDIAN = "runFromMeridian";
29      private static final String RUN_TO_HOURS = "runToHours";
30      private static final String RUN_TO_MERIDIAN = "runToMeridian";
31      private static final String WEEKDAY = "weekday";
32      private static final String DAY = "day";
33      private static final String WEEK = "week";
34      private static final String DAYS_OF_MONTH_OPT = "daysOfMonthOpt";
35      private static final String MONTH_DAY = "monthDay";
36      private static final String INTERVAL = "interval";
37      private static final String DAY_OF_WEEK_OF_MONTH = "dayOfWeekOfMonth";
38  
39      private Map params;
40      private String cronString;
41      private String mode;
42      private boolean dayOfWeekOfMonth;
43      private String dayOfMonth;
44      private String minutes;
45      private String hoursRunOnce;
46      private String hoursRunOnceMeridian;
47      private String hoursFrom;
48      private String hoursFromMeridian;
49      private String hoursTo;
50      private String hoursToMeridian;
51      private String specifiedDaysOfWeek;
52      private String dayInMonthOrdinal;
53      private String incrementInMinutes;
54      private String seconds;
55  
56      public CronEditorBean()
57      {
58          this.params = new HashMap();
59      }
60  
61      /**
62       * Initialises to the state defined by the given params, which are identified
63       * by the presence of paramPrefix on the key.
64       *
65       * @param paramPrefix the prefix used on each of the params.
66       * @param params      state parameters to use.
67       */
68      public CronEditorBean(String paramPrefix, /*<String, String[]>*/ Map params)
69      {
70          this.params = params;
71          this.cronString = getParam(paramPrefix, CRON_STRING);
72          this.mode = getParam(paramPrefix, DAILY_WEEKLY_MONTHLY);
73          // note that we don't expect seconds from the UI
74          this.minutes = getParam(paramPrefix, RUN_ONCE_MINS);
75          this.hoursRunOnce = getParam(paramPrefix, RUN_ONCE_HOURS);
76          this.hoursRunOnceMeridian = getParam(paramPrefix, RUN_ONCE_MERIDIAN);
77          this.hoursFrom = getParam(paramPrefix, RUN_FROM_HOURS);
78          this.hoursFromMeridian = getParam(paramPrefix, RUN_FROM_MERIDIAN);
79          this.hoursTo = getParam(paramPrefix, RUN_TO_HOURS);
80          this.hoursToMeridian = getParam(paramPrefix, RUN_TO_MERIDIAN);
81          String[] daysOfWeek = (String[]) params.get(paramPrefix + DOT + WEEKDAY);
82          if (DAYS_OF_MONTH_SPEC_MODE.equals(mode))
83          {
84              this.specifiedDaysOfWeek = getParam(paramPrefix, DAY);
85              this.dayInMonthOrdinal = getParam(paramPrefix, WEEK);
86              // Find the sub-mode
87              String dayOfWeekOfMonthString = getParam(paramPrefix, DAYS_OF_MONTH_OPT);
88              this.dayOfWeekOfMonth = DAY_OF_WEEK_OF_MONTH.equals(dayOfWeekOfMonthString);
89          }
90          else if (DAYS_OF_WEEK_SPEC_MODE.equals(mode))
91          {
92              this.specifiedDaysOfWeek = StringUtils.join(daysOfWeek, ',');
93          }
94  
95          this.dayOfMonth = getParam(paramPrefix, MONTH_DAY);
96          this.incrementInMinutes = getParam(paramPrefix, INTERVAL);
97      }
98  
99      /**
100      * Used to validate the hours to see that the from hour is before the to hour, if specified.
101      * Note: to allow the range to run "all-day" (eg. from 12AM to 12AM), we need to also allow the from and to times to
102      * be equal.
103      *
104      * @return true if from is before to or if they have not been specified.
105      */
106     public boolean isRangeHoursValid()
107     {
108         if (hoursFrom != null && hoursFromMeridian != null && hoursTo != null && hoursToMeridian != null && incrementInMinutes != null && !incrementInMinutes.equals("0"))
109         {
110             try
111             {
112                 int hoursFromInt = Integer.parseInt(hoursFrom);
113                 int hoursToInt = Integer.parseInt(hoursTo);
114                 return DateUtils.get24HourTime(hoursFromMeridian, hoursFromInt) <= DateUtils.get24HourTime(hoursToMeridian, hoursToInt);
115             }
116             catch (NumberFormatException e)
117             {
118                 // Something is freaky with the hours, hmmmm....
119                 return false;
120             }
121         }
122         return true;
123     }
124 
125     /**
126      * Indicates that a range of time is being specified.
127      *
128      * @return true only if the form state indicates a from and to time.
129      */
130     public boolean isRange()
131     {
132         return !incrementInMinutes.equals("0") && (isDailyMode() || isDayPerWeekMode());
133     }
134 
135     /**
136      * Indicates that the range of time specified spans across the full 24 hours in a day (which is indicated by the
137      * computed hoursFrom in 24 hour time being equal to the computed hoursTo in 24 hour time).
138      *
139      * @return true only if we have specified a range, and that range spans across the 24 hours.
140      */
141     public boolean is24HourRange()
142     {
143         boolean result = false;
144         if (isRange())
145         {
146             int hoursFromInt = Integer.parseInt(hoursFrom);
147             int hoursToInt = Integer.parseInt(hoursTo);
148             result = DateUtils.get24HourTime(hoursFromMeridian, hoursFromInt) == DateUtils.get24HourTime(hoursToMeridian, hoursToInt);
149         }
150         return result;
151     }
152 
153     /**
154      * Returns the cron string that the object was constructed with. This method does not guarantee that the returned
155      * cron string is valid according the the {@link org.quartz.CronTrigger}.
156      *
157      * @return unmodified cronString
158      */
159     public String getCronString()
160     {
161         return cronString;
162     }
163 
164     public String getMode()
165     {
166         return mode;
167     }
168 
169     /**
170      * Will return true if the passed in cron string is not valid for the editor.
171      *
172      * @return true if the cron string can not be handled, false otherwise.
173      */
174     public boolean isAdvancedMode()
175     {
176         return ADVANCED_MODE.equals(mode);
177     }
178 
179     /**
180      * Will return true if the editors daily mode can handle the provided cron string.
181      *
182      * @return if we're in daily mode.
183      */
184     public boolean isDailyMode()
185     {
186         return DAILY_SPEC_MODE.equals(mode);
187     }
188 
189     /**
190      * Will return true if the editors day per week mode can handle the provided cron string.
191      *
192      * @return true if {@link #mode} is equal to {@link #DAYS_OF_WEEK_SPEC_MODE}, false otherwise
193      */
194     public boolean isDayPerWeekMode()
195     {
196         return DAYS_OF_WEEK_SPEC_MODE.equals(mode);
197     }
198 
199     /**
200      * Will return true if the editors days per month mode can handle the provided cron string.
201      *
202      * @return true if {@link #mode} is equal to {@link #DAYS_OF_MONTH_SPEC_MODE}, false otherwise
203      */
204     public boolean isDaysPerMonthMode()
205     {
206         return DAYS_OF_MONTH_SPEC_MODE.equals(mode);
207     }
208 
209     /**
210      * Returns true if {@link #isDaysPerMonthMode()} is true and the sub-mode of day of week per month has been
211      * selected.
212      */
213     public boolean isDayOfWeekOfMonth()
214     {
215         return dayOfWeekOfMonth;
216     }
217 
218     public boolean getDayOfWeekOfMonth()
219     {
220         return dayOfWeekOfMonth;
221     }
222 
223     /**
224      * Gets the day of month field specified in the cron string. Should be between 1-31 or L.
225      */
226     public String getDayOfMonth()
227     {
228         return dayOfMonth;
229     }
230 
231     /**
232      * Gets the minutes specified. Should be between 0-59.
233      */
234     public String getMinutes()
235     {
236         return minutes;
237     }
238 
239     /**
240      * Returns the lower bound of the hour range if this entry has a range.
241      *
242      * This end of the range is inclusive - e.g. if HoursFrom is 3PM and HoursTo is 5PM, the duration of the range
243      * is 2 hours.
244      *
245      * @return the lower bound or null if this is not a range hour entry.
246      */
247     public String getHoursFrom()
248     {
249         return hoursFrom;
250     }
251 
252     /**
253      * Returns the upper bound of the hour range if this entry has a range.
254      *
255      * This end of the range is exclusive - e.g. if HoursFrom is 3PM and HoursTo is 5PM, the duration of the range
256      * is 2 hours.
257      *
258      * @return the upper bound or null if this is not a range hour entry.
259      */
260     public String getHoursTo()
261     {
262         return hoursTo;
263     }
264 
265     /**
266      * Returns the meridian indicator @{link #AM} or @{link #PM} for the lower bound of a range entry.
267      *
268      * @return the meridian belonging to the lower bound hour or null if this is not a range entry.
269      */
270     public String getHoursFromMeridian()
271     {
272         return hoursFromMeridian;
273     }
274 
275     /**
276      * Returns the meridian indicator @{link #AM} or @{link #PM} for the upper bound of a range entry.
277      *
278      * @return the meridian belonging to the upper bound hour or null if this is not a range entry.
279      */
280     public String getHoursToMeridian()
281     {
282         return hoursToMeridian;
283     }
284 
285     /**
286      * Returns the single hour value for this entry if it is a run once entry.
287      *
288      * @return the hour value or null if this is not a run once hour entry.
289      */
290     public String getHoursRunOnce()
291     {
292         return hoursRunOnce;
293     }
294 
295     /**
296      * Returns the meridian indicator @{link #AM} or @{link #PM} for the entry if it is a run once entry.
297      *
298      * @return the meridian belonging single hour value or null if this is not a run once entry.
299      */
300     public String getHoursRunOnceMeridian()
301     {
302         return hoursRunOnceMeridian;
303     }
304 
305     /**
306      * Returns true if the passed in day has been specified, false otherwise.
307      *
308      * @param dayStr a string representing a day (e.g. 1-7).
309      * @return true if the day has been specified, false otherwise.
310      */
311     public boolean isDaySpecified(String dayStr)
312     {
313         return specifiedDaysOfWeek != null && StringUtils.contains(specifiedDaysOfWeek, dayStr);
314     }
315 
316     /**
317      * Returns a number that represents the first, second third etc. day of the week in a month.
318      *
319      * @return the ordinal or null if this entry doesn't specify it.
320      */
321     public String getDayInMonthOrdinal()
322     {
323         return dayInMonthOrdinal;
324     }
325 
326     /**
327      * Returns all the days that have been specified in a comma separated list.
328      *
329      * @return string representing days (e.g. "1,2,3").
330      */
331     public String getSpecifiedDaysPerWeek()
332     {
333         return specifiedDaysOfWeek;
334     }
335 
336     /**
337      * Used to determine the total increment in minutes derived by the hour and minutes fields' increment parts.
338      * An increment of 0 indicates no repetition based on the hours or minutes fields and will happen if the
339      * repetition is once a day.
340      *
341      * @return a minute increment or "0"
342      */
343     public String getIncrementInMinutes()
344     {
345         return incrementInMinutes;
346     }
347 
348     /**
349      * The full cron string that may have been specified. This can come from the advanced tab where the user has
350      * specified their own cron string. This does not validate the cron string.
351      *
352      * @param cronString a valid cron string.
353      */
354     public void setCronString(String cronString)
355     {
356         this.cronString = cronString;
357     }
358 
359     public void setMode(String mode)
360     {
361         this.mode = mode;
362     }
363 
364     public void setDayOfWeekOfMonth(boolean dayOfWeekOfMonth)
365     {
366         this.dayOfWeekOfMonth = dayOfWeekOfMonth;
367     }
368 
369     public void setDayOfMonth(String dayOfMonth)
370     {
371         this.dayOfMonth = dayOfMonth;
372     }
373 
374     public void setMinutes(String minutes)
375     {
376         this.minutes = minutes;
377     }
378 
379     public void setHoursFrom(String hoursFrom)
380     {
381         this.hoursFrom = hoursFrom;
382     }
383 
384     public void setHoursTo(String hoursTo)
385     {
386         this.hoursTo = hoursTo;
387     }
388 
389     public void setHoursFromMeridian(String hoursFromMeridian)
390     {
391         this.hoursFromMeridian = hoursFromMeridian;
392     }
393 
394     public void setHoursToMeridian(String hoursToMeridian)
395     {
396         this.hoursToMeridian = hoursToMeridian;
397     }
398 
399     public void setHoursRunOnce(String hoursRunOnce)
400     {
401         this.hoursRunOnce = hoursRunOnce;
402     }
403 
404     public void setHoursRunOnceMeridian(String hoursRunOnceMeridian)
405     {
406         this.hoursRunOnceMeridian = hoursRunOnceMeridian;
407     }
408 
409     public void setSpecifiedDaysOfWeek(String specifiedDaysOfWeek)
410     {
411         this.specifiedDaysOfWeek = specifiedDaysOfWeek;
412     }
413 
414     public void setDayInMonthOrdinal(String dayInMonthOrdinal)
415     {
416         this.dayInMonthOrdinal = dayInMonthOrdinal;
417     }
418 
419     /**
420      * Set the interval of repetition. "0" indicates there is no repetition.
421      *
422      * @param incrementInMinutes the interval or "0"
423      */
424     public void setIncrementInMinutes(String incrementInMinutes)
425     {
426         this.incrementInMinutes = incrementInMinutes;
427     }
428 
429     /**
430      * Looks up a single String value with the given prefixed key.
431      *
432      * @param paramPrefix prefix the parameter must have
433      * @param key         key value to use for lookup.
434      * @return the first String or null.
435      */
436     private String getParam(String paramPrefix, String key)
437     {
438         String[] paramArr = (String[]) params.get(paramPrefix + DOT + key);
439         if (paramArr != null && paramArr.length == 1)
440         {
441             return paramArr[0];
442         }
443         return null;
444     }
445 
446     public void setSeconds(String seconds)
447     {
448         this.seconds = seconds;
449     }
450 
451     public String getSeconds()
452     {
453         return seconds;
454     }
455 }