Replaced cron4j library with source code.
This commit is contained in:
parent
ad68a0b985
commit
28ebef24f4
@ -2,7 +2,6 @@
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="lib" path="dist/libs/c3p0-0.9.5.2.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/cron4j-2.2.5.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/ecj-4.4.2.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/exp4j-0.4.8.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/mail-1.5.2.jar"/>
|
||||
|
BIN
L2J_Mobius_Classic/dist/libs/cron4j-2.2.5.jar
vendored
BIN
L2J_Mobius_Classic/dist/libs/cron4j-2.2.5.jar
vendored
Binary file not shown.
@ -29,9 +29,8 @@ import java.util.logging.Logger;
|
||||
import com.l2jmobius.commons.database.DatabaseFactory;
|
||||
import com.l2jmobius.gameserver.ThreadPoolManager;
|
||||
import com.l2jmobius.gameserver.model.StatsSet;
|
||||
|
||||
import it.sauronsoftware.cron4j.PastPredictor;
|
||||
import it.sauronsoftware.cron4j.Predictor;
|
||||
import com.l2jmobius.gameserver.model.eventengine.cron4j.PastPredictor;
|
||||
import com.l2jmobius.gameserver.model.eventengine.cron4j.Predictor;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
/**
|
||||
* This ValueMatcher always returns true!
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
class AlwaysTrueValueMatcher implements ValueMatcher
|
||||
{
|
||||
/**
|
||||
* Always true!
|
||||
*/
|
||||
@Override
|
||||
public boolean match(int value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A ValueMatcher whose rules are in a plain array of integer values. When asked to validate a value, this ValueMatcher checks if it is in the array and, if not, checks whether the last-day-of-month setting applies.
|
||||
* </p>
|
||||
* @author Paul Fernley
|
||||
*/
|
||||
class DayOfMonthValueMatcher extends IntArrayValueMatcher
|
||||
{
|
||||
private static final int[] lastDays =
|
||||
{
|
||||
31,
|
||||
28,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
30,
|
||||
31
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the ValueMatcher.
|
||||
* @param values An ArrayList of Integer elements, one for every value accepted by the matcher. The match() method will return true only if its parameter will be one of this list or the last-day-of-month setting applies.
|
||||
*/
|
||||
public DayOfMonthValueMatcher(ArrayList<?> values)
|
||||
{
|
||||
super(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given value is included in the matcher list or the last-day-of-month setting applies.
|
||||
* @param value
|
||||
* @param month
|
||||
* @param isLeapYear
|
||||
* @return
|
||||
*/
|
||||
public boolean match(int value, int month, boolean isLeapYear)
|
||||
{
|
||||
return (super.match(value) || ((value > 27) && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
|
||||
}
|
||||
|
||||
public boolean isLastDayOfMonth(int value, int month, boolean isLeapYear)
|
||||
{
|
||||
if (isLeapYear && (month == 2))
|
||||
{
|
||||
return value == 29;
|
||||
}
|
||||
return value == lastDays[month - 1];
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A ValueMatcher whose rules are in a plain array of integer values. When asked to validate a value, this ValueMatcher checks if it is in the array.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
class IntArrayValueMatcher implements ValueMatcher
|
||||
{
|
||||
/**
|
||||
* The accepted values.
|
||||
*/
|
||||
private final int[] values;
|
||||
|
||||
/**
|
||||
* Builds the ValueMatcher.
|
||||
* @param integers An ArrayList of Integer elements, one for every value accepted by the matcher. The match() method will return true only if its parameter will be one of this list.
|
||||
*/
|
||||
public IntArrayValueMatcher(ArrayList<?> integers)
|
||||
{
|
||||
int size = integers.size();
|
||||
values = new int[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
values[i] = ((Integer) integers.get(i)).intValue();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given value is included in the matcher list.
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean match(int value)
|
||||
{
|
||||
for (int value2 : values)
|
||||
{
|
||||
if (value2 == value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This kind of exception is thrown if an invalid scheduling pattern is encountered by the scheduler.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
public class InvalidPatternException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Package-reserved construction.
|
||||
*/
|
||||
InvalidPatternException()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Package-reserved construction.
|
||||
* @param message String
|
||||
*/
|
||||
InvalidPatternException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public class PastPredictor
|
||||
{
|
||||
/**
|
||||
* The scheduling pattern on which the predictor works.
|
||||
*/
|
||||
private final SchedulingPattern _schedulingPattern;
|
||||
|
||||
/**
|
||||
* The start time for the next prediction.
|
||||
*/
|
||||
private long _time;
|
||||
|
||||
/**
|
||||
* The time zone for the prediction.
|
||||
*/
|
||||
private TimeZone _timeZone = TimeZone.getDefault();
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public PastPredictor(String schedulingPattern, long start) throws InvalidPatternException
|
||||
{
|
||||
_schedulingPattern = new SchedulingPattern(schedulingPattern);
|
||||
_time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public PastPredictor(String schedulingPattern, Date start) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public PastPredictor(String schedulingPattern) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public PastPredictor(SchedulingPattern schedulingPattern, long start)
|
||||
{
|
||||
_schedulingPattern = schedulingPattern;
|
||||
_time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public PastPredictor(SchedulingPattern schedulingPattern, Date start)
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @since 2.0
|
||||
*/
|
||||
public PastPredictor(SchedulingPattern schedulingPattern)
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time zone for predictions.
|
||||
* @param timeZone The time zone for predictions.
|
||||
* @since 2.2.5
|
||||
*/
|
||||
public void setTimeZone(TimeZone timeZone)
|
||||
{
|
||||
_timeZone = timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the previous matching moment as a millis value.
|
||||
* @return The previous matching moment as a millis value.
|
||||
*/
|
||||
public synchronized long prevMatchingTime()
|
||||
{
|
||||
// Go a minute back.
|
||||
_time -= 60000;
|
||||
// Is it matching?
|
||||
if (_schedulingPattern.match(_time))
|
||||
{
|
||||
return _time;
|
||||
}
|
||||
// Go through the matcher groups.
|
||||
int size = _schedulingPattern.matcherSize;
|
||||
long[] times = new long[size];
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
// Ok, split the time!
|
||||
GregorianCalendar c = new GregorianCalendar();
|
||||
c.setTimeInMillis(_time);
|
||||
c.setTimeZone(_timeZone);
|
||||
int minute = c.get(Calendar.MINUTE);
|
||||
int hour = c.get(Calendar.HOUR_OF_DAY);
|
||||
int dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
int month = c.get(Calendar.MONTH);
|
||||
int year = c.get(Calendar.YEAR);
|
||||
// Gets the matchers.
|
||||
ValueMatcher minuteMatcher = _schedulingPattern.minuteMatchers.get(k);
|
||||
ValueMatcher hourMatcher = _schedulingPattern.hourMatchers.get(k);
|
||||
ValueMatcher dayOfMonthMatcher = _schedulingPattern.dayOfMonthMatchers.get(k);
|
||||
ValueMatcher dayOfWeekMatcher = _schedulingPattern.dayOfWeekMatchers.get(k);
|
||||
ValueMatcher monthMatcher = _schedulingPattern.monthMatchers.get(k);
|
||||
for (;;)
|
||||
{ // day of week
|
||||
for (;;)
|
||||
{ // month
|
||||
for (;;)
|
||||
{ // day of month
|
||||
for (;;)
|
||||
{ // hour
|
||||
for (;;)
|
||||
{ // minutes
|
||||
if (minuteMatcher.match(minute))
|
||||
{
|
||||
break;
|
||||
}
|
||||
minute--;
|
||||
if (minute < 0)
|
||||
{
|
||||
minute = 59;
|
||||
hour--;
|
||||
}
|
||||
}
|
||||
if (hour < 0)
|
||||
{
|
||||
hour = 23;
|
||||
dayOfMonth--;
|
||||
}
|
||||
if (hourMatcher.match(hour))
|
||||
{
|
||||
break;
|
||||
}
|
||||
hour--;
|
||||
minute = 59;
|
||||
}
|
||||
if (dayOfMonth < 1)
|
||||
{
|
||||
dayOfMonth = 31;
|
||||
month--;
|
||||
}
|
||||
if (month < Calendar.JANUARY)
|
||||
{
|
||||
month = Calendar.DECEMBER;
|
||||
year--;
|
||||
}
|
||||
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher)
|
||||
{
|
||||
DayOfMonthValueMatcher aux = (DayOfMonthValueMatcher) dayOfMonthMatcher;
|
||||
if (aux.match(dayOfMonth, month + 1, c.isLeapYear(year)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth--;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
}
|
||||
else if (dayOfMonthMatcher.match(dayOfMonth))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
dayOfMonth--;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
}
|
||||
}
|
||||
if (monthMatcher.match(month + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
month--;
|
||||
dayOfMonth = 31;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
}
|
||||
// Is this ok?
|
||||
c = new GregorianCalendar();
|
||||
c.setTimeZone(_timeZone);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
c.set(Calendar.HOUR_OF_DAY, hour);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.YEAR, year);
|
||||
// Day-of-month/month/year compatibility check.
|
||||
int oldDayOfMonth = dayOfMonth;
|
||||
int oldMonth = month;
|
||||
int oldYear = year;
|
||||
dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
month = c.get(Calendar.MONTH);
|
||||
year = c.get(Calendar.YEAR);
|
||||
if ((month != oldMonth) || (dayOfMonth != oldDayOfMonth) || (year != oldYear))
|
||||
{
|
||||
do
|
||||
{
|
||||
dayOfMonth = oldDayOfMonth - 1;
|
||||
month = oldMonth;
|
||||
year = oldYear;
|
||||
c = new GregorianCalendar();
|
||||
c.setTimeZone(_timeZone);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
c.set(Calendar.HOUR_OF_DAY, hour);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.YEAR, year);
|
||||
// Day-of-month/month/year compatibility check.
|
||||
oldDayOfMonth = dayOfMonth;
|
||||
oldMonth = month;
|
||||
oldYear = year;
|
||||
dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
month = c.get(Calendar.MONTH);
|
||||
year = c.get(Calendar.YEAR);
|
||||
|
||||
}
|
||||
while ((month != oldMonth) || (dayOfMonth != oldDayOfMonth) || (year != oldYear));
|
||||
// Take another spin!
|
||||
continue;
|
||||
}
|
||||
// Day of week.
|
||||
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
|
||||
if (dayOfWeekMatcher.match(dayOfWeek - 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth--;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
if (dayOfMonth < 1)
|
||||
{
|
||||
dayOfMonth = 31;
|
||||
month--;
|
||||
if (month < Calendar.JANUARY)
|
||||
{
|
||||
month = Calendar.DECEMBER;
|
||||
year--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Seems it matches!
|
||||
times[k] = (c.getTimeInMillis() / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
// Which one?
|
||||
long min = Long.MAX_VALUE;
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
if (times[k] < min)
|
||||
{
|
||||
min = times[k];
|
||||
}
|
||||
}
|
||||
// Updates the object current time value.
|
||||
_time = min;
|
||||
// Here it is.
|
||||
return _time;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the previous matching moment as a {@link Date} object.
|
||||
* @return The previous matching moment as a {@link Date} object.
|
||||
*/
|
||||
public synchronized Date prevMatchingDate()
|
||||
{
|
||||
return new Date(prevMatchingTime());
|
||||
}
|
||||
}
|
@ -0,0 +1,310 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A predictor is able to predict when a scheduling pattern will be matched.
|
||||
* </p>
|
||||
* <p>
|
||||
* Suppose you want to know when the scheduler will execute a task scheduled with the pattern <em>0 3 * jan-jun,sep-dec mon-fri</em>. You can predict the next <em>n</em> execution of the task using a Predictor instance:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* String pattern = "0 3 * jan-jun,sep-dec mon-fri";
|
||||
* Predictor p = new Predictor(pattern);
|
||||
* for (int i = 0; i < n; i++)
|
||||
* {
|
||||
* System.out.println(p.nextMatchingDate());
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Carlo Pelliccia
|
||||
* @since 1.1
|
||||
*/
|
||||
public class Predictor
|
||||
{
|
||||
/**
|
||||
* The scheduling pattern on which the predictor works.
|
||||
*/
|
||||
private final SchedulingPattern schedulingPattern;
|
||||
|
||||
/**
|
||||
* The start time for the next prediction.
|
||||
*/
|
||||
private long time;
|
||||
|
||||
/**
|
||||
* The time zone for the prediction.
|
||||
*/
|
||||
private TimeZone timeZone = TimeZone.getDefault();
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public Predictor(String schedulingPattern, long start) throws InvalidPatternException
|
||||
{
|
||||
this.schedulingPattern = new SchedulingPattern(schedulingPattern);
|
||||
this.time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public Predictor(String schedulingPattern, Date start) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public Predictor(String schedulingPattern) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Predictor(SchedulingPattern schedulingPattern, long start)
|
||||
{
|
||||
this.schedulingPattern = schedulingPattern;
|
||||
this.time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Predictor(SchedulingPattern schedulingPattern, Date start)
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Predictor(SchedulingPattern schedulingPattern)
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time zone for predictions.
|
||||
* @param timeZone The time zone for predictions.
|
||||
* @since 2.2.5
|
||||
*/
|
||||
public void setTimeZone(TimeZone timeZone)
|
||||
{
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the next matching moment as a millis value.
|
||||
* @return The next matching moment as a millis value.
|
||||
*/
|
||||
public synchronized long nextMatchingTime()
|
||||
{
|
||||
// Go a minute ahead.
|
||||
time += 60000;
|
||||
// Is it matching?
|
||||
if (schedulingPattern.match(time))
|
||||
{
|
||||
return time;
|
||||
}
|
||||
// Go through the matcher groups.
|
||||
int size = schedulingPattern.matcherSize;
|
||||
long[] times = new long[size];
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
// Ok, split the time!
|
||||
GregorianCalendar c = new GregorianCalendar();
|
||||
c.setTimeInMillis(time);
|
||||
c.setTimeZone(timeZone);
|
||||
int minute = c.get(Calendar.MINUTE);
|
||||
int hour = c.get(Calendar.HOUR_OF_DAY);
|
||||
int dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
int month = c.get(Calendar.MONTH);
|
||||
int year = c.get(Calendar.YEAR);
|
||||
// Gets the matchers.
|
||||
ValueMatcher minuteMatcher = schedulingPattern.minuteMatchers.get(k);
|
||||
ValueMatcher hourMatcher = schedulingPattern.hourMatchers.get(k);
|
||||
ValueMatcher dayOfMonthMatcher = schedulingPattern.dayOfMonthMatchers.get(k);
|
||||
ValueMatcher dayOfWeekMatcher = schedulingPattern.dayOfWeekMatchers.get(k);
|
||||
ValueMatcher monthMatcher = schedulingPattern.monthMatchers.get(k);
|
||||
for (;;)
|
||||
{ // day of week
|
||||
for (;;)
|
||||
{ // month
|
||||
for (;;)
|
||||
{ // day of month
|
||||
for (;;)
|
||||
{ // hour
|
||||
for (;;)
|
||||
{ // minutes
|
||||
if (minuteMatcher.match(minute))
|
||||
{
|
||||
break;
|
||||
}
|
||||
minute++;
|
||||
if (minute > 59)
|
||||
{
|
||||
minute = 0;
|
||||
hour++;
|
||||
}
|
||||
}
|
||||
if (hour > 23)
|
||||
{
|
||||
hour = 0;
|
||||
dayOfMonth++;
|
||||
}
|
||||
if (hourMatcher.match(hour))
|
||||
{
|
||||
break;
|
||||
}
|
||||
hour++;
|
||||
minute = 0;
|
||||
}
|
||||
if (dayOfMonth > 31)
|
||||
{
|
||||
dayOfMonth = 1;
|
||||
month++;
|
||||
}
|
||||
if (month > Calendar.DECEMBER)
|
||||
{
|
||||
month = Calendar.JANUARY;
|
||||
year++;
|
||||
}
|
||||
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher)
|
||||
{
|
||||
DayOfMonthValueMatcher aux = (DayOfMonthValueMatcher) dayOfMonthMatcher;
|
||||
if (aux.match(dayOfMonth, month + 1, c.isLeapYear(year)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth++;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
}
|
||||
else if (dayOfMonthMatcher.match(dayOfMonth))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
dayOfMonth++;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
}
|
||||
}
|
||||
if (monthMatcher.match(month + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
month++;
|
||||
dayOfMonth = 1;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
}
|
||||
// Is this ok?
|
||||
c = new GregorianCalendar();
|
||||
c.setTimeZone(timeZone);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
c.set(Calendar.HOUR_OF_DAY, hour);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.YEAR, year);
|
||||
// Day-of-month/month/year compatibility check.
|
||||
int oldDayOfMonth = dayOfMonth;
|
||||
int oldMonth = month;
|
||||
int oldYear = year;
|
||||
dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
month = c.get(Calendar.MONTH);
|
||||
year = c.get(Calendar.YEAR);
|
||||
if ((month != oldMonth) || (dayOfMonth != oldDayOfMonth) || (year != oldYear))
|
||||
{
|
||||
// Take another spin!
|
||||
continue;
|
||||
}
|
||||
// Day of week.
|
||||
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
|
||||
if (dayOfWeekMatcher.match(dayOfWeek - 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth++;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
if (dayOfMonth > 31)
|
||||
{
|
||||
dayOfMonth = 1;
|
||||
month++;
|
||||
if (month > Calendar.DECEMBER)
|
||||
{
|
||||
month = Calendar.JANUARY;
|
||||
year++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Seems it matches!
|
||||
times[k] = (c.getTimeInMillis() / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
// Which one?
|
||||
long min = Long.MAX_VALUE;
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
if (times[k] < min)
|
||||
{
|
||||
min = times[k];
|
||||
}
|
||||
}
|
||||
// Updates the object current time value.
|
||||
time = min;
|
||||
// Here it is.
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the next matching moment as a {@link Date} object.
|
||||
* @return The next matching moment as a {@link Date} object.
|
||||
*/
|
||||
public synchronized Date nextMatchingDate()
|
||||
{
|
||||
return new Date(nextMatchingTime());
|
||||
}
|
||||
}
|
@ -0,0 +1,741 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li><strong>Minutes sub-pattern</strong>. During which minutes of the hour should the task been launched? The values range is from 0 to 59.</li>
|
||||
* <li><strong>Hours sub-pattern</strong>. During which hours of the day should the task been launched? The values range is from 0 to 23.</li>
|
||||
* <li><strong>Days of month sub-pattern</strong>. During which days of the month should the task been launched? The values range is from 1 to 31. The special value L can be used to recognize the last day of month.</li>
|
||||
* <li><strong>Months sub-pattern</strong>. During which months of the year should the task been launched? The values range is from 1 (January) to 12 (December), otherwise this sub-pattern allows the aliases "jan", "feb", "mar", "apr", "may",
|
||||
* "jun", "jul", "aug", "sep", "oct", "nov" and "dec".</li>
|
||||
* <li><strong>Days of week sub-pattern</strong>. During which days of the week should the task been launched? The values range is from 0 (Sunday) to 6 (Saturday), otherwise this sub-pattern allows the aliases "sun", "mon", "tue", "wed", "thu",
|
||||
* "fri" and "sat".</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* The star wildcard character is also admitted, indicating "every minute of the hour", "every hour of the day", "every day of the month", "every month of the year" and "every day of the week", according to the sub-pattern in which it is used.
|
||||
* </p>
|
||||
* <p>
|
||||
* Once the scheduler is started, a task will be launched when the five parts in its scheduling pattern will be true at the same time.
|
||||
* </p>
|
||||
* <p>
|
||||
* Some examples:
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>5 * * * *</strong><br />
|
||||
* This pattern causes a task to be launched once every hour, at the begin of the fifth minute (00:05, 01:05, 02:05 etc.).
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* * * * *</strong><br />
|
||||
* This pattern causes a task to be launched every minute.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 * * Mon</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of Monday.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 16 * Mon</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of Monday, 16th, but only if the day is the 16th of the month.
|
||||
* </p>
|
||||
* <p>
|
||||
* Every sub-pattern can contain two or more comma separated values.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>59 11 * * 1,2,3,4,5</strong><br />
|
||||
* This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
|
||||
* </p>
|
||||
* <p>
|
||||
* Values intervals are admitted and defined using the minus character.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>59 11 * * 1-5</strong><br />
|
||||
* This pattern is equivalent to the previous one.
|
||||
* </p>
|
||||
* <p>
|
||||
* The slash character can be used to identify step values within a range. It can be used both in the form <em>*/c</em> and <em>a-b/c</em>. The subpattern is matched every <em>c</em> values of the range <em>0,maxvalue</em> or <em>a-b</em>.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>*/5 * * * *</strong><br />
|
||||
* This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>3-18/5 * * * *</strong><br />
|
||||
* This pattern causes a task to be launched every 5 minutes starting from the third minute of the hour, up to the 18th (0:03, 0:08, 0:13, 0:18, 1:03, 1:08 and so on).
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>*/15 9-17 * * *</strong><br />
|
||||
* This pattern causes a task to be launched every 15 minutes between the 9th and 17th hour of the day (9:00, 9:15, 9:30, 9:45 and so on... note that the last execution will be at 17:45).
|
||||
* </p>
|
||||
* <p>
|
||||
* All the fresh described syntax rules can be used together.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 10-16/2 * *</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of the day, but only if the day is the 10th, the 12th, the 14th or the 16th of the month.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 1-15,17,20-25 * *</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of the day, but the day of the month must be between the 1st and the 15th, the 20th and the 25, or at least it must be the 17th.
|
||||
* </p>
|
||||
* <p>
|
||||
* Finally cron4j lets you combine more scheduling patterns into one, with the pipe character:
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>0 5 * * *|8 10 * * *|22 17 * * *</strong><br />
|
||||
* This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SchedulingPattern
|
||||
{
|
||||
/**
|
||||
* The parser for the minute values.
|
||||
*/
|
||||
private static final ValueParser MINUTE_VALUE_PARSER = new MinuteValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the hour values.
|
||||
*/
|
||||
private static final ValueParser HOUR_VALUE_PARSER = new HourValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the day of month values.
|
||||
*/
|
||||
private static final ValueParser DAY_OF_MONTH_VALUE_PARSER = new DayOfMonthValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the month values.
|
||||
*/
|
||||
private static final ValueParser MONTH_VALUE_PARSER = new MonthValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the day of week values.
|
||||
*/
|
||||
private static final ValueParser DAY_OF_WEEK_VALUE_PARSER = new DayOfWeekValueParser();
|
||||
|
||||
/**
|
||||
* Validates a string as a scheduling pattern.
|
||||
* @param schedulingPattern The pattern to validate.
|
||||
* @return true if the given string represents a valid scheduling pattern; false otherwise.
|
||||
*/
|
||||
public static boolean validate(String schedulingPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
new SchedulingPattern(schedulingPattern);
|
||||
}
|
||||
catch (InvalidPatternException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pattern as a string.
|
||||
*/
|
||||
private final String asString;
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "minute" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> minuteMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "hour" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> hourMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "day of month" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> dayOfMonthMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "month" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> monthMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "day of week" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> dayOfWeekMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* How many matcher groups in this pattern?
|
||||
*/
|
||||
protected int matcherSize = 0;
|
||||
|
||||
/**
|
||||
* Builds a SchedulingPattern parsing it from a string.
|
||||
* @param pattern The pattern as a crontab-like string.
|
||||
* @throws InvalidPatternException If the supplied string is not a valid pattern.
|
||||
*/
|
||||
public SchedulingPattern(String pattern) throws InvalidPatternException
|
||||
{
|
||||
this.asString = pattern;
|
||||
StringTokenizer st1 = new StringTokenizer(pattern, "|");
|
||||
if (st1.countTokens() < 1)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern: \"" + pattern + "\"");
|
||||
}
|
||||
while (st1.hasMoreTokens())
|
||||
{
|
||||
String localPattern = st1.nextToken();
|
||||
StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
|
||||
if (st2.countTokens() != 5)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern: \"" + localPattern + "\"");
|
||||
}
|
||||
try
|
||||
{
|
||||
minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing minutes field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing days of month field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
monthMatchers.add(buildValueMatcher(st2.nextToken(), MONTH_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing months field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
dayOfWeekMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_WEEK_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
|
||||
}
|
||||
matcherSize++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A ValueMatcher utility builder.
|
||||
* @param str The pattern part for the ValueMatcher creation.
|
||||
* @param parser The parser used to parse the values.
|
||||
* @return The requested ValueMatcher.
|
||||
* @throws Exception If the supplied pattern part is not valid.
|
||||
*/
|
||||
private ValueMatcher buildValueMatcher(String str, ValueParser parser) throws Exception
|
||||
{
|
||||
if ((str.length() == 1) && str.equals("*"))
|
||||
{
|
||||
return new AlwaysTrueValueMatcher();
|
||||
}
|
||||
ArrayList<Object> values = new ArrayList<>();
|
||||
StringTokenizer st = new StringTokenizer(str, ",");
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
String element = st.nextToken();
|
||||
ArrayList<Integer> local;
|
||||
try
|
||||
{
|
||||
local = parseListElement(element, parser);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
|
||||
}
|
||||
for (Integer integer : local)
|
||||
{
|
||||
Object value = integer;
|
||||
if (!values.contains(value))
|
||||
{
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (values.size() == 0)
|
||||
{
|
||||
throw new Exception("invalid field \"" + str + "\"");
|
||||
}
|
||||
if (parser == DAY_OF_MONTH_VALUE_PARSER)
|
||||
{
|
||||
return new DayOfMonthValueMatcher(values);
|
||||
}
|
||||
return new IntArrayValueMatcher(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an element of a list of values of the pattern.
|
||||
* @param str The element string.
|
||||
* @param parser The parser used to parse the values.
|
||||
* @return A list of integers representing the allowed values.
|
||||
* @throws Exception If the supplied pattern part is not valid.
|
||||
*/
|
||||
private ArrayList<Integer> parseListElement(String str, ValueParser parser) throws Exception
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(str, "/");
|
||||
int size = st.countTokens();
|
||||
if ((size < 1) || (size > 2))
|
||||
{
|
||||
throw new Exception("syntax error");
|
||||
}
|
||||
ArrayList<Integer> values;
|
||||
try
|
||||
{
|
||||
values = parseRange(st.nextToken(), parser);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid range, " + e.getMessage());
|
||||
}
|
||||
if (size == 2)
|
||||
{
|
||||
String dStr = st.nextToken();
|
||||
int div;
|
||||
try
|
||||
{
|
||||
div = Integer.parseInt(dStr);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new Exception("invalid divisor \"" + dStr + "\"");
|
||||
}
|
||||
if (div < 1)
|
||||
{
|
||||
throw new Exception("non positive divisor \"" + div + "\"");
|
||||
}
|
||||
ArrayList<Integer> values2 = new ArrayList<>();
|
||||
for (int i = 0; i < values.size(); i += div)
|
||||
{
|
||||
values2.add(values.get(i));
|
||||
}
|
||||
return values2;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a range of values.
|
||||
* @param str The range string.
|
||||
* @param parser The parser used to parse the values.
|
||||
* @return A list of integers representing the allowed values.
|
||||
* @throws Exception If the supplied pattern part is not valid.
|
||||
*/
|
||||
private ArrayList<Integer> parseRange(String str, ValueParser parser) throws Exception
|
||||
{
|
||||
if (str.equals("*"))
|
||||
{
|
||||
int min = parser.getMinValue();
|
||||
int max = parser.getMaxValue();
|
||||
ArrayList<Integer> values = new ArrayList<>();
|
||||
for (int i = min; i <= max; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
StringTokenizer st = new StringTokenizer(str, "-");
|
||||
int size = st.countTokens();
|
||||
if ((size < 1) || (size > 2))
|
||||
{
|
||||
throw new Exception("syntax error");
|
||||
}
|
||||
String v1Str = st.nextToken();
|
||||
int v1;
|
||||
try
|
||||
{
|
||||
v1 = parser.parse(v1Str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid value \"" + v1Str + "\", " + e.getMessage());
|
||||
}
|
||||
if (size == 1)
|
||||
{
|
||||
ArrayList<Integer> values = new ArrayList<>();
|
||||
values.add(new Integer(v1));
|
||||
return values;
|
||||
}
|
||||
String v2Str = st.nextToken();
|
||||
int v2;
|
||||
try
|
||||
{
|
||||
v2 = parser.parse(v2Str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid value \"" + v2Str + "\", " + e.getMessage());
|
||||
}
|
||||
ArrayList<Integer> values = new ArrayList<>();
|
||||
if (v1 < v2)
|
||||
{
|
||||
for (int i = v1; i <= v2; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
}
|
||||
else if (v1 > v2)
|
||||
{
|
||||
int min = parser.getMinValue();
|
||||
int max = parser.getMaxValue();
|
||||
for (int i = v1; i <= max; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
for (int i = min; i <= v2; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// v1 == v2
|
||||
values.add(new Integer(v1));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods returns true if the given timestamp (expressed as a UNIX-era millis value) matches the pattern, according to the given time zone.
|
||||
* @param timezone A time zone.
|
||||
* @param millis The timestamp, as a UNIX-era millis value.
|
||||
* @return true if the given timestamp matches the pattern.
|
||||
*/
|
||||
public boolean match(TimeZone timezone, long millis)
|
||||
{
|
||||
GregorianCalendar gc = new GregorianCalendar();
|
||||
gc.setTimeInMillis(millis);
|
||||
gc.setTimeZone(timezone);
|
||||
int minute = gc.get(Calendar.MINUTE);
|
||||
int hour = gc.get(Calendar.HOUR_OF_DAY);
|
||||
int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH);
|
||||
int month = gc.get(Calendar.MONTH) + 1;
|
||||
int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1;
|
||||
int year = gc.get(Calendar.YEAR);
|
||||
for (int i = 0; i < matcherSize; i++)
|
||||
{
|
||||
ValueMatcher minuteMatcher = minuteMatchers.get(i);
|
||||
ValueMatcher hourMatcher = hourMatchers.get(i);
|
||||
ValueMatcher dayOfMonthMatcher = dayOfMonthMatchers.get(i);
|
||||
ValueMatcher monthMatcher = monthMatchers.get(i);
|
||||
ValueMatcher dayOfWeekMatcher = dayOfWeekMatchers.get(i);
|
||||
boolean eval = minuteMatcher.match(minute) && hourMatcher.match(hour) && ((dayOfMonthMatcher instanceof DayOfMonthValueMatcher) ? ((DayOfMonthValueMatcher) dayOfMonthMatcher).match(dayOfMonth, month, gc.isLeapYear(year)) : dayOfMonthMatcher.match(dayOfMonth)) && monthMatcher.match(month) && dayOfWeekMatcher.match(dayOfWeek);
|
||||
if (eval)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods returns true if the given timestamp (expressed as a UNIX-era millis value) matches the pattern, according to the system default time zone.
|
||||
* @param millis The timestamp, as a UNIX-era millis value.
|
||||
* @return true if the given timestamp matches the pattern.
|
||||
*/
|
||||
public boolean match(long millis)
|
||||
{
|
||||
return match(TimeZone.getDefault(), millis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pattern as a string.
|
||||
* @return The pattern as a string.
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return asString;
|
||||
}
|
||||
|
||||
/**
|
||||
* This utility method changes an alias to an int value.
|
||||
* @param value The value.
|
||||
* @param aliases The aliases list.
|
||||
* @param offset The offset appplied to the aliases list indices.
|
||||
* @return The parsed value.
|
||||
* @throws Exception If the expressed values doesn't match any alias.
|
||||
*/
|
||||
static int parseAlias(String value, String[] aliases, int offset) throws Exception
|
||||
{
|
||||
for (int i = 0; i < aliases.length; i++)
|
||||
{
|
||||
if (aliases[i].equalsIgnoreCase(value))
|
||||
{
|
||||
return offset + i;
|
||||
}
|
||||
}
|
||||
throw new Exception("invalid alias \"" + value + "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition for a value parser.
|
||||
*/
|
||||
private static interface ValueParser
|
||||
{
|
||||
/**
|
||||
* Attempts to parse a value.
|
||||
* @param value The value.
|
||||
* @return The parsed value.
|
||||
* @throws Exception If the value can't be parsed.
|
||||
*/
|
||||
public int parse(String value) throws Exception;
|
||||
|
||||
/**
|
||||
* Returns the minimum value accepred by the parser.
|
||||
* @return The minimum value accepred by the parser.
|
||||
*/
|
||||
public int getMinValue();
|
||||
|
||||
/**
|
||||
* Returns the maximum value accepred by the parser.
|
||||
* @return The maximum value accepred by the parser.
|
||||
*/
|
||||
public int getMaxValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple value parser.
|
||||
*/
|
||||
private static class SimpleValueParser implements ValueParser
|
||||
{
|
||||
/**
|
||||
* The minimum allowed value.
|
||||
*/
|
||||
protected int minValue;
|
||||
|
||||
/**
|
||||
* The maximum allowed value.
|
||||
*/
|
||||
protected int maxValue;
|
||||
|
||||
/**
|
||||
* Builds the value parser.
|
||||
* @param minValue The minimum allowed value.
|
||||
* @param maxValue The maximum allowed value.
|
||||
*/
|
||||
public SimpleValueParser(int minValue, int maxValue)
|
||||
{
|
||||
this.minValue = minValue;
|
||||
this.maxValue = maxValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
int i;
|
||||
try
|
||||
{
|
||||
i = Integer.parseInt(value);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new Exception("invalid integer value");
|
||||
}
|
||||
if ((i < minValue) || (i > maxValue))
|
||||
{
|
||||
throw new Exception("value out of range");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinValue()
|
||||
{
|
||||
return minValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxValue()
|
||||
{
|
||||
return maxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The minutes value parser.
|
||||
*/
|
||||
private static class MinuteValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Builds the value parser.
|
||||
*/
|
||||
public MinuteValueParser()
|
||||
{
|
||||
super(0, 59);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The hours value parser.
|
||||
*/
|
||||
private static class HourValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Builds the value parser.
|
||||
*/
|
||||
public HourValueParser()
|
||||
{
|
||||
super(0, 23);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The days of month value parser.
|
||||
*/
|
||||
private static class DayOfMonthValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Builds the value parser.
|
||||
*/
|
||||
public DayOfMonthValueParser()
|
||||
{
|
||||
super(1, 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* Added to support last-day-of-month.
|
||||
* @param value The value to be parsed
|
||||
* @return the integer day of the month or 32 for last day of the month
|
||||
* @throws Exception if the input value is invalid
|
||||
*/
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
if (value.equalsIgnoreCase("L"))
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
return super.parse(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value parser for the months field.
|
||||
*/
|
||||
private static class MonthValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Months aliases.
|
||||
*/
|
||||
private static String[] ALIASES =
|
||||
{
|
||||
"jan",
|
||||
"feb",
|
||||
"mar",
|
||||
"apr",
|
||||
"may",
|
||||
"jun",
|
||||
"jul",
|
||||
"aug",
|
||||
"sep",
|
||||
"oct",
|
||||
"nov",
|
||||
"dec"
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the months value parser.
|
||||
*/
|
||||
public MonthValueParser()
|
||||
{
|
||||
super(1, 12);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
// try as a simple value
|
||||
return super.parse(value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// try as an alias
|
||||
return parseAlias(value, ALIASES, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value parser for the months field.
|
||||
*/
|
||||
private static class DayOfWeekValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Days of week aliases.
|
||||
*/
|
||||
private static String[] ALIASES =
|
||||
{
|
||||
"sun",
|
||||
"mon",
|
||||
"tue",
|
||||
"wed",
|
||||
"thu",
|
||||
"fri",
|
||||
"sat"
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the months value parser.
|
||||
*/
|
||||
public DayOfWeekValueParser()
|
||||
{
|
||||
super(0, 7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
// try as a simple value
|
||||
return super.parse(value) % 7;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// try as an alias
|
||||
return parseAlias(value, ALIASES, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
interface ValueMatcher
|
||||
{
|
||||
/**
|
||||
* Validate the given integer value against a set of rules.
|
||||
* @param value The value.
|
||||
* @return true if the given value matches the rules of the ValueMatcher, false otherwise.
|
||||
*/
|
||||
public boolean match(int value);
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/cron4j-2.2.5.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
</listAttribute>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/cron4j-2.2.5.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Classic/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
</listAttribute>
|
||||
|
@ -2,7 +2,6 @@
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="lib" path="dist/libs/c3p0-0.9.5.2.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/cron4j-2.2.5.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/ecj-4.4.2.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/exp4j-0.4.8.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/mail-1.5.2.jar"/>
|
||||
|
BIN
L2J_Mobius_Helios/dist/libs/cron4j-2.2.5.jar
vendored
BIN
L2J_Mobius_Helios/dist/libs/cron4j-2.2.5.jar
vendored
Binary file not shown.
@ -29,9 +29,8 @@ import java.util.logging.Logger;
|
||||
import com.l2jmobius.commons.database.DatabaseFactory;
|
||||
import com.l2jmobius.gameserver.ThreadPoolManager;
|
||||
import com.l2jmobius.gameserver.model.StatsSet;
|
||||
|
||||
import it.sauronsoftware.cron4j.PastPredictor;
|
||||
import it.sauronsoftware.cron4j.Predictor;
|
||||
import com.l2jmobius.gameserver.model.eventengine.cron4j.PastPredictor;
|
||||
import com.l2jmobius.gameserver.model.eventengine.cron4j.Predictor;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
/**
|
||||
* This ValueMatcher always returns true!
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
class AlwaysTrueValueMatcher implements ValueMatcher
|
||||
{
|
||||
/**
|
||||
* Always true!
|
||||
*/
|
||||
@Override
|
||||
public boolean match(int value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A ValueMatcher whose rules are in a plain array of integer values. When asked to validate a value, this ValueMatcher checks if it is in the array and, if not, checks whether the last-day-of-month setting applies.
|
||||
* </p>
|
||||
* @author Paul Fernley
|
||||
*/
|
||||
class DayOfMonthValueMatcher extends IntArrayValueMatcher
|
||||
{
|
||||
private static final int[] lastDays =
|
||||
{
|
||||
31,
|
||||
28,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
30,
|
||||
31
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the ValueMatcher.
|
||||
* @param values An ArrayList of Integer elements, one for every value accepted by the matcher. The match() method will return true only if its parameter will be one of this list or the last-day-of-month setting applies.
|
||||
*/
|
||||
public DayOfMonthValueMatcher(ArrayList<?> values)
|
||||
{
|
||||
super(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given value is included in the matcher list or the last-day-of-month setting applies.
|
||||
* @param value
|
||||
* @param month
|
||||
* @param isLeapYear
|
||||
* @return
|
||||
*/
|
||||
public boolean match(int value, int month, boolean isLeapYear)
|
||||
{
|
||||
return (super.match(value) || ((value > 27) && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
|
||||
}
|
||||
|
||||
public boolean isLastDayOfMonth(int value, int month, boolean isLeapYear)
|
||||
{
|
||||
if (isLeapYear && (month == 2))
|
||||
{
|
||||
return value == 29;
|
||||
}
|
||||
return value == lastDays[month - 1];
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A ValueMatcher whose rules are in a plain array of integer values. When asked to validate a value, this ValueMatcher checks if it is in the array.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
class IntArrayValueMatcher implements ValueMatcher
|
||||
{
|
||||
/**
|
||||
* The accepted values.
|
||||
*/
|
||||
private final int[] values;
|
||||
|
||||
/**
|
||||
* Builds the ValueMatcher.
|
||||
* @param integers An ArrayList of Integer elements, one for every value accepted by the matcher. The match() method will return true only if its parameter will be one of this list.
|
||||
*/
|
||||
public IntArrayValueMatcher(ArrayList<?> integers)
|
||||
{
|
||||
int size = integers.size();
|
||||
values = new int[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
values[i] = ((Integer) integers.get(i)).intValue();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given value is included in the matcher list.
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean match(int value)
|
||||
{
|
||||
for (int value2 : values)
|
||||
{
|
||||
if (value2 == value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This kind of exception is thrown if an invalid scheduling pattern is encountered by the scheduler.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
public class InvalidPatternException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Package-reserved construction.
|
||||
*/
|
||||
InvalidPatternException()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Package-reserved construction.
|
||||
* @param message String
|
||||
*/
|
||||
InvalidPatternException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public class PastPredictor
|
||||
{
|
||||
/**
|
||||
* The scheduling pattern on which the predictor works.
|
||||
*/
|
||||
private final SchedulingPattern _schedulingPattern;
|
||||
|
||||
/**
|
||||
* The start time for the next prediction.
|
||||
*/
|
||||
private long _time;
|
||||
|
||||
/**
|
||||
* The time zone for the prediction.
|
||||
*/
|
||||
private TimeZone _timeZone = TimeZone.getDefault();
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public PastPredictor(String schedulingPattern, long start) throws InvalidPatternException
|
||||
{
|
||||
_schedulingPattern = new SchedulingPattern(schedulingPattern);
|
||||
_time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public PastPredictor(String schedulingPattern, Date start) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public PastPredictor(String schedulingPattern) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public PastPredictor(SchedulingPattern schedulingPattern, long start)
|
||||
{
|
||||
_schedulingPattern = schedulingPattern;
|
||||
_time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public PastPredictor(SchedulingPattern schedulingPattern, Date start)
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @since 2.0
|
||||
*/
|
||||
public PastPredictor(SchedulingPattern schedulingPattern)
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time zone for predictions.
|
||||
* @param timeZone The time zone for predictions.
|
||||
* @since 2.2.5
|
||||
*/
|
||||
public void setTimeZone(TimeZone timeZone)
|
||||
{
|
||||
_timeZone = timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the previous matching moment as a millis value.
|
||||
* @return The previous matching moment as a millis value.
|
||||
*/
|
||||
public synchronized long prevMatchingTime()
|
||||
{
|
||||
// Go a minute back.
|
||||
_time -= 60000;
|
||||
// Is it matching?
|
||||
if (_schedulingPattern.match(_time))
|
||||
{
|
||||
return _time;
|
||||
}
|
||||
// Go through the matcher groups.
|
||||
int size = _schedulingPattern.matcherSize;
|
||||
long[] times = new long[size];
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
// Ok, split the time!
|
||||
GregorianCalendar c = new GregorianCalendar();
|
||||
c.setTimeInMillis(_time);
|
||||
c.setTimeZone(_timeZone);
|
||||
int minute = c.get(Calendar.MINUTE);
|
||||
int hour = c.get(Calendar.HOUR_OF_DAY);
|
||||
int dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
int month = c.get(Calendar.MONTH);
|
||||
int year = c.get(Calendar.YEAR);
|
||||
// Gets the matchers.
|
||||
ValueMatcher minuteMatcher = _schedulingPattern.minuteMatchers.get(k);
|
||||
ValueMatcher hourMatcher = _schedulingPattern.hourMatchers.get(k);
|
||||
ValueMatcher dayOfMonthMatcher = _schedulingPattern.dayOfMonthMatchers.get(k);
|
||||
ValueMatcher dayOfWeekMatcher = _schedulingPattern.dayOfWeekMatchers.get(k);
|
||||
ValueMatcher monthMatcher = _schedulingPattern.monthMatchers.get(k);
|
||||
for (;;)
|
||||
{ // day of week
|
||||
for (;;)
|
||||
{ // month
|
||||
for (;;)
|
||||
{ // day of month
|
||||
for (;;)
|
||||
{ // hour
|
||||
for (;;)
|
||||
{ // minutes
|
||||
if (minuteMatcher.match(minute))
|
||||
{
|
||||
break;
|
||||
}
|
||||
minute--;
|
||||
if (minute < 0)
|
||||
{
|
||||
minute = 59;
|
||||
hour--;
|
||||
}
|
||||
}
|
||||
if (hour < 0)
|
||||
{
|
||||
hour = 23;
|
||||
dayOfMonth--;
|
||||
}
|
||||
if (hourMatcher.match(hour))
|
||||
{
|
||||
break;
|
||||
}
|
||||
hour--;
|
||||
minute = 59;
|
||||
}
|
||||
if (dayOfMonth < 1)
|
||||
{
|
||||
dayOfMonth = 31;
|
||||
month--;
|
||||
}
|
||||
if (month < Calendar.JANUARY)
|
||||
{
|
||||
month = Calendar.DECEMBER;
|
||||
year--;
|
||||
}
|
||||
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher)
|
||||
{
|
||||
DayOfMonthValueMatcher aux = (DayOfMonthValueMatcher) dayOfMonthMatcher;
|
||||
if (aux.match(dayOfMonth, month + 1, c.isLeapYear(year)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth--;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
}
|
||||
else if (dayOfMonthMatcher.match(dayOfMonth))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
dayOfMonth--;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
}
|
||||
}
|
||||
if (monthMatcher.match(month + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
month--;
|
||||
dayOfMonth = 31;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
}
|
||||
// Is this ok?
|
||||
c = new GregorianCalendar();
|
||||
c.setTimeZone(_timeZone);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
c.set(Calendar.HOUR_OF_DAY, hour);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.YEAR, year);
|
||||
// Day-of-month/month/year compatibility check.
|
||||
int oldDayOfMonth = dayOfMonth;
|
||||
int oldMonth = month;
|
||||
int oldYear = year;
|
||||
dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
month = c.get(Calendar.MONTH);
|
||||
year = c.get(Calendar.YEAR);
|
||||
if ((month != oldMonth) || (dayOfMonth != oldDayOfMonth) || (year != oldYear))
|
||||
{
|
||||
do
|
||||
{
|
||||
dayOfMonth = oldDayOfMonth - 1;
|
||||
month = oldMonth;
|
||||
year = oldYear;
|
||||
c = new GregorianCalendar();
|
||||
c.setTimeZone(_timeZone);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
c.set(Calendar.HOUR_OF_DAY, hour);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.YEAR, year);
|
||||
// Day-of-month/month/year compatibility check.
|
||||
oldDayOfMonth = dayOfMonth;
|
||||
oldMonth = month;
|
||||
oldYear = year;
|
||||
dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
month = c.get(Calendar.MONTH);
|
||||
year = c.get(Calendar.YEAR);
|
||||
|
||||
}
|
||||
while ((month != oldMonth) || (dayOfMonth != oldDayOfMonth) || (year != oldYear));
|
||||
// Take another spin!
|
||||
continue;
|
||||
}
|
||||
// Day of week.
|
||||
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
|
||||
if (dayOfWeekMatcher.match(dayOfWeek - 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth--;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
if (dayOfMonth < 1)
|
||||
{
|
||||
dayOfMonth = 31;
|
||||
month--;
|
||||
if (month < Calendar.JANUARY)
|
||||
{
|
||||
month = Calendar.DECEMBER;
|
||||
year--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Seems it matches!
|
||||
times[k] = (c.getTimeInMillis() / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
// Which one?
|
||||
long min = Long.MAX_VALUE;
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
if (times[k] < min)
|
||||
{
|
||||
min = times[k];
|
||||
}
|
||||
}
|
||||
// Updates the object current time value.
|
||||
_time = min;
|
||||
// Here it is.
|
||||
return _time;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the previous matching moment as a {@link Date} object.
|
||||
* @return The previous matching moment as a {@link Date} object.
|
||||
*/
|
||||
public synchronized Date prevMatchingDate()
|
||||
{
|
||||
return new Date(prevMatchingTime());
|
||||
}
|
||||
}
|
@ -0,0 +1,310 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A predictor is able to predict when a scheduling pattern will be matched.
|
||||
* </p>
|
||||
* <p>
|
||||
* Suppose you want to know when the scheduler will execute a task scheduled with the pattern <em>0 3 * jan-jun,sep-dec mon-fri</em>. You can predict the next <em>n</em> execution of the task using a Predictor instance:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* String pattern = "0 3 * jan-jun,sep-dec mon-fri";
|
||||
* Predictor p = new Predictor(pattern);
|
||||
* for (int i = 0; i < n; i++)
|
||||
* {
|
||||
* System.out.println(p.nextMatchingDate());
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Carlo Pelliccia
|
||||
* @since 1.1
|
||||
*/
|
||||
public class Predictor
|
||||
{
|
||||
/**
|
||||
* The scheduling pattern on which the predictor works.
|
||||
*/
|
||||
private final SchedulingPattern schedulingPattern;
|
||||
|
||||
/**
|
||||
* The start time for the next prediction.
|
||||
*/
|
||||
private long time;
|
||||
|
||||
/**
|
||||
* The time zone for the prediction.
|
||||
*/
|
||||
private TimeZone timeZone = TimeZone.getDefault();
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public Predictor(String schedulingPattern, long start) throws InvalidPatternException
|
||||
{
|
||||
this.schedulingPattern = new SchedulingPattern(schedulingPattern);
|
||||
this.time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public Predictor(String schedulingPattern, Date start) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public Predictor(String schedulingPattern) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Predictor(SchedulingPattern schedulingPattern, long start)
|
||||
{
|
||||
this.schedulingPattern = schedulingPattern;
|
||||
this.time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Predictor(SchedulingPattern schedulingPattern, Date start)
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Predictor(SchedulingPattern schedulingPattern)
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time zone for predictions.
|
||||
* @param timeZone The time zone for predictions.
|
||||
* @since 2.2.5
|
||||
*/
|
||||
public void setTimeZone(TimeZone timeZone)
|
||||
{
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the next matching moment as a millis value.
|
||||
* @return The next matching moment as a millis value.
|
||||
*/
|
||||
public synchronized long nextMatchingTime()
|
||||
{
|
||||
// Go a minute ahead.
|
||||
time += 60000;
|
||||
// Is it matching?
|
||||
if (schedulingPattern.match(time))
|
||||
{
|
||||
return time;
|
||||
}
|
||||
// Go through the matcher groups.
|
||||
int size = schedulingPattern.matcherSize;
|
||||
long[] times = new long[size];
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
// Ok, split the time!
|
||||
GregorianCalendar c = new GregorianCalendar();
|
||||
c.setTimeInMillis(time);
|
||||
c.setTimeZone(timeZone);
|
||||
int minute = c.get(Calendar.MINUTE);
|
||||
int hour = c.get(Calendar.HOUR_OF_DAY);
|
||||
int dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
int month = c.get(Calendar.MONTH);
|
||||
int year = c.get(Calendar.YEAR);
|
||||
// Gets the matchers.
|
||||
ValueMatcher minuteMatcher = schedulingPattern.minuteMatchers.get(k);
|
||||
ValueMatcher hourMatcher = schedulingPattern.hourMatchers.get(k);
|
||||
ValueMatcher dayOfMonthMatcher = schedulingPattern.dayOfMonthMatchers.get(k);
|
||||
ValueMatcher dayOfWeekMatcher = schedulingPattern.dayOfWeekMatchers.get(k);
|
||||
ValueMatcher monthMatcher = schedulingPattern.monthMatchers.get(k);
|
||||
for (;;)
|
||||
{ // day of week
|
||||
for (;;)
|
||||
{ // month
|
||||
for (;;)
|
||||
{ // day of month
|
||||
for (;;)
|
||||
{ // hour
|
||||
for (;;)
|
||||
{ // minutes
|
||||
if (minuteMatcher.match(minute))
|
||||
{
|
||||
break;
|
||||
}
|
||||
minute++;
|
||||
if (minute > 59)
|
||||
{
|
||||
minute = 0;
|
||||
hour++;
|
||||
}
|
||||
}
|
||||
if (hour > 23)
|
||||
{
|
||||
hour = 0;
|
||||
dayOfMonth++;
|
||||
}
|
||||
if (hourMatcher.match(hour))
|
||||
{
|
||||
break;
|
||||
}
|
||||
hour++;
|
||||
minute = 0;
|
||||
}
|
||||
if (dayOfMonth > 31)
|
||||
{
|
||||
dayOfMonth = 1;
|
||||
month++;
|
||||
}
|
||||
if (month > Calendar.DECEMBER)
|
||||
{
|
||||
month = Calendar.JANUARY;
|
||||
year++;
|
||||
}
|
||||
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher)
|
||||
{
|
||||
DayOfMonthValueMatcher aux = (DayOfMonthValueMatcher) dayOfMonthMatcher;
|
||||
if (aux.match(dayOfMonth, month + 1, c.isLeapYear(year)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth++;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
}
|
||||
else if (dayOfMonthMatcher.match(dayOfMonth))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
dayOfMonth++;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
}
|
||||
}
|
||||
if (monthMatcher.match(month + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
month++;
|
||||
dayOfMonth = 1;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
}
|
||||
// Is this ok?
|
||||
c = new GregorianCalendar();
|
||||
c.setTimeZone(timeZone);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
c.set(Calendar.HOUR_OF_DAY, hour);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.YEAR, year);
|
||||
// Day-of-month/month/year compatibility check.
|
||||
int oldDayOfMonth = dayOfMonth;
|
||||
int oldMonth = month;
|
||||
int oldYear = year;
|
||||
dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
month = c.get(Calendar.MONTH);
|
||||
year = c.get(Calendar.YEAR);
|
||||
if ((month != oldMonth) || (dayOfMonth != oldDayOfMonth) || (year != oldYear))
|
||||
{
|
||||
// Take another spin!
|
||||
continue;
|
||||
}
|
||||
// Day of week.
|
||||
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
|
||||
if (dayOfWeekMatcher.match(dayOfWeek - 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth++;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
if (dayOfMonth > 31)
|
||||
{
|
||||
dayOfMonth = 1;
|
||||
month++;
|
||||
if (month > Calendar.DECEMBER)
|
||||
{
|
||||
month = Calendar.JANUARY;
|
||||
year++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Seems it matches!
|
||||
times[k] = (c.getTimeInMillis() / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
// Which one?
|
||||
long min = Long.MAX_VALUE;
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
if (times[k] < min)
|
||||
{
|
||||
min = times[k];
|
||||
}
|
||||
}
|
||||
// Updates the object current time value.
|
||||
time = min;
|
||||
// Here it is.
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the next matching moment as a {@link Date} object.
|
||||
* @return The next matching moment as a {@link Date} object.
|
||||
*/
|
||||
public synchronized Date nextMatchingDate()
|
||||
{
|
||||
return new Date(nextMatchingTime());
|
||||
}
|
||||
}
|
@ -0,0 +1,741 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li><strong>Minutes sub-pattern</strong>. During which minutes of the hour should the task been launched? The values range is from 0 to 59.</li>
|
||||
* <li><strong>Hours sub-pattern</strong>. During which hours of the day should the task been launched? The values range is from 0 to 23.</li>
|
||||
* <li><strong>Days of month sub-pattern</strong>. During which days of the month should the task been launched? The values range is from 1 to 31. The special value L can be used to recognize the last day of month.</li>
|
||||
* <li><strong>Months sub-pattern</strong>. During which months of the year should the task been launched? The values range is from 1 (January) to 12 (December), otherwise this sub-pattern allows the aliases "jan", "feb", "mar", "apr", "may",
|
||||
* "jun", "jul", "aug", "sep", "oct", "nov" and "dec".</li>
|
||||
* <li><strong>Days of week sub-pattern</strong>. During which days of the week should the task been launched? The values range is from 0 (Sunday) to 6 (Saturday), otherwise this sub-pattern allows the aliases "sun", "mon", "tue", "wed", "thu",
|
||||
* "fri" and "sat".</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* The star wildcard character is also admitted, indicating "every minute of the hour", "every hour of the day", "every day of the month", "every month of the year" and "every day of the week", according to the sub-pattern in which it is used.
|
||||
* </p>
|
||||
* <p>
|
||||
* Once the scheduler is started, a task will be launched when the five parts in its scheduling pattern will be true at the same time.
|
||||
* </p>
|
||||
* <p>
|
||||
* Some examples:
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>5 * * * *</strong><br />
|
||||
* This pattern causes a task to be launched once every hour, at the begin of the fifth minute (00:05, 01:05, 02:05 etc.).
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* * * * *</strong><br />
|
||||
* This pattern causes a task to be launched every minute.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 * * Mon</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of Monday.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 16 * Mon</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of Monday, 16th, but only if the day is the 16th of the month.
|
||||
* </p>
|
||||
* <p>
|
||||
* Every sub-pattern can contain two or more comma separated values.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>59 11 * * 1,2,3,4,5</strong><br />
|
||||
* This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
|
||||
* </p>
|
||||
* <p>
|
||||
* Values intervals are admitted and defined using the minus character.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>59 11 * * 1-5</strong><br />
|
||||
* This pattern is equivalent to the previous one.
|
||||
* </p>
|
||||
* <p>
|
||||
* The slash character can be used to identify step values within a range. It can be used both in the form <em>*/c</em> and <em>a-b/c</em>. The subpattern is matched every <em>c</em> values of the range <em>0,maxvalue</em> or <em>a-b</em>.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>*/5 * * * *</strong><br />
|
||||
* This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>3-18/5 * * * *</strong><br />
|
||||
* This pattern causes a task to be launched every 5 minutes starting from the third minute of the hour, up to the 18th (0:03, 0:08, 0:13, 0:18, 1:03, 1:08 and so on).
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>*/15 9-17 * * *</strong><br />
|
||||
* This pattern causes a task to be launched every 15 minutes between the 9th and 17th hour of the day (9:00, 9:15, 9:30, 9:45 and so on... note that the last execution will be at 17:45).
|
||||
* </p>
|
||||
* <p>
|
||||
* All the fresh described syntax rules can be used together.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 10-16/2 * *</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of the day, but only if the day is the 10th, the 12th, the 14th or the 16th of the month.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 1-15,17,20-25 * *</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of the day, but the day of the month must be between the 1st and the 15th, the 20th and the 25, or at least it must be the 17th.
|
||||
* </p>
|
||||
* <p>
|
||||
* Finally cron4j lets you combine more scheduling patterns into one, with the pipe character:
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>0 5 * * *|8 10 * * *|22 17 * * *</strong><br />
|
||||
* This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SchedulingPattern
|
||||
{
|
||||
/**
|
||||
* The parser for the minute values.
|
||||
*/
|
||||
private static final ValueParser MINUTE_VALUE_PARSER = new MinuteValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the hour values.
|
||||
*/
|
||||
private static final ValueParser HOUR_VALUE_PARSER = new HourValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the day of month values.
|
||||
*/
|
||||
private static final ValueParser DAY_OF_MONTH_VALUE_PARSER = new DayOfMonthValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the month values.
|
||||
*/
|
||||
private static final ValueParser MONTH_VALUE_PARSER = new MonthValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the day of week values.
|
||||
*/
|
||||
private static final ValueParser DAY_OF_WEEK_VALUE_PARSER = new DayOfWeekValueParser();
|
||||
|
||||
/**
|
||||
* Validates a string as a scheduling pattern.
|
||||
* @param schedulingPattern The pattern to validate.
|
||||
* @return true if the given string represents a valid scheduling pattern; false otherwise.
|
||||
*/
|
||||
public static boolean validate(String schedulingPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
new SchedulingPattern(schedulingPattern);
|
||||
}
|
||||
catch (InvalidPatternException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pattern as a string.
|
||||
*/
|
||||
private final String asString;
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "minute" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> minuteMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "hour" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> hourMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "day of month" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> dayOfMonthMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "month" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> monthMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "day of week" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> dayOfWeekMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* How many matcher groups in this pattern?
|
||||
*/
|
||||
protected int matcherSize = 0;
|
||||
|
||||
/**
|
||||
* Builds a SchedulingPattern parsing it from a string.
|
||||
* @param pattern The pattern as a crontab-like string.
|
||||
* @throws InvalidPatternException If the supplied string is not a valid pattern.
|
||||
*/
|
||||
public SchedulingPattern(String pattern) throws InvalidPatternException
|
||||
{
|
||||
this.asString = pattern;
|
||||
StringTokenizer st1 = new StringTokenizer(pattern, "|");
|
||||
if (st1.countTokens() < 1)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern: \"" + pattern + "\"");
|
||||
}
|
||||
while (st1.hasMoreTokens())
|
||||
{
|
||||
String localPattern = st1.nextToken();
|
||||
StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
|
||||
if (st2.countTokens() != 5)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern: \"" + localPattern + "\"");
|
||||
}
|
||||
try
|
||||
{
|
||||
minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing minutes field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing days of month field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
monthMatchers.add(buildValueMatcher(st2.nextToken(), MONTH_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing months field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
dayOfWeekMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_WEEK_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
|
||||
}
|
||||
matcherSize++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A ValueMatcher utility builder.
|
||||
* @param str The pattern part for the ValueMatcher creation.
|
||||
* @param parser The parser used to parse the values.
|
||||
* @return The requested ValueMatcher.
|
||||
* @throws Exception If the supplied pattern part is not valid.
|
||||
*/
|
||||
private ValueMatcher buildValueMatcher(String str, ValueParser parser) throws Exception
|
||||
{
|
||||
if ((str.length() == 1) && str.equals("*"))
|
||||
{
|
||||
return new AlwaysTrueValueMatcher();
|
||||
}
|
||||
ArrayList<Object> values = new ArrayList<>();
|
||||
StringTokenizer st = new StringTokenizer(str, ",");
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
String element = st.nextToken();
|
||||
ArrayList<Integer> local;
|
||||
try
|
||||
{
|
||||
local = parseListElement(element, parser);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
|
||||
}
|
||||
for (Integer integer : local)
|
||||
{
|
||||
Object value = integer;
|
||||
if (!values.contains(value))
|
||||
{
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (values.size() == 0)
|
||||
{
|
||||
throw new Exception("invalid field \"" + str + "\"");
|
||||
}
|
||||
if (parser == DAY_OF_MONTH_VALUE_PARSER)
|
||||
{
|
||||
return new DayOfMonthValueMatcher(values);
|
||||
}
|
||||
return new IntArrayValueMatcher(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an element of a list of values of the pattern.
|
||||
* @param str The element string.
|
||||
* @param parser The parser used to parse the values.
|
||||
* @return A list of integers representing the allowed values.
|
||||
* @throws Exception If the supplied pattern part is not valid.
|
||||
*/
|
||||
private ArrayList<Integer> parseListElement(String str, ValueParser parser) throws Exception
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(str, "/");
|
||||
int size = st.countTokens();
|
||||
if ((size < 1) || (size > 2))
|
||||
{
|
||||
throw new Exception("syntax error");
|
||||
}
|
||||
ArrayList<Integer> values;
|
||||
try
|
||||
{
|
||||
values = parseRange(st.nextToken(), parser);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid range, " + e.getMessage());
|
||||
}
|
||||
if (size == 2)
|
||||
{
|
||||
String dStr = st.nextToken();
|
||||
int div;
|
||||
try
|
||||
{
|
||||
div = Integer.parseInt(dStr);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new Exception("invalid divisor \"" + dStr + "\"");
|
||||
}
|
||||
if (div < 1)
|
||||
{
|
||||
throw new Exception("non positive divisor \"" + div + "\"");
|
||||
}
|
||||
ArrayList<Integer> values2 = new ArrayList<>();
|
||||
for (int i = 0; i < values.size(); i += div)
|
||||
{
|
||||
values2.add(values.get(i));
|
||||
}
|
||||
return values2;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a range of values.
|
||||
* @param str The range string.
|
||||
* @param parser The parser used to parse the values.
|
||||
* @return A list of integers representing the allowed values.
|
||||
* @throws Exception If the supplied pattern part is not valid.
|
||||
*/
|
||||
private ArrayList<Integer> parseRange(String str, ValueParser parser) throws Exception
|
||||
{
|
||||
if (str.equals("*"))
|
||||
{
|
||||
int min = parser.getMinValue();
|
||||
int max = parser.getMaxValue();
|
||||
ArrayList<Integer> values = new ArrayList<>();
|
||||
for (int i = min; i <= max; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
StringTokenizer st = new StringTokenizer(str, "-");
|
||||
int size = st.countTokens();
|
||||
if ((size < 1) || (size > 2))
|
||||
{
|
||||
throw new Exception("syntax error");
|
||||
}
|
||||
String v1Str = st.nextToken();
|
||||
int v1;
|
||||
try
|
||||
{
|
||||
v1 = parser.parse(v1Str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid value \"" + v1Str + "\", " + e.getMessage());
|
||||
}
|
||||
if (size == 1)
|
||||
{
|
||||
ArrayList<Integer> values = new ArrayList<>();
|
||||
values.add(new Integer(v1));
|
||||
return values;
|
||||
}
|
||||
String v2Str = st.nextToken();
|
||||
int v2;
|
||||
try
|
||||
{
|
||||
v2 = parser.parse(v2Str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid value \"" + v2Str + "\", " + e.getMessage());
|
||||
}
|
||||
ArrayList<Integer> values = new ArrayList<>();
|
||||
if (v1 < v2)
|
||||
{
|
||||
for (int i = v1; i <= v2; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
}
|
||||
else if (v1 > v2)
|
||||
{
|
||||
int min = parser.getMinValue();
|
||||
int max = parser.getMaxValue();
|
||||
for (int i = v1; i <= max; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
for (int i = min; i <= v2; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// v1 == v2
|
||||
values.add(new Integer(v1));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods returns true if the given timestamp (expressed as a UNIX-era millis value) matches the pattern, according to the given time zone.
|
||||
* @param timezone A time zone.
|
||||
* @param millis The timestamp, as a UNIX-era millis value.
|
||||
* @return true if the given timestamp matches the pattern.
|
||||
*/
|
||||
public boolean match(TimeZone timezone, long millis)
|
||||
{
|
||||
GregorianCalendar gc = new GregorianCalendar();
|
||||
gc.setTimeInMillis(millis);
|
||||
gc.setTimeZone(timezone);
|
||||
int minute = gc.get(Calendar.MINUTE);
|
||||
int hour = gc.get(Calendar.HOUR_OF_DAY);
|
||||
int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH);
|
||||
int month = gc.get(Calendar.MONTH) + 1;
|
||||
int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1;
|
||||
int year = gc.get(Calendar.YEAR);
|
||||
for (int i = 0; i < matcherSize; i++)
|
||||
{
|
||||
ValueMatcher minuteMatcher = minuteMatchers.get(i);
|
||||
ValueMatcher hourMatcher = hourMatchers.get(i);
|
||||
ValueMatcher dayOfMonthMatcher = dayOfMonthMatchers.get(i);
|
||||
ValueMatcher monthMatcher = monthMatchers.get(i);
|
||||
ValueMatcher dayOfWeekMatcher = dayOfWeekMatchers.get(i);
|
||||
boolean eval = minuteMatcher.match(minute) && hourMatcher.match(hour) && ((dayOfMonthMatcher instanceof DayOfMonthValueMatcher) ? ((DayOfMonthValueMatcher) dayOfMonthMatcher).match(dayOfMonth, month, gc.isLeapYear(year)) : dayOfMonthMatcher.match(dayOfMonth)) && monthMatcher.match(month) && dayOfWeekMatcher.match(dayOfWeek);
|
||||
if (eval)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods returns true if the given timestamp (expressed as a UNIX-era millis value) matches the pattern, according to the system default time zone.
|
||||
* @param millis The timestamp, as a UNIX-era millis value.
|
||||
* @return true if the given timestamp matches the pattern.
|
||||
*/
|
||||
public boolean match(long millis)
|
||||
{
|
||||
return match(TimeZone.getDefault(), millis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pattern as a string.
|
||||
* @return The pattern as a string.
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return asString;
|
||||
}
|
||||
|
||||
/**
|
||||
* This utility method changes an alias to an int value.
|
||||
* @param value The value.
|
||||
* @param aliases The aliases list.
|
||||
* @param offset The offset appplied to the aliases list indices.
|
||||
* @return The parsed value.
|
||||
* @throws Exception If the expressed values doesn't match any alias.
|
||||
*/
|
||||
static int parseAlias(String value, String[] aliases, int offset) throws Exception
|
||||
{
|
||||
for (int i = 0; i < aliases.length; i++)
|
||||
{
|
||||
if (aliases[i].equalsIgnoreCase(value))
|
||||
{
|
||||
return offset + i;
|
||||
}
|
||||
}
|
||||
throw new Exception("invalid alias \"" + value + "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition for a value parser.
|
||||
*/
|
||||
private static interface ValueParser
|
||||
{
|
||||
/**
|
||||
* Attempts to parse a value.
|
||||
* @param value The value.
|
||||
* @return The parsed value.
|
||||
* @throws Exception If the value can't be parsed.
|
||||
*/
|
||||
public int parse(String value) throws Exception;
|
||||
|
||||
/**
|
||||
* Returns the minimum value accepred by the parser.
|
||||
* @return The minimum value accepred by the parser.
|
||||
*/
|
||||
public int getMinValue();
|
||||
|
||||
/**
|
||||
* Returns the maximum value accepred by the parser.
|
||||
* @return The maximum value accepred by the parser.
|
||||
*/
|
||||
public int getMaxValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple value parser.
|
||||
*/
|
||||
private static class SimpleValueParser implements ValueParser
|
||||
{
|
||||
/**
|
||||
* The minimum allowed value.
|
||||
*/
|
||||
protected int minValue;
|
||||
|
||||
/**
|
||||
* The maximum allowed value.
|
||||
*/
|
||||
protected int maxValue;
|
||||
|
||||
/**
|
||||
* Builds the value parser.
|
||||
* @param minValue The minimum allowed value.
|
||||
* @param maxValue The maximum allowed value.
|
||||
*/
|
||||
public SimpleValueParser(int minValue, int maxValue)
|
||||
{
|
||||
this.minValue = minValue;
|
||||
this.maxValue = maxValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
int i;
|
||||
try
|
||||
{
|
||||
i = Integer.parseInt(value);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new Exception("invalid integer value");
|
||||
}
|
||||
if ((i < minValue) || (i > maxValue))
|
||||
{
|
||||
throw new Exception("value out of range");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinValue()
|
||||
{
|
||||
return minValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxValue()
|
||||
{
|
||||
return maxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The minutes value parser.
|
||||
*/
|
||||
private static class MinuteValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Builds the value parser.
|
||||
*/
|
||||
public MinuteValueParser()
|
||||
{
|
||||
super(0, 59);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The hours value parser.
|
||||
*/
|
||||
private static class HourValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Builds the value parser.
|
||||
*/
|
||||
public HourValueParser()
|
||||
{
|
||||
super(0, 23);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The days of month value parser.
|
||||
*/
|
||||
private static class DayOfMonthValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Builds the value parser.
|
||||
*/
|
||||
public DayOfMonthValueParser()
|
||||
{
|
||||
super(1, 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* Added to support last-day-of-month.
|
||||
* @param value The value to be parsed
|
||||
* @return the integer day of the month or 32 for last day of the month
|
||||
* @throws Exception if the input value is invalid
|
||||
*/
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
if (value.equalsIgnoreCase("L"))
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
return super.parse(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value parser for the months field.
|
||||
*/
|
||||
private static class MonthValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Months aliases.
|
||||
*/
|
||||
private static String[] ALIASES =
|
||||
{
|
||||
"jan",
|
||||
"feb",
|
||||
"mar",
|
||||
"apr",
|
||||
"may",
|
||||
"jun",
|
||||
"jul",
|
||||
"aug",
|
||||
"sep",
|
||||
"oct",
|
||||
"nov",
|
||||
"dec"
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the months value parser.
|
||||
*/
|
||||
public MonthValueParser()
|
||||
{
|
||||
super(1, 12);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
// try as a simple value
|
||||
return super.parse(value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// try as an alias
|
||||
return parseAlias(value, ALIASES, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value parser for the months field.
|
||||
*/
|
||||
private static class DayOfWeekValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Days of week aliases.
|
||||
*/
|
||||
private static String[] ALIASES =
|
||||
{
|
||||
"sun",
|
||||
"mon",
|
||||
"tue",
|
||||
"wed",
|
||||
"thu",
|
||||
"fri",
|
||||
"sat"
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the months value parser.
|
||||
*/
|
||||
public DayOfWeekValueParser()
|
||||
{
|
||||
super(0, 7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
// try as a simple value
|
||||
return super.parse(value) % 7;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// try as an alias
|
||||
return parseAlias(value, ALIASES, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
interface ValueMatcher
|
||||
{
|
||||
/**
|
||||
* Validate the given integer value against a set of rules.
|
||||
* @param value The value.
|
||||
* @return true if the given value matches the rules of the ValueMatcher, false otherwise.
|
||||
*/
|
||||
public boolean match(int value);
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/cron4j-2.2.5.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
</listAttribute>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/cron4j-2.2.5.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Helios/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
</listAttribute>
|
||||
|
@ -2,7 +2,6 @@
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="lib" path="dist/libs/c3p0-0.9.5.2.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/cron4j-2.2.5.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/ecj-4.4.2.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/exp4j-0.4.8.jar"/>
|
||||
<classpathentry kind="lib" path="dist/libs/mail-1.5.2.jar"/>
|
||||
|
BIN
L2J_Mobius_Underground/dist/libs/cron4j-2.2.5.jar
vendored
BIN
L2J_Mobius_Underground/dist/libs/cron4j-2.2.5.jar
vendored
Binary file not shown.
@ -29,9 +29,8 @@ import java.util.logging.Logger;
|
||||
import com.l2jmobius.commons.database.DatabaseFactory;
|
||||
import com.l2jmobius.gameserver.ThreadPoolManager;
|
||||
import com.l2jmobius.gameserver.model.StatsSet;
|
||||
|
||||
import it.sauronsoftware.cron4j.PastPredictor;
|
||||
import it.sauronsoftware.cron4j.Predictor;
|
||||
import com.l2jmobius.gameserver.model.eventengine.cron4j.PastPredictor;
|
||||
import com.l2jmobius.gameserver.model.eventengine.cron4j.Predictor;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
/**
|
||||
* This ValueMatcher always returns true!
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
class AlwaysTrueValueMatcher implements ValueMatcher
|
||||
{
|
||||
/**
|
||||
* Always true!
|
||||
*/
|
||||
@Override
|
||||
public boolean match(int value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A ValueMatcher whose rules are in a plain array of integer values. When asked to validate a value, this ValueMatcher checks if it is in the array and, if not, checks whether the last-day-of-month setting applies.
|
||||
* </p>
|
||||
* @author Paul Fernley
|
||||
*/
|
||||
class DayOfMonthValueMatcher extends IntArrayValueMatcher
|
||||
{
|
||||
private static final int[] lastDays =
|
||||
{
|
||||
31,
|
||||
28,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
30,
|
||||
31
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the ValueMatcher.
|
||||
* @param values An ArrayList of Integer elements, one for every value accepted by the matcher. The match() method will return true only if its parameter will be one of this list or the last-day-of-month setting applies.
|
||||
*/
|
||||
public DayOfMonthValueMatcher(ArrayList<?> values)
|
||||
{
|
||||
super(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given value is included in the matcher list or the last-day-of-month setting applies.
|
||||
* @param value
|
||||
* @param month
|
||||
* @param isLeapYear
|
||||
* @return
|
||||
*/
|
||||
public boolean match(int value, int month, boolean isLeapYear)
|
||||
{
|
||||
return (super.match(value) || ((value > 27) && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
|
||||
}
|
||||
|
||||
public boolean isLastDayOfMonth(int value, int month, boolean isLeapYear)
|
||||
{
|
||||
if (isLeapYear && (month == 2))
|
||||
{
|
||||
return value == 29;
|
||||
}
|
||||
return value == lastDays[month - 1];
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A ValueMatcher whose rules are in a plain array of integer values. When asked to validate a value, this ValueMatcher checks if it is in the array.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
class IntArrayValueMatcher implements ValueMatcher
|
||||
{
|
||||
/**
|
||||
* The accepted values.
|
||||
*/
|
||||
private final int[] values;
|
||||
|
||||
/**
|
||||
* Builds the ValueMatcher.
|
||||
* @param integers An ArrayList of Integer elements, one for every value accepted by the matcher. The match() method will return true only if its parameter will be one of this list.
|
||||
*/
|
||||
public IntArrayValueMatcher(ArrayList<?> integers)
|
||||
{
|
||||
int size = integers.size();
|
||||
values = new int[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
values[i] = ((Integer) integers.get(i)).intValue();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given value is included in the matcher list.
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean match(int value)
|
||||
{
|
||||
for (int value2 : values)
|
||||
{
|
||||
if (value2 == value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This kind of exception is thrown if an invalid scheduling pattern is encountered by the scheduler.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
public class InvalidPatternException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Package-reserved construction.
|
||||
*/
|
||||
InvalidPatternException()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Package-reserved construction.
|
||||
* @param message String
|
||||
*/
|
||||
InvalidPatternException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public class PastPredictor
|
||||
{
|
||||
/**
|
||||
* The scheduling pattern on which the predictor works.
|
||||
*/
|
||||
private final SchedulingPattern _schedulingPattern;
|
||||
|
||||
/**
|
||||
* The start time for the next prediction.
|
||||
*/
|
||||
private long _time;
|
||||
|
||||
/**
|
||||
* The time zone for the prediction.
|
||||
*/
|
||||
private TimeZone _timeZone = TimeZone.getDefault();
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public PastPredictor(String schedulingPattern, long start) throws InvalidPatternException
|
||||
{
|
||||
_schedulingPattern = new SchedulingPattern(schedulingPattern);
|
||||
_time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public PastPredictor(String schedulingPattern, Date start) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public PastPredictor(String schedulingPattern) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public PastPredictor(SchedulingPattern schedulingPattern, long start)
|
||||
{
|
||||
_schedulingPattern = schedulingPattern;
|
||||
_time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public PastPredictor(SchedulingPattern schedulingPattern, Date start)
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @since 2.0
|
||||
*/
|
||||
public PastPredictor(SchedulingPattern schedulingPattern)
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time zone for predictions.
|
||||
* @param timeZone The time zone for predictions.
|
||||
* @since 2.2.5
|
||||
*/
|
||||
public void setTimeZone(TimeZone timeZone)
|
||||
{
|
||||
_timeZone = timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the previous matching moment as a millis value.
|
||||
* @return The previous matching moment as a millis value.
|
||||
*/
|
||||
public synchronized long prevMatchingTime()
|
||||
{
|
||||
// Go a minute back.
|
||||
_time -= 60000;
|
||||
// Is it matching?
|
||||
if (_schedulingPattern.match(_time))
|
||||
{
|
||||
return _time;
|
||||
}
|
||||
// Go through the matcher groups.
|
||||
int size = _schedulingPattern.matcherSize;
|
||||
long[] times = new long[size];
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
// Ok, split the time!
|
||||
GregorianCalendar c = new GregorianCalendar();
|
||||
c.setTimeInMillis(_time);
|
||||
c.setTimeZone(_timeZone);
|
||||
int minute = c.get(Calendar.MINUTE);
|
||||
int hour = c.get(Calendar.HOUR_OF_DAY);
|
||||
int dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
int month = c.get(Calendar.MONTH);
|
||||
int year = c.get(Calendar.YEAR);
|
||||
// Gets the matchers.
|
||||
ValueMatcher minuteMatcher = _schedulingPattern.minuteMatchers.get(k);
|
||||
ValueMatcher hourMatcher = _schedulingPattern.hourMatchers.get(k);
|
||||
ValueMatcher dayOfMonthMatcher = _schedulingPattern.dayOfMonthMatchers.get(k);
|
||||
ValueMatcher dayOfWeekMatcher = _schedulingPattern.dayOfWeekMatchers.get(k);
|
||||
ValueMatcher monthMatcher = _schedulingPattern.monthMatchers.get(k);
|
||||
for (;;)
|
||||
{ // day of week
|
||||
for (;;)
|
||||
{ // month
|
||||
for (;;)
|
||||
{ // day of month
|
||||
for (;;)
|
||||
{ // hour
|
||||
for (;;)
|
||||
{ // minutes
|
||||
if (minuteMatcher.match(minute))
|
||||
{
|
||||
break;
|
||||
}
|
||||
minute--;
|
||||
if (minute < 0)
|
||||
{
|
||||
minute = 59;
|
||||
hour--;
|
||||
}
|
||||
}
|
||||
if (hour < 0)
|
||||
{
|
||||
hour = 23;
|
||||
dayOfMonth--;
|
||||
}
|
||||
if (hourMatcher.match(hour))
|
||||
{
|
||||
break;
|
||||
}
|
||||
hour--;
|
||||
minute = 59;
|
||||
}
|
||||
if (dayOfMonth < 1)
|
||||
{
|
||||
dayOfMonth = 31;
|
||||
month--;
|
||||
}
|
||||
if (month < Calendar.JANUARY)
|
||||
{
|
||||
month = Calendar.DECEMBER;
|
||||
year--;
|
||||
}
|
||||
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher)
|
||||
{
|
||||
DayOfMonthValueMatcher aux = (DayOfMonthValueMatcher) dayOfMonthMatcher;
|
||||
if (aux.match(dayOfMonth, month + 1, c.isLeapYear(year)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth--;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
}
|
||||
else if (dayOfMonthMatcher.match(dayOfMonth))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
dayOfMonth--;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
}
|
||||
}
|
||||
if (monthMatcher.match(month + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
month--;
|
||||
dayOfMonth = 31;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
}
|
||||
// Is this ok?
|
||||
c = new GregorianCalendar();
|
||||
c.setTimeZone(_timeZone);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
c.set(Calendar.HOUR_OF_DAY, hour);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.YEAR, year);
|
||||
// Day-of-month/month/year compatibility check.
|
||||
int oldDayOfMonth = dayOfMonth;
|
||||
int oldMonth = month;
|
||||
int oldYear = year;
|
||||
dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
month = c.get(Calendar.MONTH);
|
||||
year = c.get(Calendar.YEAR);
|
||||
if ((month != oldMonth) || (dayOfMonth != oldDayOfMonth) || (year != oldYear))
|
||||
{
|
||||
do
|
||||
{
|
||||
dayOfMonth = oldDayOfMonth - 1;
|
||||
month = oldMonth;
|
||||
year = oldYear;
|
||||
c = new GregorianCalendar();
|
||||
c.setTimeZone(_timeZone);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
c.set(Calendar.HOUR_OF_DAY, hour);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.YEAR, year);
|
||||
// Day-of-month/month/year compatibility check.
|
||||
oldDayOfMonth = dayOfMonth;
|
||||
oldMonth = month;
|
||||
oldYear = year;
|
||||
dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
month = c.get(Calendar.MONTH);
|
||||
year = c.get(Calendar.YEAR);
|
||||
|
||||
}
|
||||
while ((month != oldMonth) || (dayOfMonth != oldDayOfMonth) || (year != oldYear));
|
||||
// Take another spin!
|
||||
continue;
|
||||
}
|
||||
// Day of week.
|
||||
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
|
||||
if (dayOfWeekMatcher.match(dayOfWeek - 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth--;
|
||||
hour = 23;
|
||||
minute = 59;
|
||||
if (dayOfMonth < 1)
|
||||
{
|
||||
dayOfMonth = 31;
|
||||
month--;
|
||||
if (month < Calendar.JANUARY)
|
||||
{
|
||||
month = Calendar.DECEMBER;
|
||||
year--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Seems it matches!
|
||||
times[k] = (c.getTimeInMillis() / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
// Which one?
|
||||
long min = Long.MAX_VALUE;
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
if (times[k] < min)
|
||||
{
|
||||
min = times[k];
|
||||
}
|
||||
}
|
||||
// Updates the object current time value.
|
||||
_time = min;
|
||||
// Here it is.
|
||||
return _time;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the previous matching moment as a {@link Date} object.
|
||||
* @return The previous matching moment as a {@link Date} object.
|
||||
*/
|
||||
public synchronized Date prevMatchingDate()
|
||||
{
|
||||
return new Date(prevMatchingTime());
|
||||
}
|
||||
}
|
@ -0,0 +1,310 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A predictor is able to predict when a scheduling pattern will be matched.
|
||||
* </p>
|
||||
* <p>
|
||||
* Suppose you want to know when the scheduler will execute a task scheduled with the pattern <em>0 3 * jan-jun,sep-dec mon-fri</em>. You can predict the next <em>n</em> execution of the task using a Predictor instance:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* String pattern = "0 3 * jan-jun,sep-dec mon-fri";
|
||||
* Predictor p = new Predictor(pattern);
|
||||
* for (int i = 0; i < n; i++)
|
||||
* {
|
||||
* System.out.println(p.nextMatchingDate());
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Carlo Pelliccia
|
||||
* @since 1.1
|
||||
*/
|
||||
public class Predictor
|
||||
{
|
||||
/**
|
||||
* The scheduling pattern on which the predictor works.
|
||||
*/
|
||||
private final SchedulingPattern schedulingPattern;
|
||||
|
||||
/**
|
||||
* The start time for the next prediction.
|
||||
*/
|
||||
private long time;
|
||||
|
||||
/**
|
||||
* The time zone for the prediction.
|
||||
*/
|
||||
private TimeZone timeZone = TimeZone.getDefault();
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public Predictor(String schedulingPattern, long start) throws InvalidPatternException
|
||||
{
|
||||
this.schedulingPattern = new SchedulingPattern(schedulingPattern);
|
||||
this.time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public Predictor(String schedulingPattern, Date start) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @throws InvalidPatternException In the given scheduling pattern isn't valid.
|
||||
*/
|
||||
public Predictor(String schedulingPattern) throws InvalidPatternException
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Predictor(SchedulingPattern schedulingPattern, long start)
|
||||
{
|
||||
this.schedulingPattern = schedulingPattern;
|
||||
this.time = (start / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @param start The start time of the prediction.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Predictor(SchedulingPattern schedulingPattern, Date start)
|
||||
{
|
||||
this(schedulingPattern, start.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* It builds a predictor with the given scheduling pattern and the current system time as the prediction start time.
|
||||
* @param schedulingPattern The pattern on which the prediction will be based.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Predictor(SchedulingPattern schedulingPattern)
|
||||
{
|
||||
this(schedulingPattern, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time zone for predictions.
|
||||
* @param timeZone The time zone for predictions.
|
||||
* @since 2.2.5
|
||||
*/
|
||||
public void setTimeZone(TimeZone timeZone)
|
||||
{
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the next matching moment as a millis value.
|
||||
* @return The next matching moment as a millis value.
|
||||
*/
|
||||
public synchronized long nextMatchingTime()
|
||||
{
|
||||
// Go a minute ahead.
|
||||
time += 60000;
|
||||
// Is it matching?
|
||||
if (schedulingPattern.match(time))
|
||||
{
|
||||
return time;
|
||||
}
|
||||
// Go through the matcher groups.
|
||||
int size = schedulingPattern.matcherSize;
|
||||
long[] times = new long[size];
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
// Ok, split the time!
|
||||
GregorianCalendar c = new GregorianCalendar();
|
||||
c.setTimeInMillis(time);
|
||||
c.setTimeZone(timeZone);
|
||||
int minute = c.get(Calendar.MINUTE);
|
||||
int hour = c.get(Calendar.HOUR_OF_DAY);
|
||||
int dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
int month = c.get(Calendar.MONTH);
|
||||
int year = c.get(Calendar.YEAR);
|
||||
// Gets the matchers.
|
||||
ValueMatcher minuteMatcher = schedulingPattern.minuteMatchers.get(k);
|
||||
ValueMatcher hourMatcher = schedulingPattern.hourMatchers.get(k);
|
||||
ValueMatcher dayOfMonthMatcher = schedulingPattern.dayOfMonthMatchers.get(k);
|
||||
ValueMatcher dayOfWeekMatcher = schedulingPattern.dayOfWeekMatchers.get(k);
|
||||
ValueMatcher monthMatcher = schedulingPattern.monthMatchers.get(k);
|
||||
for (;;)
|
||||
{ // day of week
|
||||
for (;;)
|
||||
{ // month
|
||||
for (;;)
|
||||
{ // day of month
|
||||
for (;;)
|
||||
{ // hour
|
||||
for (;;)
|
||||
{ // minutes
|
||||
if (minuteMatcher.match(minute))
|
||||
{
|
||||
break;
|
||||
}
|
||||
minute++;
|
||||
if (minute > 59)
|
||||
{
|
||||
minute = 0;
|
||||
hour++;
|
||||
}
|
||||
}
|
||||
if (hour > 23)
|
||||
{
|
||||
hour = 0;
|
||||
dayOfMonth++;
|
||||
}
|
||||
if (hourMatcher.match(hour))
|
||||
{
|
||||
break;
|
||||
}
|
||||
hour++;
|
||||
minute = 0;
|
||||
}
|
||||
if (dayOfMonth > 31)
|
||||
{
|
||||
dayOfMonth = 1;
|
||||
month++;
|
||||
}
|
||||
if (month > Calendar.DECEMBER)
|
||||
{
|
||||
month = Calendar.JANUARY;
|
||||
year++;
|
||||
}
|
||||
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher)
|
||||
{
|
||||
DayOfMonthValueMatcher aux = (DayOfMonthValueMatcher) dayOfMonthMatcher;
|
||||
if (aux.match(dayOfMonth, month + 1, c.isLeapYear(year)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth++;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
}
|
||||
else if (dayOfMonthMatcher.match(dayOfMonth))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
dayOfMonth++;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
}
|
||||
}
|
||||
if (monthMatcher.match(month + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
month++;
|
||||
dayOfMonth = 1;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
}
|
||||
// Is this ok?
|
||||
c = new GregorianCalendar();
|
||||
c.setTimeZone(timeZone);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
c.set(Calendar.HOUR_OF_DAY, hour);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.YEAR, year);
|
||||
// Day-of-month/month/year compatibility check.
|
||||
int oldDayOfMonth = dayOfMonth;
|
||||
int oldMonth = month;
|
||||
int oldYear = year;
|
||||
dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
|
||||
month = c.get(Calendar.MONTH);
|
||||
year = c.get(Calendar.YEAR);
|
||||
if ((month != oldMonth) || (dayOfMonth != oldDayOfMonth) || (year != oldYear))
|
||||
{
|
||||
// Take another spin!
|
||||
continue;
|
||||
}
|
||||
// Day of week.
|
||||
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
|
||||
if (dayOfWeekMatcher.match(dayOfWeek - 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dayOfMonth++;
|
||||
hour = 0;
|
||||
minute = 0;
|
||||
if (dayOfMonth > 31)
|
||||
{
|
||||
dayOfMonth = 1;
|
||||
month++;
|
||||
if (month > Calendar.DECEMBER)
|
||||
{
|
||||
month = Calendar.JANUARY;
|
||||
year++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Seems it matches!
|
||||
times[k] = (c.getTimeInMillis() / (1000 * 60)) * 1000 * 60;
|
||||
}
|
||||
// Which one?
|
||||
long min = Long.MAX_VALUE;
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
if (times[k] < min)
|
||||
{
|
||||
min = times[k];
|
||||
}
|
||||
}
|
||||
// Updates the object current time value.
|
||||
time = min;
|
||||
// Here it is.
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the next matching moment as a {@link Date} object.
|
||||
* @return The next matching moment as a {@link Date} object.
|
||||
*/
|
||||
public synchronized Date nextMatchingDate()
|
||||
{
|
||||
return new Date(nextMatchingTime());
|
||||
}
|
||||
}
|
@ -0,0 +1,741 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li><strong>Minutes sub-pattern</strong>. During which minutes of the hour should the task been launched? The values range is from 0 to 59.</li>
|
||||
* <li><strong>Hours sub-pattern</strong>. During which hours of the day should the task been launched? The values range is from 0 to 23.</li>
|
||||
* <li><strong>Days of month sub-pattern</strong>. During which days of the month should the task been launched? The values range is from 1 to 31. The special value L can be used to recognize the last day of month.</li>
|
||||
* <li><strong>Months sub-pattern</strong>. During which months of the year should the task been launched? The values range is from 1 (January) to 12 (December), otherwise this sub-pattern allows the aliases "jan", "feb", "mar", "apr", "may",
|
||||
* "jun", "jul", "aug", "sep", "oct", "nov" and "dec".</li>
|
||||
* <li><strong>Days of week sub-pattern</strong>. During which days of the week should the task been launched? The values range is from 0 (Sunday) to 6 (Saturday), otherwise this sub-pattern allows the aliases "sun", "mon", "tue", "wed", "thu",
|
||||
* "fri" and "sat".</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* The star wildcard character is also admitted, indicating "every minute of the hour", "every hour of the day", "every day of the month", "every month of the year" and "every day of the week", according to the sub-pattern in which it is used.
|
||||
* </p>
|
||||
* <p>
|
||||
* Once the scheduler is started, a task will be launched when the five parts in its scheduling pattern will be true at the same time.
|
||||
* </p>
|
||||
* <p>
|
||||
* Some examples:
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>5 * * * *</strong><br />
|
||||
* This pattern causes a task to be launched once every hour, at the begin of the fifth minute (00:05, 01:05, 02:05 etc.).
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* * * * *</strong><br />
|
||||
* This pattern causes a task to be launched every minute.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 * * Mon</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of Monday.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 16 * Mon</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of Monday, 16th, but only if the day is the 16th of the month.
|
||||
* </p>
|
||||
* <p>
|
||||
* Every sub-pattern can contain two or more comma separated values.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>59 11 * * 1,2,3,4,5</strong><br />
|
||||
* This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
|
||||
* </p>
|
||||
* <p>
|
||||
* Values intervals are admitted and defined using the minus character.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>59 11 * * 1-5</strong><br />
|
||||
* This pattern is equivalent to the previous one.
|
||||
* </p>
|
||||
* <p>
|
||||
* The slash character can be used to identify step values within a range. It can be used both in the form <em>*/c</em> and <em>a-b/c</em>. The subpattern is matched every <em>c</em> values of the range <em>0,maxvalue</em> or <em>a-b</em>.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>*/5 * * * *</strong><br />
|
||||
* This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>3-18/5 * * * *</strong><br />
|
||||
* This pattern causes a task to be launched every 5 minutes starting from the third minute of the hour, up to the 18th (0:03, 0:08, 0:13, 0:18, 1:03, 1:08 and so on).
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>*/15 9-17 * * *</strong><br />
|
||||
* This pattern causes a task to be launched every 15 minutes between the 9th and 17th hour of the day (9:00, 9:15, 9:30, 9:45 and so on... note that the last execution will be at 17:45).
|
||||
* </p>
|
||||
* <p>
|
||||
* All the fresh described syntax rules can be used together.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 10-16/2 * *</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of the day, but only if the day is the 10th, the 12th, the 14th or the 16th of the month.
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>* 12 1-15,17,20-25 * *</strong><br />
|
||||
* This pattern causes a task to be launched every minute during the 12th hour of the day, but the day of the month must be between the 1st and the 15th, the 20th and the 25, or at least it must be the 17th.
|
||||
* </p>
|
||||
* <p>
|
||||
* Finally cron4j lets you combine more scheduling patterns into one, with the pipe character:
|
||||
* </p>
|
||||
* <p>
|
||||
* <strong>0 5 * * *|8 10 * * *|22 17 * * *</strong><br />
|
||||
* This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SchedulingPattern
|
||||
{
|
||||
/**
|
||||
* The parser for the minute values.
|
||||
*/
|
||||
private static final ValueParser MINUTE_VALUE_PARSER = new MinuteValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the hour values.
|
||||
*/
|
||||
private static final ValueParser HOUR_VALUE_PARSER = new HourValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the day of month values.
|
||||
*/
|
||||
private static final ValueParser DAY_OF_MONTH_VALUE_PARSER = new DayOfMonthValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the month values.
|
||||
*/
|
||||
private static final ValueParser MONTH_VALUE_PARSER = new MonthValueParser();
|
||||
|
||||
/**
|
||||
* The parser for the day of week values.
|
||||
*/
|
||||
private static final ValueParser DAY_OF_WEEK_VALUE_PARSER = new DayOfWeekValueParser();
|
||||
|
||||
/**
|
||||
* Validates a string as a scheduling pattern.
|
||||
* @param schedulingPattern The pattern to validate.
|
||||
* @return true if the given string represents a valid scheduling pattern; false otherwise.
|
||||
*/
|
||||
public static boolean validate(String schedulingPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
new SchedulingPattern(schedulingPattern);
|
||||
}
|
||||
catch (InvalidPatternException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pattern as a string.
|
||||
*/
|
||||
private final String asString;
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "minute" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> minuteMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "hour" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> hourMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "day of month" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> dayOfMonthMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "month" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> monthMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The ValueMatcher list for the "day of week" field.
|
||||
*/
|
||||
protected ArrayList<ValueMatcher> dayOfWeekMatchers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* How many matcher groups in this pattern?
|
||||
*/
|
||||
protected int matcherSize = 0;
|
||||
|
||||
/**
|
||||
* Builds a SchedulingPattern parsing it from a string.
|
||||
* @param pattern The pattern as a crontab-like string.
|
||||
* @throws InvalidPatternException If the supplied string is not a valid pattern.
|
||||
*/
|
||||
public SchedulingPattern(String pattern) throws InvalidPatternException
|
||||
{
|
||||
this.asString = pattern;
|
||||
StringTokenizer st1 = new StringTokenizer(pattern, "|");
|
||||
if (st1.countTokens() < 1)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern: \"" + pattern + "\"");
|
||||
}
|
||||
while (st1.hasMoreTokens())
|
||||
{
|
||||
String localPattern = st1.nextToken();
|
||||
StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
|
||||
if (st2.countTokens() != 5)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern: \"" + localPattern + "\"");
|
||||
}
|
||||
try
|
||||
{
|
||||
minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing minutes field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing days of month field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
monthMatchers.add(buildValueMatcher(st2.nextToken(), MONTH_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing months field: " + e.getMessage() + ".");
|
||||
}
|
||||
try
|
||||
{
|
||||
dayOfWeekMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_WEEK_VALUE_PARSER));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidPatternException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
|
||||
}
|
||||
matcherSize++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A ValueMatcher utility builder.
|
||||
* @param str The pattern part for the ValueMatcher creation.
|
||||
* @param parser The parser used to parse the values.
|
||||
* @return The requested ValueMatcher.
|
||||
* @throws Exception If the supplied pattern part is not valid.
|
||||
*/
|
||||
private ValueMatcher buildValueMatcher(String str, ValueParser parser) throws Exception
|
||||
{
|
||||
if ((str.length() == 1) && str.equals("*"))
|
||||
{
|
||||
return new AlwaysTrueValueMatcher();
|
||||
}
|
||||
ArrayList<Object> values = new ArrayList<>();
|
||||
StringTokenizer st = new StringTokenizer(str, ",");
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
String element = st.nextToken();
|
||||
ArrayList<Integer> local;
|
||||
try
|
||||
{
|
||||
local = parseListElement(element, parser);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
|
||||
}
|
||||
for (Integer integer : local)
|
||||
{
|
||||
Object value = integer;
|
||||
if (!values.contains(value))
|
||||
{
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (values.size() == 0)
|
||||
{
|
||||
throw new Exception("invalid field \"" + str + "\"");
|
||||
}
|
||||
if (parser == DAY_OF_MONTH_VALUE_PARSER)
|
||||
{
|
||||
return new DayOfMonthValueMatcher(values);
|
||||
}
|
||||
return new IntArrayValueMatcher(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an element of a list of values of the pattern.
|
||||
* @param str The element string.
|
||||
* @param parser The parser used to parse the values.
|
||||
* @return A list of integers representing the allowed values.
|
||||
* @throws Exception If the supplied pattern part is not valid.
|
||||
*/
|
||||
private ArrayList<Integer> parseListElement(String str, ValueParser parser) throws Exception
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(str, "/");
|
||||
int size = st.countTokens();
|
||||
if ((size < 1) || (size > 2))
|
||||
{
|
||||
throw new Exception("syntax error");
|
||||
}
|
||||
ArrayList<Integer> values;
|
||||
try
|
||||
{
|
||||
values = parseRange(st.nextToken(), parser);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid range, " + e.getMessage());
|
||||
}
|
||||
if (size == 2)
|
||||
{
|
||||
String dStr = st.nextToken();
|
||||
int div;
|
||||
try
|
||||
{
|
||||
div = Integer.parseInt(dStr);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new Exception("invalid divisor \"" + dStr + "\"");
|
||||
}
|
||||
if (div < 1)
|
||||
{
|
||||
throw new Exception("non positive divisor \"" + div + "\"");
|
||||
}
|
||||
ArrayList<Integer> values2 = new ArrayList<>();
|
||||
for (int i = 0; i < values.size(); i += div)
|
||||
{
|
||||
values2.add(values.get(i));
|
||||
}
|
||||
return values2;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a range of values.
|
||||
* @param str The range string.
|
||||
* @param parser The parser used to parse the values.
|
||||
* @return A list of integers representing the allowed values.
|
||||
* @throws Exception If the supplied pattern part is not valid.
|
||||
*/
|
||||
private ArrayList<Integer> parseRange(String str, ValueParser parser) throws Exception
|
||||
{
|
||||
if (str.equals("*"))
|
||||
{
|
||||
int min = parser.getMinValue();
|
||||
int max = parser.getMaxValue();
|
||||
ArrayList<Integer> values = new ArrayList<>();
|
||||
for (int i = min; i <= max; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
StringTokenizer st = new StringTokenizer(str, "-");
|
||||
int size = st.countTokens();
|
||||
if ((size < 1) || (size > 2))
|
||||
{
|
||||
throw new Exception("syntax error");
|
||||
}
|
||||
String v1Str = st.nextToken();
|
||||
int v1;
|
||||
try
|
||||
{
|
||||
v1 = parser.parse(v1Str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid value \"" + v1Str + "\", " + e.getMessage());
|
||||
}
|
||||
if (size == 1)
|
||||
{
|
||||
ArrayList<Integer> values = new ArrayList<>();
|
||||
values.add(new Integer(v1));
|
||||
return values;
|
||||
}
|
||||
String v2Str = st.nextToken();
|
||||
int v2;
|
||||
try
|
||||
{
|
||||
v2 = parser.parse(v2Str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("invalid value \"" + v2Str + "\", " + e.getMessage());
|
||||
}
|
||||
ArrayList<Integer> values = new ArrayList<>();
|
||||
if (v1 < v2)
|
||||
{
|
||||
for (int i = v1; i <= v2; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
}
|
||||
else if (v1 > v2)
|
||||
{
|
||||
int min = parser.getMinValue();
|
||||
int max = parser.getMaxValue();
|
||||
for (int i = v1; i <= max; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
for (int i = min; i <= v2; i++)
|
||||
{
|
||||
values.add(new Integer(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// v1 == v2
|
||||
values.add(new Integer(v1));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods returns true if the given timestamp (expressed as a UNIX-era millis value) matches the pattern, according to the given time zone.
|
||||
* @param timezone A time zone.
|
||||
* @param millis The timestamp, as a UNIX-era millis value.
|
||||
* @return true if the given timestamp matches the pattern.
|
||||
*/
|
||||
public boolean match(TimeZone timezone, long millis)
|
||||
{
|
||||
GregorianCalendar gc = new GregorianCalendar();
|
||||
gc.setTimeInMillis(millis);
|
||||
gc.setTimeZone(timezone);
|
||||
int minute = gc.get(Calendar.MINUTE);
|
||||
int hour = gc.get(Calendar.HOUR_OF_DAY);
|
||||
int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH);
|
||||
int month = gc.get(Calendar.MONTH) + 1;
|
||||
int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1;
|
||||
int year = gc.get(Calendar.YEAR);
|
||||
for (int i = 0; i < matcherSize; i++)
|
||||
{
|
||||
ValueMatcher minuteMatcher = minuteMatchers.get(i);
|
||||
ValueMatcher hourMatcher = hourMatchers.get(i);
|
||||
ValueMatcher dayOfMonthMatcher = dayOfMonthMatchers.get(i);
|
||||
ValueMatcher monthMatcher = monthMatchers.get(i);
|
||||
ValueMatcher dayOfWeekMatcher = dayOfWeekMatchers.get(i);
|
||||
boolean eval = minuteMatcher.match(minute) && hourMatcher.match(hour) && ((dayOfMonthMatcher instanceof DayOfMonthValueMatcher) ? ((DayOfMonthValueMatcher) dayOfMonthMatcher).match(dayOfMonth, month, gc.isLeapYear(year)) : dayOfMonthMatcher.match(dayOfMonth)) && monthMatcher.match(month) && dayOfWeekMatcher.match(dayOfWeek);
|
||||
if (eval)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods returns true if the given timestamp (expressed as a UNIX-era millis value) matches the pattern, according to the system default time zone.
|
||||
* @param millis The timestamp, as a UNIX-era millis value.
|
||||
* @return true if the given timestamp matches the pattern.
|
||||
*/
|
||||
public boolean match(long millis)
|
||||
{
|
||||
return match(TimeZone.getDefault(), millis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pattern as a string.
|
||||
* @return The pattern as a string.
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return asString;
|
||||
}
|
||||
|
||||
/**
|
||||
* This utility method changes an alias to an int value.
|
||||
* @param value The value.
|
||||
* @param aliases The aliases list.
|
||||
* @param offset The offset appplied to the aliases list indices.
|
||||
* @return The parsed value.
|
||||
* @throws Exception If the expressed values doesn't match any alias.
|
||||
*/
|
||||
static int parseAlias(String value, String[] aliases, int offset) throws Exception
|
||||
{
|
||||
for (int i = 0; i < aliases.length; i++)
|
||||
{
|
||||
if (aliases[i].equalsIgnoreCase(value))
|
||||
{
|
||||
return offset + i;
|
||||
}
|
||||
}
|
||||
throw new Exception("invalid alias \"" + value + "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition for a value parser.
|
||||
*/
|
||||
private static interface ValueParser
|
||||
{
|
||||
/**
|
||||
* Attempts to parse a value.
|
||||
* @param value The value.
|
||||
* @return The parsed value.
|
||||
* @throws Exception If the value can't be parsed.
|
||||
*/
|
||||
public int parse(String value) throws Exception;
|
||||
|
||||
/**
|
||||
* Returns the minimum value accepred by the parser.
|
||||
* @return The minimum value accepred by the parser.
|
||||
*/
|
||||
public int getMinValue();
|
||||
|
||||
/**
|
||||
* Returns the maximum value accepred by the parser.
|
||||
* @return The maximum value accepred by the parser.
|
||||
*/
|
||||
public int getMaxValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple value parser.
|
||||
*/
|
||||
private static class SimpleValueParser implements ValueParser
|
||||
{
|
||||
/**
|
||||
* The minimum allowed value.
|
||||
*/
|
||||
protected int minValue;
|
||||
|
||||
/**
|
||||
* The maximum allowed value.
|
||||
*/
|
||||
protected int maxValue;
|
||||
|
||||
/**
|
||||
* Builds the value parser.
|
||||
* @param minValue The minimum allowed value.
|
||||
* @param maxValue The maximum allowed value.
|
||||
*/
|
||||
public SimpleValueParser(int minValue, int maxValue)
|
||||
{
|
||||
this.minValue = minValue;
|
||||
this.maxValue = maxValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
int i;
|
||||
try
|
||||
{
|
||||
i = Integer.parseInt(value);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new Exception("invalid integer value");
|
||||
}
|
||||
if ((i < minValue) || (i > maxValue))
|
||||
{
|
||||
throw new Exception("value out of range");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinValue()
|
||||
{
|
||||
return minValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxValue()
|
||||
{
|
||||
return maxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The minutes value parser.
|
||||
*/
|
||||
private static class MinuteValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Builds the value parser.
|
||||
*/
|
||||
public MinuteValueParser()
|
||||
{
|
||||
super(0, 59);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The hours value parser.
|
||||
*/
|
||||
private static class HourValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Builds the value parser.
|
||||
*/
|
||||
public HourValueParser()
|
||||
{
|
||||
super(0, 23);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The days of month value parser.
|
||||
*/
|
||||
private static class DayOfMonthValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Builds the value parser.
|
||||
*/
|
||||
public DayOfMonthValueParser()
|
||||
{
|
||||
super(1, 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* Added to support last-day-of-month.
|
||||
* @param value The value to be parsed
|
||||
* @return the integer day of the month or 32 for last day of the month
|
||||
* @throws Exception if the input value is invalid
|
||||
*/
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
if (value.equalsIgnoreCase("L"))
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
return super.parse(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value parser for the months field.
|
||||
*/
|
||||
private static class MonthValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Months aliases.
|
||||
*/
|
||||
private static String[] ALIASES =
|
||||
{
|
||||
"jan",
|
||||
"feb",
|
||||
"mar",
|
||||
"apr",
|
||||
"may",
|
||||
"jun",
|
||||
"jul",
|
||||
"aug",
|
||||
"sep",
|
||||
"oct",
|
||||
"nov",
|
||||
"dec"
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the months value parser.
|
||||
*/
|
||||
public MonthValueParser()
|
||||
{
|
||||
super(1, 12);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
// try as a simple value
|
||||
return super.parse(value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// try as an alias
|
||||
return parseAlias(value, ALIASES, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value parser for the months field.
|
||||
*/
|
||||
private static class DayOfWeekValueParser extends SimpleValueParser
|
||||
{
|
||||
/**
|
||||
* Days of week aliases.
|
||||
*/
|
||||
private static String[] ALIASES =
|
||||
{
|
||||
"sun",
|
||||
"mon",
|
||||
"tue",
|
||||
"wed",
|
||||
"thu",
|
||||
"fri",
|
||||
"sat"
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the months value parser.
|
||||
*/
|
||||
public DayOfWeekValueParser()
|
||||
{
|
||||
super(0, 7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
// try as a simple value
|
||||
return super.parse(value) % 7;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// try as an alias
|
||||
return parseAlias(value, ALIASES, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.model.eventengine.cron4j;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
|
||||
* </p>
|
||||
* @author Carlo Pelliccia
|
||||
*/
|
||||
interface ValueMatcher
|
||||
{
|
||||
/**
|
||||
* Validate the given integer value against a set of rules.
|
||||
* @param value The value.
|
||||
* @return true if the given value matches the rules of the ValueMatcher, false otherwise.
|
||||
*/
|
||||
public boolean match(int value);
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/cron4j-2.2.5.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
</listAttribute>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/cron4j-2.2.5.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/c3p0-0.9.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/ecj-4.4.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/exp4j-0.4.8.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mail-1.5.2.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mchange-commons-java-0.2.12.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/mysql-connector-java-5.1.43.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;archive detectRoot=&quot;true&quot; path=&quot;/L2J_Mobius_Underground/dist/libs/netty-all-4.1.14.Final.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.archive"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;default/&gt;&#13;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
</listAttribute>
|
||||
|
Loading…
Reference in New Issue
Block a user