Source Code Cross Referenced for CronExpression.java in  » Project-Management » quartz » org » quartz » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Project Management » quartz » org.quartz 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        package org.quartz;
0002:
0003:        import java.io.Serializable;
0004:        import java.text.ParseException;
0005:        import java.util.Calendar;
0006:        import java.util.Date;
0007:        import java.util.HashMap;
0008:        import java.util.Iterator;
0009:        import java.util.Locale;
0010:        import java.util.Map;
0011:        import java.util.SortedSet;
0012:        import java.util.StringTokenizer;
0013:        import java.util.TimeZone;
0014:        import java.util.TreeSet;
0015:
0016:        /**
0017:         * Provides a parser and evaluator for unix-like cron expressions. Cron 
0018:         * expressions provide the ability to specify complex time combinations such as
0019:         * "At 8:00am every Monday through Friday" or "At 1:30am every 
0020:         * last Friday of the month". 
0021:         * <P>
0022:         * Cron expressions are comprised of 6 required fields and one optional field
0023:         * separated by white space. The fields respectively are described as follows:
0024:         * 
0025:         * <table cellspacing="8">
0026:         * <tr>
0027:         * <th align="left">Field Name</th>
0028:         * <th align="left">&nbsp;</th>
0029:         * <th align="left">Allowed Values</th>
0030:         * <th align="left">&nbsp;</th>
0031:         * <th align="left">Allowed Special Characters</th>
0032:         * </tr>
0033:         * <tr>
0034:         * <td align="left"><code>Seconds</code></td>
0035:         * <td align="left">&nbsp;</th>
0036:         * <td align="left"><code>0-59</code></td>
0037:         * <td align="left">&nbsp;</th>
0038:         * <td align="left"><code>, - * /</code></td>
0039:         * </tr>
0040:         * <tr>
0041:         * <td align="left"><code>Minutes</code></td>
0042:         * <td align="left">&nbsp;</th>
0043:         * <td align="left"><code>0-59</code></td>
0044:         * <td align="left">&nbsp;</th>
0045:         * <td align="left"><code>, - * /</code></td>
0046:         * </tr>
0047:         * <tr>
0048:         * <td align="left"><code>Hours</code></td>
0049:         * <td align="left">&nbsp;</th>
0050:         * <td align="left"><code>0-23</code></td>
0051:         * <td align="left">&nbsp;</th>
0052:         * <td align="left"><code>, - * /</code></td>
0053:         * </tr>
0054:         * <tr>
0055:         * <td align="left"><code>Day-of-month</code></td>
0056:         * <td align="left">&nbsp;</th>
0057:         * <td align="left"><code>1-31</code></td>
0058:         * <td align="left">&nbsp;</th>
0059:         * <td align="left"><code>, - * ? / L W</code></td>
0060:         * </tr>
0061:         * <tr>
0062:         * <td align="left"><code>Month</code></td>
0063:         * <td align="left">&nbsp;</th>
0064:         * <td align="left"><code>1-12 or JAN-DEC</code></td>
0065:         * <td align="left">&nbsp;</th>
0066:         * <td align="left"><code>, - * /</code></td>
0067:         * </tr>
0068:         * <tr>
0069:         * <td align="left"><code>Day-of-Week</code></td>
0070:         * <td align="left">&nbsp;</th>
0071:         * <td align="left"><code>1-7 or SUN-SAT</code></td>
0072:         * <td align="left">&nbsp;</th>
0073:         * <td align="left"><code>, - * ? / L #</code></td>
0074:         * </tr>
0075:         * <tr>
0076:         * <td align="left"><code>Year (Optional)</code></td>
0077:         * <td align="left">&nbsp;</th>
0078:         * <td align="left"><code>empty, 1970-2099</code></td>
0079:         * <td align="left">&nbsp;</th>
0080:         * <td align="left"><code>, - * /</code></td>
0081:         * </tr>
0082:         * </table>
0083:         * <P>
0084:         * The '*' character is used to specify all values. For example, &quot;*&quot; 
0085:         * in the minute field means &quot;every minute&quot;.
0086:         * <P>
0087:         * The '?' character is allowed for the day-of-month and day-of-week fields. It
0088:         * is used to specify 'no specific value'. This is useful when you need to
0089:         * specify something in one of the two fileds, but not the other.
0090:         * <P>
0091:         * The '-' character is used to specify ranges For example &quot;10-12&quot; in
0092:         * the hour field means &quot;the hours 10, 11 and 12&quot;.
0093:         * <P>
0094:         * The ',' character is used to specify additional values. For example
0095:         * &quot;MON,WED,FRI&quot; in the day-of-week field means &quot;the days Monday,
0096:         * Wednesday, and Friday&quot;.
0097:         * <P>
0098:         * The '/' character is used to specify increments. For example &quot;0/15&quot;
0099:         * in the seconds field means &quot;the seconds 0, 15, 30, and 45&quot;. And 
0100:         * &quot;5/15&quot; in the seconds field means &quot;the seconds 5, 20, 35, and
0101:         * 50&quot;.  Specifying '*' before the  '/' is equivalent to specifying 0 is
0102:         * the value to start with. Essentially, for each field in the expression, there
0103:         * is a set of numbers that can be turned on or off. For seconds and minutes, 
0104:         * the numbers range from 0 to 59. For hours 0 to 23, for days of the month 0 to
0105:         * 31, and for months 1 to 12. The &quot;/&quot; character simply helps you turn
0106:         * on every &quot;nth&quot; value in the given set. Thus &quot;7/6&quot; in the
0107:         * month field only turns on month &quot;7&quot;, it does NOT mean every 6th 
0108:         * month, please note that subtlety.  
0109:         * <P>
0110:         * The 'L' character is allowed for the day-of-month and day-of-week fields.
0111:         * This character is short-hand for &quot;last&quot;, but it has different 
0112:         * meaning in each of the two fields. For example, the value &quot;L&quot; in 
0113:         * the day-of-month field means &quot;the last day of the month&quot; - day 31 
0114:         * for January, day 28 for February on non-leap years. If used in the 
0115:         * day-of-week field by itself, it simply means &quot;7&quot; or 
0116:         * &quot;SAT&quot;. But if used in the day-of-week field after another value, it
0117:         * means &quot;the last xxx day of the month&quot; - for example &quot;6L&quot;
0118:         * means &quot;the last friday of the month&quot;. When using the 'L' option, it
0119:         * is important not to specify lists, or ranges of values, as you'll get 
0120:         * confusing results.
0121:         * <P>
0122:         * The 'W' character is allowed for the day-of-month field.  This character 
0123:         * is used to specify the weekday (Monday-Friday) nearest the given day.  As an 
0124:         * example, if you were to specify &quot;15W&quot; as the value for the 
0125:         * day-of-month field, the meaning is: &quot;the nearest weekday to the 15th of
0126:         * the month&quot;. So if the 15th is a Saturday, the trigger will fire on 
0127:         * Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the
0128:         * 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th. 
0129:         * However if you specify &quot;1W&quot; as the value for day-of-month, and the
0130:         * 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not 
0131:         * 'jump' over the boundary of a month's days.  The 'W' character can only be 
0132:         * specified when the day-of-month is a single day, not a range or list of days.
0133:         * <P>
0134:         * The 'L' and 'W' characters can also be combined for the day-of-month 
0135:         * expression to yield 'LW', which translates to &quot;last weekday of the 
0136:         * month&quot;.
0137:         * <P>
0138:         * The '#' character is allowed for the day-of-week field. This character is
0139:         * used to specify &quot;the nth&quot; XXX day of the month. For example, the 
0140:         * value of &quot;6#3&quot; in the day-of-week field means the third Friday of 
0141:         * the month (day 6 = Friday and &quot;#3&quot; = the 3rd one in the month). 
0142:         * Other examples: &quot;2#1&quot; = the first Monday of the month and 
0143:         * &quot;4#5&quot; = the fifth Wednesday of the month. Note that if you specify
0144:         * &quot;#5&quot; and there is not 5 of the given day-of-week in the month, then
0145:         * no firing will occur that month.
0146:         * <P>
0147:         * <!--The 'C' character is allowed for the day-of-month and day-of-week fields.
0148:         * This character is short-hand for "calendar". This means values are
0149:         * calculated against the associated calendar, if any. If no calendar is
0150:         * associated, then it is equivalent to having an all-inclusive calendar. A
0151:         * value of "5C" in the day-of-month field means "the first day included by the
0152:         * calendar on or after the 5th". A value of "1C" in the day-of-week field
0153:         * means "the first day included by the calendar on or after sunday".-->
0154:         * <P>
0155:         * The legal characters and the names of months and days of the week are not
0156:         * case sensitive.
0157:         * 
0158:         * <p>
0159:         * <b>NOTES:</b>
0160:         * <ul>
0161:         * <li>Support for specifying both a day-of-week and a day-of-month value is
0162:         * not complete (you'll need to use the '?' character in on of these fields).
0163:         * </li>
0164:         * </ul>
0165:         * </p>
0166:         * 
0167:         * 
0168:         * @author Sharada Jambula, James House
0169:         * @author Contributions from Mads Henderson
0170:         * @author Refactoring from CronTrigger to CronExpression by Aaron Craven
0171:         */
0172:        public class CronExpression implements  Serializable, Cloneable {
0173:
0174:            private static final long serialVersionUID = 12423409423L;
0175:
0176:            protected static final int SECOND = 0;
0177:            protected static final int MINUTE = 1;
0178:            protected static final int HOUR = 2;
0179:            protected static final int DAY_OF_MONTH = 3;
0180:            protected static final int MONTH = 4;
0181:            protected static final int DAY_OF_WEEK = 5;
0182:            protected static final int YEAR = 6;
0183:            protected static final int ALL_SPEC_INT = 99; // '*'
0184:            protected static final int NO_SPEC_INT = 98; // '?'
0185:            protected static final Integer ALL_SPEC = new Integer(ALL_SPEC_INT);
0186:            protected static final Integer NO_SPEC = new Integer(NO_SPEC_INT);
0187:
0188:            protected static Map monthMap = new HashMap(20);
0189:            protected static Map dayMap = new HashMap(60);
0190:            static {
0191:                monthMap.put("JAN", new Integer(0));
0192:                monthMap.put("FEB", new Integer(1));
0193:                monthMap.put("MAR", new Integer(2));
0194:                monthMap.put("APR", new Integer(3));
0195:                monthMap.put("MAY", new Integer(4));
0196:                monthMap.put("JUN", new Integer(5));
0197:                monthMap.put("JUL", new Integer(6));
0198:                monthMap.put("AUG", new Integer(7));
0199:                monthMap.put("SEP", new Integer(8));
0200:                monthMap.put("OCT", new Integer(9));
0201:                monthMap.put("NOV", new Integer(10));
0202:                monthMap.put("DEC", new Integer(11));
0203:
0204:                dayMap.put("SUN", new Integer(1));
0205:                dayMap.put("MON", new Integer(2));
0206:                dayMap.put("TUE", new Integer(3));
0207:                dayMap.put("WED", new Integer(4));
0208:                dayMap.put("THU", new Integer(5));
0209:                dayMap.put("FRI", new Integer(6));
0210:                dayMap.put("SAT", new Integer(7));
0211:            }
0212:
0213:            private String cronExpression = null;
0214:            private TimeZone timeZone = null;
0215:            protected transient TreeSet seconds;
0216:            protected transient TreeSet minutes;
0217:            protected transient TreeSet hours;
0218:            protected transient TreeSet daysOfMonth;
0219:            protected transient TreeSet months;
0220:            protected transient TreeSet daysOfWeek;
0221:            protected transient TreeSet years;
0222:
0223:            protected transient boolean lastdayOfWeek = false;
0224:            protected transient int nthdayOfWeek = 0;
0225:            protected transient boolean lastdayOfMonth = false;
0226:            protected transient boolean nearestWeekday = false;
0227:            protected transient boolean expressionParsed = false;
0228:
0229:            /**
0230:             * Constructs a new <CODE>CronExpression</CODE> based on the specified 
0231:             * parameter.
0232:             * 
0233:             * @param cronExpression String representation of the cron expression the
0234:             *                       new object should represent
0235:             * @throws java.text.ParseException
0236:             *         if the string expression cannot be parsed into a valid 
0237:             *         <CODE>CronExpression</CODE>
0238:             */
0239:            public CronExpression(String cronExpression) throws ParseException {
0240:                if (cronExpression == null) {
0241:                    throw new IllegalArgumentException(
0242:                            "cronExpression cannot be null");
0243:                }
0244:
0245:                this .cronExpression = cronExpression;
0246:
0247:                buildExpression(cronExpression.toUpperCase(Locale.US));
0248:            }
0249:
0250:            /**
0251:             * Indicates whether the given date satisfies the cron expression. Note that
0252:             * milliseconds are ignored, so two Dates falling on different milliseconds
0253:             * of the same second will always have the same result here.
0254:             * 
0255:             * @param date the date to evaluate
0256:             * @return a boolean indicating whether the given date satisfies the cron
0257:             *         expression
0258:             */
0259:            public boolean isSatisfiedBy(Date date) {
0260:                Calendar testDateCal = Calendar.getInstance();
0261:                testDateCal.setTime(date);
0262:                testDateCal.set(Calendar.MILLISECOND, 0);
0263:                Date originalDate = testDateCal.getTime();
0264:
0265:                testDateCal.add(Calendar.SECOND, -1);
0266:
0267:                Date timeAfter = getTimeAfter(testDateCal.getTime());
0268:
0269:                return ((timeAfter != null) && (timeAfter.equals(originalDate)));
0270:            }
0271:
0272:            /**
0273:             * Returns the next date/time <I>after</I> the given date/time which
0274:             * satisfies the cron expression.
0275:             * 
0276:             * @param date the date/time at which to begin the search for the next valid
0277:             *             date/time
0278:             * @return the next valid date/time
0279:             */
0280:            public Date getNextValidTimeAfter(Date date) {
0281:                return getTimeAfter(date);
0282:            }
0283:
0284:            /**
0285:             * Returns the next date/time <I>after</I> the given date/time which does
0286:             * <I>not</I> satisfy the expression
0287:             * 
0288:             * @param date the date/time at which to begin the search for the next 
0289:             *             invalid date/time
0290:             * @return the next valid date/time
0291:             */
0292:            public Date getNextInvalidTimeAfter(Date date) {
0293:                long difference = 1000;
0294:
0295:                //move back to the nearest second so differences will be accurate
0296:                Calendar adjustCal = Calendar.getInstance();
0297:                adjustCal.setTime(date);
0298:                adjustCal.set(Calendar.MILLISECOND, 0);
0299:                Date lastDate = adjustCal.getTime();
0300:
0301:                Date newDate = null;
0302:
0303:                //TODO: (QUARTZ-481) IMPROVE THIS! The following is a BAD solution to this problem. Performance will be very bad here, depending on the cron expression. It is, however A solution.
0304:
0305:                //keep getting the next included time until it's farther than one second
0306:                // apart. At that point, lastDate is the last valid fire time. We return
0307:                // the second immediately following it.
0308:                while (difference == 1000) {
0309:                    newDate = getTimeAfter(lastDate);
0310:
0311:                    difference = newDate.getTime() - lastDate.getTime();
0312:
0313:                    if (difference == 1000) {
0314:                        lastDate = newDate;
0315:                    }
0316:                }
0317:
0318:                return new Date(lastDate.getTime() + 1000);
0319:            }
0320:
0321:            /**
0322:             * Returns the time zone for which this <code>CronExpression</code> 
0323:             * will be resolved.
0324:             */
0325:            public TimeZone getTimeZone() {
0326:                if (timeZone == null) {
0327:                    timeZone = TimeZone.getDefault();
0328:                }
0329:
0330:                return timeZone;
0331:            }
0332:
0333:            /**
0334:             * Sets the time zone for which  this <code>CronExpression</code> 
0335:             * will be resolved.
0336:             */
0337:            public void setTimeZone(TimeZone timeZone) {
0338:                this .timeZone = timeZone;
0339:            }
0340:
0341:            /**
0342:             * Returns the string representation of the <CODE>CronExpression</CODE>
0343:             * 
0344:             * @return a string representation of the <CODE>CronExpression</CODE>
0345:             */
0346:            public String toString() {
0347:                return cronExpression;
0348:            }
0349:
0350:            /**
0351:             * Indicates whether the specified cron expression can be parsed into a 
0352:             * valid cron expression
0353:             * 
0354:             * @param cronExpression the expression to evaluate
0355:             * @return a boolean indicating whether the given expression is a valid cron
0356:             *         expression
0357:             */
0358:            public static boolean isValidExpression(String cronExpression) {
0359:
0360:                try {
0361:                    new CronExpression(cronExpression);
0362:                } catch (ParseException pe) {
0363:                    return false;
0364:                }
0365:
0366:                return true;
0367:            }
0368:
0369:            ////////////////////////////////////////////////////////////////////////////
0370:            //
0371:            // Expression Parsing Functions
0372:            //
0373:            ////////////////////////////////////////////////////////////////////////////
0374:
0375:            protected void buildExpression(String expression)
0376:                    throws ParseException {
0377:                expressionParsed = true;
0378:
0379:                try {
0380:
0381:                    if (seconds == null) {
0382:                        seconds = new TreeSet();
0383:                    }
0384:                    if (minutes == null) {
0385:                        minutes = new TreeSet();
0386:                    }
0387:                    if (hours == null) {
0388:                        hours = new TreeSet();
0389:                    }
0390:                    if (daysOfMonth == null) {
0391:                        daysOfMonth = new TreeSet();
0392:                    }
0393:                    if (months == null) {
0394:                        months = new TreeSet();
0395:                    }
0396:                    if (daysOfWeek == null) {
0397:                        daysOfWeek = new TreeSet();
0398:                    }
0399:                    if (years == null) {
0400:                        years = new TreeSet();
0401:                    }
0402:
0403:                    int exprOn = SECOND;
0404:
0405:                    StringTokenizer exprsTok = new StringTokenizer(expression,
0406:                            " \t", false);
0407:
0408:                    while (exprsTok.hasMoreTokens() && exprOn <= YEAR) {
0409:                        String expr = exprsTok.nextToken().trim();
0410:                        StringTokenizer vTok = new StringTokenizer(expr, ",");
0411:                        while (vTok.hasMoreTokens()) {
0412:                            String v = vTok.nextToken();
0413:                            storeExpressionVals(0, v, exprOn);
0414:                        }
0415:
0416:                        exprOn++;
0417:                    }
0418:
0419:                    if (exprOn <= DAY_OF_WEEK) {
0420:                        throw new ParseException(
0421:                                "Unexpected end of expression.", expression
0422:                                        .length());
0423:                    }
0424:
0425:                    if (exprOn <= YEAR) {
0426:                        storeExpressionVals(0, "*", YEAR);
0427:                    }
0428:
0429:                } catch (ParseException pe) {
0430:                    throw pe;
0431:                } catch (Exception e) {
0432:                    throw new ParseException("Illegal cron expression format ("
0433:                            + e.toString() + ")", 0);
0434:                }
0435:            }
0436:
0437:            protected int storeExpressionVals(int pos, String s, int type)
0438:                    throws ParseException {
0439:
0440:                int incr = 0;
0441:                int i = skipWhiteSpace(pos, s);
0442:                if (i >= s.length()) {
0443:                    return i;
0444:                }
0445:                char c = s.charAt(i);
0446:                if ((c >= 'A') && (c <= 'Z') && (!s.equals("L"))
0447:                        && (!s.equals("LW"))) {
0448:                    String sub = s.substring(i, i + 3);
0449:                    int sval = -1;
0450:                    int eval = -1;
0451:                    if (type == MONTH) {
0452:                        sval = getMonthNumber(sub) + 1;
0453:                        if (sval < 0) {
0454:                            throw new ParseException("Invalid Month value: '"
0455:                                    + sub + "'", i);
0456:                        }
0457:                        if (s.length() > i + 3) {
0458:                            c = s.charAt(i + 3);
0459:                            if (c == '-') {
0460:                                i += 4;
0461:                                sub = s.substring(i, i + 3);
0462:                                eval = getMonthNumber(sub) + 1;
0463:                                if (eval < 0) {
0464:                                    throw new ParseException(
0465:                                            "Invalid Month value: '" + sub
0466:                                                    + "'", i);
0467:                                }
0468:                            }
0469:                        }
0470:                    } else if (type == DAY_OF_WEEK) {
0471:                        sval = getDayOfWeekNumber(sub);
0472:                        if (sval < 0) {
0473:                            throw new ParseException(
0474:                                    "Invalid Day-of-Week value: '" + sub + "'",
0475:                                    i);
0476:                        }
0477:                        if (s.length() > i + 3) {
0478:                            c = s.charAt(i + 3);
0479:                            if (c == '-') {
0480:                                i += 4;
0481:                                sub = s.substring(i, i + 3);
0482:                                eval = getDayOfWeekNumber(sub);
0483:                                if (eval < 0) {
0484:                                    throw new ParseException(
0485:                                            "Invalid Day-of-Week value: '"
0486:                                                    + sub + "'", i);
0487:                                }
0488:                                if (sval > eval) {
0489:                                    throw new ParseException(
0490:                                            "Invalid Day-of-Week sequence: "
0491:                                                    + sval + " > " + eval, i);
0492:                                }
0493:                            } else if (c == '#') {
0494:                                try {
0495:                                    i += 4;
0496:                                    nthdayOfWeek = Integer.parseInt(s
0497:                                            .substring(i));
0498:                                    if (nthdayOfWeek < 1 || nthdayOfWeek > 5) {
0499:                                        throw new Exception();
0500:                                    }
0501:                                } catch (Exception e) {
0502:                                    throw new ParseException(
0503:                                            "A numeric value between 1 and 5 must follow the '#' option",
0504:                                            i);
0505:                                }
0506:                            } else if (c == 'L') {
0507:                                lastdayOfWeek = true;
0508:                                i++;
0509:                            }
0510:                        }
0511:
0512:                    } else {
0513:                        throw new ParseException(
0514:                                "Illegal characters for this position: '" + sub
0515:                                        + "'", i);
0516:                    }
0517:                    if (eval != -1) {
0518:                        incr = 1;
0519:                    }
0520:                    addToSet(sval, eval, incr, type);
0521:                    return (i + 3);
0522:                }
0523:
0524:                if (c == '?') {
0525:                    i++;
0526:                    if ((i + 1) < s.length()
0527:                            && (s.charAt(i) != ' ' && s.charAt(i + 1) != '\t')) {
0528:                        throw new ParseException(
0529:                                "Illegal character after '?': " + s.charAt(i),
0530:                                i);
0531:                    }
0532:                    if (type != DAY_OF_WEEK && type != DAY_OF_MONTH) {
0533:                        throw new ParseException(
0534:                                "'?' can only be specfied for Day-of-Month or Day-of-Week.",
0535:                                i);
0536:                    }
0537:                    if (type == DAY_OF_WEEK && !lastdayOfMonth) {
0538:                        int val = ((Integer) daysOfMonth.last()).intValue();
0539:                        if (val == NO_SPEC_INT) {
0540:                            throw new ParseException(
0541:                                    "'?' can only be specfied for Day-of-Month -OR- Day-of-Week.",
0542:                                    i);
0543:                        }
0544:                    }
0545:
0546:                    addToSet(NO_SPEC_INT, -1, 0, type);
0547:                    return i;
0548:                }
0549:
0550:                if (c == '*' || c == '/') {
0551:                    if (c == '*' && (i + 1) >= s.length()) {
0552:                        addToSet(ALL_SPEC_INT, -1, incr, type);
0553:                        return i + 1;
0554:                    } else if (c == '/'
0555:                            && ((i + 1) >= s.length() || s.charAt(i + 1) == ' ' || s
0556:                                    .charAt(i + 1) == '\t')) {
0557:                        throw new ParseException(
0558:                                "'/' must be followed by an integer.", i);
0559:                    } else if (c == '*') {
0560:                        i++;
0561:                    }
0562:                    c = s.charAt(i);
0563:                    if (c == '/') { // is an increment specified?
0564:                        i++;
0565:                        if (i >= s.length()) {
0566:                            throw new ParseException(
0567:                                    "Unexpected end of string.", i);
0568:                        }
0569:
0570:                        incr = getNumericValue(s, i);
0571:
0572:                        i++;
0573:                        if (incr > 10) {
0574:                            i++;
0575:                        }
0576:                        if (incr > 59 && (type == SECOND || type == MINUTE)) {
0577:                            throw new ParseException(
0578:                                    "Increment > 60 : " + incr, i);
0579:                        } else if (incr > 23 && (type == HOUR)) {
0580:                            throw new ParseException(
0581:                                    "Increment > 24 : " + incr, i);
0582:                        } else if (incr > 31 && (type == DAY_OF_MONTH)) {
0583:                            throw new ParseException(
0584:                                    "Increment > 31 : " + incr, i);
0585:                        } else if (incr > 7 && (type == DAY_OF_WEEK)) {
0586:                            throw new ParseException("Increment > 7 : " + incr,
0587:                                    i);
0588:                        } else if (incr > 12 && (type == MONTH)) {
0589:                            throw new ParseException(
0590:                                    "Increment > 12 : " + incr, i);
0591:                        }
0592:                    } else {
0593:                        incr = 1;
0594:                    }
0595:
0596:                    addToSet(ALL_SPEC_INT, -1, incr, type);
0597:                    return i;
0598:                } else if (c == 'L') {
0599:                    i++;
0600:                    if (type == DAY_OF_MONTH) {
0601:                        lastdayOfMonth = true;
0602:                    }
0603:                    if (type == DAY_OF_WEEK) {
0604:                        addToSet(7, 7, 0, type);
0605:                    }
0606:                    if (type == DAY_OF_MONTH && s.length() > i) {
0607:                        c = s.charAt(i);
0608:                        if (c == 'W') {
0609:                            nearestWeekday = true;
0610:                            i++;
0611:                        }
0612:                    }
0613:                    return i;
0614:                } else if (c >= '0' && c <= '9') {
0615:                    int val = Integer.parseInt(String.valueOf(c));
0616:                    i++;
0617:                    if (i >= s.length()) {
0618:                        addToSet(val, -1, -1, type);
0619:                    } else {
0620:                        c = s.charAt(i);
0621:                        if (c >= '0' && c <= '9') {
0622:                            ValueSet vs = getValue(val, s, i);
0623:                            val = vs.value;
0624:                            i = vs.pos;
0625:                        }
0626:                        i = checkNext(i, s, val, type);
0627:                        return i;
0628:                    }
0629:                } else {
0630:                    throw new ParseException("Unexpected character: " + c, i);
0631:                }
0632:
0633:                return i;
0634:            }
0635:
0636:            protected int checkNext(int pos, String s, int val, int type)
0637:                    throws ParseException {
0638:
0639:                int end = -1;
0640:                int i = pos;
0641:
0642:                if (i >= s.length()) {
0643:                    addToSet(val, end, -1, type);
0644:                    return i;
0645:                }
0646:
0647:                char c = s.charAt(pos);
0648:
0649:                if (c == 'L') {
0650:                    if (type == DAY_OF_WEEK) {
0651:                        lastdayOfWeek = true;
0652:                    } else {
0653:                        throw new ParseException(
0654:                                "'L' option is not valid here. (pos=" + i + ")",
0655:                                i);
0656:                    }
0657:                    TreeSet set = getSet(type);
0658:                    set.add(new Integer(val));
0659:                    i++;
0660:                    return i;
0661:                }
0662:
0663:                if (c == 'W') {
0664:                    if (type == DAY_OF_MONTH) {
0665:                        nearestWeekday = true;
0666:                    } else {
0667:                        throw new ParseException(
0668:                                "'W' option is not valid here. (pos=" + i + ")",
0669:                                i);
0670:                    }
0671:                    TreeSet set = getSet(type);
0672:                    set.add(new Integer(val));
0673:                    i++;
0674:                    return i;
0675:                }
0676:
0677:                if (c == '#') {
0678:                    if (type != DAY_OF_WEEK) {
0679:                        throw new ParseException(
0680:                                "'#' option is not valid here. (pos=" + i + ")",
0681:                                i);
0682:                    }
0683:                    i++;
0684:                    try {
0685:                        nthdayOfWeek = Integer.parseInt(s.substring(i));
0686:                        if (nthdayOfWeek < 1 || nthdayOfWeek > 5) {
0687:                            throw new Exception();
0688:                        }
0689:                    } catch (Exception e) {
0690:                        throw new ParseException(
0691:                                "A numeric value between 1 and 5 must follow the '#' option",
0692:                                i);
0693:                    }
0694:
0695:                    TreeSet set = getSet(type);
0696:                    set.add(new Integer(val));
0697:                    i++;
0698:                    return i;
0699:                }
0700:
0701:                if (c == '-') {
0702:                    i++;
0703:                    c = s.charAt(i);
0704:                    int v = Integer.parseInt(String.valueOf(c));
0705:                    end = v;
0706:                    i++;
0707:                    if (i >= s.length()) {
0708:                        addToSet(val, end, 1, type);
0709:                        return i;
0710:                    }
0711:                    c = s.charAt(i);
0712:                    if (c >= '0' && c <= '9') {
0713:                        ValueSet vs = getValue(v, s, i);
0714:                        int v1 = vs.value;
0715:                        end = v1;
0716:                        i = vs.pos;
0717:                    }
0718:                    if (i < s.length() && ((c = s.charAt(i)) == '/')) {
0719:                        i++;
0720:                        c = s.charAt(i);
0721:                        int v2 = Integer.parseInt(String.valueOf(c));
0722:                        i++;
0723:                        if (i >= s.length()) {
0724:                            addToSet(val, end, v2, type);
0725:                            return i;
0726:                        }
0727:                        c = s.charAt(i);
0728:                        if (c >= '0' && c <= '9') {
0729:                            ValueSet vs = getValue(v2, s, i);
0730:                            int v3 = vs.value;
0731:                            addToSet(val, end, v3, type);
0732:                            i = vs.pos;
0733:                            return i;
0734:                        } else {
0735:                            addToSet(val, end, v2, type);
0736:                            return i;
0737:                        }
0738:                    } else {
0739:                        addToSet(val, end, 1, type);
0740:                        return i;
0741:                    }
0742:                }
0743:
0744:                if (c == '/') {
0745:                    i++;
0746:                    c = s.charAt(i);
0747:                    int v2 = Integer.parseInt(String.valueOf(c));
0748:                    i++;
0749:                    if (i >= s.length()) {
0750:                        addToSet(val, end, v2, type);
0751:                        return i;
0752:                    }
0753:                    c = s.charAt(i);
0754:                    if (c >= '0' && c <= '9') {
0755:                        ValueSet vs = getValue(v2, s, i);
0756:                        int v3 = vs.value;
0757:                        addToSet(val, end, v3, type);
0758:                        i = vs.pos;
0759:                        return i;
0760:                    } else {
0761:                        throw new ParseException("Unexpected character '" + c
0762:                                + "' after '/'", i);
0763:                    }
0764:                }
0765:
0766:                addToSet(val, end, 0, type);
0767:                i++;
0768:                return i;
0769:            }
0770:
0771:            public String getCronExpression() {
0772:                return cronExpression;
0773:            }
0774:
0775:            public String getExpressionSummary() {
0776:                StringBuffer buf = new StringBuffer();
0777:
0778:                buf.append("seconds: ");
0779:                buf.append(getExpressionSetSummary(seconds));
0780:                buf.append("\n");
0781:                buf.append("minutes: ");
0782:                buf.append(getExpressionSetSummary(minutes));
0783:                buf.append("\n");
0784:                buf.append("hours: ");
0785:                buf.append(getExpressionSetSummary(hours));
0786:                buf.append("\n");
0787:                buf.append("daysOfMonth: ");
0788:                buf.append(getExpressionSetSummary(daysOfMonth));
0789:                buf.append("\n");
0790:                buf.append("months: ");
0791:                buf.append(getExpressionSetSummary(months));
0792:                buf.append("\n");
0793:                buf.append("daysOfWeek: ");
0794:                buf.append(getExpressionSetSummary(daysOfWeek));
0795:                buf.append("\n");
0796:                buf.append("lastdayOfWeek: ");
0797:                buf.append(lastdayOfWeek);
0798:                buf.append("\n");
0799:                buf.append("nearestWeekday: ");
0800:                buf.append(nearestWeekday);
0801:                buf.append("\n");
0802:                buf.append("NthDayOfWeek: ");
0803:                buf.append(nthdayOfWeek);
0804:                buf.append("\n");
0805:                buf.append("lastdayOfMonth: ");
0806:                buf.append(lastdayOfMonth);
0807:                buf.append("\n");
0808:                buf.append("years: ");
0809:                buf.append(getExpressionSetSummary(years));
0810:                buf.append("\n");
0811:
0812:                return buf.toString();
0813:            }
0814:
0815:            protected String getExpressionSetSummary(java.util.Set set) {
0816:
0817:                if (set.contains(NO_SPEC)) {
0818:                    return "?";
0819:                }
0820:                if (set.contains(ALL_SPEC)) {
0821:                    return "*";
0822:                }
0823:
0824:                StringBuffer buf = new StringBuffer();
0825:
0826:                Iterator itr = set.iterator();
0827:                boolean first = true;
0828:                while (itr.hasNext()) {
0829:                    Integer iVal = (Integer) itr.next();
0830:                    String val = iVal.toString();
0831:                    if (!first) {
0832:                        buf.append(",");
0833:                    }
0834:                    buf.append(val);
0835:                    first = false;
0836:                }
0837:
0838:                return buf.toString();
0839:            }
0840:
0841:            protected String getExpressionSetSummary(java.util.ArrayList list) {
0842:
0843:                if (list.contains(NO_SPEC)) {
0844:                    return "?";
0845:                }
0846:                if (list.contains(ALL_SPEC)) {
0847:                    return "*";
0848:                }
0849:
0850:                StringBuffer buf = new StringBuffer();
0851:
0852:                Iterator itr = list.iterator();
0853:                boolean first = true;
0854:                while (itr.hasNext()) {
0855:                    Integer iVal = (Integer) itr.next();
0856:                    String val = iVal.toString();
0857:                    if (!first) {
0858:                        buf.append(",");
0859:                    }
0860:                    buf.append(val);
0861:                    first = false;
0862:                }
0863:
0864:                return buf.toString();
0865:            }
0866:
0867:            protected int skipWhiteSpace(int i, String s) {
0868:                for (; i < s.length()
0869:                        && (s.charAt(i) == ' ' || s.charAt(i) == '\t'); i++) {
0870:                    ;
0871:                }
0872:
0873:                return i;
0874:            }
0875:
0876:            protected int findNextWhiteSpace(int i, String s) {
0877:                for (; i < s.length()
0878:                        && (s.charAt(i) != ' ' || s.charAt(i) != '\t'); i++) {
0879:                    ;
0880:                }
0881:
0882:                return i;
0883:            }
0884:
0885:            protected void addToSet(int val, int end, int incr, int type)
0886:                    throws ParseException {
0887:
0888:                TreeSet set = getSet(type);
0889:
0890:                if (type == SECOND || type == MINUTE) {
0891:                    if ((val < 0 || val > 59 || end > 59)
0892:                            && (val != ALL_SPEC_INT)) {
0893:                        throw new ParseException(
0894:                                "Minute and Second values must be between 0 and 59",
0895:                                -1);
0896:                    }
0897:                } else if (type == HOUR) {
0898:                    if ((val < 0 || val > 23 || end > 23)
0899:                            && (val != ALL_SPEC_INT)) {
0900:                        throw new ParseException(
0901:                                "Hour values must be between 0 and 23", -1);
0902:                    }
0903:                } else if (type == DAY_OF_MONTH) {
0904:                    if ((val < 1 || val > 31 || end > 31)
0905:                            && (val != ALL_SPEC_INT) && (val != NO_SPEC_INT)) {
0906:                        throw new ParseException(
0907:                                "Day of month values must be between 1 and 31",
0908:                                -1);
0909:                    }
0910:                } else if (type == MONTH) {
0911:                    if ((val < 1 || val > 12 || end > 12)
0912:                            && (val != ALL_SPEC_INT)) {
0913:                        throw new ParseException(
0914:                                "Month values must be between 1 and 12", -1);
0915:                    }
0916:                } else if (type == DAY_OF_WEEK) {
0917:                    if ((val == 0 || val > 7 || end > 7)
0918:                            && (val != ALL_SPEC_INT) && (val != NO_SPEC_INT)) {
0919:                        throw new ParseException(
0920:                                "Day-of-Week values must be between 1 and 7",
0921:                                -1);
0922:                    }
0923:                }
0924:
0925:                if ((incr == 0 || incr == -1) && val != ALL_SPEC_INT) {
0926:                    if (val != -1) {
0927:                        set.add(new Integer(val));
0928:                    } else {
0929:                        set.add(NO_SPEC);
0930:                    }
0931:
0932:                    return;
0933:                }
0934:
0935:                int startAt = val;
0936:                int stopAt = end;
0937:
0938:                if (val == ALL_SPEC_INT && incr <= 0) {
0939:                    incr = 1;
0940:                    set.add(ALL_SPEC); // put in a marker, but also fill values
0941:                }
0942:
0943:                if (type == SECOND || type == MINUTE) {
0944:                    if (stopAt == -1) {
0945:                        stopAt = 59;
0946:                    }
0947:                    if (startAt == -1 || startAt == ALL_SPEC_INT) {
0948:                        startAt = 0;
0949:                    }
0950:                } else if (type == HOUR) {
0951:                    if (stopAt == -1) {
0952:                        stopAt = 23;
0953:                    }
0954:                    if (startAt == -1 || startAt == ALL_SPEC_INT) {
0955:                        startAt = 0;
0956:                    }
0957:                } else if (type == DAY_OF_MONTH) {
0958:                    if (stopAt == -1) {
0959:                        stopAt = 31;
0960:                    }
0961:                    if (startAt == -1 || startAt == ALL_SPEC_INT) {
0962:                        startAt = 1;
0963:                    }
0964:                } else if (type == MONTH) {
0965:                    if (stopAt == -1) {
0966:                        stopAt = 12;
0967:                    }
0968:                    if (startAt == -1 || startAt == ALL_SPEC_INT) {
0969:                        startAt = 1;
0970:                    }
0971:                } else if (type == DAY_OF_WEEK) {
0972:                    if (stopAt == -1) {
0973:                        stopAt = 7;
0974:                    }
0975:                    if (startAt == -1 || startAt == ALL_SPEC_INT) {
0976:                        startAt = 1;
0977:                    }
0978:                } else if (type == YEAR) {
0979:                    if (stopAt == -1) {
0980:                        stopAt = 2099;
0981:                    }
0982:                    if (startAt == -1 || startAt == ALL_SPEC_INT) {
0983:                        startAt = 1970;
0984:                    }
0985:                }
0986:
0987:                for (int i = startAt; i <= stopAt; i += incr) {
0988:                    set.add(new Integer(i));
0989:                }
0990:            }
0991:
0992:            protected TreeSet getSet(int type) {
0993:                switch (type) {
0994:                case SECOND:
0995:                    return seconds;
0996:                case MINUTE:
0997:                    return minutes;
0998:                case HOUR:
0999:                    return hours;
1000:                case DAY_OF_MONTH:
1001:                    return daysOfMonth;
1002:                case MONTH:
1003:                    return months;
1004:                case DAY_OF_WEEK:
1005:                    return daysOfWeek;
1006:                case YEAR:
1007:                    return years;
1008:                default:
1009:                    return null;
1010:                }
1011:            }
1012:
1013:            protected ValueSet getValue(int v, String s, int i) {
1014:                char c = s.charAt(i);
1015:                String s1 = String.valueOf(v);
1016:                while (c >= '0' && c <= '9') {
1017:                    s1 += c;
1018:                    i++;
1019:                    if (i >= s.length()) {
1020:                        break;
1021:                    }
1022:                    c = s.charAt(i);
1023:                }
1024:                ValueSet val = new ValueSet();
1025:
1026:                val.pos = (i < s.length()) ? i : i + 1;
1027:                val.value = Integer.parseInt(s1);
1028:                return val;
1029:            }
1030:
1031:            protected int getNumericValue(String s, int i) {
1032:                int endOfVal = findNextWhiteSpace(i, s);
1033:                String val = s.substring(i, endOfVal);
1034:                return Integer.parseInt(val);
1035:            }
1036:
1037:            protected int getMonthNumber(String s) {
1038:                Integer integer = (Integer) monthMap.get(s);
1039:
1040:                if (integer == null) {
1041:                    return -1;
1042:                }
1043:
1044:                return integer.intValue();
1045:            }
1046:
1047:            protected int getDayOfWeekNumber(String s) {
1048:                Integer integer = (Integer) dayMap.get(s);
1049:
1050:                if (integer == null) {
1051:                    return -1;
1052:                }
1053:
1054:                return integer.intValue();
1055:            }
1056:
1057:            ////////////////////////////////////////////////////////////////////////////
1058:            //
1059:            // Computation Functions
1060:            //
1061:            ////////////////////////////////////////////////////////////////////////////
1062:
1063:            protected Date getTimeAfter(Date afterTime) {
1064:
1065:                Calendar cl = Calendar.getInstance(getTimeZone());
1066:
1067:                // move ahead one second, since we're computing the time *after* the
1068:                // given time
1069:                afterTime = new Date(afterTime.getTime() + 1000);
1070:                // CronTrigger does not deal with milliseconds
1071:                cl.setTime(afterTime);
1072:                cl.set(Calendar.MILLISECOND, 0);
1073:
1074:                boolean gotOne = false;
1075:                // loop until we've computed the next time, or we've past the endTime
1076:                while (!gotOne) {
1077:
1078:                    //if (endTime != null && cl.getTime().after(endTime)) return null;
1079:                    if (cl.get(Calendar.YEAR) > 2999) // prevent endless loop...
1080:                        return null;
1081:
1082:                    SortedSet st = null;
1083:                    int t = 0;
1084:
1085:                    int sec = cl.get(Calendar.SECOND);
1086:                    int min = cl.get(Calendar.MINUTE);
1087:
1088:                    // get second.................................................
1089:                    st = seconds.tailSet(new Integer(sec));
1090:                    if (st != null && st.size() != 0) {
1091:                        sec = ((Integer) st.first()).intValue();
1092:                    } else {
1093:                        sec = ((Integer) seconds.first()).intValue();
1094:                        min++;
1095:                        cl.set(Calendar.MINUTE, min);
1096:                    }
1097:                    cl.set(Calendar.SECOND, sec);
1098:
1099:                    min = cl.get(Calendar.MINUTE);
1100:                    int hr = cl.get(Calendar.HOUR_OF_DAY);
1101:                    t = -1;
1102:
1103:                    // get minute.................................................
1104:                    st = minutes.tailSet(new Integer(min));
1105:                    if (st != null && st.size() != 0) {
1106:                        t = min;
1107:                        min = ((Integer) st.first()).intValue();
1108:                    } else {
1109:                        min = ((Integer) minutes.first()).intValue();
1110:                        hr++;
1111:                    }
1112:                    if (min != t) {
1113:                        cl.set(Calendar.SECOND, 0);
1114:                        cl.set(Calendar.MINUTE, min);
1115:                        setCalendarHour(cl, hr);
1116:                        continue;
1117:                    }
1118:                    cl.set(Calendar.MINUTE, min);
1119:
1120:                    hr = cl.get(Calendar.HOUR_OF_DAY);
1121:                    int day = cl.get(Calendar.DAY_OF_MONTH);
1122:                    t = -1;
1123:
1124:                    // get hour...................................................
1125:                    st = hours.tailSet(new Integer(hr));
1126:                    if (st != null && st.size() != 0) {
1127:                        t = hr;
1128:                        hr = ((Integer) st.first()).intValue();
1129:                    } else {
1130:                        hr = ((Integer) hours.first()).intValue();
1131:                        day++;
1132:                    }
1133:                    if (hr != t) {
1134:                        cl.set(Calendar.SECOND, 0);
1135:                        cl.set(Calendar.MINUTE, 0);
1136:                        cl.set(Calendar.DAY_OF_MONTH, day);
1137:                        setCalendarHour(cl, hr);
1138:                        continue;
1139:                    }
1140:                    cl.set(Calendar.HOUR_OF_DAY, hr);
1141:
1142:                    day = cl.get(Calendar.DAY_OF_MONTH);
1143:                    int mon = cl.get(Calendar.MONTH) + 1;
1144:                    // '+ 1' because calendar is 0-based for this field, and we are
1145:                    // 1-based
1146:                    t = -1;
1147:                    int tmon = mon;
1148:
1149:                    // get day...................................................
1150:                    boolean dayOfMSpec = !daysOfMonth.contains(NO_SPEC);
1151:                    boolean dayOfWSpec = !daysOfWeek.contains(NO_SPEC);
1152:                    if (dayOfMSpec && !dayOfWSpec) { // get day by day of month rule
1153:                        st = daysOfMonth.tailSet(new Integer(day));
1154:                        if (lastdayOfMonth) {
1155:                            if (!nearestWeekday) {
1156:                                t = day;
1157:                                day = getLastDayOfMonth(mon, cl
1158:                                        .get(Calendar.YEAR));
1159:                            } else {
1160:                                t = day;
1161:                                day = getLastDayOfMonth(mon, cl
1162:                                        .get(Calendar.YEAR));
1163:
1164:                                java.util.Calendar tcal = java.util.Calendar
1165:                                        .getInstance();
1166:                                tcal.set(Calendar.SECOND, 0);
1167:                                tcal.set(Calendar.MINUTE, 0);
1168:                                tcal.set(Calendar.HOUR_OF_DAY, 0);
1169:                                tcal.set(Calendar.DAY_OF_MONTH, day);
1170:                                tcal.set(Calendar.MONTH, mon - 1);
1171:                                tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR));
1172:
1173:                                int ldom = getLastDayOfMonth(mon, cl
1174:                                        .get(Calendar.YEAR));
1175:                                int dow = tcal.get(Calendar.DAY_OF_WEEK);
1176:
1177:                                if (dow == Calendar.SATURDAY && day == 1) {
1178:                                    day += 2;
1179:                                } else if (dow == Calendar.SATURDAY) {
1180:                                    day -= 1;
1181:                                } else if (dow == Calendar.SUNDAY
1182:                                        && day == ldom) {
1183:                                    day -= 2;
1184:                                } else if (dow == Calendar.SUNDAY) {
1185:                                    day += 1;
1186:                                }
1187:
1188:                                tcal.set(Calendar.SECOND, sec);
1189:                                tcal.set(Calendar.MINUTE, min);
1190:                                tcal.set(Calendar.HOUR_OF_DAY, hr);
1191:                                tcal.set(Calendar.DAY_OF_MONTH, day);
1192:                                tcal.set(Calendar.MONTH, mon - 1);
1193:                                Date nTime = tcal.getTime();
1194:                                if (nTime.before(afterTime)) {
1195:                                    day = 1;
1196:                                    mon++;
1197:                                }
1198:                            }
1199:                        } else if (nearestWeekday) {
1200:                            t = day;
1201:                            day = ((Integer) daysOfMonth.first()).intValue();
1202:
1203:                            java.util.Calendar tcal = java.util.Calendar
1204:                                    .getInstance();
1205:                            tcal.set(Calendar.SECOND, 0);
1206:                            tcal.set(Calendar.MINUTE, 0);
1207:                            tcal.set(Calendar.HOUR_OF_DAY, 0);
1208:                            tcal.set(Calendar.DAY_OF_MONTH, day);
1209:                            tcal.set(Calendar.MONTH, mon - 1);
1210:                            tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR));
1211:
1212:                            int ldom = getLastDayOfMonth(mon, cl
1213:                                    .get(Calendar.YEAR));
1214:                            int dow = tcal.get(Calendar.DAY_OF_WEEK);
1215:
1216:                            if (dow == Calendar.SATURDAY && day == 1) {
1217:                                day += 2;
1218:                            } else if (dow == Calendar.SATURDAY) {
1219:                                day -= 1;
1220:                            } else if (dow == Calendar.SUNDAY && day == ldom) {
1221:                                day -= 2;
1222:                            } else if (dow == Calendar.SUNDAY) {
1223:                                day += 1;
1224:                            }
1225:
1226:                            tcal.set(Calendar.SECOND, sec);
1227:                            tcal.set(Calendar.MINUTE, min);
1228:                            tcal.set(Calendar.HOUR_OF_DAY, hr);
1229:                            tcal.set(Calendar.DAY_OF_MONTH, day);
1230:                            tcal.set(Calendar.MONTH, mon - 1);
1231:                            Date nTime = tcal.getTime();
1232:                            if (nTime.before(afterTime)) {
1233:                                day = ((Integer) daysOfMonth.first())
1234:                                        .intValue();
1235:                                ;
1236:                                mon++;
1237:                            }
1238:                        } else if (st != null && st.size() != 0) {
1239:                            t = day;
1240:                            day = ((Integer) st.first()).intValue();
1241:                        } else {
1242:                            day = ((Integer) daysOfMonth.first()).intValue();
1243:                            mon++;
1244:                        }
1245:
1246:                        if (day != t || mon != tmon) {
1247:                            cl.set(Calendar.SECOND, 0);
1248:                            cl.set(Calendar.MINUTE, 0);
1249:                            cl.set(Calendar.HOUR_OF_DAY, 0);
1250:                            cl.set(Calendar.DAY_OF_MONTH, day);
1251:                            cl.set(Calendar.MONTH, mon - 1);
1252:                            // '- 1' because calendar is 0-based for this field, and we
1253:                            // are 1-based
1254:                            continue;
1255:                        }
1256:                    } else if (dayOfWSpec && !dayOfMSpec) { // get day by day of week rule
1257:                        if (lastdayOfWeek) { // are we looking for the last XXX day of
1258:                            // the month?
1259:                            int dow = ((Integer) daysOfWeek.first()).intValue(); // desired
1260:                            // d-o-w
1261:                            int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w
1262:                            int daysToAdd = 0;
1263:                            if (cDow < dow) {
1264:                                daysToAdd = dow - cDow;
1265:                            }
1266:                            if (cDow > dow) {
1267:                                daysToAdd = dow + (7 - cDow);
1268:                            }
1269:
1270:                            int lDay = getLastDayOfMonth(mon, cl
1271:                                    .get(Calendar.YEAR));
1272:
1273:                            if (day + daysToAdd > lDay) { // did we already miss the
1274:                                // last one?
1275:                                cl.set(Calendar.SECOND, 0);
1276:                                cl.set(Calendar.MINUTE, 0);
1277:                                cl.set(Calendar.HOUR_OF_DAY, 0);
1278:                                cl.set(Calendar.DAY_OF_MONTH, 1);
1279:                                cl.set(Calendar.MONTH, mon);
1280:                                // no '- 1' here because we are promoting the month
1281:                                continue;
1282:                            }
1283:
1284:                            // find date of last occurance of this day in this month...
1285:                            while ((day + daysToAdd + 7) <= lDay) {
1286:                                daysToAdd += 7;
1287:                            }
1288:
1289:                            day += daysToAdd;
1290:
1291:                            if (daysToAdd > 0) {
1292:                                cl.set(Calendar.SECOND, 0);
1293:                                cl.set(Calendar.MINUTE, 0);
1294:                                cl.set(Calendar.HOUR_OF_DAY, 0);
1295:                                cl.set(Calendar.DAY_OF_MONTH, day);
1296:                                cl.set(Calendar.MONTH, mon - 1);
1297:                                // '- 1' here because we are not promoting the month
1298:                                continue;
1299:                            }
1300:
1301:                        } else if (nthdayOfWeek != 0) {
1302:                            // are we looking for the Nth XXX day in the month?
1303:                            int dow = ((Integer) daysOfWeek.first()).intValue(); // desired
1304:                            // d-o-w
1305:                            int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w
1306:                            int daysToAdd = 0;
1307:                            if (cDow < dow) {
1308:                                daysToAdd = dow - cDow;
1309:                            } else if (cDow > dow) {
1310:                                daysToAdd = dow + (7 - cDow);
1311:                            }
1312:
1313:                            boolean dayShifted = false;
1314:                            if (daysToAdd > 0) {
1315:                                dayShifted = true;
1316:                            }
1317:
1318:                            day += daysToAdd;
1319:                            int weekOfMonth = day / 7;
1320:                            if (day % 7 > 0) {
1321:                                weekOfMonth++;
1322:                            }
1323:
1324:                            daysToAdd = (nthdayOfWeek - weekOfMonth) * 7;
1325:                            day += daysToAdd;
1326:                            if (daysToAdd < 0
1327:                                    || day > getLastDayOfMonth(mon, cl
1328:                                            .get(Calendar.YEAR))) {
1329:                                cl.set(Calendar.SECOND, 0);
1330:                                cl.set(Calendar.MINUTE, 0);
1331:                                cl.set(Calendar.HOUR_OF_DAY, 0);
1332:                                cl.set(Calendar.DAY_OF_MONTH, 1);
1333:                                cl.set(Calendar.MONTH, mon);
1334:                                // no '- 1' here because we are promoting the month
1335:                                continue;
1336:                            } else if (daysToAdd > 0 || dayShifted) {
1337:                                cl.set(Calendar.SECOND, 0);
1338:                                cl.set(Calendar.MINUTE, 0);
1339:                                cl.set(Calendar.HOUR_OF_DAY, 0);
1340:                                cl.set(Calendar.DAY_OF_MONTH, day);
1341:                                cl.set(Calendar.MONTH, mon - 1);
1342:                                // '- 1' here because we are NOT promoting the month
1343:                                continue;
1344:                            }
1345:                        } else {
1346:                            int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w
1347:                            int dow = ((Integer) daysOfWeek.first()).intValue(); // desired
1348:                            // d-o-w
1349:                            st = daysOfWeek.tailSet(new Integer(cDow));
1350:                            if (st != null && st.size() > 0) {
1351:                                dow = ((Integer) st.first()).intValue();
1352:                            }
1353:
1354:                            int daysToAdd = 0;
1355:                            if (cDow < dow) {
1356:                                daysToAdd = dow - cDow;
1357:                            }
1358:                            if (cDow > dow) {
1359:                                daysToAdd = dow + (7 - cDow);
1360:                            }
1361:
1362:                            int lDay = getLastDayOfMonth(mon, cl
1363:                                    .get(Calendar.YEAR));
1364:
1365:                            if (day + daysToAdd > lDay) { // will we pass the end of
1366:                                // the month?
1367:                                cl.set(Calendar.SECOND, 0);
1368:                                cl.set(Calendar.MINUTE, 0);
1369:                                cl.set(Calendar.HOUR_OF_DAY, 0);
1370:                                cl.set(Calendar.DAY_OF_MONTH, 1);
1371:                                cl.set(Calendar.MONTH, mon);
1372:                                // no '- 1' here because we are promoting the month
1373:                                continue;
1374:                            } else if (daysToAdd > 0) { // are we swithing days?
1375:                                cl.set(Calendar.SECOND, 0);
1376:                                cl.set(Calendar.MINUTE, 0);
1377:                                cl.set(Calendar.HOUR_OF_DAY, 0);
1378:                                cl.set(Calendar.DAY_OF_MONTH, day + daysToAdd);
1379:                                cl.set(Calendar.MONTH, mon - 1);
1380:                                // '- 1' because calendar is 0-based for this field,
1381:                                // and we are 1-based
1382:                                continue;
1383:                            }
1384:                        }
1385:                    } else { // dayOfWSpec && !dayOfMSpec
1386:                        throw new UnsupportedOperationException(
1387:                                "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.");
1388:                        // TODO:
1389:                    }
1390:                    cl.set(Calendar.DAY_OF_MONTH, day);
1391:
1392:                    mon = cl.get(Calendar.MONTH) + 1;
1393:                    // '+ 1' because calendar is 0-based for this field, and we are
1394:                    // 1-based
1395:                    int year = cl.get(Calendar.YEAR);
1396:                    t = -1;
1397:
1398:                    // test for expressions that never generate a valid fire date,
1399:                    // but keep looping...
1400:                    if (year > 2099) {
1401:                        return null;
1402:                    }
1403:
1404:                    // get month...................................................
1405:                    st = months.tailSet(new Integer(mon));
1406:                    if (st != null && st.size() != 0) {
1407:                        t = mon;
1408:                        mon = ((Integer) st.first()).intValue();
1409:                    } else {
1410:                        mon = ((Integer) months.first()).intValue();
1411:                        year++;
1412:                    }
1413:                    if (mon != t) {
1414:                        cl.set(Calendar.SECOND, 0);
1415:                        cl.set(Calendar.MINUTE, 0);
1416:                        cl.set(Calendar.HOUR_OF_DAY, 0);
1417:                        cl.set(Calendar.DAY_OF_MONTH, 1);
1418:                        cl.set(Calendar.MONTH, mon - 1);
1419:                        // '- 1' because calendar is 0-based for this field, and we are
1420:                        // 1-based
1421:                        cl.set(Calendar.YEAR, year);
1422:                        continue;
1423:                    }
1424:                    cl.set(Calendar.MONTH, mon - 1);
1425:                    // '- 1' because calendar is 0-based for this field, and we are
1426:                    // 1-based
1427:
1428:                    year = cl.get(Calendar.YEAR);
1429:                    t = -1;
1430:
1431:                    // get year...................................................
1432:                    st = years.tailSet(new Integer(year));
1433:                    if (st != null && st.size() != 0) {
1434:                        t = year;
1435:                        year = ((Integer) st.first()).intValue();
1436:                    } else {
1437:                        return null; // ran out of years...
1438:                    }
1439:
1440:                    if (year != t) {
1441:                        cl.set(Calendar.SECOND, 0);
1442:                        cl.set(Calendar.MINUTE, 0);
1443:                        cl.set(Calendar.HOUR_OF_DAY, 0);
1444:                        cl.set(Calendar.DAY_OF_MONTH, 1);
1445:                        cl.set(Calendar.MONTH, 0);
1446:                        // '- 1' because calendar is 0-based for this field, and we are
1447:                        // 1-based
1448:                        cl.set(Calendar.YEAR, year);
1449:                        continue;
1450:                    }
1451:                    cl.set(Calendar.YEAR, year);
1452:
1453:                    gotOne = true;
1454:                } // while( !done )
1455:
1456:                return cl.getTime();
1457:            }
1458:
1459:            /**
1460:             * Advance the calendar to the particular hour paying particular attention
1461:             * to daylight saving problems.
1462:             * 
1463:             * @param cal
1464:             * @param hour
1465:             */
1466:            protected void setCalendarHour(Calendar cal, int hour) {
1467:                cal.set(java.util.Calendar.HOUR_OF_DAY, hour);
1468:                if (cal.get(java.util.Calendar.HOUR_OF_DAY) != hour
1469:                        && hour != 24) {
1470:                    cal.set(java.util.Calendar.HOUR_OF_DAY, hour + 1);
1471:                }
1472:            }
1473:
1474:            /**
1475:             * NOT YET IMPLEMENTED: Returns the time before the given time
1476:             * that the <code>CronExpression</code> matches.
1477:             */
1478:            protected Date getTimeBefore(Date endTime) {
1479:                // TODO: implement QUARTZ-423
1480:                return null;
1481:            }
1482:
1483:            /**
1484:             * NOT YET IMPLEMENTED: Returns the final time that the 
1485:             * <code>CronExpression</code> will match.
1486:             */
1487:            public Date getFinalFireTime() {
1488:                // TODO: implement QUARTZ-423
1489:                return null;
1490:            }
1491:
1492:            protected boolean isLeapYear(int year) {
1493:                return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
1494:            }
1495:
1496:            protected int getLastDayOfMonth(int monthNum, int year) {
1497:
1498:                switch (monthNum) {
1499:                case 1:
1500:                    return 31;
1501:                case 2:
1502:                    return (isLeapYear(year)) ? 29 : 28;
1503:                case 3:
1504:                    return 31;
1505:                case 4:
1506:                    return 30;
1507:                case 5:
1508:                    return 31;
1509:                case 6:
1510:                    return 30;
1511:                case 7:
1512:                    return 31;
1513:                case 8:
1514:                    return 31;
1515:                case 9:
1516:                    return 30;
1517:                case 10:
1518:                    return 31;
1519:                case 11:
1520:                    return 30;
1521:                case 12:
1522:                    return 31;
1523:                default:
1524:                    throw new IllegalArgumentException("Illegal month number: "
1525:                            + monthNum);
1526:                }
1527:            }
1528:
1529:            private void readObject(java.io.ObjectInputStream stream)
1530:                    throws java.io.IOException, ClassNotFoundException {
1531:
1532:                stream.defaultReadObject();
1533:                try {
1534:                    buildExpression(cronExpression);
1535:                } catch (Exception ignore) {
1536:                } // never happens
1537:            }
1538:
1539:            public Object clone() {
1540:                CronExpression copy = null;
1541:                try {
1542:                    copy = new CronExpression(getCronExpression());
1543:                    copy.setTimeZone(getTimeZone());
1544:                } catch (ParseException ex) { // never happens since the source is valid...
1545:                    throw new IncompatibleClassChangeError("Not Cloneable.");
1546:                }
1547:                return copy;
1548:            }
1549:        }
1550:
1551:        class ValueSet {
1552:            public int value;
1553:
1554:            public int pos;
1555:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.