/* * Copyright 2009 NCHOVY * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.krakenapps.cron.impl; import java.util.Calendar; import java.util.Date; import org.krakenapps.cron.Schedule; public abstract class NextOccurenceCalculator { private static final int MONTH_TYPE = 1; private static final int DAY_TYPE = 2; private static final int HOUR_TYPE = 3; private static final int MINUTE_TYPE = 4; private static final int None = -1; private static void setAllFirstValue(Schedule sche, Calendar base, NextOccurence next, int type) throws Exception { for (int i = type; i < 5; i++) { next.set(i, getFirstValue(sche, i, base)); } } /** * returns the first date corresponding to the schedule, given current time */ public static Date getNextOccurence(Schedule sche, Date current) { Calendar base = Calendar.getInstance(); NextOccurence next = new NextOccurence(); base.setTime(current); base.set(Calendar.SECOND, 0); for (int type = MINUTE_TYPE; type > 0; type--) adjustNextOccurence(sche, base, next, type); next.set(base.get(Calendar.YEAR), None, None, None, None); return newDate(next); } private static final int calendarTypes[] = { Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR_OF_DAY, Calendar.MINUTE }; private static final int upperCalendarTypes[] = { 0, Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR_OF_DAY }; private static void adjustNextOccurence(Schedule sche, Calendar base, NextOccurence next, int type) { try { int test = getNextValue(sche, type, base); if (test == None) { base.add(upperCalendarTypes[type], 1); if (type == DAY_TYPE && getFirstValue(sche, DAY_TYPE, base) == None) base.add(upperCalendarTypes[type], 1); if (type == MONTH_TYPE) base.set(Calendar.MONTH, getFirstValue(sche, type, base)); setAllFirstValue(sche, base, next, type); } else if (test > base.get(calendarTypes[type])) { if (type == MONTH_TYPE) base.set(Calendar.MONTH, test); next.set(type, test); setAllFirstValue(sche, base, next, type + 1); } else { next.set(type, test); } } catch (Exception e) { // must succeed. ignore. } } private static int getFirstValue(Schedule sche, int type, Calendar base) throws Exception { switch (type) { case MONTH_TYPE: return sche.get(CronField.Type.MONTH).first(); case DAY_TYPE: return sche.get(CronField.Type.DAY_OF_MONTH).first(base, sche.get(CronField.Type.DAY_OF_WEEK)); case HOUR_TYPE: return sche.get(CronField.Type.HOUR).first(); case MINUTE_TYPE: return sche.get(CronField.Type.MINUTE).first(); } throw new Exception(" unsupported first value type"); } private static int getNextValue(Schedule sche, int type, Calendar base) throws Exception { switch (type) { case MONTH_TYPE: return sche.get(CronField.Type.MONTH).next(base.get(Calendar.MONTH)); case DAY_TYPE: return sche.get(CronField.Type.DAY_OF_MONTH).next(base, sche.get(CronField.Type.DAY_OF_WEEK)); // merge-or case HOUR_TYPE: return sche.get(CronField.Type.HOUR).next(base.get(Calendar.HOUR_OF_DAY)); case MINUTE_TYPE: return sche.get(CronField.Type.MINUTE).next(base.get(Calendar.MINUTE)); } throw new Exception(" unsupported first value type"); } private static Date newDate(NextOccurence next) { Calendar result = Calendar.getInstance(); result.set(Calendar.SECOND, 0); result.set(Calendar.MINUTE, next.minute); result.set(Calendar.HOUR_OF_DAY, next.hour); result.set(Calendar.DAY_OF_MONTH, next.day + 1); // offset result.set(Calendar.MONTH, next.month); result.set(Calendar.YEAR, next.year); return result.getTime(); } public static class NextOccurence { private static final int None = -1; public int year; public int month; public int day; public int hour; public int minute; public void set(int type, int value) { switch (type) { case 1: this.month = value; break; case 2: this.day = value; break; case 3: this.hour = value; break; case 4: this.minute = value; break; } } public void set(int year, int month, int day, int hour, int minute) { if (year != None) this.year = year; if (month != None) this.month = month; if (day != None) this.day = day; if (hour != None) this.hour = hour; if (minute != None) this.minute = minute; } @Override public String toString() { return String.format("%d-%d-%d %d:%d", year, month, day, hour, minute); } } }