/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.gwt.widgets.client.utils; import java.util.EnumSet; import org.pentaho.gwt.widgets.client.i18n.WidgetsLocalizedMessages; import org.pentaho.gwt.widgets.client.i18n.WidgetsLocalizedMessagesSingleton; /** * * @author Steven Barkdull * */ public class CronParser { private static final WidgetsLocalizedMessages MSGS = WidgetsLocalizedMessagesSingleton.getInstance().getMessages(); private String cronStr; private RecurrenceType recurrenceType = RecurrenceType.EveryWeekday; private int startSecond = -1; private int startMinute = -1; private int startHour = -1; // for use by internal algorithms private String dayOfMonthToke = null; // from cronString[3] private String monthToke = null; // from cronString[4] private String dayOfWeekToke = null; // from cronString[5] public static final int REQUIRED_NUM_TOKENS = 6; public static final String ALL = "*"; //$NON-NLS-1$ public static final String DONT_CARE = "?"; //$NON-NLS-1$ public static final String N_TH = "#"; //$NON-NLS-1$ public static final String LAST = "L"; //$NON-NLS-1$ private static final int MIN_CRON_FIELDS = 6; private static final int MAX_CRON_FIELDS = 7; enum CronField { SECONDS( 0 ), MINUTES( 1 ), HOURS( 2 ), DAY_OF_MONTH( 3 ), MONTH( 4 ), DAY_OF_WEEK( 5 ), YEAR( 6 ); CronField( int value ) { this.value = value; } private final int value; public int value() { return value; } } public enum RecurrenceType { Unknown, EveryWeekday, WeeklyOn, DayNOfMonth, NthDayNameOfMonth, LastDayNameOfMonth, EveryMonthNameN, NthDayNameOfMonthName, LastDayNameOfMonthName; public static RecurrenceType stringToScheduleType( String str ) throws EnumException { for ( RecurrenceType d : EnumSet.range( RecurrenceType.Unknown, RecurrenceType.LastDayNameOfMonthName ) ) { if ( d.toString().equals( str ) ) { return d; } } throw new EnumException( MSGS.invalidStringForRecurrenceType( str ) ); } }; public CronParser( String cronStr ) { this.cronStr = cronStr.trim(); } @SuppressWarnings( "unused" ) private static boolean isMultipleOfN( int testValue, int n ) { assert n != 0 : "isMultipleOfN(), n cannot be zero."; //$NON-NLS-1$ return ( ( testValue / n ) * n ) == testValue; } private static final String MATCH_DAY_OF_MONTH_FIELD_RE = "^(\\*|\\?|L|([0-9]{1,2}(W|(,[0-9]{1,2}){0,30}))|([0-9]{1,2}[/-][0-9]{1,2}))$"; //$NON-NLS-1$ public static boolean isDayOfMonthField( String fld ) { return fld.matches( MATCH_DAY_OF_MONTH_FIELD_RE ); } private static final String MATCH_MONTH_FIELD_RE = "^(\\*|([0-9]{1,2}(,[0-9]{1,2}){0,11})|([0-9]{1,2}[/-][0-9]{1,2}))$"; //$NON-NLS-1$ public static boolean isMonthField( String fld ) { return fld.matches( MATCH_MONTH_FIELD_RE ); } private static final String MATCH_DAY_OF_WEEK_FIELD_RE = "^(\\*|\\?|L|([0-9]{1,2}(,[0-9]{1,2}){0,6})|([0-9]{1,2}[/#-][0-9]{1,2}))$"; //$NON-NLS-1$ public static boolean isDayOfWeekField( String fld ) { return fld.matches( MATCH_DAY_OF_WEEK_FIELD_RE ); } private static final String MATCH_YEAR_FIELD_RE = "^\\*|[0-9]{1,4}$"; //$NON-NLS-1$ public static boolean isYearField( String fld ) { return fld.matches( MATCH_YEAR_FIELD_RE ); } /** * NOTE: doesn't validate that month, day-of-month or day-of-week integers are in range. TODO sbarkdull, maybe it * should? Maybe return an int identifying the index of the character in the cron string where the first syntax error * is, if no error, return -1. * * @param strInt * @return */ public static boolean isValidCronString( String strInt ) { boolean isValid = true; String[] parts = strInt.split( "\\s" ); //$NON-NLS-1$ if ( ( parts.length < MIN_CRON_FIELDS ) || ( parts.length > MAX_CRON_FIELDS ) ) { return false; } isValid &= isValidSecondsField( parts[0] ); isValid &= isValidMinutesField( parts[1] ); isValid &= isValidHoursField( parts[2] ); isValid &= isDayOfMonthField( parts[3] ); isValid &= isMonthField( parts[4] ); isValid &= isDayOfWeekField( parts[5] ); isValid &= ( DONT_CARE.equals( parts[3] ) || DONT_CARE.equals( parts[5] ) ); if ( parts.length == MAX_CRON_FIELDS ) { isValid &= isYearField( parts[6] ); } return isValid; } // match * or a 1 or 2 digit number followed by either - or / followed by any // 1 or 2 digit number, or, an group of comma separated 1 or 2 digit numbers private static String MATCH_SECONDS_RE = "^\\s*(\\*|\\d{1,2}[/-]\\d{1,2}|\\d{1,2}(,\\d{1,2})*)\\s*$"; //$NON-NLS-1$ /** * NOTE: does not check to make sure that integers are in a valid range * * @param strSecs * @return */ private static boolean isValidSecondsField( String strSecs ) { return strSecs.matches( MATCH_SECONDS_RE ); } private static String MATCH_MINUTES_RE = MATCH_SECONDS_RE; /** * NOTE: does not check to make sure that integers are in a valid range * * @param strMins * @return */ private static boolean isValidMinutesField( String strMins ) { return strMins.matches( MATCH_MINUTES_RE ); } private static String MATCH_HOURS_RE = MATCH_SECONDS_RE; /** * NOTE: does not check to make sure that integers are in a valid range * * @param strHours * @return */ private static boolean isValidHoursField( String strHours ) { return strHours.matches( MATCH_HOURS_RE ); } private String getCronString() { return cronStr; } @SuppressWarnings( "unused" ) private int getStartSecond() { return startSecond; } @SuppressWarnings( "unused" ) private int getStartMinute() { return startMinute; } @SuppressWarnings( "unused" ) private int getStartHour() { return startHour; } private static final String EVERY_WEEK_DAY_RE = "^\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\?\\s+\\*\\s+2\\-6$"; //$NON-NLS-1$ /** * Abstract cron string: 0 MM HH ? * 2-6 * * @param cronStr * @return */ private boolean isEveryWeekdayToken() { return cronStr.matches( EVERY_WEEK_DAY_RE ); } private static final String WEEKLY_ON_RE = "^\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\?\\s+\\*\\s+\\d(,\\d){0,6}$"; //$NON-NLS-1$ /** * Abstract cron string: 0 MM HH ? * DAY-LIST, where DAY-LIST is like 1,4,5 * * @param cronStr * @return */ private boolean isWeeklyOnToken() { return cronStr.matches( WEEKLY_ON_RE ); } private static final String DAY_N_OF_MONTH_RE = "^\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\*\\s+\\?$"; //$NON-NLS-1$ /** * Abstract cron string: 0 MM HH DAY * ? Fires at HH:MM on the DAY (i.e. 1-31) of each month * * @param dayOfMonthToke * @param monthToke * @param dayOfWeekToke * @return */ private boolean isDayNOfMonthToken() { boolean bReMatch = cronStr.matches( DAY_N_OF_MONTH_RE ); return bReMatch; } /** * Abstract cron string: 0 MM HH ? * DAY#N (N ε {1,2,3,4} DAY ε {1,2,3,4,5,6,7}) */ private static final String NTH_DAY_NAME_OF_MONTH_RE = "^\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\?\\s+\\*\\s+[1-7]{1}#[1-4]{1}$"; //$NON-NLS-1$ /** * Abstract cron string: 0 MM HH ? * DAY#N * * @param dayOfMonthToke * @param monthToke * @param dayOfWeekToke * @return */ private boolean isNthDayNameOfMonthToken() { boolean bReMatch = cronStr.matches( NTH_DAY_NAME_OF_MONTH_RE ); return bReMatch; } private static final String LAST_DAY_NAME_OF_MONTH_RE = "^\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\?\\s+\\*\\s+[1-7]{1}L$"; //$NON-NLS-1$ /** * Abstract cron string: 0 MM HH ? * DAYL (DAY ε {1,2,3,4,5,6,7}, where these integers map to days of the week) * * @param dayOfMonthToke * @param monthToke * @param dayOfWeekToke * @return */ private boolean isLastDayNameOfMonthToken() { boolean bReMatch = cronStr.matches( LAST_DAY_NAME_OF_MONTH_RE ); return bReMatch; } private static final String EVERY_MONTH_NAME_N_RE = "^\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\?$"; //$NON-NLS-1$ /** * Abstract cron string: 0 MM HH N MONTH ? (N ε {1-31} MONTH ε {1-12}) * * @param cronStr * @return */ private boolean isEveryMonthNameNToken() { boolean bReMatch = cronStr.matches( EVERY_MONTH_NAME_N_RE ); return bReMatch; } private static final String N_DAY_NAME_OF_MONTH_NAME_RE = "^\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\?\\s+\\d{1,2}\\s+[1-7]{1}#[1-4]{1}$"; //$NON-NLS-1$ /** * Abstract cron string: 0 MM HH ? MONTH DAY#N (MONTH ε {1-12}, N ε {1,2,3,4} DAY ε {1,2,3,4,5,6,7}) * * @param cronStr * @return */ private boolean isNthDayNameOfMonthNameToken() { boolean bReMatch = cronStr.matches( N_DAY_NAME_OF_MONTH_NAME_RE ); return bReMatch; } private static final String LAST_DAY_NAME_OF_MONTH_NAME_RE = "^\\d{1,2}\\s+\\d{1,2}\\s+\\d{1,2}\\s+\\?\\s+\\d{1,2}\\s+\\d{1}L$"; //$NON-NLS-1$ /** * Abstract cron string: 0 MM HH ? MONTH DAYL (MONTH ε {1-12}, DAY ε {1,2,3,4,5,6,7}) * * @param cronStr * @return */ private boolean isLastDayNameOfMonthNameToken() { boolean bReMatch = cronStr.matches( LAST_DAY_NAME_OF_MONTH_NAME_RE ); return bReMatch; } private RecurrenceType getSchedType() { return recurrenceType; } private RecurrenceType getScheduleType() throws CronParseException { if ( isEveryWeekdayToken() ) { return RecurrenceType.EveryWeekday; } else if ( isWeeklyOnToken() ) { return RecurrenceType.WeeklyOn; } else if ( isDayNOfMonthToken() ) { return RecurrenceType.DayNOfMonth; } else if ( isNthDayNameOfMonthToken() ) { return RecurrenceType.NthDayNameOfMonth; } else if ( isLastDayNameOfMonthToken() ) { return RecurrenceType.LastDayNameOfMonth; } else if ( isEveryMonthNameNToken() ) { return RecurrenceType.EveryMonthNameN; } else if ( isNthDayNameOfMonthNameToken() ) { return RecurrenceType.NthDayNameOfMonthName; } else if ( isLastDayNameOfMonthNameToken() ) { return RecurrenceType.LastDayNameOfMonthName; } else { return RecurrenceType.Unknown; } } /** * * @return The recurrence string corresponding to the cron string (this.cronStr). * * @throws CronParseException * if the CRON string is an invalid CRON string */ public String parseToRecurrenceString() throws CronParseException { tokenizeForRecurrence(); // tokenizeForRecurrence() will set recurrenceType. return getRecurrenceString(); } /** * * @throws CronParseException */ @SuppressWarnings( "deprecation" ) private void tokenizeForRecurrence() throws CronParseException { String[] tokens = cronStr.split( "\\s" ); // split on white space //$NON-NLS-1$ if ( REQUIRED_NUM_TOKENS != tokens.length ) { throw new CronParseException( MSGS.invalidNumTokens() ); } recurrenceType = getScheduleType(); if ( RecurrenceType.Unknown == recurrenceType ) { throw new CronParseException( MSGS.cronStringCannotTransformToRecurrenceString( this.cronStr ) ); } if ( StringUtils.isPositiveInteger( tokens[CronField.SECONDS.value] ) ) { int num = Integer.parseInt( tokens[CronField.SECONDS.value] ); if ( !TimeUtil.isSecond( num ) ) { throw new CronParseException( MSGS.invalidSecondsToken( tokens[0] ) ); } else { startSecond = num; } } if ( StringUtils.isPositiveInteger( tokens[CronField.MINUTES.value] ) ) { int num = Integer.parseInt( tokens[CronField.MINUTES.value] ); if ( !TimeUtil.isMinute( num ) ) { throw new CronParseException( MSGS.invalidMinutesToken( tokens[0] ) ); } else { startMinute = num; } } if ( StringUtils.isPositiveInteger( tokens[CronField.HOURS.value] ) ) { int num = Integer.parseInt( tokens[CronField.HOURS.value] ); if ( !TimeUtil.isHour( num ) ) { throw new CronParseException( MSGS.invalidHoursToken( tokens[0] ) ); } else { startHour = num; } } dayOfMonthToke = tokens[CronField.DAY_OF_MONTH.value]; monthToke = tokens[CronField.MONTH.value]; dayOfWeekToke = tokens[CronField.DAY_OF_WEEK.value]; } private boolean isDayOfMonthValid() { switch ( this.recurrenceType ) { case DayNOfMonth: // fall through case EveryMonthNameN: return true; default: return false; } } private int getDayOfMonth() throws CronParseException { String strVal; switch ( this.recurrenceType ) { case DayNOfMonth: // token 3 is N // fall through to next case EveryMonthNameN: // token 3 is N strVal = dayOfMonthToke; break; default: // oops throw new CronParseException( MSGS.invalidDayOfMonthForRecurrenceType( this.recurrenceType.toString() ) ); } int dayOfMonth = Integer.parseInt( strVal ); if ( !TimeUtil.isDayOfMonth( dayOfMonth ) ) { throw new CronParseException( MSGS.invalidDayOfMonth( strVal ) ); } return dayOfMonth; } private boolean isDaysOfWeekValid() { switch ( this.recurrenceType ) { case WeeklyOn: return true; default: return false; } } private int[] getDaysOfWeek() throws CronParseException { switch ( this.recurrenceType ) { case WeeklyOn: // token 5 is comma separated list of 1-7 unique integers between 1 and 7 String[] days = dayOfWeekToke.split( "," ); //$NON-NLS-1$ int[] intDays = new int[days.length]; for ( int ii = 0; ii < days.length; ++ii ) { intDays[ii] = Integer.parseInt( days[ii] ); if ( !TimeUtil.isDayOfWeek( intDays[ii] ) ) { throw new CronParseException( MSGS.invalidDayOfWeek( days[ii] ) ); } } return intDays; default: throw new CronParseException( MSGS.invalidDayOfWeekForRecurrenceType( this.recurrenceType.toString() ) ); } } private boolean isWhichWeekOfMonthValid() { switch ( this.recurrenceType ) { case NthDayNameOfMonth: // fall through case NthDayNameOfMonthName: // token 5 is DAY#N, want N return true; default: return false; } } /** * * @return integer between 1 and 4 inclusive * @throws CronParseException */ private int getWhichWeekOfMonth() throws CronParseException { String strVal; switch ( this.recurrenceType ) { case NthDayNameOfMonth: // fall through case NthDayNameOfMonthName: // token 5 is DAY#N, want N strVal = dayOfWeekToke.split( N_TH )[1]; int weekOfMonth = Integer.parseInt( strVal ); if ( !TimeUtil.isWeekOfMonth( weekOfMonth ) ) { throw new CronParseException( MSGS.invalidWeekOfMonth( strVal ) ); } return weekOfMonth; default: throw new CronParseException( MSGS.invalidWeekOfMonthForRecurrenceType( this.recurrenceType.toString() ) ); } } private boolean isWhichDayOfWeekValid() { switch ( this.recurrenceType ) { case NthDayNameOfMonth: // fall through case NthDayNameOfMonthName: // fall through case LastDayNameOfMonth: // fall through case LastDayNameOfMonthName: // fall through return true; default: return false; } } /** * * @return integer between 1 and 7 inclusive * @throws CronParseException */ private int getWhichDayOfWeek() throws CronParseException { int dayOfWeek; switch ( this.recurrenceType ) { case NthDayNameOfMonth: // fall through case NthDayNameOfMonthName: // token 5 is DAY#N, want DAY dayOfWeek = Integer.parseInt( dayOfWeekToke.split( N_TH )[0] ); break; case LastDayNameOfMonth: // fall through case LastDayNameOfMonthName: // token 5 is NL, want N String strDay = dayOfWeekToke.substring( 0, dayOfWeekToke.length() - 1 ); // trim off the trailing L dayOfWeek = Integer.parseInt( strDay ); break; default: throw new CronParseException( MSGS.invalidDayOfMonthForRecurrenceType( this.recurrenceType.toString() ) ); } if ( !TimeUtil.isDayOfWeek( dayOfWeek ) ) { throw new CronParseException( MSGS.invalidDayOfMonth( Integer.toString( dayOfWeek ) ) ); } return dayOfWeek; } private boolean isWhichMonthOfYearValid() { switch ( this.recurrenceType ) { case EveryMonthNameN: // fall through case NthDayNameOfMonthName: // fall through case LastDayNameOfMonthName: // fall through return true; default: return false; } } /** * * @return integer between 1 and 12 inclusive * @throws CronParseException */ private int getWhichMonthOfYear() throws CronParseException { int monthOfYear; switch ( this.recurrenceType ) { case EveryMonthNameN: // fall through case NthDayNameOfMonthName: // fall through case LastDayNameOfMonthName: // token 4 is N monthOfYear = Integer.parseInt( monthToke ); break; default: throw new CronParseException( MSGS.invalidMonthOfYearForRecurrenceType( this.recurrenceType.toString() ) ); } if ( !TimeUtil.isMonthOfYear( monthOfYear ) ) { throw new CronParseException( MSGS.invalidMonthOfYear( monthToke ) ); } return monthOfYear; } /** * Tokens: 1. The recurrence type, one of the values in the enum RecurrenceType 2. start second (0-59) 3. start minute * (0-59) 4. start hour (0-24) 5. if valid, a comma separated list of integers that map to the days of the week (1-7), * see isDaysOfWeekValid for valid RecurrenceTypes 6. if valid, an integer representing a day of the month (1-31), see * isDayOfMonthValid for valid RecurrenceTypes 7. if valid, an integer representing a day of the week (1-7), see * isWhichDayOfWeekValid for valid RecurrenceTypes 8. if valid, an integer representing a week of the month (1-4), see * isWhichWeekOfMonthValid for valid RecurrenceTypes 9. if valid, an integer representing a month of the year (1-12), * see isWhichMonthOfYearValid for valid RecurrenceTypes All or none of the items 5-9 may be present. Tokens are order * dependent. Tokens are not position dependent, meaning that the same token may occur and position N in one * recurrence type, and position N+1 in others. * * @return * @throws CronParseException */ private String getRecurrenceString() throws CronParseException { StringBuilder sb = new StringBuilder(); sb.append( this.recurrenceType.toString() ).append( " " ); //$NON-NLS-1$ sb.append( startSecond ).append( " " ).append( startMinute ).append( " " ).append( startHour ).append( " " ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ if ( isDaysOfWeekValid() ) { int[] days = getDaysOfWeek(); for ( int ii = 0; ii < days.length; ++ii ) { int day = days[ii]; sb.append( day ).append( ( ii < days.length - 1 ) ? "," : "" ); //$NON-NLS-1$ //$NON-NLS-2$ } sb.append( " " ); //$NON-NLS-1$ } if ( isDayOfMonthValid() ) { sb.append( getDayOfMonth() ).append( " " ); //$NON-NLS-1$ } if ( isWhichDayOfWeekValid() ) { sb.append( getWhichDayOfWeek() ).append( " " ); //$NON-NLS-1$ } if ( isWhichWeekOfMonthValid() ) { sb.append( getWhichWeekOfMonth() ).append( " " ); //$NON-NLS-1$ } if ( isWhichMonthOfYearValid() ) { sb.append( getWhichMonthOfYear() ).append( " " ); //$NON-NLS-1$ } return sb.toString().trim(); } @SuppressWarnings( "deprecation" ) private static void validateIsInt( String strInt ) throws CronParseException { if ( !StringUtils.isPositiveInteger( strInt ) ) { throw new CronParseException( MSGS.invalidIntegerToken( strInt ) ); } } private static final String COMMA_SEPARATED_LIST_OF_WEEKDAY_INTS = "^[1-7](,[1-7]){0,6}$"; //$NON-NLS-1$ private static void validateIsCommaSeparatedListOfWeekdays( String strInts ) throws CronParseException { // TODO, could make sure that the ints are unique boolean matches = strInts.matches( COMMA_SEPARATED_LIST_OF_WEEKDAY_INTS ); if ( !matches ) { throw new CronParseException( MSGS.invalidIntegerListToken( strInts ) ); } } // TODO sbarkdull, instead of validating that values are int, validate that the are // second, minute, hour, day of week, day of month, month of year, week of month // TODO should this method be throwing ONLY RuntimeExceptions? /** * Given a recurrenceTokenString, generate the corresponding CRON string. * * @param recurrenceStr * String * @return * @throws RuntimeException * if the recurrenceTokenString contains the Unknown RecurrenceType. */ public static String recurrenceStringToCronString( String recurrenceTokenString ) throws RuntimeException, CronParseException { StringBuilder sb = new StringBuilder(); String cronToken3, cronToken4, cronToken5; String[] recurrenceTokens = recurrenceTokenString.trim().split( "\\s+" ); //$NON-NLS-1$ validateIsInt( recurrenceTokens[1] ); validateIsInt( recurrenceTokens[2] ); validateIsInt( recurrenceTokens[3] ); sb.append( recurrenceTokens[1] ).append( " " ) //$NON-NLS-1$ .append( recurrenceTokens[2] ).append( " " ) //$NON-NLS-1$ .append( recurrenceTokens[3] ).append( " " ); //$NON-NLS-1$ RecurrenceType st; try { st = RecurrenceType.stringToScheduleType( recurrenceTokens[0] ); } catch ( EnumException e ) { throw new CronParseException( e.getMessage() ); } switch ( st ) { case EveryWeekday: cronToken3 = DONT_CARE; cronToken4 = ALL; cronToken5 = "2-6"; //$NON-NLS-1$ break; case WeeklyOn: validateIsCommaSeparatedListOfWeekdays( recurrenceTokens[4] ); cronToken3 = DONT_CARE; cronToken4 = ALL; cronToken5 = recurrenceTokens[4]; break; case DayNOfMonth: validateIsInt( recurrenceTokens[4] ); cronToken3 = recurrenceTokens[4]; cronToken4 = ALL; cronToken5 = DONT_CARE; break; case NthDayNameOfMonth: validateIsInt( recurrenceTokens[4] ); validateIsInt( recurrenceTokens[5] ); cronToken3 = DONT_CARE; cronToken4 = ALL; cronToken5 = recurrenceTokens[4] + N_TH + recurrenceTokens[5]; break; case LastDayNameOfMonth: validateIsInt( recurrenceTokens[4] ); cronToken3 = DONT_CARE; cronToken4 = ALL; cronToken5 = recurrenceTokens[4] + LAST; break; case EveryMonthNameN: validateIsInt( recurrenceTokens[4] ); validateIsInt( recurrenceTokens[5] ); cronToken3 = recurrenceTokens[4]; cronToken4 = recurrenceTokens[5]; cronToken5 = DONT_CARE; break; case NthDayNameOfMonthName: validateIsInt( recurrenceTokens[4] ); validateIsInt( recurrenceTokens[5] ); validateIsInt( recurrenceTokens[6] ); cronToken3 = DONT_CARE; cronToken4 = recurrenceTokens[6]; cronToken5 = recurrenceTokens[4] + N_TH + recurrenceTokens[5]; break; case LastDayNameOfMonthName: cronToken3 = DONT_CARE; cronToken4 = recurrenceTokens[5]; cronToken5 = recurrenceTokens[4] + LAST; break; case Unknown: throw new RuntimeException( MSGS.illegalRecurrenceTypeUnknown() ); default: throw new RuntimeException( MSGS.invalidRecurrenceType( recurrenceTokens[0] ) ); } sb.append( cronToken3 ).append( " " ); //$NON-NLS-1$ sb.append( cronToken4 ).append( " " ); //$NON-NLS-1$ sb.append( cronToken5 ).append( " " ); //$NON-NLS-1$ return sb.toString().trim(); } public static void main( String[] args ) { // for (CronField f : EnumSet.range(CronField.SECONDS, CronField.YEAR)) { // System.out.println(f); // System.out.println(f.ordinal()); // } // // for ( int ii=0; ii< 256; ++ii ) { // boolean b = isMultipleOfN( ii, 24 ); // if ( b ) { // System.out.println( "isMultipleOfN( " + ii + ", 24): " + b ); // } // } String[] cronSamples = { "0 59 23 ? *", // invalid # tokens //$NON-NLS-1$ "0 59 23 ? * 2#4 2008", // invalid # tokens //$NON-NLS-1$ "0 22 4 0/3 * ?", // EveryNthDayOfMonth Fires at 4:22am on the 1,4,7... //$NON-NLS-1$ "0 14 21 ? * 2-6", // EveryWeekday Fires at 2:21pm every weekday //$NON-NLS-1$ "0 33 6 ? * 1", //WeeklyOn //$NON-NLS-1$ "0 33 6 ? * 1,2", //WeeklyOn //$NON-NLS-1$ "0 33 6 ? * 1,2,3", //WeeklyOn //$NON-NLS-1$ "0 33 6 ? * 1,2,3,4", //WeeklyOn //$NON-NLS-1$ "0 33 6 ? * 1,2,3,4,5", //WeeklyOn //$NON-NLS-1$ "0 33 6 ? * 1,2,3,4,5,6", //WeeklyOn //$NON-NLS-1$ "0 33 6 ? * 1,2,3,4,5,6,7", //WeeklyOn //$NON-NLS-1$ "0 33 6 ? * 1,2,3,4,5,6,7,8", //WeeklyOn, must fail //$NON-NLS-1$ "0 5 5 13 * ?", // DayNOfMonth //$NON-NLS-1$ "0 59 23 ? * 2#4", // NthDayNameOfMonth //$NON-NLS-1$ "0 59 23 ? * 5#4", // NthDayNameOfMonth, should fail //$NON-NLS-1$ "0 59 23 ? * 2#8", // NthDayNameOfMonth, should fail //$NON-NLS-1$ "0 59 23 ? * 2#4", // NthDayNameOfMonth Fires at 11:59pm on the 2nd Wed. //$NON-NLS-1$ "0 33 5 ? * 3L", // LastDayNameOfMonth Fires at 5:33am on the last Tues. of the month //$NON-NLS-1$ "0 23 4 ? * 7L", // LastDayNameOfMonth Fires at 4:23am on the last Sat. of the month //$NON-NLS-1$ "0 01 02 28 2 ?", //EveryMonthNameN //$NON-NLS-1$ "1 1 1 8 4 ?", // EveryMonthNameN //$NON-NLS-1$ "0 3 5 ? 4 2#1", //NthDayNameOfMonthName 1st mon of april //$NON-NLS-1$ "0 3 5 ? 4 7#4", //NthDayNameOfMonthName 4th sat of april //$NON-NLS-1$ "0 3 5 ? 4 1#1", //NthDayNameOfMonthName 1st sun of april //$NON-NLS-1$ "0 3 8 ? 6 5L", //LastDayNameOfMonthName //$NON-NLS-1$ "0 59 12 ? 1 1L", //LastDayNameOfMonthName //$NON-NLS-1$ "" //$NON-NLS-1$ }; for ( int ii = 0; ii < cronSamples.length; ++ii ) { String cronStr = cronSamples[ii]; try { CronParser cp = new CronParser( cronStr ); cp.parseToRecurrenceString(); System.out.println( "cront str: " + cronStr + " -- " + cp.getSchedType().toString() ); //$NON-NLS-1$ //$NON-NLS-2$ } catch ( CronParseException e ) { System.out.println( "cron str: " + cronStr + " " + e.getMessage() ); //$NON-NLS-1$ //$NON-NLS-2$ } } CronParser cp = new CronParser( "0 22 4 0/3 * ?" ); // EveryNthDayOfMonth //$NON-NLS-1$ try { cp.parseToRecurrenceString(); } catch ( CronParseException e ) { System.out.println( e ); } try { String recurrenceTokens = cp.getRecurrenceString(); System.out .println( "recurrenceTokens: " + recurrenceTokens + " [" + recurrenceStringToCronString( recurrenceTokens ) + "] (" + cp.getCronString() + ")" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ assert cp.getCronString().equals( recurrenceStringToCronString( recurrenceTokens ) ) : "Failed on: " + recurrenceTokens; //$NON-NLS-1$ } catch ( CronParseException e ) { e.printStackTrace(); } assert cp.isDayOfMonthValid() : "isDayOfMonthValid EveryNthDayOfMonth"; //$NON-NLS-1$ try { assert cp.getDayOfMonth() == 3 : "cp.getDayOfMonth() == 3"; //$NON-NLS-1$ } catch ( CronParseException e1 ) { assert false : "CronParseException"; //$NON-NLS-1$ } assert !cp.isDaysOfWeekValid() : "isDaysOfWeekValid EveryNthDayOfMonth"; //$NON-NLS-1$ assert !cp.isWhichDayOfWeekValid() : "isWhichDayOfWeekValid EveryNthDayOfMonth"; //$NON-NLS-1$ assert !cp.isWhichMonthOfYearValid() : "isWhichMonthOfYearValid EveryNthDayOfMonth"; //$NON-NLS-1$ assert !cp.isWhichWeekOfMonthValid() : "isWhichWeekOfMonthValid EveryNthDayOfMonth"; //$NON-NLS-1$ cp = new CronParser( "0 14 21 ? * 2-6" ); // EveryWeekday //$NON-NLS-1$ try { cp.parseToRecurrenceString(); } catch ( CronParseException e ) { System.out.println( e ); } try { String recurrenceTokens = cp.getRecurrenceString(); System.out .println( "recurrenceTokens: " + recurrenceTokens + " [" + recurrenceStringToCronString( recurrenceTokens ) + "] (" + cp.getCronString() + ")" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ assert cp.getCronString().equals( recurrenceStringToCronString( recurrenceTokens ) ) : "Failed on: " + recurrenceTokens; //$NON-NLS-1$ } catch ( CronParseException e ) { e.printStackTrace(); } assert !cp.isDayOfMonthValid() : "isDayOfMonthValid EveryWeekday"; //$NON-NLS-1$ assert !cp.isDaysOfWeekValid() : "isDaysOfWeekValid EveryWeekday"; //$NON-NLS-1$ assert !cp.isWhichDayOfWeekValid() : "isWhichDayOfWeekValid EveryWeekday"; //$NON-NLS-1$ assert !cp.isWhichMonthOfYearValid() : "isWhichMonthOfYearValid EveryWeekday"; //$NON-NLS-1$ assert !cp.isWhichWeekOfMonthValid() : "isWhichWeekOfMonthValid EveryWeekday"; //$NON-NLS-1$ cp = new CronParser( "0 33 6 ? * 1,3,5" ); //WeeklyOn //$NON-NLS-1$ try { cp.parseToRecurrenceString(); } catch ( CronParseException e ) { System.out.println( e ); } try { String recurrenceTokens = cp.getRecurrenceString(); System.out.println( "recurrenceTokens: " + recurrenceTokens + " [" + recurrenceStringToCronString( recurrenceTokens ) + "] (" + cp.getCronString() + ")" ); assert cp.getCronString().equals( recurrenceStringToCronString( recurrenceTokens ) ) : "Failed on: " + recurrenceTokens; //$NON-NLS-1$ } catch ( CronParseException e ) { e.printStackTrace(); } assert !cp.isDayOfMonthValid() : "isDayOfMonthValid WeeklyOn"; //$NON-NLS-1$ assert cp.isDaysOfWeekValid() : "isDaysOfWeekValid WeeklyOn"; //$NON-NLS-1$ try { int[] days = cp.getDaysOfWeek(); assert days[0] == 1 : "days[0] == 1"; //$NON-NLS-1$ assert days[1] == 3 : "days[0] == 3"; //$NON-NLS-1$ assert days[2] == 5 : "days[0] == 5"; //$NON-NLS-1$ } catch ( CronParseException e1 ) { assert false : "CronParseException"; } assert !cp.isWhichDayOfWeekValid() : "isWhichDayOfWeekValid WeeklyOn"; //$NON-NLS-1$ assert !cp.isWhichMonthOfYearValid() : "isWhichMonthOfYearValid WeeklyOn"; //$NON-NLS-1$ assert !cp.isWhichWeekOfMonthValid() : "isWhichWeekOfMonthValid WeeklyOn"; //$NON-NLS-1$ cp = new CronParser( "0 5 5 13 * ?" ); // DayNOfMonth //$NON-NLS-1$ try { cp.parseToRecurrenceString(); } catch ( CronParseException e ) { System.out.println( e ); } try { String recurrenceTokens = cp.getRecurrenceString(); System.out.println( "recurrenceTokens: " + recurrenceTokens + " [" + recurrenceStringToCronString( recurrenceTokens ) + "] (" + cp.getCronString() + ")" ); assert cp.getCronString().equals( recurrenceStringToCronString( recurrenceTokens ) ) : "Failed on: " + recurrenceTokens; //$NON-NLS-1$ } catch ( CronParseException e ) { e.printStackTrace(); } assert cp.isDayOfMonthValid() : "isDayOfMonthValid DayNOfMonth"; //$NON-NLS-1$ try { assert cp.getDayOfMonth() == 13 : "cp.getDayOfMonth() == 13"; } catch ( CronParseException e1 ) { assert false : "CronParseException"; } assert !cp.isDaysOfWeekValid() : "isDaysOfWeekValid DayNOfMonth"; //$NON-NLS-1$ assert !cp.isWhichDayOfWeekValid() : "isWhichDayOfWeekValid DayNOfMonth"; //$NON-NLS-1$ assert !cp.isWhichMonthOfYearValid() : "isWhichMonthOfYearValid DayNOfMonth"; //$NON-NLS-1$ assert !cp.isWhichWeekOfMonthValid() : "isWhichWeekOfMonthValid DayNOfMonth"; //$NON-NLS-1$ cp = new CronParser( "0 59 23 ? * 2#4" ); // NthDayNameOfMonth //$NON-NLS-1$ try { cp.parseToRecurrenceString(); } catch ( CronParseException e ) { System.out.println( e ); } try { String recurrenceTokens = cp.getRecurrenceString(); System.out.println( "recurrenceTokens: " + recurrenceTokens + " [" + recurrenceStringToCronString( recurrenceTokens ) + "] (" + cp.getCronString() + ")" ); assert cp.getCronString().equals( recurrenceStringToCronString( recurrenceTokens ) ) : "Failed on: " + recurrenceTokens; //$NON-NLS-1$ } catch ( CronParseException e ) { e.printStackTrace(); } assert !cp.isDayOfMonthValid() : "isDayOfMonthValid NthDayNameOfMonth"; //$NON-NLS-1$ assert !cp.isDaysOfWeekValid() : "isDaysOfWeekValid NthDayNameOfMonth"; //$NON-NLS-1$ assert cp.isWhichDayOfWeekValid() : "isWhichDayOfWeekValid NthDayNameOfMonth"; //$NON-NLS-1$ try { assert cp.getWhichDayOfWeek() == 2 : "cp.getWhichDayOfWeek() == 2"; } catch ( CronParseException e1 ) { assert false : "CronParseException"; } assert !cp.isWhichMonthOfYearValid() : "isWhichMonthOfYearValid NthDayNameOfMonth"; //$NON-NLS-1$ assert cp.isWhichWeekOfMonthValid() : "isWhichWeekOfMonthValid NthDayNameOfMonth"; //$NON-NLS-1$ try { assert cp.getWhichWeekOfMonth() == 4 : "cp.getWhichWeekOfMonth() == 4"; } catch ( CronParseException e1 ) { assert false : "CronParseException"; } cp = new CronParser( "0 33 5 ? * 3L" ); // LastDayNameOfMonth try { cp.parseToRecurrenceString(); } catch ( CronParseException e ) { System.out.println( e ); } try { String recurrenceTokens = cp.getRecurrenceString(); System.out.println( "recurrenceTokens: " + recurrenceTokens + " [" + recurrenceStringToCronString( recurrenceTokens ) + "] (" + cp.getCronString() + ")" ); assert cp.getCronString().equals( recurrenceStringToCronString( recurrenceTokens ) ) : "Failed on: " + recurrenceTokens; //$NON-NLS-1$ } catch ( CronParseException e ) { e.printStackTrace(); } assert !cp.isDayOfMonthValid() : "isDayOfMonthValid LastDayNameOfMonth"; //$NON-NLS-1$ assert !cp.isDaysOfWeekValid() : "isDaysOfWeekValid LastDayNameOfMonth"; //$NON-NLS-1$ assert cp.isWhichDayOfWeekValid() : "isWhichDayOfWeekValid LastDayNameOfMonth"; //$NON-NLS-1$ try { assert cp.getWhichDayOfWeek() == 3 : "cp.getWhichDayOfWeek() == 3"; } catch ( CronParseException e1 ) { assert false : "CronParseException"; } assert !cp.isWhichMonthOfYearValid() : "isWhichMonthOfYearValid LastDayNameOfMonth"; //$NON-NLS-1$ assert !cp.isWhichWeekOfMonthValid() : "isWhichWeekOfMonthValid LastDayNameOfMonth"; //$NON-NLS-1$ cp = new CronParser( "0 1 2 28 2 ?" ); // EveryMonthNameN try { cp.parseToRecurrenceString(); } catch ( CronParseException e ) { System.out.println( e ); } try { String recurrenceTokens = cp.getRecurrenceString(); System.out.println( "recurrenceTokens: " + recurrenceTokens + " [" + recurrenceStringToCronString( recurrenceTokens ) + "] (" + cp.getCronString() + ")" ); assert cp.getCronString().equals( recurrenceStringToCronString( recurrenceTokens ) ) : "Failed on: " + recurrenceTokens; //$NON-NLS-1$ } catch ( CronParseException e ) { // TODO Auto-generated catch block e.printStackTrace(); } assert cp.isDayOfMonthValid() : "isDayOfMonthValid EveryMonthNameN"; //$NON-NLS-1$ try { assert cp.getDayOfMonth() == 28 : "cp.getDayOfMonth() == 28"; } catch ( CronParseException e1 ) { assert false : "CronParseException"; } assert !cp.isDaysOfWeekValid() : "isDaysOfWeekValid EveryMonthNameN"; //$NON-NLS-1$ assert !cp.isWhichDayOfWeekValid() : "isWhichDayOfWeekValid EveryMonthNameN"; //$NON-NLS-1$ assert cp.isWhichMonthOfYearValid() : "isWhichMonthOfYearValid EveryMonthNameN"; //$NON-NLS-1$ try { assert cp.getWhichMonthOfYear() == 2 : "cp.getWhichMonthOfYear() == 2"; } catch ( CronParseException e1 ) { assert false : "CronParseException"; } assert !cp.isWhichWeekOfMonthValid() : "isWhichWeekOfMonthValid EveryMonthNameN"; //$NON-NLS-1$ cp = new CronParser( "0 3 5 ? 12 7#3" ); // NthDayNameOfMonthName try { cp.parseToRecurrenceString(); } catch ( CronParseException e ) { System.out.println( e ); } try { String recurrenceTokens = cp.getRecurrenceString(); System.out.println( "recurrenceTokens: " + recurrenceTokens + " [" + recurrenceStringToCronString( recurrenceTokens ) + "] (" + cp.getCronString() + ")" ); assert cp.getCronString().equals( recurrenceStringToCronString( recurrenceTokens ) ) : "Failed on: " + recurrenceTokens; //$NON-NLS-1$ } catch ( CronParseException e ) { e.printStackTrace(); } assert !cp.isDayOfMonthValid() : "isDayOfMonthValid NthDayNameOfMonthName"; //$NON-NLS-1$ assert !cp.isDaysOfWeekValid() : "isDaysOfWeekValid NthDayNameOfMonthName"; //$NON-NLS-1$ assert cp.isWhichDayOfWeekValid() : "isWhichDayOfWeekValid NthDayNameOfMonthName"; //$NON-NLS-1$ try { assert cp.getWhichDayOfWeek() == 7 : "cp.getWhichDayOfWeek() == 7"; } catch ( CronParseException e1 ) { assert false : "CronParseException"; } assert cp.isWhichMonthOfYearValid() : "isWhichMonthOfYearValid NthDayNameOfMonthName"; //$NON-NLS-1$ try { assert cp.getWhichMonthOfYear() == 12 : "cp.getWhichMonthOfYear() == 12"; } catch ( CronParseException e1 ) { assert false : "CronParseException"; } assert cp.isWhichWeekOfMonthValid() : "isWhichWeekOfMonthValid NthDayNameOfMonthName"; //$NON-NLS-1$ try { assert cp.getWhichWeekOfMonth() == 3 : "cp.getWhichWeekOfMonth() == 3"; } catch ( CronParseException e1 ) { assert false : "CronParseException"; } cp = new CronParser( "0 3 8 ? 6 5L" ); //LastDayNameOfMonthName //$NON-NLS-1$ try { cp.parseToRecurrenceString(); } catch ( CronParseException e ) { System.out.println( e ); } try { String recurrenceTokens = cp.getRecurrenceString(); System.out.println( "recurrenceTokens: " + recurrenceTokens + " [" + recurrenceStringToCronString( recurrenceTokens ) + "] (" + cp.getCronString() + ")" ); assert cp.getCronString().equals( recurrenceStringToCronString( recurrenceTokens ) ) : "Failed on: " + recurrenceTokens; //$NON-NLS-1$ } catch ( CronParseException e ) { e.printStackTrace(); } assert !cp.isDayOfMonthValid() : "isDayOfMonthValid LastDayNameOfMonthName"; //$NON-NLS-1$ assert !cp.isDaysOfWeekValid() : "isDaysOfWeekValid LastDayNameOfMonthName"; //$NON-NLS-1$ assert cp.isWhichDayOfWeekValid() : "isWhichDayOfWeekValid LastDayNameOfMonthName"; //$NON-NLS-1$ try { assert cp.getWhichDayOfWeek() == 5 : "cp.getWhichDayOfWeek() == 5"; //$NON-NLS-1$ } catch ( CronParseException e1 ) { assert false : "CronParseException"; //$NON-NLS-1$ } assert cp.isWhichMonthOfYearValid() : "isWhichMonthOfYearValid LastDayNameOfMonthName"; //$NON-NLS-1$ try { assert cp.getWhichMonthOfYear() == 6 : "cp.getWhichMonthOfYear() == 6"; } catch ( CronParseException e1 ) { assert false : "CronParseException"; } assert !cp.isWhichWeekOfMonthValid() : "isWhichWeekOfMonthValid LastDayNameOfMonthName"; //$NON-NLS-1$ boolean bThrewException = false; RecurrenceType st; try { st = RecurrenceType.stringToScheduleType( RecurrenceType.DayNOfMonth.toString() ); System.out.println( "DayNOfMonth=" + st.toString() ); //$NON-NLS-1$ } catch ( EnumException e1 ) { bThrewException = true; } assert !bThrewException : "Should not have thrown exception"; //$NON-NLS-1$ bThrewException = false; try { st = RecurrenceType.stringToScheduleType( RecurrenceType.NthDayNameOfMonth.toString() ); System.out.println( "NthDayNameOfMonth=" + st.toString() ); //$NON-NLS-1$ } catch ( EnumException e1 ) { bThrewException = true; } assert !bThrewException : "Should not have thrown exception"; //$NON-NLS-1$ bThrewException = false; try { st = RecurrenceType.stringToScheduleType( RecurrenceType.EveryMonthNameN.toString() ); System.out.println( "EveryMonthNameN=" + st.toString() ); //$NON-NLS-1$ } catch ( EnumException e1 ) { bThrewException = true; } assert !bThrewException : "Should not have thrown exception"; //$NON-NLS-1$ bThrewException = false; @SuppressWarnings( "unused" ) String r = null; try { r = recurrenceStringToCronString( "EveryNthDayOfMonth 0 22 4 toke" ); //$NON-NLS-1$ } catch ( CronParseException e ) { bThrewException = true; } assert bThrewException : "Should have thrown exception"; //$NON-NLS-1$ bThrewException = false; try { r = recurrenceStringToCronString( "WeeklyOn 0 33 6 1,toke,5" ); //$NON-NLS-1$ } catch ( CronParseException e ) { bThrewException = true; } assert bThrewException : "Should have thrown exception"; //$NON-NLS-1$ bThrewException = false; try { r = recurrenceStringToCronString( "DayNOfMonth 0 5 toke 13" ); //$NON-NLS-1$ } catch ( CronParseException e ) { bThrewException = true; } assert bThrewException : "Should have thrown exception"; //$NON-NLS-1$ bThrewException = false; try { r = recurrenceStringToCronString( "NthDayNameOfMonthName 0 3 5 7 3 toke" ); //$NON-NLS-1$ } catch ( CronParseException e ) { bThrewException = true; } assert bThrewException : "Should have thrown exception"; //$NON-NLS-1$ bThrewException = false; } }