PageRenderTime 60ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/pentaho-gwt-widgets/src/org/pentaho/gwt/widgets/client/utils/CronExpression.java

https://github.com/wgorman/pentaho-commons-gwt-modules
Java | 866 lines | 557 code | 97 blank | 212 comment | 310 complexity | bb794ae9763aeb89baa1da2b58c25993 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*
  2. ***********************************************************************************************************************
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  5. * the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  10. * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. * specific language governing permissions and limitations under the License.
  12. *
  13. * This file is a copy from the Quartz project - http://quartz-scheduler.org/ from version 1.5.1
  14. * with the following changes:
  15. * a- Removed all non-GWT friendly classes (Locale, Calendar, StringTokenizer, ObjectOutputStream, etc.
  16. * b- Re-implemented the buildExpression method using split instead of StringTokenizer
  17. * c- Externalized all strings to the i18n package
  18. * d- Code-formatted to meet Pentaho standards
  19. * e- Removed extraneous helper methods not needed for cron validation
  20. *
  21. * These changes were expressly made to allow GWT compilation (read Javascript translation) for validation of
  22. * Quartz-specific cron expressions. All other comments and attributions remain intact.
  23. *
  24. * Copyright 2012 Pentaho Corporation. All rights reserved.
  25. * @author Marc Batchelor
  26. *
  27. ***********************************************************************************************************************
  28. */
  29. package org.pentaho.gwt.widgets.client.utils;
  30. import java.util.HashMap;
  31. import java.util.Map;
  32. import java.util.TreeSet;
  33. import org.pentaho.gwt.widgets.client.i18n.WidgetsLocalizedMessages;
  34. import org.pentaho.gwt.widgets.client.i18n.WidgetsLocalizedMessagesSingleton;
  35. /**
  36. * Provides a parser and evaluator for unix-like cron expressions. Cron
  37. * expressions provide the ability to specify complex time combinations such as
  38. * "At 8:00am every Monday through Friday" or "At 1:30am every
  39. * last Friday of the month".
  40. * <P>
  41. * Cron expressions are comprised of 6 required fields and one optional field
  42. * separated by white space. The fields respectively are described as follows:
  43. *
  44. * <table cellspacing="8">
  45. * <tr>
  46. * <th align="left">Field Name</th>
  47. * <th align="left">&nbsp;</th>
  48. * <th align="left">Allowed Values</th>
  49. * <th align="left">&nbsp;</th>
  50. * <th align="left">Allowed Special Characters</th>
  51. * </tr>
  52. * <tr>
  53. * <td align="left"><code>Seconds</code></td>
  54. * <td align="left">&nbsp;</th>
  55. * <td align="left"><code>0-59</code></td>
  56. * <td align="left">&nbsp;</th>
  57. * <td align="left"><code>, - * /</code></td>
  58. * </tr>
  59. * <tr>
  60. * <td align="left"><code>Minutes</code></td>
  61. * <td align="left">&nbsp;</th>
  62. * <td align="left"><code>0-59</code></td>
  63. * <td align="left">&nbsp;</th>
  64. * <td align="left"><code>, - * /</code></td>
  65. * </tr>
  66. * <tr>
  67. * <td align="left"><code>Hours</code></td>
  68. * <td align="left">&nbsp;</th>
  69. * <td align="left"><code>0-23</code></td>
  70. * <td align="left">&nbsp;</th>
  71. * <td align="left"><code>, - * /</code></td>
  72. * </tr>
  73. * <tr>
  74. * <td align="left"><code>Day-of-month</code></td>
  75. * <td align="left">&nbsp;</th>
  76. * <td align="left"><code>1-31</code></td>
  77. * <td align="left">&nbsp;</th>
  78. * <td align="left"><code>, - * ? / L W C</code></td>
  79. * </tr>
  80. * <tr>
  81. * <td align="left"><code>Month</code></td>
  82. * <td align="left">&nbsp;</th>
  83. * <td align="left"><code>1-12 or JAN-DEC</code></td>
  84. * <td align="left">&nbsp;</th>
  85. * <td align="left"><code>, - * /</code></td>
  86. * </tr>
  87. * <tr>
  88. * <td align="left"><code>Day-of-Week</code></td>
  89. * <td align="left">&nbsp;</th>
  90. * <td align="left"><code>1-7 or SUN-SAT</code></td>
  91. * <td align="left">&nbsp;</th>
  92. * <td align="left"><code>, - * ? / L #</code></td>
  93. * </tr>
  94. * <tr>
  95. * <td align="left"><code>Year (Optional)</code></td>
  96. * <td align="left">&nbsp;</th>
  97. * <td align="left"><code>empty, 1970-2099</code></td>
  98. * <td align="left">&nbsp;</th>
  99. * <td align="left"><code>, - * /</code></td>
  100. * </tr>
  101. * </table>
  102. * <P>
  103. * The '*' character is used to specify all values. For example, &quot;*&quot;
  104. * in the minute field means &quot;every minute&quot;.
  105. * <P>
  106. * The '?' character is allowed for the day-of-month and day-of-week fields. It
  107. * is used to specify 'no specific value'. This is useful when you need to
  108. * specify something in one of the two fileds, but not the other.
  109. * <P>
  110. * The '-' character is used to specify ranges For example &quot;10-12&quot; in
  111. * the hour field means &quot;the hours 10, 11 and 12&quot;.
  112. * <P>
  113. * The ',' character is used to specify additional values. For example
  114. * &quot;MON,WED,FRI&quot; in the day-of-week field means &quot;the days Monday,
  115. * Wednesday, and Friday&quot;.
  116. * <P>
  117. * The '/' character is used to specify increments. For example &quot;0/15&quot;
  118. * in the seconds field means &quot;the seconds 0, 15, 30, and 45&quot;. And
  119. * &quot;5/15&quot; in the seconds field means &quot;the seconds 5, 20, 35, and
  120. * 50&quot;. Specifying '*' before the '/' is equivalent to specifying 0 is
  121. * the value to start with. Essentially, for each field in the expression, there
  122. * is a set of numbers that can be turned on or off. For seconds and minutes,
  123. * the numbers range from 0 to 59. For hours 0 to 23, for days of the month 0 to
  124. * 31, and for months 1 to 12. The &quot;/&quot; character simply helps you turn
  125. * on every &quot;nth&quot; value in the given set. Thus &quot;7/6&quot; in the
  126. * month field only turns on month &quot;7&quot;, it does NOT mean every 6th
  127. * month, please note that subtlety.
  128. * <P>
  129. * The 'L' character is allowed for the day-of-month and day-of-week fields.
  130. * This character is short-hand for &quot;last&quot;, but it has different
  131. * meaning in each of the two fields. For example, the value &quot;L&quot; in
  132. * the day-of-month field means &quot;the last day of the month&quot; - day 31
  133. * for January, day 28 for February on non-leap years. If used in the
  134. * day-of-week field by itself, it simply means &quot;7&quot; or
  135. * &quot;SAT&quot;. But if used in the day-of-week field after another value, it
  136. * means &quot;the last xxx day of the month&quot; - for example &quot;6L&quot;
  137. * means &quot;the last friday of the month&quot;. When using the 'L' option, it
  138. * is important not to specify lists, or ranges of values, as you'll get
  139. * confusing results.
  140. * <P>
  141. * The 'W' character is allowed for the day-of-month field. This character
  142. * is used to specify the weekday (Monday-Friday) nearest the given day. As an
  143. * example, if you were to specify &quot;15W&quot; as the value for the
  144. * day-of-month field, the meaning is: &quot;the nearest weekday to the 15th of
  145. * the month&quot;. So if the 15th is a Saturday, the trigger will fire on
  146. * Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the
  147. * 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th.
  148. * However if you specify &quot;1W&quot; as the value for day-of-month, and the
  149. * 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not
  150. * 'jump' over the boundary of a month's days. The 'W' character can only be
  151. * specified when the day-of-month is a single day, not a range or list of days.
  152. * <P>
  153. * The 'L' and 'W' characters can also be combined for the day-of-month
  154. * expression to yield 'LW', which translates to &quot;last weekday of the
  155. * month&quot;.
  156. * <P>
  157. * The '#' character is allowed for the day-of-week field. This character is
  158. * used to specify &quot;the nth&quot; XXX day of the month. For example, the
  159. * value of &quot;6#3&quot; in the day-of-week field means the third Friday of
  160. * the month (day 6 = Friday and &quot;#3&quot; = the 3rd one in the month).
  161. * Other examples: &quot;2#1&quot; = the first Monday of the month and
  162. * &quot;4#5&quot; = the fifth Wednesday of the month. Note that if you specify
  163. * &quot;#5&quot; and there is not 5 of the given day-of-week in the month, then
  164. * no firing will occur that month.
  165. * <P>
  166. * <!--The 'C' character is allowed for the day-of-month and day-of-week fields.
  167. * This character is short-hand for "calendar". This means values are
  168. * calculated against the associated calendar, if any. If no calendar is
  169. * associated, then it is equivalent to having an all-inclusive calendar. A
  170. * value of "5C" in the day-of-month field means "the first day included by the
  171. * calendar on or after the 5th". A value of "1C" in the day-of-week field
  172. * means "the first day included by the calendar on or after sunday".-->
  173. * <P>
  174. * The legal characters and the names of months and days of the week are not
  175. * case sensitive.
  176. *
  177. * <p>
  178. * <b>NOTES:</b>
  179. * <ul>
  180. * <li>Support for specifying both a day-of-week and a day-of-month value is
  181. * not complete (you'll need to use the '?' character in on of these fields).
  182. * </li>
  183. * </ul>
  184. * </p>
  185. *
  186. *
  187. * @author Sharada Jambula, James House
  188. * @author Contributions from Mads Henderson
  189. * @author Refactoring from CronTrigger to CronExpression by Aaron Craven
  190. */
  191. public class CronExpression {
  192. private static final WidgetsLocalizedMessages MSGS = WidgetsLocalizedMessagesSingleton.getInstance().getMessages();
  193. private static final long serialVersionUID = 12423409423L;
  194. protected static final int SECOND = 0;
  195. protected static final int MINUTE = 1;
  196. protected static final int HOUR = 2;
  197. protected static final int DAY_OF_MONTH = 3;
  198. protected static final int MONTH = 4;
  199. protected static final int DAY_OF_WEEK = 5;
  200. protected static final int YEAR = 6;
  201. protected static final int ALL_SPEC_INT = 99; // '*'
  202. protected static final int NO_SPEC_INT = 98; // '?'
  203. protected static final Integer ALL_SPEC = new Integer(ALL_SPEC_INT);
  204. protected static final Integer NO_SPEC = new Integer(NO_SPEC_INT);
  205. protected static Map monthMap = new HashMap(20);
  206. protected static Map dayMap = new HashMap(60);
  207. static {
  208. monthMap.put("JAN", new Integer(0));
  209. monthMap.put("FEB", new Integer(1));
  210. monthMap.put("MAR", new Integer(2));
  211. monthMap.put("APR", new Integer(3));
  212. monthMap.put("MAY", new Integer(4));
  213. monthMap.put("JUN", new Integer(5));
  214. monthMap.put("JUL", new Integer(6));
  215. monthMap.put("AUG", new Integer(7));
  216. monthMap.put("SEP", new Integer(8));
  217. monthMap.put("OCT", new Integer(9));
  218. monthMap.put("NOV", new Integer(10));
  219. monthMap.put("DEC", new Integer(11));
  220. dayMap.put("SUN", new Integer(1));
  221. dayMap.put("MON", new Integer(2));
  222. dayMap.put("TUE", new Integer(3));
  223. dayMap.put("WED", new Integer(4));
  224. dayMap.put("THU", new Integer(5));
  225. dayMap.put("FRI", new Integer(6));
  226. dayMap.put("SAT", new Integer(7));
  227. }
  228. private String cronExpression = null;
  229. protected transient TreeSet seconds;
  230. protected transient TreeSet minutes;
  231. protected transient TreeSet hours;
  232. protected transient TreeSet daysOfMonth;
  233. protected transient TreeSet months;
  234. protected transient TreeSet daysOfWeek;
  235. protected transient TreeSet years;
  236. protected transient boolean lastdayOfWeek = false;
  237. protected transient int nthdayOfWeek = 0;
  238. protected transient boolean lastdayOfMonth = false;
  239. protected transient boolean nearestWeekday = false;
  240. protected transient boolean calendardayOfWeek = false;
  241. protected transient boolean calendardayOfMonth = false;
  242. protected transient boolean expressionParsed = false;
  243. /**
  244. * Constructs a new <CODE>CronExpression</CODE> based on the specified
  245. * parameter.
  246. *
  247. * @param cronExpression String representation of the cron expression the
  248. * new object should represent
  249. * @throws java.text.ParseException
  250. * if the string expression cannot be parsed into a valid
  251. * <CODE>CronExpression</CODE>
  252. */
  253. public CronExpression(String cronExpression) throws ParseException {
  254. if (cronExpression == null) {
  255. throw new IllegalArgumentException(MSGS.cronExpressionNull());
  256. }
  257. this.cronExpression = cronExpression;
  258. buildExpression(cronExpression.toUpperCase());
  259. }
  260. /**
  261. * Returns the string representation of the <CODE>CronExpression</CODE>
  262. *
  263. * @return a string representation of the <CODE>CronExpression</CODE>
  264. */
  265. public String toString() {
  266. return cronExpression;
  267. }
  268. /**
  269. * Indicates whether the specified cron expression can be parsed into a
  270. * valid cron expression
  271. *
  272. * @param cronExpression the expression to evaluate
  273. * @return a boolean indicating whether the given expression is a valid cron
  274. * expression
  275. */
  276. public static boolean isValidExpression(String cronExpression) {
  277. try {
  278. new CronExpression(cronExpression);
  279. } catch (ParseException pe) {
  280. return false;
  281. }
  282. return true;
  283. }
  284. ////////////////////////////////////////////////////////////////////////////
  285. //
  286. // Expression Parsing Functions
  287. //
  288. ////////////////////////////////////////////////////////////////////////////
  289. protected void buildExpression(String expression) throws ParseException {
  290. expressionParsed = true;
  291. try {
  292. if (seconds == null)
  293. seconds = new TreeSet();
  294. if (minutes == null)
  295. minutes = new TreeSet();
  296. if (hours == null)
  297. hours = new TreeSet();
  298. if (daysOfMonth == null)
  299. daysOfMonth = new TreeSet();
  300. if (months == null)
  301. months = new TreeSet();
  302. if (daysOfWeek == null)
  303. daysOfWeek = new TreeSet();
  304. if (years == null)
  305. years = new TreeSet();
  306. int exprOn = SECOND;
  307. String[] exprsTok = expression.split(" |\\t");
  308. for (int i = 0; i < exprsTok.length; i++) {
  309. if (exprOn > YEAR) {
  310. break;
  311. }
  312. String expr = exprsTok[i];
  313. String[] vtok = expr.split(",");
  314. for (int j = 0; j < vtok.length; j++) {
  315. String v = vtok[j];
  316. storeExpressionVals(0, v, exprOn);
  317. }
  318. exprOn++;
  319. }
  320. if (exprOn <= DAY_OF_WEEK)
  321. throw new ParseException(MSGS.cronUnexpectedEndOfExpression(), expression.length());
  322. if (exprOn <= YEAR)
  323. storeExpressionVals(0, "*", YEAR);
  324. } catch (ParseException pe) {
  325. throw pe;
  326. } catch (Exception e) {
  327. throw new ParseException(MSGS.cronIllegalExpressionFormat(e.toString()), 0);
  328. }
  329. }
  330. protected int storeExpressionVals(int pos, String s, int type) throws ParseException {
  331. int incr = 0;
  332. int i = skipWhiteSpace(pos, s);
  333. if (i >= s.length())
  334. return i;
  335. char c = s.charAt(i);
  336. if ((c >= 'A') && (c <= 'Z') && (!s.equals("L")) && (!s.equals("LW"))) {
  337. String sub = s.substring(i, i + 3);
  338. int sval = -1;
  339. int eval = -1;
  340. if (type == MONTH) {
  341. sval = getMonthNumber(sub) + 1;
  342. if (sval < 0) {
  343. throw new ParseException(MSGS.cronInvalidMonthValue(sub), i);
  344. }
  345. if (s.length() > i + 3) {
  346. c = s.charAt(i + 3);
  347. if (c == '-') {
  348. i += 4;
  349. sub = s.substring(i, i + 3);
  350. eval = getMonthNumber(sub) + 1;
  351. if (eval < 0) {
  352. throw new ParseException(MSGS.cronInvalidMonthValue(sub), i);
  353. }
  354. }
  355. }
  356. } else if (type == DAY_OF_WEEK) {
  357. sval = getDayOfWeekNumber(sub);
  358. if (sval < 0) {
  359. throw new ParseException(MSGS.cronInvalidDOWValue(sub), i);
  360. }
  361. if (s.length() > i + 3) {
  362. c = s.charAt(i + 3);
  363. if (c == '-') {
  364. i += 4;
  365. sub = s.substring(i, i + 3);
  366. eval = getDayOfWeekNumber(sub);
  367. if (eval < 0) {
  368. throw new ParseException(MSGS.cronInvalidDOWValue(sub), i);
  369. }
  370. if (sval > eval) {
  371. throw new ParseException(MSGS.cronInvalidDOWSequence(Integer.toString(sval), Integer.toString(eval)), i);
  372. }
  373. } else if (c == '#') {
  374. try {
  375. i += 4;
  376. nthdayOfWeek = Integer.parseInt(s.substring(i));
  377. if (nthdayOfWeek < 1 || nthdayOfWeek > 5)
  378. throw new Exception();
  379. } catch (Exception e) {
  380. throw new ParseException(MSGS.cronIllegalHashFollowingNumeric(), i);
  381. }
  382. } else if (c == 'L') {
  383. lastdayOfWeek = true;
  384. i++;
  385. }
  386. }
  387. } else {
  388. throw new ParseException(MSGS.cronIllegalCharactersForPosition(sub), i);
  389. }
  390. if (eval != -1) {
  391. incr = 1;
  392. }
  393. addToSet(sval, eval, incr, type);
  394. return (i + 3);
  395. }
  396. if (c == '?') {
  397. i++;
  398. if ((i + 1) < s.length() && (s.charAt(i) != ' ' && s.charAt(i + 1) != '\t')) {
  399. throw new ParseException(
  400. MSGS.cronIllegalCharacterAfter( "?", String.valueOf(s.charAt(i))), i);
  401. }
  402. if (type != DAY_OF_WEEK && type != DAY_OF_MONTH) {
  403. throw new ParseException(MSGS.cronIllegalQuestionMark(), i);
  404. }
  405. if (type == DAY_OF_WEEK && !lastdayOfMonth) {
  406. int val = ((Integer) daysOfMonth.last()).intValue();
  407. if (val == NO_SPEC_INT) {
  408. throw new ParseException(MSGS.cronIllegalQuestionMark(), i);
  409. }
  410. }
  411. addToSet(NO_SPEC_INT, -1, 0, type);
  412. return i;
  413. }
  414. if (c == '*' || c == '/') {
  415. if (c == '*' && (i + 1) >= s.length()) {
  416. addToSet(ALL_SPEC_INT, -1, incr, type);
  417. return i + 1;
  418. } else if (c == '/' && ((i + 1) >= s.length() || s.charAt(i + 1) == ' ' || s.charAt(i + 1) == '\t')) {
  419. throw new ParseException(MSGS.cronIllegalSlash(), i);
  420. } else if (c == '*') {
  421. i++;
  422. }
  423. c = s.charAt(i);
  424. if (c == '/') { // is an increment specified?
  425. i++;
  426. if (i >= s.length()) {
  427. throw new ParseException(MSGS.cronUnexpectedEndOfString(), i);
  428. }
  429. incr = getNumericValue(s, i);
  430. i++;
  431. if (incr > 10)
  432. i++;
  433. if (incr > 59 && (type == SECOND || type == MINUTE)) {
  434. throw new ParseException(MSGS.cronIllegalIncrement("60", Integer.toString(incr)), i);
  435. } else if (incr > 23 && (type == HOUR)) {
  436. throw new ParseException(MSGS.cronIllegalIncrement("24", Integer.toString(incr)), i);
  437. } else if (incr > 31 && (type == DAY_OF_MONTH)) {
  438. throw new ParseException(MSGS.cronIllegalIncrement("31", Integer.toString(incr)), i);
  439. } else if (incr > 7 && (type == DAY_OF_WEEK)) {
  440. throw new ParseException(MSGS.cronIllegalIncrement("7", Integer.toString(incr)), i);
  441. } else if (incr > 12 && (type == MONTH)) {
  442. throw new ParseException(MSGS.cronIllegalIncrement("12", Integer.toString(incr)), i);
  443. }
  444. } else
  445. incr = 1;
  446. addToSet(ALL_SPEC_INT, -1, incr, type);
  447. return i;
  448. } else if (c == 'L') {
  449. i++;
  450. if (type == DAY_OF_MONTH)
  451. lastdayOfMonth = true;
  452. if (type == DAY_OF_WEEK)
  453. addToSet(7, 7, 0, type);
  454. if (type == DAY_OF_MONTH && s.length() > i) {
  455. c = s.charAt(i);
  456. if (c == 'W') {
  457. nearestWeekday = true;
  458. i++;
  459. }
  460. }
  461. return i;
  462. } else if (c >= '0' && c <= '9') {
  463. int val = Integer.parseInt(String.valueOf(c));
  464. i++;
  465. if (i >= s.length()) {
  466. addToSet(val, -1, -1, type);
  467. } else {
  468. c = s.charAt(i);
  469. if (c >= '0' && c <= '9') {
  470. ValueSet vs = getValue(val, s, i);
  471. val = vs.value;
  472. i = vs.pos;
  473. }
  474. i = checkNext(i, s, val, type);
  475. return i;
  476. }
  477. } else {
  478. throw new ParseException(MSGS.cronUnexpectedCharacter(String.valueOf(c)), i);
  479. }
  480. return i;
  481. }
  482. protected int checkNext(int pos, String s, int val, int type) throws ParseException {
  483. int end = -1;
  484. int i = pos;
  485. if (i >= s.length()) {
  486. addToSet(val, end, -1, type);
  487. return i;
  488. }
  489. char c = s.charAt(pos);
  490. if (c == 'L') {
  491. if (type == DAY_OF_WEEK) {
  492. lastdayOfWeek = true;
  493. } else {
  494. throw new ParseException(MSGS.cronOptionIsNotValidHere("L", Integer.toString(i)), i);
  495. }
  496. TreeSet set = getSet(type);
  497. set.add(new Integer(val));
  498. i++;
  499. return i;
  500. }
  501. if (c == 'W') {
  502. if (type == DAY_OF_MONTH) {
  503. nearestWeekday = true;
  504. } else {
  505. throw new ParseException(MSGS.cronOptionIsNotValidHere("W", Integer.toString(i)), i);
  506. }
  507. TreeSet set = getSet(type);
  508. set.add(new Integer(val));
  509. i++;
  510. return i;
  511. }
  512. if (c == '#') {
  513. if (type != DAY_OF_WEEK) {
  514. throw new ParseException(MSGS.cronOptionIsNotValidHere("#", Integer.toString(i)), i);
  515. }
  516. i++;
  517. try {
  518. nthdayOfWeek = Integer.parseInt(s.substring(i));
  519. if (nthdayOfWeek < 1 || nthdayOfWeek > 5) {
  520. throw new Exception();
  521. }
  522. } catch (Exception e) {
  523. throw new ParseException(MSGS.cronIllegalHashFollowingNumeric(), i);
  524. }
  525. TreeSet set = getSet(type);
  526. set.add(new Integer(val));
  527. i++;
  528. return i;
  529. }
  530. if (c == 'C') {
  531. if (type == DAY_OF_WEEK) {
  532. calendardayOfWeek = true;
  533. } else if (type == DAY_OF_MONTH) {
  534. calendardayOfMonth = true;
  535. } else {
  536. throw new ParseException(MSGS.cronOptionIsNotValidHere("C", Integer.toString(i)), i);
  537. }
  538. TreeSet set = getSet(type);
  539. set.add(new Integer(val));
  540. i++;
  541. return i;
  542. }
  543. if (c == '-') {
  544. i++;
  545. c = s.charAt(i);
  546. int v = Integer.parseInt(String.valueOf(c));
  547. end = v;
  548. i++;
  549. if (i >= s.length()) {
  550. addToSet(val, end, 1, type);
  551. return i;
  552. }
  553. c = s.charAt(i);
  554. if (c >= '0' && c <= '9') {
  555. ValueSet vs = getValue(v, s, i);
  556. int v1 = vs.value;
  557. end = v1;
  558. i = vs.pos;
  559. }
  560. if (i < s.length() && ((c = s.charAt(i)) == '/')) {
  561. i++;
  562. c = s.charAt(i);
  563. int v2 = Integer.parseInt(String.valueOf(c));
  564. i++;
  565. if (i >= s.length()) {
  566. addToSet(val, end, v2, type);
  567. return i;
  568. }
  569. c = s.charAt(i);
  570. if (c >= '0' && c <= '9') {
  571. ValueSet vs = getValue(v2, s, i);
  572. int v3 = vs.value;
  573. addToSet(val, end, v3, type);
  574. i = vs.pos;
  575. return i;
  576. } else {
  577. addToSet(val, end, v2, type);
  578. return i;
  579. }
  580. } else {
  581. addToSet(val, end, 1, type);
  582. return i;
  583. }
  584. }
  585. if (c == '/') {
  586. i++;
  587. c = s.charAt(i);
  588. int v2 = Integer.parseInt(String.valueOf(c));
  589. i++;
  590. if (i >= s.length()) {
  591. addToSet(val, end, v2, type);
  592. return i;
  593. }
  594. c = s.charAt(i);
  595. if (c >= '0' && c <= '9') {
  596. ValueSet vs = getValue(v2, s, i);
  597. int v3 = vs.value;
  598. addToSet(val, end, v3, type);
  599. i = vs.pos;
  600. return i;
  601. } else {
  602. throw new ParseException(MSGS.cronUnexpectedCharacterAfterSlash(String.valueOf(c)), i);
  603. }
  604. }
  605. addToSet(val, end, 0, type);
  606. i++;
  607. return i;
  608. }
  609. public String getCronExpression() {
  610. return cronExpression;
  611. }
  612. protected int skipWhiteSpace(int i, String s) {
  613. for (; i < s.length() && (s.charAt(i) == ' ' || s.charAt(i) == '\t'); i++)
  614. ;
  615. return i;
  616. }
  617. protected int findNextWhiteSpace(int i, String s) {
  618. for (; i < s.length() && (s.charAt(i) != ' ' || s.charAt(i) != '\t'); i++)
  619. ;
  620. return i;
  621. }
  622. protected void addToSet(int val, int end, int incr, int type) throws ParseException {
  623. TreeSet set = getSet(type);
  624. if (type == SECOND || type == MINUTE) {
  625. if ((val < 0 || val > 59 || end > 59) && (val != ALL_SPEC_INT)) {
  626. throw new ParseException(MSGS.cronInvalidMinuteSecondValue(), -1);
  627. }
  628. } else if (type == HOUR) {
  629. if ((val < 0 || val > 23 || end > 23) && (val != ALL_SPEC_INT)) {
  630. throw new ParseException(MSGS.cronInvalidHourValue(), -1);
  631. }
  632. } else if (type == DAY_OF_MONTH) {
  633. if ((val < 1 || val > 31 || end > 31) && (val != ALL_SPEC_INT) && (val != NO_SPEC_INT)) {
  634. throw new ParseException(MSGS.cronInvalidDayOfMonthValue(), -1);
  635. }
  636. } else if (type == MONTH) {
  637. if ((val < 1 || val > 12 || end > 12) && (val != ALL_SPEC_INT)) {
  638. throw new ParseException(MSGS.cronInvalidMonthValueGeneral(), -1);
  639. }
  640. } else if (type == DAY_OF_WEEK) {
  641. if ((val == 0 || val > 7 || end > 7) && (val != ALL_SPEC_INT) && (val != NO_SPEC_INT)) {
  642. throw new ParseException(MSGS.cronInvalidDayOfWeekValue(), -1);
  643. }
  644. }
  645. if ((incr == 0 || incr == -1) && val != ALL_SPEC_INT) {
  646. if (val != -1) {
  647. set.add(new Integer(val));
  648. } else {
  649. set.add(NO_SPEC);
  650. }
  651. return;
  652. }
  653. int startAt = val;
  654. int stopAt = end;
  655. if (val == ALL_SPEC_INT && incr <= 0) {
  656. incr = 1;
  657. set.add(ALL_SPEC); // put in a marker, but also fill values
  658. }
  659. if (type == SECOND || type == MINUTE) {
  660. if (stopAt == -1) {
  661. stopAt = 59;
  662. }
  663. if (startAt == -1 || startAt == ALL_SPEC_INT) {
  664. startAt = 0;
  665. }
  666. } else if (type == HOUR) {
  667. if (stopAt == -1) {
  668. stopAt = 23;
  669. }
  670. if (startAt == -1 || startAt == ALL_SPEC_INT) {
  671. startAt = 0;
  672. }
  673. } else if (type == DAY_OF_MONTH) {
  674. if (stopAt == -1) {
  675. stopAt = 31;
  676. }
  677. if (startAt == -1 || startAt == ALL_SPEC_INT) {
  678. startAt = 1;
  679. }
  680. } else if (type == MONTH) {
  681. if (stopAt == -1) {
  682. stopAt = 12;
  683. }
  684. if (startAt == -1 || startAt == ALL_SPEC_INT) {
  685. startAt = 1;
  686. }
  687. } else if (type == DAY_OF_WEEK) {
  688. if (stopAt == -1) {
  689. stopAt = 7;
  690. }
  691. if (startAt == -1 || startAt == ALL_SPEC_INT) {
  692. startAt = 1;
  693. }
  694. } else if (type == YEAR) {
  695. if (stopAt == -1) {
  696. stopAt = 2099;
  697. }
  698. if (startAt == -1 || startAt == ALL_SPEC_INT) {
  699. startAt = 1970;
  700. }
  701. }
  702. for (int i = startAt; i <= stopAt; i += incr) {
  703. set.add(new Integer(i));
  704. }
  705. }
  706. protected TreeSet getSet(int type) {
  707. switch (type) {
  708. case SECOND:
  709. return seconds;
  710. case MINUTE:
  711. return minutes;
  712. case HOUR:
  713. return hours;
  714. case DAY_OF_MONTH:
  715. return daysOfMonth;
  716. case MONTH:
  717. return months;
  718. case DAY_OF_WEEK:
  719. return daysOfWeek;
  720. case YEAR:
  721. return years;
  722. default:
  723. return null;
  724. }
  725. }
  726. protected ValueSet getValue(int v, String s, int i) {
  727. char c = s.charAt(i);
  728. String s1 = String.valueOf(v);
  729. while (c >= '0' && c <= '9') {
  730. s1 += c;
  731. i++;
  732. if (i >= s.length()) {
  733. break;
  734. }
  735. c = s.charAt(i);
  736. }
  737. ValueSet val = new ValueSet();
  738. if (i < s.length()) {
  739. val.pos = i;
  740. } else {
  741. val.pos = i + 1;
  742. }
  743. val.value = Integer.parseInt(s1);
  744. return val;
  745. }
  746. protected int getNumericValue(String s, int i) {
  747. int endOfVal = findNextWhiteSpace(i, s);
  748. String val = s.substring(i, endOfVal);
  749. return Integer.parseInt(val);
  750. }
  751. protected int getMonthNumber(String s) {
  752. Integer integer = (Integer) monthMap.get(s);
  753. if (integer == null) {
  754. return -1;
  755. }
  756. return integer.intValue();
  757. }
  758. protected int getDayOfWeekNumber(String s) {
  759. Integer integer = (Integer) dayMap.get(s);
  760. if (integer == null) {
  761. return -1;
  762. }
  763. return integer.intValue();
  764. }
  765. }
  766. class ValueSet {
  767. public int value;
  768. public int pos;
  769. }