+ * 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.
+ *
+ * @author Carlo Pelliccia
+ */
+ private static class IntArrayValueMatcher implements ValueMatcher
+ {
+ /**
+ * The accepted values.
+ */
+ private final int[] _values;
+
+ /**
+ * Builds the ValueMatcher.
+ * @param integers a List 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(List integers)
+ {
+ final int size = integers.size();
+ _values = new int[size];
+ for (int i = 0; i < size; i++)
+ {
+ try
+ {
+ _values[i] = integers.get(i).intValue();
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Returns true if the given value is included in the matcher list.
+ */
+ @Override
+ public boolean match(int value)
+ {
+ for (int i = 0; i < _values.length; i++)
+ {
+ if (_values[i] == value)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * This ValueMatcher always returns true!
+ * @author Carlo Pelliccia
+ */
+ private static class AlwaysTrueValueMatcher implements ValueMatcher
+ {
+ /**
+ * Always true!
+ */
+ @Override
+ public boolean match(int value)
+ {
+ return true;
+ }
+ }
+
+ /**
+ *
+ * This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
+ *
+ * @author Carlo Pelliccia
+ */
+ private static 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);
+ }
+
+ /**
+ * The value parser for the day of week field.
+ */
+ private static class DayOfWeekValueParser extends SimpleValueParser
+ {
+ /**
+ * Days of week aliases.
+ */
+ private static String[] ALIASES =
+ {
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat"
+ };
+
+ /**
+ * Builds the day value parser.
+ */
+ public DayOfWeekValueParser()
+ {
+ super(DAY_OF_WEEK_MIN_VALUE, DAY_OF_WEEK_MAX_VALUE);
+ }
+
+ @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);
+ }
+ }
+ }
+
+ /**
+ * The value parser for the months field.
+ */
+ private static class MonthValueParser extends SimpleValueParser
+ {
+ /**
+ * Months of year aliases.
+ */
+ private static String[] ALIASES = new String[]
+ {
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec"
+ };
+
+ /**
+ * Builds the months value parser.
+ */
+ public MonthValueParser()
+ {
+ super(MONTH_MIN_VALUE, MONTH_MAX_VALUE);
+ }
+
+ @Override
+ public int parse(String value) throws Exception
+ {
+ try
+ {
+ return super.parse(value);
+ }
+ catch (Exception e)
+ {
+ return parseAlias(value, ALIASES, 1);
+ }
+ }
+ }
+
+ /**
+ * The value parser for the day of month field.
+ */
+ private static class DayOfMonthValueParser extends SimpleValueParser
+ {
+ /**
+ * Builds the value parser.
+ */
+ public DayOfMonthValueParser()
+ {
+ super(DAY_OF_MONTH_MIN_VALUE, DAY_OF_MONTH_MAX_VALUE);
+ }
+
+ @Override
+ public int parse(String value) throws Exception
+ {
+ if (value.equalsIgnoreCase("L"))
+ {
+ return 32;
+ }
+ return super.parse(value);
+ }
+ }
+
+ /**
+ * The value parser for the hour field.
+ */
+ private static class HourValueParser extends SimpleValueParser
+ {
+ /**
+ * Builds the value parser.
+ */
+ public HourValueParser()
+ {
+ super(HOUR_MIN_VALUE, HOUR_MAX_VALUE);
+ }
+ }
+
+ /**
+ * The minutes value parser.
+ */
+ private static class MinuteValueParser extends SimpleValueParser
+ {
+ /**
+ * Builds the value parser.
+ */
+ public MinuteValueParser()
+ {
+ super(MINUTE_MIN_VALUE, MINUTE_MAX_VALUE);
+ }
+ }
+
+ /**
+ * 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)
+ {
+ _minValue = minValue;
+ _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;
+ }
+ }
+
+ /**
+ * 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 accepted by the parser.
+ * @return The minimum value accepted by the parser.
+ */
+ public int getMinValue();
+
+ /**
+ * Returns the maximum value accepted by the parser.
+ * @return The maximum value accepted by the parser.
+ */
+ public int getMaxValue();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/time/AddPattern.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/time/AddPattern.java
deleted file mode 100644
index 893a279fd4..0000000000
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/time/AddPattern.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-public class AddPattern implements NextTime
-{
- private int _monthInc = -1;
- private int _monthSet = -1;
- private int _dayOfMonthInc = -1;
- private int _dayOfMonthSet = -1;
- private int _hourOfDayInc = -1;
- private int _hourOfDaySet = -1;
- private int _minuteInc = -1;
- private int _minuteSet = -1;
-
- public AddPattern(String pattern)
- {
- String[] timeparts;
- String[] parts = pattern.split("\\s+");
- if (parts.length == 2)
- {
- String datemodstr;
- String datepartsstr = parts[0];
- String[] dateparts = datepartsstr.split(":");
- if (dateparts.length == 2)
- {
- if (dateparts[0].startsWith("+"))
- {
- _monthInc = Integer.parseInt(dateparts[0].substring(1));
- }
- else
- {
- _monthSet = Integer.parseInt(dateparts[0]) - 1;
- }
- }
- if ((datemodstr = dateparts[dateparts.length - 1]).startsWith("+"))
- {
- _dayOfMonthInc = Integer.parseInt(datemodstr.substring(1));
- }
- else
- {
- _dayOfMonthSet = Integer.parseInt(datemodstr);
- }
- }
- if ((timeparts = parts[parts.length - 1].split(":"))[0].startsWith("+"))
- {
- _hourOfDayInc = Integer.parseInt(timeparts[0].substring(1));
- }
- else
- {
- _hourOfDaySet = Integer.parseInt(timeparts[0]);
- }
- if (timeparts[1].startsWith("+"))
- {
- _minuteInc = Integer.parseInt(timeparts[1].substring(1));
- }
- else
- {
- _minuteSet = Integer.parseInt(timeparts[1]);
- }
- }
-
- @Override
- public long next(long millis)
- {
- final GregorianCalendar gc = new GregorianCalendar(TimeZone.getDefault());
- gc.setTimeInMillis(millis);
- if (_monthInc >= 0)
- {
- gc.add(2, _monthInc);
- }
- if (_monthSet >= 0)
- {
- gc.set(2, _monthSet);
- }
- if (_dayOfMonthInc >= 0)
- {
- gc.add(5, _dayOfMonthInc);
- }
- if (_dayOfMonthSet >= 0)
- {
- gc.set(5, _dayOfMonthSet);
- }
- if (_hourOfDayInc >= 0)
- {
- gc.add(11, _hourOfDayInc);
- }
- if (_hourOfDaySet >= 0)
- {
- gc.set(11, _hourOfDaySet);
- }
- if (_minuteInc >= 0)
- {
- gc.add(12, _minuteInc);
- }
- if (_minuteSet >= 0)
- {
- gc.set(12, _minuteSet);
- }
- return gc.getTimeInMillis();
- }
-}
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/time/NextTime.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/time/NextTime.java
deleted file mode 100644
index d08d90164d..0000000000
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/time/NextTime.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-public interface NextTime
-{
- public long next(long var1);
-}
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/time/SchedulingPattern.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/time/SchedulingPattern.java
index d9fdd655f3..a9677e31ae 100644
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/time/SchedulingPattern.java
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/time/SchedulingPattern.java
@@ -1,23 +1,25 @@
/*
- * This file is part of the L2J Mobius project.
+ * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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.
- *
+ * it under the terms of the GNU Lesser General Public License version
+ * 2.1, as published by the Free Software Foundation.
+ *
* 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 .
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License 2.1 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License version 2.1 along with this program.
+ * If not, see .
*/
package org.l2jmobius.commons.time;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
@@ -26,7 +28,94 @@ import java.util.TreeMap;
import org.l2jmobius.commons.util.Rnd;
-public class SchedulingPattern implements NextTime
+/**
+ *
+ * A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
+ *
+ *
+ * - Minutes sub-pattern. During which minutes of the hour should the task been launched? The values range is from 0 to 59.
+ * - Hours sub-pattern. During which hours of the day should the task been launched? The values range is from 0 to 23.
+ * - Days of month sub-pattern. 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.
+ * - Months sub-pattern. 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".
+ * - Days of week sub-pattern. 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".
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * Some examples:
+ *
+ *
+ * 5 * * * *
+ * 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.).
+ *
+ *
+ * * * * * *
+ * This pattern causes a task to be launched every minute.
+ *
+ *
+ * * 12 * * Mon
+ * This pattern causes a task to be launched every minute during the 12th hour of Monday.
+ *
+ *
+ * * 12 16 * Mon
+ * 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.
+ *
+ *
+ * Every sub-pattern can contain two or more comma separated values.
+ *
+ *
+ * 59 11 * * 1,2,3,4,5
+ * This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
+ *
+ *
+ * Values intervals are admitted and defined using the minus character.
+ *
+ *
+ * 59 11 * * 1-5
+ * This pattern is equivalent to the previous one.
+ *
+ *
+ * The slash character can be used to identify step values within a range. It can be used both in the form */c and a-b/c. The subpattern is matched every c values of the range 0,maxvalue or a-b.
+ *
+ *
+ * */5 * * * *
+ * This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
+ *
+ *
+ * 3-18/5 * * * *
+ * 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).
+ *
+ *
+ * */15 9-17 * * *
+ * 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).
+ *
+ *
+ * All the fresh described syntax rules can be used together.
+ *
+ *
+ * * 12 10-16/2 * *
+ * 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.
+ *
+ *
+ * * 12 1-15,17,20-25 * *
+ * 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.
+ *
+ *
+ * Finally lets you combine more scheduling patterns into one, with the pipe character:
+ *
+ *
+ * 0 5 * * *|8 10 * * *|22 17 * * *
+ * This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
+ *
+ * @author Carlo Pelliccia
+ */
+public class SchedulingPattern
{
private static final int MINUTE_MIN_VALUE = 0;
private static final int MINUTE_MAX_VALUE = 59;
@@ -38,24 +127,78 @@ public class SchedulingPattern implements NextTime
private static final int MONTH_MAX_VALUE = 12;
private static final int DAY_OF_WEEK_MIN_VALUE = 0;
private static final int DAY_OF_WEEK_MAX_VALUE = 7;
+
+ /**
+ * 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();
+
+ /**
+ * The pattern as a string.
+ */
private final String _asString;
+
+ /**
+ * The ValueMatcher list for the "minute" field.
+ */
protected List _minuteMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "hour" field.
+ */
protected List _hourMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of month" field.
+ */
protected List _dayOfMonthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "month" field.
+ */
protected List _monthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of week" field.
+ */
protected List _dayOfWeekMatchers = new ArrayList<>();
+
+ /**
+ * How many matcher groups in this pattern?
+ */
protected int _matcherSize = 0;
+
protected Map _hourAdder = new TreeMap<>();
protected Map _hourAdderRnd = new TreeMap<>();
protected Map _dayOfYearAdder = new TreeMap<>();
protected Map _minuteAdderRnd = new TreeMap<>();
protected Map _weekOfYearAdder = new TreeMap<>();
+ /**
+ * 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
@@ -69,10 +212,15 @@ public class SchedulingPattern implements NextTime
return true;
}
+ /**
+ * Builds a SchedulingPattern parsing it from a string.
+ * @param pattern The pattern as a crontab-like string.
+ * @throws RuntimeException If the supplied string is not a valid pattern.
+ */
public SchedulingPattern(String pattern) throws RuntimeException
{
_asString = pattern;
- StringTokenizer st1 = new StringTokenizer(pattern, "|");
+ final StringTokenizer st1 = new StringTokenizer(pattern, "|");
if (st1.countTokens() < 1)
{
throw new RuntimeException("invalid pattern: \"" + pattern + "\"");
@@ -80,39 +228,16 @@ public class SchedulingPattern implements NextTime
while (st1.hasMoreTokens())
{
- int i;
- String localPattern = st1.nextToken();
- StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
- int tokCnt = st2.countTokens();
- if ((tokCnt < 5) || (tokCnt > 6))
+ final String localPattern = st1.nextToken();
+ final StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
+ if (st2.countTokens() != 5)
{
throw new RuntimeException("invalid pattern: \"" + localPattern + "\"");
}
try
{
- String minutePattern = st2.nextToken();
- String[] minutePatternParts = minutePattern.split(":");
- if (minutePatternParts.length > 1)
- {
- for (i = 0; i < (minutePatternParts.length - 1); ++i)
- {
- if (minutePatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (minutePatternParts[i].startsWith("~"))
- {
- _minuteAdderRnd.put(_matcherSize, Integer.parseInt(minutePatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + minutePatternParts[i] + "\"");
- }
- minutePattern = minutePatternParts[minutePatternParts.length - 1];
- }
- _minuteMatchers.add(buildValueMatcher(minutePattern, MINUTE_VALUE_PARSER));
+ _minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
}
catch (Exception e)
{
@@ -121,35 +246,9 @@ public class SchedulingPattern implements NextTime
try
{
- String hourPattern = st2.nextToken();
- String[] hourPatternParts = hourPattern.split(":");
- if (hourPatternParts.length > 1)
- {
- for (i = 0; i < (hourPatternParts.length - 1); ++i)
- {
- if (hourPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (hourPatternParts[i].startsWith("+"))
- {
- _hourAdder.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- if (hourPatternParts[i].startsWith("~"))
- {
- _hourAdderRnd.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + hourPatternParts[i] + "\"");
- }
- hourPattern = hourPatternParts[hourPatternParts.length - 1];
- }
- _hourMatchers.add(buildValueMatcher(hourPattern, HOUR_VALUE_PARSER));
+ _hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
}
+
catch (Exception e)
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
@@ -157,28 +256,7 @@ public class SchedulingPattern implements NextTime
try
{
- String dayOfMonthPattern = st2.nextToken();
- String[] dayOfMonthPatternParts = dayOfMonthPattern.split(":");
- if (dayOfMonthPatternParts.length > 1)
- {
- for (i = 0; i < (dayOfMonthPatternParts.length - 1); ++i)
- {
- if (dayOfMonthPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (dayOfMonthPatternParts[i].startsWith("+"))
- {
- _dayOfYearAdder.put(_matcherSize, Integer.parseInt(dayOfMonthPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown day modifier \"" + dayOfMonthPatternParts[i] + "\"");
- }
- dayOfMonthPattern = dayOfMonthPatternParts[dayOfMonthPatternParts.length - 1];
- }
- _dayOfMonthMatchers.add(buildValueMatcher(dayOfMonthPattern, DAY_OF_MONTH_VALUE_PARSER));
+ _dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
}
catch (Exception e)
{
@@ -202,28 +280,17 @@ public class SchedulingPattern implements NextTime
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
}
-
- if (st2.hasMoreTokens())
- {
- try
- {
- String weekOfYearAdderText = st2.nextToken();
- if (weekOfYearAdderText.charAt(0) != '+')
- {
- throw new RuntimeException("Unknown week of year addition in pattern \"" + localPattern + "\".");
- }
- weekOfYearAdderText = weekOfYearAdderText.substring(1);
- _weekOfYearAdder.put(_matcherSize, Integer.parseInt(weekOfYearAdderText));
- }
- catch (Exception e)
- {
- throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
- }
- }
- ++_matcherSize;
+ _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("*"))
@@ -231,12 +298,12 @@ public class SchedulingPattern implements NextTime
return new AlwaysTrueValueMatcher();
}
- ArrayList values = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(str, ",");
+ final List values = new ArrayList<>();
+ final StringTokenizer st = new StringTokenizer(str, ",");
while (st.hasMoreTokens())
{
+ final String element = st.nextToken();
List local;
- String element = st.nextToken();
try
{
local = parseListElement(element, parser);
@@ -245,15 +312,13 @@ public class SchedulingPattern implements NextTime
{
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
}
-
- for (Integer value : local)
+ for (Iterator i = local.iterator(); i.hasNext();)
{
- if (values.contains(value))
+ final Object value = i.next();
+ if (!values.contains(value))
{
- continue;
+ values.add((Integer) value);
}
-
- values.add(value);
}
}
@@ -270,16 +335,23 @@ public class SchedulingPattern implements NextTime
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 List parseListElement(String str, ValueParser parser) throws Exception
{
- List values;
- StringTokenizer st = new StringTokenizer(str, "/");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "/");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
+ List values;
try
{
values = parseRange(st.nextToken(), parser);
@@ -291,9 +363,8 @@ public class SchedulingPattern implements NextTime
if (size == 2)
{
+ final String dStr = st.nextToken();
int div;
- String dStr = st.nextToken();
-
try
{
div = Integer.parseInt(dStr);
@@ -308,41 +379,47 @@ public class SchedulingPattern implements NextTime
throw new Exception("non positive divisor \"" + div + "\"");
}
- ArrayList values2 = new ArrayList<>();
+ final List 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 List parseRange(String str, ValueParser parser) throws Exception
{
- int v2;
- int v1;
if (str.equals("*"))
{
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- ArrayList values = new ArrayList<>();
- for (int i = min; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ final List values = new ArrayList<>();
+ for (int i = min; i <= max; i++)
{
values.add(i);
}
return values;
}
- StringTokenizer st = new StringTokenizer(str, "-");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "-");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
- String v1Str = st.nextToken();
+ final String v1Str = st.nextToken();
+ int v1;
try
{
v1 = parser.parse(v1Str);
@@ -354,12 +431,13 @@ public class SchedulingPattern implements NextTime
if (size == 1)
{
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
values.add(v1);
return values;
}
- String v2Str = st.nextToken();
+ final String v2Str = st.nextToken();
+ int v2;
try
{
v2 = parser.parse(v2Str);
@@ -369,91 +447,90 @@ public class SchedulingPattern implements NextTime
throw new Exception("invalid value \"" + v2Str + "\", " + e.getMessage());
}
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
if (v1 < v2)
{
- for (int i = v1; i <= v2; ++i)
+ for (int i = v1; i <= v2; i++)
{
values.add(i);
}
}
else if (v1 > v2)
{
- int i;
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- for (i = v1; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ for (int i = v1; i <= max; i++)
{
values.add(i);
}
- for (i = min; i <= v2; ++i)
+ for (int i = min; i <= v2; i++)
{
values.add(i);
}
}
else
{
+ // v1 == v2
values.add(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(timezone);
+ final GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(millis);
- gc.set(13, 0);
- gc.set(14, 0);
- for (int i = 0; i < _matcherSize; ++i)
+ gc.setTimeZone(timezone);
+ final int minute = gc.get(Calendar.MINUTE);
+ final int hour = gc.get(Calendar.HOUR_OF_DAY);
+ final int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH);
+ final int month = gc.get(Calendar.MONTH) + 1;
+ final int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1;
+ final int year = gc.get(Calendar.YEAR);
+ for (int i = 0; i < _matcherSize; i++)
{
- boolean eval = false;
- if (_weekOfYearAdder.containsKey(i))
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+ if (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))
{
- gc.add(3, -_weekOfYearAdder.get(i).intValue());
+ return true;
}
- if (_dayOfYearAdder.containsKey(i))
- {
- gc.add(6, -_dayOfYearAdder.get(i).intValue());
- }
- if (_hourAdder.containsKey(i))
- {
- gc.add(10, -_hourAdder.get(i).intValue());
- }
- int minute = gc.get(MONTH_MAX_VALUE);
- int hour = gc.get(11);
- int dayOfMonth = gc.get(5);
- int month = gc.get(2) + 1;
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
- int year = gc.get(1);
- 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);
- @SuppressWarnings("unused")
- boolean bl = 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) ? true : (eval = false);
- if (!eval)
- {
- continue;
- }
- 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);
}
+ /**
+ * It returns the next matching moment as a millis value.
+ * @param timezone
+ * @param millis
+ * @return The next matching moment as a millis value.
+ */
public long next(TimeZone timezone, long millis)
{
- long result = -1L;
- GregorianCalendar gc = new GregorianCalendar(timezone);
+ long result = -1;
+ final GregorianCalendar gc = new GregorianCalendar(timezone);
for (int i = 0; i < _matcherSize; ++i)
{
- long next = -1L;
+ long next = -1;
gc.setTimeInMillis(millis);
gc.set(13, 0);
gc.set(14, 0);
@@ -469,27 +546,28 @@ public class SchedulingPattern implements NextTime
{
gc.add(10, _hourAdder.get(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);
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+
SEARCH: do
{
- int year = gc.get(1);
- boolean isLeapYear = gc.isLeapYear(year);
+ final int year = gc.get(1);
+ final boolean isLeapYear = gc.isLeapYear(year);
for (int month = gc.get(2) + 1; month <= MONTH_MAX_VALUE; ++month)
{
if (monthMatcher.match(month))
{
gc.set(2, month - 1);
- int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ final int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
for (int dayOfMonth = gc.get(5); dayOfMonth <= maxDayOfMonth; ++dayOfMonth)
{
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher ? ((DayOfMonthValueMatcher) dayOfMonthMatcher).match(dayOfMonth, month, isLeapYear) : dayOfMonthMatcher.match(dayOfMonth))
{
gc.set(5, dayOfMonth);
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
+ final int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
if (dayOfWeekMatcher.match(dayOfWeek))
{
for (int hour = gc.get(11); hour <= HOUR_MAX_VALUE; ++hour)
@@ -505,13 +583,13 @@ public class SchedulingPattern implements NextTime
}
gc.set(MONTH_MAX_VALUE, minute);
- long next0 = gc.getTimeInMillis();
+ final long next0 = gc.getTimeInMillis();
if (next0 <= millis)
{
continue;
}
- if ((next != -1L) && (next0 >= next))
+ if ((next != -1) && (next0 >= next))
{
break SEARCH;
}
@@ -519,7 +597,7 @@ public class SchedulingPattern implements NextTime
next = next0;
if (_hourAdderRnd.containsKey(i))
{
- next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000L;
+ next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000;
}
if (!_minuteAdderRnd.containsKey(i))
@@ -527,7 +605,7 @@ public class SchedulingPattern implements NextTime
break SEARCH;
}
- next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000L;
+ next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000;
break SEARCH;
}
}
@@ -550,7 +628,7 @@ public class SchedulingPattern implements NextTime
}
while (true);
- if ((next <= millis) || ((result != -1L) && (next >= result)))
+ if ((next <= millis) || ((result != -1) && (next >= result)))
{
continue;
}
@@ -560,57 +638,89 @@ public class SchedulingPattern implements NextTime
return result;
}
- @Override
+ /**
+ * It returns the next matching moment as a long.
+ * @param millis
+ * @return The next matching moment as a long.
+ */
public long next(long millis)
{
return next(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.
+ */
private static int parseAlias(String value, String[] aliases, int offset) throws Exception
{
- for (int i = 0; i < aliases.length; ++i)
+ for (int i = 0; i < aliases.length; i++)
{
- if (!aliases[i].equalsIgnoreCase(value))
+ if (aliases[i].equalsIgnoreCase(value))
{
- continue;
+ return offset + i;
}
- return offset + i;
}
throw new Exception("invalid alias \"" + value + "\"");
}
+ /**
+ *
+ * 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.
+ *
+ * @author Paul Fernley
+ */
private static class DayOfMonthValueMatcher extends IntArrayValueMatcher
{
- private static final int[] lastDays = new int[]
+ private static final int[] LAST_DAYS =
{
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
28,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE
+ 31
};
+ /**
+ * 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 or the last-day-of-month setting applies.
+ */
public DayOfMonthValueMatcher(List integers)
{
super(integers);
}
+ /**
+ * 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) && DayOfMonthValueMatcher.isLastDayOfMonth(value, month, isLeapYear));
+ return (super.match(value) || ((value > 27) && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
}
public static int getLastDayOfMonth(int month, boolean isLeapYear)
@@ -620,29 +730,41 @@ public class SchedulingPattern implements NextTime
return 29;
}
- return lastDays[month - 1];
+ return LAST_DAYS[month - 1];
}
public static boolean isLastDayOfMonth(int value, int month, boolean isLeapYear)
{
- return value == DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ return value == getLastDayOfMonth(month, isLeapYear);
}
}
+ /**
+ *
+ * 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.
+ *
+ * @author Carlo Pelliccia
+ */
private static class IntArrayValueMatcher implements ValueMatcher
{
- private final int[] values;
+ /**
+ * The accepted values.
+ */
+ private final int[] _values;
+ /**
+ * Builds the ValueMatcher.
+ * @param integers a List 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(List integers)
{
- int size = integers.size();
- values = new int[size];
- for (int i = 0; i < size; ++i)
+ final int size = integers.size();
+ _values = new int[size];
+ for (int i = 0; i < size; i++)
{
try
{
- values[i] = integers.get(i);
- continue;
+ _values[i] = integers.get(i).intValue();
}
catch (Exception e)
{
@@ -651,27 +773,32 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Returns true if the given value is included in the matcher list.
+ */
@Override
public boolean match(int value)
{
- for (int i = 0; i < values.length; ++i)
+ for (int i = 0; i < _values.length; i++)
{
- if (values[i] != value)
+ if (_values[i] == value)
{
- continue;
+ return true;
}
- return true;
}
return false;
}
}
+ /**
+ * This ValueMatcher always returns true!
+ * @author Carlo Pelliccia
+ */
private static class AlwaysTrueValueMatcher implements ValueMatcher
{
- private AlwaysTrueValueMatcher()
- {
- }
-
+ /**
+ * Always true!
+ */
@Override
public boolean match(int value)
{
@@ -679,14 +806,31 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ *
+ * This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
+ *
+ * @author Carlo Pelliccia
+ */
private static interface ValueMatcher
{
- public boolean match(int var1);
+ /**
+ * 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);
}
+ /**
+ * The value parser for the day of week field.
+ */
private static class DayOfWeekValueParser extends SimpleValueParser
{
- private static String[] ALIASES = new String[]
+ /**
+ * Days of week aliases.
+ */
+ private static String[] ALIASES =
{
"sun",
"mon",
@@ -697,6 +841,9 @@ public class SchedulingPattern implements NextTime
"sat"
};
+ /**
+ * Builds the day value parser.
+ */
public DayOfWeekValueParser()
{
super(DAY_OF_WEEK_MIN_VALUE, DAY_OF_WEEK_MAX_VALUE);
@@ -707,17 +854,25 @@ public class SchedulingPattern implements NextTime
{
try
{
- return super.parse(value) % DAY_OF_WEEK_MAX_VALUE;
+ // try as a simple value
+ return super.parse(value) % 7;
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 0);
+ // try as an alias
+ return parseAlias(value, ALIASES, 0);
}
}
}
+ /**
+ * The value parser for the months field.
+ */
private static class MonthValueParser extends SimpleValueParser
{
+ /**
+ * Months of year aliases.
+ */
private static String[] ALIASES = new String[]
{
"jan",
@@ -734,6 +889,9 @@ public class SchedulingPattern implements NextTime
"dec"
};
+ /**
+ * Builds the months value parser.
+ */
public MonthValueParser()
{
super(MONTH_MIN_VALUE, MONTH_MAX_VALUE);
@@ -748,13 +906,19 @@ public class SchedulingPattern implements NextTime
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 1);
+ return parseAlias(value, ALIASES, 1);
}
}
}
+ /**
+ * The value parser for the day of month field.
+ */
private static class DayOfMonthValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public DayOfMonthValueParser()
{
super(DAY_OF_MONTH_MIN_VALUE, DAY_OF_MONTH_MAX_VALUE);
@@ -771,27 +935,54 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * The value parser for the hour field.
+ */
private static class HourValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public HourValueParser()
{
super(HOUR_MIN_VALUE, HOUR_MAX_VALUE);
}
}
+ /**
+ * The minutes value parser.
+ */
private static class MinuteValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public MinuteValueParser()
{
super(MINUTE_MIN_VALUE, MINUTE_MAX_VALUE);
}
}
+ /**
+ * 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)
{
_minValue = minValue;
@@ -830,12 +1021,29 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Definition for a value parser.
+ */
private static interface ValueParser
{
- public int parse(String var1) throws Exception;
+ /**
+ * 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 accepted by the parser.
+ * @return The minimum value accepted by the parser.
+ */
public int getMinValue();
+ /**
+ * Returns the maximum value accepted by the parser.
+ * @return The maximum value accepted by the parser.
+ */
public int getMaxValue();
}
}
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/time/AddPattern.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/time/AddPattern.java
deleted file mode 100644
index 893a279fd4..0000000000
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/time/AddPattern.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-public class AddPattern implements NextTime
-{
- private int _monthInc = -1;
- private int _monthSet = -1;
- private int _dayOfMonthInc = -1;
- private int _dayOfMonthSet = -1;
- private int _hourOfDayInc = -1;
- private int _hourOfDaySet = -1;
- private int _minuteInc = -1;
- private int _minuteSet = -1;
-
- public AddPattern(String pattern)
- {
- String[] timeparts;
- String[] parts = pattern.split("\\s+");
- if (parts.length == 2)
- {
- String datemodstr;
- String datepartsstr = parts[0];
- String[] dateparts = datepartsstr.split(":");
- if (dateparts.length == 2)
- {
- if (dateparts[0].startsWith("+"))
- {
- _monthInc = Integer.parseInt(dateparts[0].substring(1));
- }
- else
- {
- _monthSet = Integer.parseInt(dateparts[0]) - 1;
- }
- }
- if ((datemodstr = dateparts[dateparts.length - 1]).startsWith("+"))
- {
- _dayOfMonthInc = Integer.parseInt(datemodstr.substring(1));
- }
- else
- {
- _dayOfMonthSet = Integer.parseInt(datemodstr);
- }
- }
- if ((timeparts = parts[parts.length - 1].split(":"))[0].startsWith("+"))
- {
- _hourOfDayInc = Integer.parseInt(timeparts[0].substring(1));
- }
- else
- {
- _hourOfDaySet = Integer.parseInt(timeparts[0]);
- }
- if (timeparts[1].startsWith("+"))
- {
- _minuteInc = Integer.parseInt(timeparts[1].substring(1));
- }
- else
- {
- _minuteSet = Integer.parseInt(timeparts[1]);
- }
- }
-
- @Override
- public long next(long millis)
- {
- final GregorianCalendar gc = new GregorianCalendar(TimeZone.getDefault());
- gc.setTimeInMillis(millis);
- if (_monthInc >= 0)
- {
- gc.add(2, _monthInc);
- }
- if (_monthSet >= 0)
- {
- gc.set(2, _monthSet);
- }
- if (_dayOfMonthInc >= 0)
- {
- gc.add(5, _dayOfMonthInc);
- }
- if (_dayOfMonthSet >= 0)
- {
- gc.set(5, _dayOfMonthSet);
- }
- if (_hourOfDayInc >= 0)
- {
- gc.add(11, _hourOfDayInc);
- }
- if (_hourOfDaySet >= 0)
- {
- gc.set(11, _hourOfDaySet);
- }
- if (_minuteInc >= 0)
- {
- gc.add(12, _minuteInc);
- }
- if (_minuteSet >= 0)
- {
- gc.set(12, _minuteSet);
- }
- return gc.getTimeInMillis();
- }
-}
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/time/NextTime.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/time/NextTime.java
deleted file mode 100644
index d08d90164d..0000000000
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/time/NextTime.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-public interface NextTime
-{
- public long next(long var1);
-}
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/time/SchedulingPattern.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/time/SchedulingPattern.java
index d9fdd655f3..a9677e31ae 100644
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/time/SchedulingPattern.java
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/time/SchedulingPattern.java
@@ -1,23 +1,25 @@
/*
- * This file is part of the L2J Mobius project.
+ * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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.
- *
+ * it under the terms of the GNU Lesser General Public License version
+ * 2.1, as published by the Free Software Foundation.
+ *
* 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 .
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License 2.1 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License version 2.1 along with this program.
+ * If not, see .
*/
package org.l2jmobius.commons.time;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
@@ -26,7 +28,94 @@ import java.util.TreeMap;
import org.l2jmobius.commons.util.Rnd;
-public class SchedulingPattern implements NextTime
+/**
+ *
+ * A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
+ *
+ *
+ * - Minutes sub-pattern. During which minutes of the hour should the task been launched? The values range is from 0 to 59.
+ * - Hours sub-pattern. During which hours of the day should the task been launched? The values range is from 0 to 23.
+ * - Days of month sub-pattern. 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.
+ * - Months sub-pattern. 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".
+ * - Days of week sub-pattern. 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".
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * Some examples:
+ *
+ *
+ * 5 * * * *
+ * 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.).
+ *
+ *
+ * * * * * *
+ * This pattern causes a task to be launched every minute.
+ *
+ *
+ * * 12 * * Mon
+ * This pattern causes a task to be launched every minute during the 12th hour of Monday.
+ *
+ *
+ * * 12 16 * Mon
+ * 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.
+ *
+ *
+ * Every sub-pattern can contain two or more comma separated values.
+ *
+ *
+ * 59 11 * * 1,2,3,4,5
+ * This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
+ *
+ *
+ * Values intervals are admitted and defined using the minus character.
+ *
+ *
+ * 59 11 * * 1-5
+ * This pattern is equivalent to the previous one.
+ *
+ *
+ * The slash character can be used to identify step values within a range. It can be used both in the form */c and a-b/c. The subpattern is matched every c values of the range 0,maxvalue or a-b.
+ *
+ *
+ * */5 * * * *
+ * This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
+ *
+ *
+ * 3-18/5 * * * *
+ * 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).
+ *
+ *
+ * */15 9-17 * * *
+ * 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).
+ *
+ *
+ * All the fresh described syntax rules can be used together.
+ *
+ *
+ * * 12 10-16/2 * *
+ * 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.
+ *
+ *
+ * * 12 1-15,17,20-25 * *
+ * 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.
+ *
+ *
+ * Finally lets you combine more scheduling patterns into one, with the pipe character:
+ *
+ *
+ * 0 5 * * *|8 10 * * *|22 17 * * *
+ * This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
+ *
+ * @author Carlo Pelliccia
+ */
+public class SchedulingPattern
{
private static final int MINUTE_MIN_VALUE = 0;
private static final int MINUTE_MAX_VALUE = 59;
@@ -38,24 +127,78 @@ public class SchedulingPattern implements NextTime
private static final int MONTH_MAX_VALUE = 12;
private static final int DAY_OF_WEEK_MIN_VALUE = 0;
private static final int DAY_OF_WEEK_MAX_VALUE = 7;
+
+ /**
+ * 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();
+
+ /**
+ * The pattern as a string.
+ */
private final String _asString;
+
+ /**
+ * The ValueMatcher list for the "minute" field.
+ */
protected List _minuteMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "hour" field.
+ */
protected List _hourMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of month" field.
+ */
protected List _dayOfMonthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "month" field.
+ */
protected List _monthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of week" field.
+ */
protected List _dayOfWeekMatchers = new ArrayList<>();
+
+ /**
+ * How many matcher groups in this pattern?
+ */
protected int _matcherSize = 0;
+
protected Map _hourAdder = new TreeMap<>();
protected Map _hourAdderRnd = new TreeMap<>();
protected Map _dayOfYearAdder = new TreeMap<>();
protected Map _minuteAdderRnd = new TreeMap<>();
protected Map _weekOfYearAdder = new TreeMap<>();
+ /**
+ * 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
@@ -69,10 +212,15 @@ public class SchedulingPattern implements NextTime
return true;
}
+ /**
+ * Builds a SchedulingPattern parsing it from a string.
+ * @param pattern The pattern as a crontab-like string.
+ * @throws RuntimeException If the supplied string is not a valid pattern.
+ */
public SchedulingPattern(String pattern) throws RuntimeException
{
_asString = pattern;
- StringTokenizer st1 = new StringTokenizer(pattern, "|");
+ final StringTokenizer st1 = new StringTokenizer(pattern, "|");
if (st1.countTokens() < 1)
{
throw new RuntimeException("invalid pattern: \"" + pattern + "\"");
@@ -80,39 +228,16 @@ public class SchedulingPattern implements NextTime
while (st1.hasMoreTokens())
{
- int i;
- String localPattern = st1.nextToken();
- StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
- int tokCnt = st2.countTokens();
- if ((tokCnt < 5) || (tokCnt > 6))
+ final String localPattern = st1.nextToken();
+ final StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
+ if (st2.countTokens() != 5)
{
throw new RuntimeException("invalid pattern: \"" + localPattern + "\"");
}
try
{
- String minutePattern = st2.nextToken();
- String[] minutePatternParts = minutePattern.split(":");
- if (minutePatternParts.length > 1)
- {
- for (i = 0; i < (minutePatternParts.length - 1); ++i)
- {
- if (minutePatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (minutePatternParts[i].startsWith("~"))
- {
- _minuteAdderRnd.put(_matcherSize, Integer.parseInt(minutePatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + minutePatternParts[i] + "\"");
- }
- minutePattern = minutePatternParts[minutePatternParts.length - 1];
- }
- _minuteMatchers.add(buildValueMatcher(minutePattern, MINUTE_VALUE_PARSER));
+ _minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
}
catch (Exception e)
{
@@ -121,35 +246,9 @@ public class SchedulingPattern implements NextTime
try
{
- String hourPattern = st2.nextToken();
- String[] hourPatternParts = hourPattern.split(":");
- if (hourPatternParts.length > 1)
- {
- for (i = 0; i < (hourPatternParts.length - 1); ++i)
- {
- if (hourPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (hourPatternParts[i].startsWith("+"))
- {
- _hourAdder.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- if (hourPatternParts[i].startsWith("~"))
- {
- _hourAdderRnd.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + hourPatternParts[i] + "\"");
- }
- hourPattern = hourPatternParts[hourPatternParts.length - 1];
- }
- _hourMatchers.add(buildValueMatcher(hourPattern, HOUR_VALUE_PARSER));
+ _hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
}
+
catch (Exception e)
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
@@ -157,28 +256,7 @@ public class SchedulingPattern implements NextTime
try
{
- String dayOfMonthPattern = st2.nextToken();
- String[] dayOfMonthPatternParts = dayOfMonthPattern.split(":");
- if (dayOfMonthPatternParts.length > 1)
- {
- for (i = 0; i < (dayOfMonthPatternParts.length - 1); ++i)
- {
- if (dayOfMonthPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (dayOfMonthPatternParts[i].startsWith("+"))
- {
- _dayOfYearAdder.put(_matcherSize, Integer.parseInt(dayOfMonthPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown day modifier \"" + dayOfMonthPatternParts[i] + "\"");
- }
- dayOfMonthPattern = dayOfMonthPatternParts[dayOfMonthPatternParts.length - 1];
- }
- _dayOfMonthMatchers.add(buildValueMatcher(dayOfMonthPattern, DAY_OF_MONTH_VALUE_PARSER));
+ _dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
}
catch (Exception e)
{
@@ -202,28 +280,17 @@ public class SchedulingPattern implements NextTime
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
}
-
- if (st2.hasMoreTokens())
- {
- try
- {
- String weekOfYearAdderText = st2.nextToken();
- if (weekOfYearAdderText.charAt(0) != '+')
- {
- throw new RuntimeException("Unknown week of year addition in pattern \"" + localPattern + "\".");
- }
- weekOfYearAdderText = weekOfYearAdderText.substring(1);
- _weekOfYearAdder.put(_matcherSize, Integer.parseInt(weekOfYearAdderText));
- }
- catch (Exception e)
- {
- throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
- }
- }
- ++_matcherSize;
+ _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("*"))
@@ -231,12 +298,12 @@ public class SchedulingPattern implements NextTime
return new AlwaysTrueValueMatcher();
}
- ArrayList values = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(str, ",");
+ final List values = new ArrayList<>();
+ final StringTokenizer st = new StringTokenizer(str, ",");
while (st.hasMoreTokens())
{
+ final String element = st.nextToken();
List local;
- String element = st.nextToken();
try
{
local = parseListElement(element, parser);
@@ -245,15 +312,13 @@ public class SchedulingPattern implements NextTime
{
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
}
-
- for (Integer value : local)
+ for (Iterator i = local.iterator(); i.hasNext();)
{
- if (values.contains(value))
+ final Object value = i.next();
+ if (!values.contains(value))
{
- continue;
+ values.add((Integer) value);
}
-
- values.add(value);
}
}
@@ -270,16 +335,23 @@ public class SchedulingPattern implements NextTime
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 List parseListElement(String str, ValueParser parser) throws Exception
{
- List values;
- StringTokenizer st = new StringTokenizer(str, "/");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "/");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
+ List values;
try
{
values = parseRange(st.nextToken(), parser);
@@ -291,9 +363,8 @@ public class SchedulingPattern implements NextTime
if (size == 2)
{
+ final String dStr = st.nextToken();
int div;
- String dStr = st.nextToken();
-
try
{
div = Integer.parseInt(dStr);
@@ -308,41 +379,47 @@ public class SchedulingPattern implements NextTime
throw new Exception("non positive divisor \"" + div + "\"");
}
- ArrayList values2 = new ArrayList<>();
+ final List 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 List parseRange(String str, ValueParser parser) throws Exception
{
- int v2;
- int v1;
if (str.equals("*"))
{
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- ArrayList values = new ArrayList<>();
- for (int i = min; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ final List values = new ArrayList<>();
+ for (int i = min; i <= max; i++)
{
values.add(i);
}
return values;
}
- StringTokenizer st = new StringTokenizer(str, "-");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "-");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
- String v1Str = st.nextToken();
+ final String v1Str = st.nextToken();
+ int v1;
try
{
v1 = parser.parse(v1Str);
@@ -354,12 +431,13 @@ public class SchedulingPattern implements NextTime
if (size == 1)
{
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
values.add(v1);
return values;
}
- String v2Str = st.nextToken();
+ final String v2Str = st.nextToken();
+ int v2;
try
{
v2 = parser.parse(v2Str);
@@ -369,91 +447,90 @@ public class SchedulingPattern implements NextTime
throw new Exception("invalid value \"" + v2Str + "\", " + e.getMessage());
}
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
if (v1 < v2)
{
- for (int i = v1; i <= v2; ++i)
+ for (int i = v1; i <= v2; i++)
{
values.add(i);
}
}
else if (v1 > v2)
{
- int i;
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- for (i = v1; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ for (int i = v1; i <= max; i++)
{
values.add(i);
}
- for (i = min; i <= v2; ++i)
+ for (int i = min; i <= v2; i++)
{
values.add(i);
}
}
else
{
+ // v1 == v2
values.add(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(timezone);
+ final GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(millis);
- gc.set(13, 0);
- gc.set(14, 0);
- for (int i = 0; i < _matcherSize; ++i)
+ gc.setTimeZone(timezone);
+ final int minute = gc.get(Calendar.MINUTE);
+ final int hour = gc.get(Calendar.HOUR_OF_DAY);
+ final int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH);
+ final int month = gc.get(Calendar.MONTH) + 1;
+ final int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1;
+ final int year = gc.get(Calendar.YEAR);
+ for (int i = 0; i < _matcherSize; i++)
{
- boolean eval = false;
- if (_weekOfYearAdder.containsKey(i))
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+ if (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))
{
- gc.add(3, -_weekOfYearAdder.get(i).intValue());
+ return true;
}
- if (_dayOfYearAdder.containsKey(i))
- {
- gc.add(6, -_dayOfYearAdder.get(i).intValue());
- }
- if (_hourAdder.containsKey(i))
- {
- gc.add(10, -_hourAdder.get(i).intValue());
- }
- int minute = gc.get(MONTH_MAX_VALUE);
- int hour = gc.get(11);
- int dayOfMonth = gc.get(5);
- int month = gc.get(2) + 1;
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
- int year = gc.get(1);
- 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);
- @SuppressWarnings("unused")
- boolean bl = 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) ? true : (eval = false);
- if (!eval)
- {
- continue;
- }
- 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);
}
+ /**
+ * It returns the next matching moment as a millis value.
+ * @param timezone
+ * @param millis
+ * @return The next matching moment as a millis value.
+ */
public long next(TimeZone timezone, long millis)
{
- long result = -1L;
- GregorianCalendar gc = new GregorianCalendar(timezone);
+ long result = -1;
+ final GregorianCalendar gc = new GregorianCalendar(timezone);
for (int i = 0; i < _matcherSize; ++i)
{
- long next = -1L;
+ long next = -1;
gc.setTimeInMillis(millis);
gc.set(13, 0);
gc.set(14, 0);
@@ -469,27 +546,28 @@ public class SchedulingPattern implements NextTime
{
gc.add(10, _hourAdder.get(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);
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+
SEARCH: do
{
- int year = gc.get(1);
- boolean isLeapYear = gc.isLeapYear(year);
+ final int year = gc.get(1);
+ final boolean isLeapYear = gc.isLeapYear(year);
for (int month = gc.get(2) + 1; month <= MONTH_MAX_VALUE; ++month)
{
if (monthMatcher.match(month))
{
gc.set(2, month - 1);
- int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ final int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
for (int dayOfMonth = gc.get(5); dayOfMonth <= maxDayOfMonth; ++dayOfMonth)
{
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher ? ((DayOfMonthValueMatcher) dayOfMonthMatcher).match(dayOfMonth, month, isLeapYear) : dayOfMonthMatcher.match(dayOfMonth))
{
gc.set(5, dayOfMonth);
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
+ final int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
if (dayOfWeekMatcher.match(dayOfWeek))
{
for (int hour = gc.get(11); hour <= HOUR_MAX_VALUE; ++hour)
@@ -505,13 +583,13 @@ public class SchedulingPattern implements NextTime
}
gc.set(MONTH_MAX_VALUE, minute);
- long next0 = gc.getTimeInMillis();
+ final long next0 = gc.getTimeInMillis();
if (next0 <= millis)
{
continue;
}
- if ((next != -1L) && (next0 >= next))
+ if ((next != -1) && (next0 >= next))
{
break SEARCH;
}
@@ -519,7 +597,7 @@ public class SchedulingPattern implements NextTime
next = next0;
if (_hourAdderRnd.containsKey(i))
{
- next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000L;
+ next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000;
}
if (!_minuteAdderRnd.containsKey(i))
@@ -527,7 +605,7 @@ public class SchedulingPattern implements NextTime
break SEARCH;
}
- next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000L;
+ next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000;
break SEARCH;
}
}
@@ -550,7 +628,7 @@ public class SchedulingPattern implements NextTime
}
while (true);
- if ((next <= millis) || ((result != -1L) && (next >= result)))
+ if ((next <= millis) || ((result != -1) && (next >= result)))
{
continue;
}
@@ -560,57 +638,89 @@ public class SchedulingPattern implements NextTime
return result;
}
- @Override
+ /**
+ * It returns the next matching moment as a long.
+ * @param millis
+ * @return The next matching moment as a long.
+ */
public long next(long millis)
{
return next(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.
+ */
private static int parseAlias(String value, String[] aliases, int offset) throws Exception
{
- for (int i = 0; i < aliases.length; ++i)
+ for (int i = 0; i < aliases.length; i++)
{
- if (!aliases[i].equalsIgnoreCase(value))
+ if (aliases[i].equalsIgnoreCase(value))
{
- continue;
+ return offset + i;
}
- return offset + i;
}
throw new Exception("invalid alias \"" + value + "\"");
}
+ /**
+ *
+ * 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.
+ *
+ * @author Paul Fernley
+ */
private static class DayOfMonthValueMatcher extends IntArrayValueMatcher
{
- private static final int[] lastDays = new int[]
+ private static final int[] LAST_DAYS =
{
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
28,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE
+ 31
};
+ /**
+ * 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 or the last-day-of-month setting applies.
+ */
public DayOfMonthValueMatcher(List integers)
{
super(integers);
}
+ /**
+ * 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) && DayOfMonthValueMatcher.isLastDayOfMonth(value, month, isLeapYear));
+ return (super.match(value) || ((value > 27) && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
}
public static int getLastDayOfMonth(int month, boolean isLeapYear)
@@ -620,29 +730,41 @@ public class SchedulingPattern implements NextTime
return 29;
}
- return lastDays[month - 1];
+ return LAST_DAYS[month - 1];
}
public static boolean isLastDayOfMonth(int value, int month, boolean isLeapYear)
{
- return value == DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ return value == getLastDayOfMonth(month, isLeapYear);
}
}
+ /**
+ *
+ * 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.
+ *
+ * @author Carlo Pelliccia
+ */
private static class IntArrayValueMatcher implements ValueMatcher
{
- private final int[] values;
+ /**
+ * The accepted values.
+ */
+ private final int[] _values;
+ /**
+ * Builds the ValueMatcher.
+ * @param integers a List 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(List integers)
{
- int size = integers.size();
- values = new int[size];
- for (int i = 0; i < size; ++i)
+ final int size = integers.size();
+ _values = new int[size];
+ for (int i = 0; i < size; i++)
{
try
{
- values[i] = integers.get(i);
- continue;
+ _values[i] = integers.get(i).intValue();
}
catch (Exception e)
{
@@ -651,27 +773,32 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Returns true if the given value is included in the matcher list.
+ */
@Override
public boolean match(int value)
{
- for (int i = 0; i < values.length; ++i)
+ for (int i = 0; i < _values.length; i++)
{
- if (values[i] != value)
+ if (_values[i] == value)
{
- continue;
+ return true;
}
- return true;
}
return false;
}
}
+ /**
+ * This ValueMatcher always returns true!
+ * @author Carlo Pelliccia
+ */
private static class AlwaysTrueValueMatcher implements ValueMatcher
{
- private AlwaysTrueValueMatcher()
- {
- }
-
+ /**
+ * Always true!
+ */
@Override
public boolean match(int value)
{
@@ -679,14 +806,31 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ *
+ * This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
+ *
+ * @author Carlo Pelliccia
+ */
private static interface ValueMatcher
{
- public boolean match(int var1);
+ /**
+ * 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);
}
+ /**
+ * The value parser for the day of week field.
+ */
private static class DayOfWeekValueParser extends SimpleValueParser
{
- private static String[] ALIASES = new String[]
+ /**
+ * Days of week aliases.
+ */
+ private static String[] ALIASES =
{
"sun",
"mon",
@@ -697,6 +841,9 @@ public class SchedulingPattern implements NextTime
"sat"
};
+ /**
+ * Builds the day value parser.
+ */
public DayOfWeekValueParser()
{
super(DAY_OF_WEEK_MIN_VALUE, DAY_OF_WEEK_MAX_VALUE);
@@ -707,17 +854,25 @@ public class SchedulingPattern implements NextTime
{
try
{
- return super.parse(value) % DAY_OF_WEEK_MAX_VALUE;
+ // try as a simple value
+ return super.parse(value) % 7;
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 0);
+ // try as an alias
+ return parseAlias(value, ALIASES, 0);
}
}
}
+ /**
+ * The value parser for the months field.
+ */
private static class MonthValueParser extends SimpleValueParser
{
+ /**
+ * Months of year aliases.
+ */
private static String[] ALIASES = new String[]
{
"jan",
@@ -734,6 +889,9 @@ public class SchedulingPattern implements NextTime
"dec"
};
+ /**
+ * Builds the months value parser.
+ */
public MonthValueParser()
{
super(MONTH_MIN_VALUE, MONTH_MAX_VALUE);
@@ -748,13 +906,19 @@ public class SchedulingPattern implements NextTime
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 1);
+ return parseAlias(value, ALIASES, 1);
}
}
}
+ /**
+ * The value parser for the day of month field.
+ */
private static class DayOfMonthValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public DayOfMonthValueParser()
{
super(DAY_OF_MONTH_MIN_VALUE, DAY_OF_MONTH_MAX_VALUE);
@@ -771,27 +935,54 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * The value parser for the hour field.
+ */
private static class HourValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public HourValueParser()
{
super(HOUR_MIN_VALUE, HOUR_MAX_VALUE);
}
}
+ /**
+ * The minutes value parser.
+ */
private static class MinuteValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public MinuteValueParser()
{
super(MINUTE_MIN_VALUE, MINUTE_MAX_VALUE);
}
}
+ /**
+ * 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)
{
_minValue = minValue;
@@ -830,12 +1021,29 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Definition for a value parser.
+ */
private static interface ValueParser
{
- public int parse(String var1) throws Exception;
+ /**
+ * 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 accepted by the parser.
+ * @return The minimum value accepted by the parser.
+ */
public int getMinValue();
+ /**
+ * Returns the maximum value accepted by the parser.
+ * @return The maximum value accepted by the parser.
+ */
public int getMaxValue();
}
}
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/time/AddPattern.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/time/AddPattern.java
deleted file mode 100644
index 893a279fd4..0000000000
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/time/AddPattern.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-public class AddPattern implements NextTime
-{
- private int _monthInc = -1;
- private int _monthSet = -1;
- private int _dayOfMonthInc = -1;
- private int _dayOfMonthSet = -1;
- private int _hourOfDayInc = -1;
- private int _hourOfDaySet = -1;
- private int _minuteInc = -1;
- private int _minuteSet = -1;
-
- public AddPattern(String pattern)
- {
- String[] timeparts;
- String[] parts = pattern.split("\\s+");
- if (parts.length == 2)
- {
- String datemodstr;
- String datepartsstr = parts[0];
- String[] dateparts = datepartsstr.split(":");
- if (dateparts.length == 2)
- {
- if (dateparts[0].startsWith("+"))
- {
- _monthInc = Integer.parseInt(dateparts[0].substring(1));
- }
- else
- {
- _monthSet = Integer.parseInt(dateparts[0]) - 1;
- }
- }
- if ((datemodstr = dateparts[dateparts.length - 1]).startsWith("+"))
- {
- _dayOfMonthInc = Integer.parseInt(datemodstr.substring(1));
- }
- else
- {
- _dayOfMonthSet = Integer.parseInt(datemodstr);
- }
- }
- if ((timeparts = parts[parts.length - 1].split(":"))[0].startsWith("+"))
- {
- _hourOfDayInc = Integer.parseInt(timeparts[0].substring(1));
- }
- else
- {
- _hourOfDaySet = Integer.parseInt(timeparts[0]);
- }
- if (timeparts[1].startsWith("+"))
- {
- _minuteInc = Integer.parseInt(timeparts[1].substring(1));
- }
- else
- {
- _minuteSet = Integer.parseInt(timeparts[1]);
- }
- }
-
- @Override
- public long next(long millis)
- {
- final GregorianCalendar gc = new GregorianCalendar(TimeZone.getDefault());
- gc.setTimeInMillis(millis);
- if (_monthInc >= 0)
- {
- gc.add(2, _monthInc);
- }
- if (_monthSet >= 0)
- {
- gc.set(2, _monthSet);
- }
- if (_dayOfMonthInc >= 0)
- {
- gc.add(5, _dayOfMonthInc);
- }
- if (_dayOfMonthSet >= 0)
- {
- gc.set(5, _dayOfMonthSet);
- }
- if (_hourOfDayInc >= 0)
- {
- gc.add(11, _hourOfDayInc);
- }
- if (_hourOfDaySet >= 0)
- {
- gc.set(11, _hourOfDaySet);
- }
- if (_minuteInc >= 0)
- {
- gc.add(12, _minuteInc);
- }
- if (_minuteSet >= 0)
- {
- gc.set(12, _minuteSet);
- }
- return gc.getTimeInMillis();
- }
-}
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/time/NextTime.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/time/NextTime.java
deleted file mode 100644
index d08d90164d..0000000000
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/time/NextTime.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-public interface NextTime
-{
- public long next(long var1);
-}
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/time/SchedulingPattern.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/time/SchedulingPattern.java
index d9fdd655f3..a9677e31ae 100644
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/time/SchedulingPattern.java
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/time/SchedulingPattern.java
@@ -1,23 +1,25 @@
/*
- * This file is part of the L2J Mobius project.
+ * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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.
- *
+ * it under the terms of the GNU Lesser General Public License version
+ * 2.1, as published by the Free Software Foundation.
+ *
* 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 .
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License 2.1 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License version 2.1 along with this program.
+ * If not, see .
*/
package org.l2jmobius.commons.time;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
@@ -26,7 +28,94 @@ import java.util.TreeMap;
import org.l2jmobius.commons.util.Rnd;
-public class SchedulingPattern implements NextTime
+/**
+ *
+ * A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
+ *
+ *
+ * - Minutes sub-pattern. During which minutes of the hour should the task been launched? The values range is from 0 to 59.
+ * - Hours sub-pattern. During which hours of the day should the task been launched? The values range is from 0 to 23.
+ * - Days of month sub-pattern. 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.
+ * - Months sub-pattern. 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".
+ * - Days of week sub-pattern. 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".
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * Some examples:
+ *
+ *
+ * 5 * * * *
+ * 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.).
+ *
+ *
+ * * * * * *
+ * This pattern causes a task to be launched every minute.
+ *
+ *
+ * * 12 * * Mon
+ * This pattern causes a task to be launched every minute during the 12th hour of Monday.
+ *
+ *
+ * * 12 16 * Mon
+ * 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.
+ *
+ *
+ * Every sub-pattern can contain two or more comma separated values.
+ *
+ *
+ * 59 11 * * 1,2,3,4,5
+ * This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
+ *
+ *
+ * Values intervals are admitted and defined using the minus character.
+ *
+ *
+ * 59 11 * * 1-5
+ * This pattern is equivalent to the previous one.
+ *
+ *
+ * The slash character can be used to identify step values within a range. It can be used both in the form */c and a-b/c. The subpattern is matched every c values of the range 0,maxvalue or a-b.
+ *
+ *
+ * */5 * * * *
+ * This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
+ *
+ *
+ * 3-18/5 * * * *
+ * 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).
+ *
+ *
+ * */15 9-17 * * *
+ * 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).
+ *
+ *
+ * All the fresh described syntax rules can be used together.
+ *
+ *
+ * * 12 10-16/2 * *
+ * 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.
+ *
+ *
+ * * 12 1-15,17,20-25 * *
+ * 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.
+ *
+ *
+ * Finally lets you combine more scheduling patterns into one, with the pipe character:
+ *
+ *
+ * 0 5 * * *|8 10 * * *|22 17 * * *
+ * This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
+ *
+ * @author Carlo Pelliccia
+ */
+public class SchedulingPattern
{
private static final int MINUTE_MIN_VALUE = 0;
private static final int MINUTE_MAX_VALUE = 59;
@@ -38,24 +127,78 @@ public class SchedulingPattern implements NextTime
private static final int MONTH_MAX_VALUE = 12;
private static final int DAY_OF_WEEK_MIN_VALUE = 0;
private static final int DAY_OF_WEEK_MAX_VALUE = 7;
+
+ /**
+ * 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();
+
+ /**
+ * The pattern as a string.
+ */
private final String _asString;
+
+ /**
+ * The ValueMatcher list for the "minute" field.
+ */
protected List _minuteMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "hour" field.
+ */
protected List _hourMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of month" field.
+ */
protected List _dayOfMonthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "month" field.
+ */
protected List _monthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of week" field.
+ */
protected List _dayOfWeekMatchers = new ArrayList<>();
+
+ /**
+ * How many matcher groups in this pattern?
+ */
protected int _matcherSize = 0;
+
protected Map _hourAdder = new TreeMap<>();
protected Map _hourAdderRnd = new TreeMap<>();
protected Map _dayOfYearAdder = new TreeMap<>();
protected Map _minuteAdderRnd = new TreeMap<>();
protected Map _weekOfYearAdder = new TreeMap<>();
+ /**
+ * 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
@@ -69,10 +212,15 @@ public class SchedulingPattern implements NextTime
return true;
}
+ /**
+ * Builds a SchedulingPattern parsing it from a string.
+ * @param pattern The pattern as a crontab-like string.
+ * @throws RuntimeException If the supplied string is not a valid pattern.
+ */
public SchedulingPattern(String pattern) throws RuntimeException
{
_asString = pattern;
- StringTokenizer st1 = new StringTokenizer(pattern, "|");
+ final StringTokenizer st1 = new StringTokenizer(pattern, "|");
if (st1.countTokens() < 1)
{
throw new RuntimeException("invalid pattern: \"" + pattern + "\"");
@@ -80,39 +228,16 @@ public class SchedulingPattern implements NextTime
while (st1.hasMoreTokens())
{
- int i;
- String localPattern = st1.nextToken();
- StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
- int tokCnt = st2.countTokens();
- if ((tokCnt < 5) || (tokCnt > 6))
+ final String localPattern = st1.nextToken();
+ final StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
+ if (st2.countTokens() != 5)
{
throw new RuntimeException("invalid pattern: \"" + localPattern + "\"");
}
try
{
- String minutePattern = st2.nextToken();
- String[] minutePatternParts = minutePattern.split(":");
- if (minutePatternParts.length > 1)
- {
- for (i = 0; i < (minutePatternParts.length - 1); ++i)
- {
- if (minutePatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (minutePatternParts[i].startsWith("~"))
- {
- _minuteAdderRnd.put(_matcherSize, Integer.parseInt(minutePatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + minutePatternParts[i] + "\"");
- }
- minutePattern = minutePatternParts[minutePatternParts.length - 1];
- }
- _minuteMatchers.add(buildValueMatcher(minutePattern, MINUTE_VALUE_PARSER));
+ _minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
}
catch (Exception e)
{
@@ -121,35 +246,9 @@ public class SchedulingPattern implements NextTime
try
{
- String hourPattern = st2.nextToken();
- String[] hourPatternParts = hourPattern.split(":");
- if (hourPatternParts.length > 1)
- {
- for (i = 0; i < (hourPatternParts.length - 1); ++i)
- {
- if (hourPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (hourPatternParts[i].startsWith("+"))
- {
- _hourAdder.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- if (hourPatternParts[i].startsWith("~"))
- {
- _hourAdderRnd.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + hourPatternParts[i] + "\"");
- }
- hourPattern = hourPatternParts[hourPatternParts.length - 1];
- }
- _hourMatchers.add(buildValueMatcher(hourPattern, HOUR_VALUE_PARSER));
+ _hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
}
+
catch (Exception e)
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
@@ -157,28 +256,7 @@ public class SchedulingPattern implements NextTime
try
{
- String dayOfMonthPattern = st2.nextToken();
- String[] dayOfMonthPatternParts = dayOfMonthPattern.split(":");
- if (dayOfMonthPatternParts.length > 1)
- {
- for (i = 0; i < (dayOfMonthPatternParts.length - 1); ++i)
- {
- if (dayOfMonthPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (dayOfMonthPatternParts[i].startsWith("+"))
- {
- _dayOfYearAdder.put(_matcherSize, Integer.parseInt(dayOfMonthPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown day modifier \"" + dayOfMonthPatternParts[i] + "\"");
- }
- dayOfMonthPattern = dayOfMonthPatternParts[dayOfMonthPatternParts.length - 1];
- }
- _dayOfMonthMatchers.add(buildValueMatcher(dayOfMonthPattern, DAY_OF_MONTH_VALUE_PARSER));
+ _dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
}
catch (Exception e)
{
@@ -202,28 +280,17 @@ public class SchedulingPattern implements NextTime
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
}
-
- if (st2.hasMoreTokens())
- {
- try
- {
- String weekOfYearAdderText = st2.nextToken();
- if (weekOfYearAdderText.charAt(0) != '+')
- {
- throw new RuntimeException("Unknown week of year addition in pattern \"" + localPattern + "\".");
- }
- weekOfYearAdderText = weekOfYearAdderText.substring(1);
- _weekOfYearAdder.put(_matcherSize, Integer.parseInt(weekOfYearAdderText));
- }
- catch (Exception e)
- {
- throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
- }
- }
- ++_matcherSize;
+ _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("*"))
@@ -231,12 +298,12 @@ public class SchedulingPattern implements NextTime
return new AlwaysTrueValueMatcher();
}
- ArrayList values = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(str, ",");
+ final List values = new ArrayList<>();
+ final StringTokenizer st = new StringTokenizer(str, ",");
while (st.hasMoreTokens())
{
+ final String element = st.nextToken();
List local;
- String element = st.nextToken();
try
{
local = parseListElement(element, parser);
@@ -245,15 +312,13 @@ public class SchedulingPattern implements NextTime
{
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
}
-
- for (Integer value : local)
+ for (Iterator i = local.iterator(); i.hasNext();)
{
- if (values.contains(value))
+ final Object value = i.next();
+ if (!values.contains(value))
{
- continue;
+ values.add((Integer) value);
}
-
- values.add(value);
}
}
@@ -270,16 +335,23 @@ public class SchedulingPattern implements NextTime
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 List parseListElement(String str, ValueParser parser) throws Exception
{
- List values;
- StringTokenizer st = new StringTokenizer(str, "/");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "/");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
+ List values;
try
{
values = parseRange(st.nextToken(), parser);
@@ -291,9 +363,8 @@ public class SchedulingPattern implements NextTime
if (size == 2)
{
+ final String dStr = st.nextToken();
int div;
- String dStr = st.nextToken();
-
try
{
div = Integer.parseInt(dStr);
@@ -308,41 +379,47 @@ public class SchedulingPattern implements NextTime
throw new Exception("non positive divisor \"" + div + "\"");
}
- ArrayList values2 = new ArrayList<>();
+ final List 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 List parseRange(String str, ValueParser parser) throws Exception
{
- int v2;
- int v1;
if (str.equals("*"))
{
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- ArrayList values = new ArrayList<>();
- for (int i = min; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ final List values = new ArrayList<>();
+ for (int i = min; i <= max; i++)
{
values.add(i);
}
return values;
}
- StringTokenizer st = new StringTokenizer(str, "-");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "-");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
- String v1Str = st.nextToken();
+ final String v1Str = st.nextToken();
+ int v1;
try
{
v1 = parser.parse(v1Str);
@@ -354,12 +431,13 @@ public class SchedulingPattern implements NextTime
if (size == 1)
{
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
values.add(v1);
return values;
}
- String v2Str = st.nextToken();
+ final String v2Str = st.nextToken();
+ int v2;
try
{
v2 = parser.parse(v2Str);
@@ -369,91 +447,90 @@ public class SchedulingPattern implements NextTime
throw new Exception("invalid value \"" + v2Str + "\", " + e.getMessage());
}
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
if (v1 < v2)
{
- for (int i = v1; i <= v2; ++i)
+ for (int i = v1; i <= v2; i++)
{
values.add(i);
}
}
else if (v1 > v2)
{
- int i;
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- for (i = v1; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ for (int i = v1; i <= max; i++)
{
values.add(i);
}
- for (i = min; i <= v2; ++i)
+ for (int i = min; i <= v2; i++)
{
values.add(i);
}
}
else
{
+ // v1 == v2
values.add(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(timezone);
+ final GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(millis);
- gc.set(13, 0);
- gc.set(14, 0);
- for (int i = 0; i < _matcherSize; ++i)
+ gc.setTimeZone(timezone);
+ final int minute = gc.get(Calendar.MINUTE);
+ final int hour = gc.get(Calendar.HOUR_OF_DAY);
+ final int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH);
+ final int month = gc.get(Calendar.MONTH) + 1;
+ final int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1;
+ final int year = gc.get(Calendar.YEAR);
+ for (int i = 0; i < _matcherSize; i++)
{
- boolean eval = false;
- if (_weekOfYearAdder.containsKey(i))
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+ if (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))
{
- gc.add(3, -_weekOfYearAdder.get(i).intValue());
+ return true;
}
- if (_dayOfYearAdder.containsKey(i))
- {
- gc.add(6, -_dayOfYearAdder.get(i).intValue());
- }
- if (_hourAdder.containsKey(i))
- {
- gc.add(10, -_hourAdder.get(i).intValue());
- }
- int minute = gc.get(MONTH_MAX_VALUE);
- int hour = gc.get(11);
- int dayOfMonth = gc.get(5);
- int month = gc.get(2) + 1;
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
- int year = gc.get(1);
- 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);
- @SuppressWarnings("unused")
- boolean bl = 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) ? true : (eval = false);
- if (!eval)
- {
- continue;
- }
- 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);
}
+ /**
+ * It returns the next matching moment as a millis value.
+ * @param timezone
+ * @param millis
+ * @return The next matching moment as a millis value.
+ */
public long next(TimeZone timezone, long millis)
{
- long result = -1L;
- GregorianCalendar gc = new GregorianCalendar(timezone);
+ long result = -1;
+ final GregorianCalendar gc = new GregorianCalendar(timezone);
for (int i = 0; i < _matcherSize; ++i)
{
- long next = -1L;
+ long next = -1;
gc.setTimeInMillis(millis);
gc.set(13, 0);
gc.set(14, 0);
@@ -469,27 +546,28 @@ public class SchedulingPattern implements NextTime
{
gc.add(10, _hourAdder.get(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);
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+
SEARCH: do
{
- int year = gc.get(1);
- boolean isLeapYear = gc.isLeapYear(year);
+ final int year = gc.get(1);
+ final boolean isLeapYear = gc.isLeapYear(year);
for (int month = gc.get(2) + 1; month <= MONTH_MAX_VALUE; ++month)
{
if (monthMatcher.match(month))
{
gc.set(2, month - 1);
- int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ final int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
for (int dayOfMonth = gc.get(5); dayOfMonth <= maxDayOfMonth; ++dayOfMonth)
{
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher ? ((DayOfMonthValueMatcher) dayOfMonthMatcher).match(dayOfMonth, month, isLeapYear) : dayOfMonthMatcher.match(dayOfMonth))
{
gc.set(5, dayOfMonth);
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
+ final int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
if (dayOfWeekMatcher.match(dayOfWeek))
{
for (int hour = gc.get(11); hour <= HOUR_MAX_VALUE; ++hour)
@@ -505,13 +583,13 @@ public class SchedulingPattern implements NextTime
}
gc.set(MONTH_MAX_VALUE, minute);
- long next0 = gc.getTimeInMillis();
+ final long next0 = gc.getTimeInMillis();
if (next0 <= millis)
{
continue;
}
- if ((next != -1L) && (next0 >= next))
+ if ((next != -1) && (next0 >= next))
{
break SEARCH;
}
@@ -519,7 +597,7 @@ public class SchedulingPattern implements NextTime
next = next0;
if (_hourAdderRnd.containsKey(i))
{
- next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000L;
+ next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000;
}
if (!_minuteAdderRnd.containsKey(i))
@@ -527,7 +605,7 @@ public class SchedulingPattern implements NextTime
break SEARCH;
}
- next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000L;
+ next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000;
break SEARCH;
}
}
@@ -550,7 +628,7 @@ public class SchedulingPattern implements NextTime
}
while (true);
- if ((next <= millis) || ((result != -1L) && (next >= result)))
+ if ((next <= millis) || ((result != -1) && (next >= result)))
{
continue;
}
@@ -560,57 +638,89 @@ public class SchedulingPattern implements NextTime
return result;
}
- @Override
+ /**
+ * It returns the next matching moment as a long.
+ * @param millis
+ * @return The next matching moment as a long.
+ */
public long next(long millis)
{
return next(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.
+ */
private static int parseAlias(String value, String[] aliases, int offset) throws Exception
{
- for (int i = 0; i < aliases.length; ++i)
+ for (int i = 0; i < aliases.length; i++)
{
- if (!aliases[i].equalsIgnoreCase(value))
+ if (aliases[i].equalsIgnoreCase(value))
{
- continue;
+ return offset + i;
}
- return offset + i;
}
throw new Exception("invalid alias \"" + value + "\"");
}
+ /**
+ *
+ * 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.
+ *
+ * @author Paul Fernley
+ */
private static class DayOfMonthValueMatcher extends IntArrayValueMatcher
{
- private static final int[] lastDays = new int[]
+ private static final int[] LAST_DAYS =
{
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
28,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE
+ 31
};
+ /**
+ * 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 or the last-day-of-month setting applies.
+ */
public DayOfMonthValueMatcher(List integers)
{
super(integers);
}
+ /**
+ * 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) && DayOfMonthValueMatcher.isLastDayOfMonth(value, month, isLeapYear));
+ return (super.match(value) || ((value > 27) && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
}
public static int getLastDayOfMonth(int month, boolean isLeapYear)
@@ -620,29 +730,41 @@ public class SchedulingPattern implements NextTime
return 29;
}
- return lastDays[month - 1];
+ return LAST_DAYS[month - 1];
}
public static boolean isLastDayOfMonth(int value, int month, boolean isLeapYear)
{
- return value == DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ return value == getLastDayOfMonth(month, isLeapYear);
}
}
+ /**
+ *
+ * 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.
+ *
+ * @author Carlo Pelliccia
+ */
private static class IntArrayValueMatcher implements ValueMatcher
{
- private final int[] values;
+ /**
+ * The accepted values.
+ */
+ private final int[] _values;
+ /**
+ * Builds the ValueMatcher.
+ * @param integers a List 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(List integers)
{
- int size = integers.size();
- values = new int[size];
- for (int i = 0; i < size; ++i)
+ final int size = integers.size();
+ _values = new int[size];
+ for (int i = 0; i < size; i++)
{
try
{
- values[i] = integers.get(i);
- continue;
+ _values[i] = integers.get(i).intValue();
}
catch (Exception e)
{
@@ -651,27 +773,32 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Returns true if the given value is included in the matcher list.
+ */
@Override
public boolean match(int value)
{
- for (int i = 0; i < values.length; ++i)
+ for (int i = 0; i < _values.length; i++)
{
- if (values[i] != value)
+ if (_values[i] == value)
{
- continue;
+ return true;
}
- return true;
}
return false;
}
}
+ /**
+ * This ValueMatcher always returns true!
+ * @author Carlo Pelliccia
+ */
private static class AlwaysTrueValueMatcher implements ValueMatcher
{
- private AlwaysTrueValueMatcher()
- {
- }
-
+ /**
+ * Always true!
+ */
@Override
public boolean match(int value)
{
@@ -679,14 +806,31 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ *
+ * This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
+ *
+ * @author Carlo Pelliccia
+ */
private static interface ValueMatcher
{
- public boolean match(int var1);
+ /**
+ * 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);
}
+ /**
+ * The value parser for the day of week field.
+ */
private static class DayOfWeekValueParser extends SimpleValueParser
{
- private static String[] ALIASES = new String[]
+ /**
+ * Days of week aliases.
+ */
+ private static String[] ALIASES =
{
"sun",
"mon",
@@ -697,6 +841,9 @@ public class SchedulingPattern implements NextTime
"sat"
};
+ /**
+ * Builds the day value parser.
+ */
public DayOfWeekValueParser()
{
super(DAY_OF_WEEK_MIN_VALUE, DAY_OF_WEEK_MAX_VALUE);
@@ -707,17 +854,25 @@ public class SchedulingPattern implements NextTime
{
try
{
- return super.parse(value) % DAY_OF_WEEK_MAX_VALUE;
+ // try as a simple value
+ return super.parse(value) % 7;
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 0);
+ // try as an alias
+ return parseAlias(value, ALIASES, 0);
}
}
}
+ /**
+ * The value parser for the months field.
+ */
private static class MonthValueParser extends SimpleValueParser
{
+ /**
+ * Months of year aliases.
+ */
private static String[] ALIASES = new String[]
{
"jan",
@@ -734,6 +889,9 @@ public class SchedulingPattern implements NextTime
"dec"
};
+ /**
+ * Builds the months value parser.
+ */
public MonthValueParser()
{
super(MONTH_MIN_VALUE, MONTH_MAX_VALUE);
@@ -748,13 +906,19 @@ public class SchedulingPattern implements NextTime
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 1);
+ return parseAlias(value, ALIASES, 1);
}
}
}
+ /**
+ * The value parser for the day of month field.
+ */
private static class DayOfMonthValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public DayOfMonthValueParser()
{
super(DAY_OF_MONTH_MIN_VALUE, DAY_OF_MONTH_MAX_VALUE);
@@ -771,27 +935,54 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * The value parser for the hour field.
+ */
private static class HourValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public HourValueParser()
{
super(HOUR_MIN_VALUE, HOUR_MAX_VALUE);
}
}
+ /**
+ * The minutes value parser.
+ */
private static class MinuteValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public MinuteValueParser()
{
super(MINUTE_MIN_VALUE, MINUTE_MAX_VALUE);
}
}
+ /**
+ * 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)
{
_minValue = minValue;
@@ -830,12 +1021,29 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Definition for a value parser.
+ */
private static interface ValueParser
{
- public int parse(String var1) throws Exception;
+ /**
+ * 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 accepted by the parser.
+ * @return The minimum value accepted by the parser.
+ */
public int getMinValue();
+ /**
+ * Returns the maximum value accepted by the parser.
+ * @return The maximum value accepted by the parser.
+ */
public int getMaxValue();
}
}
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/time/AddPattern.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/time/AddPattern.java
deleted file mode 100644
index 893a279fd4..0000000000
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/time/AddPattern.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-public class AddPattern implements NextTime
-{
- private int _monthInc = -1;
- private int _monthSet = -1;
- private int _dayOfMonthInc = -1;
- private int _dayOfMonthSet = -1;
- private int _hourOfDayInc = -1;
- private int _hourOfDaySet = -1;
- private int _minuteInc = -1;
- private int _minuteSet = -1;
-
- public AddPattern(String pattern)
- {
- String[] timeparts;
- String[] parts = pattern.split("\\s+");
- if (parts.length == 2)
- {
- String datemodstr;
- String datepartsstr = parts[0];
- String[] dateparts = datepartsstr.split(":");
- if (dateparts.length == 2)
- {
- if (dateparts[0].startsWith("+"))
- {
- _monthInc = Integer.parseInt(dateparts[0].substring(1));
- }
- else
- {
- _monthSet = Integer.parseInt(dateparts[0]) - 1;
- }
- }
- if ((datemodstr = dateparts[dateparts.length - 1]).startsWith("+"))
- {
- _dayOfMonthInc = Integer.parseInt(datemodstr.substring(1));
- }
- else
- {
- _dayOfMonthSet = Integer.parseInt(datemodstr);
- }
- }
- if ((timeparts = parts[parts.length - 1].split(":"))[0].startsWith("+"))
- {
- _hourOfDayInc = Integer.parseInt(timeparts[0].substring(1));
- }
- else
- {
- _hourOfDaySet = Integer.parseInt(timeparts[0]);
- }
- if (timeparts[1].startsWith("+"))
- {
- _minuteInc = Integer.parseInt(timeparts[1].substring(1));
- }
- else
- {
- _minuteSet = Integer.parseInt(timeparts[1]);
- }
- }
-
- @Override
- public long next(long millis)
- {
- final GregorianCalendar gc = new GregorianCalendar(TimeZone.getDefault());
- gc.setTimeInMillis(millis);
- if (_monthInc >= 0)
- {
- gc.add(2, _monthInc);
- }
- if (_monthSet >= 0)
- {
- gc.set(2, _monthSet);
- }
- if (_dayOfMonthInc >= 0)
- {
- gc.add(5, _dayOfMonthInc);
- }
- if (_dayOfMonthSet >= 0)
- {
- gc.set(5, _dayOfMonthSet);
- }
- if (_hourOfDayInc >= 0)
- {
- gc.add(11, _hourOfDayInc);
- }
- if (_hourOfDaySet >= 0)
- {
- gc.set(11, _hourOfDaySet);
- }
- if (_minuteInc >= 0)
- {
- gc.add(12, _minuteInc);
- }
- if (_minuteSet >= 0)
- {
- gc.set(12, _minuteSet);
- }
- return gc.getTimeInMillis();
- }
-}
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/time/NextTime.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/time/NextTime.java
deleted file mode 100644
index d08d90164d..0000000000
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/time/NextTime.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-public interface NextTime
-{
- public long next(long var1);
-}
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/time/SchedulingPattern.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/time/SchedulingPattern.java
index d9fdd655f3..a9677e31ae 100644
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/time/SchedulingPattern.java
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/time/SchedulingPattern.java
@@ -1,23 +1,25 @@
/*
- * This file is part of the L2J Mobius project.
+ * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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.
- *
+ * it under the terms of the GNU Lesser General Public License version
+ * 2.1, as published by the Free Software Foundation.
+ *
* 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 .
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License 2.1 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License version 2.1 along with this program.
+ * If not, see .
*/
package org.l2jmobius.commons.time;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
@@ -26,7 +28,94 @@ import java.util.TreeMap;
import org.l2jmobius.commons.util.Rnd;
-public class SchedulingPattern implements NextTime
+/**
+ *
+ * A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
+ *
+ *
+ * - Minutes sub-pattern. During which minutes of the hour should the task been launched? The values range is from 0 to 59.
+ * - Hours sub-pattern. During which hours of the day should the task been launched? The values range is from 0 to 23.
+ * - Days of month sub-pattern. 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.
+ * - Months sub-pattern. 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".
+ * - Days of week sub-pattern. 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".
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * Some examples:
+ *
+ *
+ * 5 * * * *
+ * 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.).
+ *
+ *
+ * * * * * *
+ * This pattern causes a task to be launched every minute.
+ *
+ *
+ * * 12 * * Mon
+ * This pattern causes a task to be launched every minute during the 12th hour of Monday.
+ *
+ *
+ * * 12 16 * Mon
+ * 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.
+ *
+ *
+ * Every sub-pattern can contain two or more comma separated values.
+ *
+ *
+ * 59 11 * * 1,2,3,4,5
+ * This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
+ *
+ *
+ * Values intervals are admitted and defined using the minus character.
+ *
+ *
+ * 59 11 * * 1-5
+ * This pattern is equivalent to the previous one.
+ *
+ *
+ * The slash character can be used to identify step values within a range. It can be used both in the form */c and a-b/c. The subpattern is matched every c values of the range 0,maxvalue or a-b.
+ *
+ *
+ * */5 * * * *
+ * This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
+ *
+ *
+ * 3-18/5 * * * *
+ * 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).
+ *
+ *
+ * */15 9-17 * * *
+ * 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).
+ *
+ *
+ * All the fresh described syntax rules can be used together.
+ *
+ *
+ * * 12 10-16/2 * *
+ * 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.
+ *
+ *
+ * * 12 1-15,17,20-25 * *
+ * 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.
+ *
+ *
+ * Finally lets you combine more scheduling patterns into one, with the pipe character:
+ *
+ *
+ * 0 5 * * *|8 10 * * *|22 17 * * *
+ * This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
+ *
+ * @author Carlo Pelliccia
+ */
+public class SchedulingPattern
{
private static final int MINUTE_MIN_VALUE = 0;
private static final int MINUTE_MAX_VALUE = 59;
@@ -38,24 +127,78 @@ public class SchedulingPattern implements NextTime
private static final int MONTH_MAX_VALUE = 12;
private static final int DAY_OF_WEEK_MIN_VALUE = 0;
private static final int DAY_OF_WEEK_MAX_VALUE = 7;
+
+ /**
+ * 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();
+
+ /**
+ * The pattern as a string.
+ */
private final String _asString;
+
+ /**
+ * The ValueMatcher list for the "minute" field.
+ */
protected List _minuteMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "hour" field.
+ */
protected List _hourMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of month" field.
+ */
protected List _dayOfMonthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "month" field.
+ */
protected List _monthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of week" field.
+ */
protected List _dayOfWeekMatchers = new ArrayList<>();
+
+ /**
+ * How many matcher groups in this pattern?
+ */
protected int _matcherSize = 0;
+
protected Map _hourAdder = new TreeMap<>();
protected Map _hourAdderRnd = new TreeMap<>();
protected Map _dayOfYearAdder = new TreeMap<>();
protected Map _minuteAdderRnd = new TreeMap<>();
protected Map _weekOfYearAdder = new TreeMap<>();
+ /**
+ * 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
@@ -69,10 +212,15 @@ public class SchedulingPattern implements NextTime
return true;
}
+ /**
+ * Builds a SchedulingPattern parsing it from a string.
+ * @param pattern The pattern as a crontab-like string.
+ * @throws RuntimeException If the supplied string is not a valid pattern.
+ */
public SchedulingPattern(String pattern) throws RuntimeException
{
_asString = pattern;
- StringTokenizer st1 = new StringTokenizer(pattern, "|");
+ final StringTokenizer st1 = new StringTokenizer(pattern, "|");
if (st1.countTokens() < 1)
{
throw new RuntimeException("invalid pattern: \"" + pattern + "\"");
@@ -80,39 +228,16 @@ public class SchedulingPattern implements NextTime
while (st1.hasMoreTokens())
{
- int i;
- String localPattern = st1.nextToken();
- StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
- int tokCnt = st2.countTokens();
- if ((tokCnt < 5) || (tokCnt > 6))
+ final String localPattern = st1.nextToken();
+ final StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
+ if (st2.countTokens() != 5)
{
throw new RuntimeException("invalid pattern: \"" + localPattern + "\"");
}
try
{
- String minutePattern = st2.nextToken();
- String[] minutePatternParts = minutePattern.split(":");
- if (minutePatternParts.length > 1)
- {
- for (i = 0; i < (minutePatternParts.length - 1); ++i)
- {
- if (minutePatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (minutePatternParts[i].startsWith("~"))
- {
- _minuteAdderRnd.put(_matcherSize, Integer.parseInt(minutePatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + minutePatternParts[i] + "\"");
- }
- minutePattern = minutePatternParts[minutePatternParts.length - 1];
- }
- _minuteMatchers.add(buildValueMatcher(minutePattern, MINUTE_VALUE_PARSER));
+ _minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
}
catch (Exception e)
{
@@ -121,35 +246,9 @@ public class SchedulingPattern implements NextTime
try
{
- String hourPattern = st2.nextToken();
- String[] hourPatternParts = hourPattern.split(":");
- if (hourPatternParts.length > 1)
- {
- for (i = 0; i < (hourPatternParts.length - 1); ++i)
- {
- if (hourPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (hourPatternParts[i].startsWith("+"))
- {
- _hourAdder.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- if (hourPatternParts[i].startsWith("~"))
- {
- _hourAdderRnd.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + hourPatternParts[i] + "\"");
- }
- hourPattern = hourPatternParts[hourPatternParts.length - 1];
- }
- _hourMatchers.add(buildValueMatcher(hourPattern, HOUR_VALUE_PARSER));
+ _hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
}
+
catch (Exception e)
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
@@ -157,28 +256,7 @@ public class SchedulingPattern implements NextTime
try
{
- String dayOfMonthPattern = st2.nextToken();
- String[] dayOfMonthPatternParts = dayOfMonthPattern.split(":");
- if (dayOfMonthPatternParts.length > 1)
- {
- for (i = 0; i < (dayOfMonthPatternParts.length - 1); ++i)
- {
- if (dayOfMonthPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (dayOfMonthPatternParts[i].startsWith("+"))
- {
- _dayOfYearAdder.put(_matcherSize, Integer.parseInt(dayOfMonthPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown day modifier \"" + dayOfMonthPatternParts[i] + "\"");
- }
- dayOfMonthPattern = dayOfMonthPatternParts[dayOfMonthPatternParts.length - 1];
- }
- _dayOfMonthMatchers.add(buildValueMatcher(dayOfMonthPattern, DAY_OF_MONTH_VALUE_PARSER));
+ _dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
}
catch (Exception e)
{
@@ -202,28 +280,17 @@ public class SchedulingPattern implements NextTime
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
}
-
- if (st2.hasMoreTokens())
- {
- try
- {
- String weekOfYearAdderText = st2.nextToken();
- if (weekOfYearAdderText.charAt(0) != '+')
- {
- throw new RuntimeException("Unknown week of year addition in pattern \"" + localPattern + "\".");
- }
- weekOfYearAdderText = weekOfYearAdderText.substring(1);
- _weekOfYearAdder.put(_matcherSize, Integer.parseInt(weekOfYearAdderText));
- }
- catch (Exception e)
- {
- throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
- }
- }
- ++_matcherSize;
+ _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("*"))
@@ -231,12 +298,12 @@ public class SchedulingPattern implements NextTime
return new AlwaysTrueValueMatcher();
}
- ArrayList values = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(str, ",");
+ final List values = new ArrayList<>();
+ final StringTokenizer st = new StringTokenizer(str, ",");
while (st.hasMoreTokens())
{
+ final String element = st.nextToken();
List local;
- String element = st.nextToken();
try
{
local = parseListElement(element, parser);
@@ -245,15 +312,13 @@ public class SchedulingPattern implements NextTime
{
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
}
-
- for (Integer value : local)
+ for (Iterator i = local.iterator(); i.hasNext();)
{
- if (values.contains(value))
+ final Object value = i.next();
+ if (!values.contains(value))
{
- continue;
+ values.add((Integer) value);
}
-
- values.add(value);
}
}
@@ -270,16 +335,23 @@ public class SchedulingPattern implements NextTime
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 List parseListElement(String str, ValueParser parser) throws Exception
{
- List values;
- StringTokenizer st = new StringTokenizer(str, "/");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "/");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
+ List values;
try
{
values = parseRange(st.nextToken(), parser);
@@ -291,9 +363,8 @@ public class SchedulingPattern implements NextTime
if (size == 2)
{
+ final String dStr = st.nextToken();
int div;
- String dStr = st.nextToken();
-
try
{
div = Integer.parseInt(dStr);
@@ -308,41 +379,47 @@ public class SchedulingPattern implements NextTime
throw new Exception("non positive divisor \"" + div + "\"");
}
- ArrayList values2 = new ArrayList<>();
+ final List 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 List parseRange(String str, ValueParser parser) throws Exception
{
- int v2;
- int v1;
if (str.equals("*"))
{
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- ArrayList values = new ArrayList<>();
- for (int i = min; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ final List values = new ArrayList<>();
+ for (int i = min; i <= max; i++)
{
values.add(i);
}
return values;
}
- StringTokenizer st = new StringTokenizer(str, "-");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "-");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
- String v1Str = st.nextToken();
+ final String v1Str = st.nextToken();
+ int v1;
try
{
v1 = parser.parse(v1Str);
@@ -354,12 +431,13 @@ public class SchedulingPattern implements NextTime
if (size == 1)
{
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
values.add(v1);
return values;
}
- String v2Str = st.nextToken();
+ final String v2Str = st.nextToken();
+ int v2;
try
{
v2 = parser.parse(v2Str);
@@ -369,91 +447,90 @@ public class SchedulingPattern implements NextTime
throw new Exception("invalid value \"" + v2Str + "\", " + e.getMessage());
}
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
if (v1 < v2)
{
- for (int i = v1; i <= v2; ++i)
+ for (int i = v1; i <= v2; i++)
{
values.add(i);
}
}
else if (v1 > v2)
{
- int i;
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- for (i = v1; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ for (int i = v1; i <= max; i++)
{
values.add(i);
}
- for (i = min; i <= v2; ++i)
+ for (int i = min; i <= v2; i++)
{
values.add(i);
}
}
else
{
+ // v1 == v2
values.add(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(timezone);
+ final GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(millis);
- gc.set(13, 0);
- gc.set(14, 0);
- for (int i = 0; i < _matcherSize; ++i)
+ gc.setTimeZone(timezone);
+ final int minute = gc.get(Calendar.MINUTE);
+ final int hour = gc.get(Calendar.HOUR_OF_DAY);
+ final int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH);
+ final int month = gc.get(Calendar.MONTH) + 1;
+ final int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1;
+ final int year = gc.get(Calendar.YEAR);
+ for (int i = 0; i < _matcherSize; i++)
{
- boolean eval = false;
- if (_weekOfYearAdder.containsKey(i))
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+ if (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))
{
- gc.add(3, -_weekOfYearAdder.get(i).intValue());
+ return true;
}
- if (_dayOfYearAdder.containsKey(i))
- {
- gc.add(6, -_dayOfYearAdder.get(i).intValue());
- }
- if (_hourAdder.containsKey(i))
- {
- gc.add(10, -_hourAdder.get(i).intValue());
- }
- int minute = gc.get(MONTH_MAX_VALUE);
- int hour = gc.get(11);
- int dayOfMonth = gc.get(5);
- int month = gc.get(2) + 1;
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
- int year = gc.get(1);
- 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);
- @SuppressWarnings("unused")
- boolean bl = 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) ? true : (eval = false);
- if (!eval)
- {
- continue;
- }
- 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);
}
+ /**
+ * It returns the next matching moment as a millis value.
+ * @param timezone
+ * @param millis
+ * @return The next matching moment as a millis value.
+ */
public long next(TimeZone timezone, long millis)
{
- long result = -1L;
- GregorianCalendar gc = new GregorianCalendar(timezone);
+ long result = -1;
+ final GregorianCalendar gc = new GregorianCalendar(timezone);
for (int i = 0; i < _matcherSize; ++i)
{
- long next = -1L;
+ long next = -1;
gc.setTimeInMillis(millis);
gc.set(13, 0);
gc.set(14, 0);
@@ -469,27 +546,28 @@ public class SchedulingPattern implements NextTime
{
gc.add(10, _hourAdder.get(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);
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+
SEARCH: do
{
- int year = gc.get(1);
- boolean isLeapYear = gc.isLeapYear(year);
+ final int year = gc.get(1);
+ final boolean isLeapYear = gc.isLeapYear(year);
for (int month = gc.get(2) + 1; month <= MONTH_MAX_VALUE; ++month)
{
if (monthMatcher.match(month))
{
gc.set(2, month - 1);
- int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ final int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
for (int dayOfMonth = gc.get(5); dayOfMonth <= maxDayOfMonth; ++dayOfMonth)
{
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher ? ((DayOfMonthValueMatcher) dayOfMonthMatcher).match(dayOfMonth, month, isLeapYear) : dayOfMonthMatcher.match(dayOfMonth))
{
gc.set(5, dayOfMonth);
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
+ final int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
if (dayOfWeekMatcher.match(dayOfWeek))
{
for (int hour = gc.get(11); hour <= HOUR_MAX_VALUE; ++hour)
@@ -505,13 +583,13 @@ public class SchedulingPattern implements NextTime
}
gc.set(MONTH_MAX_VALUE, minute);
- long next0 = gc.getTimeInMillis();
+ final long next0 = gc.getTimeInMillis();
if (next0 <= millis)
{
continue;
}
- if ((next != -1L) && (next0 >= next))
+ if ((next != -1) && (next0 >= next))
{
break SEARCH;
}
@@ -519,7 +597,7 @@ public class SchedulingPattern implements NextTime
next = next0;
if (_hourAdderRnd.containsKey(i))
{
- next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000L;
+ next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000;
}
if (!_minuteAdderRnd.containsKey(i))
@@ -527,7 +605,7 @@ public class SchedulingPattern implements NextTime
break SEARCH;
}
- next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000L;
+ next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000;
break SEARCH;
}
}
@@ -550,7 +628,7 @@ public class SchedulingPattern implements NextTime
}
while (true);
- if ((next <= millis) || ((result != -1L) && (next >= result)))
+ if ((next <= millis) || ((result != -1) && (next >= result)))
{
continue;
}
@@ -560,57 +638,89 @@ public class SchedulingPattern implements NextTime
return result;
}
- @Override
+ /**
+ * It returns the next matching moment as a long.
+ * @param millis
+ * @return The next matching moment as a long.
+ */
public long next(long millis)
{
return next(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.
+ */
private static int parseAlias(String value, String[] aliases, int offset) throws Exception
{
- for (int i = 0; i < aliases.length; ++i)
+ for (int i = 0; i < aliases.length; i++)
{
- if (!aliases[i].equalsIgnoreCase(value))
+ if (aliases[i].equalsIgnoreCase(value))
{
- continue;
+ return offset + i;
}
- return offset + i;
}
throw new Exception("invalid alias \"" + value + "\"");
}
+ /**
+ *
+ * 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.
+ *
+ * @author Paul Fernley
+ */
private static class DayOfMonthValueMatcher extends IntArrayValueMatcher
{
- private static final int[] lastDays = new int[]
+ private static final int[] LAST_DAYS =
{
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
28,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE
+ 31
};
+ /**
+ * 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 or the last-day-of-month setting applies.
+ */
public DayOfMonthValueMatcher(List integers)
{
super(integers);
}
+ /**
+ * 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) && DayOfMonthValueMatcher.isLastDayOfMonth(value, month, isLeapYear));
+ return (super.match(value) || ((value > 27) && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
}
public static int getLastDayOfMonth(int month, boolean isLeapYear)
@@ -620,29 +730,41 @@ public class SchedulingPattern implements NextTime
return 29;
}
- return lastDays[month - 1];
+ return LAST_DAYS[month - 1];
}
public static boolean isLastDayOfMonth(int value, int month, boolean isLeapYear)
{
- return value == DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ return value == getLastDayOfMonth(month, isLeapYear);
}
}
+ /**
+ *
+ * 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.
+ *
+ * @author Carlo Pelliccia
+ */
private static class IntArrayValueMatcher implements ValueMatcher
{
- private final int[] values;
+ /**
+ * The accepted values.
+ */
+ private final int[] _values;
+ /**
+ * Builds the ValueMatcher.
+ * @param integers a List 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(List integers)
{
- int size = integers.size();
- values = new int[size];
- for (int i = 0; i < size; ++i)
+ final int size = integers.size();
+ _values = new int[size];
+ for (int i = 0; i < size; i++)
{
try
{
- values[i] = integers.get(i);
- continue;
+ _values[i] = integers.get(i).intValue();
}
catch (Exception e)
{
@@ -651,27 +773,32 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Returns true if the given value is included in the matcher list.
+ */
@Override
public boolean match(int value)
{
- for (int i = 0; i < values.length; ++i)
+ for (int i = 0; i < _values.length; i++)
{
- if (values[i] != value)
+ if (_values[i] == value)
{
- continue;
+ return true;
}
- return true;
}
return false;
}
}
+ /**
+ * This ValueMatcher always returns true!
+ * @author Carlo Pelliccia
+ */
private static class AlwaysTrueValueMatcher implements ValueMatcher
{
- private AlwaysTrueValueMatcher()
- {
- }
-
+ /**
+ * Always true!
+ */
@Override
public boolean match(int value)
{
@@ -679,14 +806,31 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ *
+ * This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
+ *
+ * @author Carlo Pelliccia
+ */
private static interface ValueMatcher
{
- public boolean match(int var1);
+ /**
+ * 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);
}
+ /**
+ * The value parser for the day of week field.
+ */
private static class DayOfWeekValueParser extends SimpleValueParser
{
- private static String[] ALIASES = new String[]
+ /**
+ * Days of week aliases.
+ */
+ private static String[] ALIASES =
{
"sun",
"mon",
@@ -697,6 +841,9 @@ public class SchedulingPattern implements NextTime
"sat"
};
+ /**
+ * Builds the day value parser.
+ */
public DayOfWeekValueParser()
{
super(DAY_OF_WEEK_MIN_VALUE, DAY_OF_WEEK_MAX_VALUE);
@@ -707,17 +854,25 @@ public class SchedulingPattern implements NextTime
{
try
{
- return super.parse(value) % DAY_OF_WEEK_MAX_VALUE;
+ // try as a simple value
+ return super.parse(value) % 7;
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 0);
+ // try as an alias
+ return parseAlias(value, ALIASES, 0);
}
}
}
+ /**
+ * The value parser for the months field.
+ */
private static class MonthValueParser extends SimpleValueParser
{
+ /**
+ * Months of year aliases.
+ */
private static String[] ALIASES = new String[]
{
"jan",
@@ -734,6 +889,9 @@ public class SchedulingPattern implements NextTime
"dec"
};
+ /**
+ * Builds the months value parser.
+ */
public MonthValueParser()
{
super(MONTH_MIN_VALUE, MONTH_MAX_VALUE);
@@ -748,13 +906,19 @@ public class SchedulingPattern implements NextTime
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 1);
+ return parseAlias(value, ALIASES, 1);
}
}
}
+ /**
+ * The value parser for the day of month field.
+ */
private static class DayOfMonthValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public DayOfMonthValueParser()
{
super(DAY_OF_MONTH_MIN_VALUE, DAY_OF_MONTH_MAX_VALUE);
@@ -771,27 +935,54 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * The value parser for the hour field.
+ */
private static class HourValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public HourValueParser()
{
super(HOUR_MIN_VALUE, HOUR_MAX_VALUE);
}
}
+ /**
+ * The minutes value parser.
+ */
private static class MinuteValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public MinuteValueParser()
{
super(MINUTE_MIN_VALUE, MINUTE_MAX_VALUE);
}
}
+ /**
+ * 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)
{
_minValue = minValue;
@@ -830,12 +1021,29 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Definition for a value parser.
+ */
private static interface ValueParser
{
- public int parse(String var1) throws Exception;
+ /**
+ * 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 accepted by the parser.
+ * @return The minimum value accepted by the parser.
+ */
public int getMinValue();
+ /**
+ * Returns the maximum value accepted by the parser.
+ * @return The maximum value accepted by the parser.
+ */
public int getMaxValue();
}
}
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/time/AddPattern.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/time/AddPattern.java
deleted file mode 100644
index 893a279fd4..0000000000
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/time/AddPattern.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-public class AddPattern implements NextTime
-{
- private int _monthInc = -1;
- private int _monthSet = -1;
- private int _dayOfMonthInc = -1;
- private int _dayOfMonthSet = -1;
- private int _hourOfDayInc = -1;
- private int _hourOfDaySet = -1;
- private int _minuteInc = -1;
- private int _minuteSet = -1;
-
- public AddPattern(String pattern)
- {
- String[] timeparts;
- String[] parts = pattern.split("\\s+");
- if (parts.length == 2)
- {
- String datemodstr;
- String datepartsstr = parts[0];
- String[] dateparts = datepartsstr.split(":");
- if (dateparts.length == 2)
- {
- if (dateparts[0].startsWith("+"))
- {
- _monthInc = Integer.parseInt(dateparts[0].substring(1));
- }
- else
- {
- _monthSet = Integer.parseInt(dateparts[0]) - 1;
- }
- }
- if ((datemodstr = dateparts[dateparts.length - 1]).startsWith("+"))
- {
- _dayOfMonthInc = Integer.parseInt(datemodstr.substring(1));
- }
- else
- {
- _dayOfMonthSet = Integer.parseInt(datemodstr);
- }
- }
- if ((timeparts = parts[parts.length - 1].split(":"))[0].startsWith("+"))
- {
- _hourOfDayInc = Integer.parseInt(timeparts[0].substring(1));
- }
- else
- {
- _hourOfDaySet = Integer.parseInt(timeparts[0]);
- }
- if (timeparts[1].startsWith("+"))
- {
- _minuteInc = Integer.parseInt(timeparts[1].substring(1));
- }
- else
- {
- _minuteSet = Integer.parseInt(timeparts[1]);
- }
- }
-
- @Override
- public long next(long millis)
- {
- final GregorianCalendar gc = new GregorianCalendar(TimeZone.getDefault());
- gc.setTimeInMillis(millis);
- if (_monthInc >= 0)
- {
- gc.add(2, _monthInc);
- }
- if (_monthSet >= 0)
- {
- gc.set(2, _monthSet);
- }
- if (_dayOfMonthInc >= 0)
- {
- gc.add(5, _dayOfMonthInc);
- }
- if (_dayOfMonthSet >= 0)
- {
- gc.set(5, _dayOfMonthSet);
- }
- if (_hourOfDayInc >= 0)
- {
- gc.add(11, _hourOfDayInc);
- }
- if (_hourOfDaySet >= 0)
- {
- gc.set(11, _hourOfDaySet);
- }
- if (_minuteInc >= 0)
- {
- gc.add(12, _minuteInc);
- }
- if (_minuteSet >= 0)
- {
- gc.set(12, _minuteSet);
- }
- return gc.getTimeInMillis();
- }
-}
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/time/NextTime.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/time/NextTime.java
deleted file mode 100644
index d08d90164d..0000000000
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/time/NextTime.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-public interface NextTime
-{
- public long next(long var1);
-}
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/time/SchedulingPattern.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/time/SchedulingPattern.java
index d9fdd655f3..a9677e31ae 100644
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/time/SchedulingPattern.java
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/time/SchedulingPattern.java
@@ -1,23 +1,25 @@
/*
- * This file is part of the L2J Mobius project.
+ * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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.
- *
+ * it under the terms of the GNU Lesser General Public License version
+ * 2.1, as published by the Free Software Foundation.
+ *
* 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 .
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License 2.1 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License version 2.1 along with this program.
+ * If not, see .
*/
package org.l2jmobius.commons.time;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
@@ -26,7 +28,94 @@ import java.util.TreeMap;
import org.l2jmobius.commons.util.Rnd;
-public class SchedulingPattern implements NextTime
+/**
+ *
+ * A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
+ *
+ *
+ * - Minutes sub-pattern. During which minutes of the hour should the task been launched? The values range is from 0 to 59.
+ * - Hours sub-pattern. During which hours of the day should the task been launched? The values range is from 0 to 23.
+ * - Days of month sub-pattern. 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.
+ * - Months sub-pattern. 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".
+ * - Days of week sub-pattern. 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".
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * Some examples:
+ *
+ *
+ * 5 * * * *
+ * 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.).
+ *
+ *
+ * * * * * *
+ * This pattern causes a task to be launched every minute.
+ *
+ *
+ * * 12 * * Mon
+ * This pattern causes a task to be launched every minute during the 12th hour of Monday.
+ *
+ *
+ * * 12 16 * Mon
+ * 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.
+ *
+ *
+ * Every sub-pattern can contain two or more comma separated values.
+ *
+ *
+ * 59 11 * * 1,2,3,4,5
+ * This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
+ *
+ *
+ * Values intervals are admitted and defined using the minus character.
+ *
+ *
+ * 59 11 * * 1-5
+ * This pattern is equivalent to the previous one.
+ *
+ *
+ * The slash character can be used to identify step values within a range. It can be used both in the form */c and a-b/c. The subpattern is matched every c values of the range 0,maxvalue or a-b.
+ *
+ *
+ * */5 * * * *
+ * This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
+ *
+ *
+ * 3-18/5 * * * *
+ * 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).
+ *
+ *
+ * */15 9-17 * * *
+ * 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).
+ *
+ *
+ * All the fresh described syntax rules can be used together.
+ *
+ *
+ * * 12 10-16/2 * *
+ * 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.
+ *
+ *
+ * * 12 1-15,17,20-25 * *
+ * 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.
+ *
+ *
+ * Finally lets you combine more scheduling patterns into one, with the pipe character:
+ *
+ *
+ * 0 5 * * *|8 10 * * *|22 17 * * *
+ * This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
+ *
+ * @author Carlo Pelliccia
+ */
+public class SchedulingPattern
{
private static final int MINUTE_MIN_VALUE = 0;
private static final int MINUTE_MAX_VALUE = 59;
@@ -38,24 +127,78 @@ public class SchedulingPattern implements NextTime
private static final int MONTH_MAX_VALUE = 12;
private static final int DAY_OF_WEEK_MIN_VALUE = 0;
private static final int DAY_OF_WEEK_MAX_VALUE = 7;
+
+ /**
+ * 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();
+
+ /**
+ * The pattern as a string.
+ */
private final String _asString;
+
+ /**
+ * The ValueMatcher list for the "minute" field.
+ */
protected List _minuteMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "hour" field.
+ */
protected List _hourMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of month" field.
+ */
protected List _dayOfMonthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "month" field.
+ */
protected List _monthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of week" field.
+ */
protected List _dayOfWeekMatchers = new ArrayList<>();
+
+ /**
+ * How many matcher groups in this pattern?
+ */
protected int _matcherSize = 0;
+
protected Map _hourAdder = new TreeMap<>();
protected Map _hourAdderRnd = new TreeMap<>();
protected Map _dayOfYearAdder = new TreeMap<>();
protected Map _minuteAdderRnd = new TreeMap<>();
protected Map _weekOfYearAdder = new TreeMap<>();
+ /**
+ * 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
@@ -69,10 +212,15 @@ public class SchedulingPattern implements NextTime
return true;
}
+ /**
+ * Builds a SchedulingPattern parsing it from a string.
+ * @param pattern The pattern as a crontab-like string.
+ * @throws RuntimeException If the supplied string is not a valid pattern.
+ */
public SchedulingPattern(String pattern) throws RuntimeException
{
_asString = pattern;
- StringTokenizer st1 = new StringTokenizer(pattern, "|");
+ final StringTokenizer st1 = new StringTokenizer(pattern, "|");
if (st1.countTokens() < 1)
{
throw new RuntimeException("invalid pattern: \"" + pattern + "\"");
@@ -80,39 +228,16 @@ public class SchedulingPattern implements NextTime
while (st1.hasMoreTokens())
{
- int i;
- String localPattern = st1.nextToken();
- StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
- int tokCnt = st2.countTokens();
- if ((tokCnt < 5) || (tokCnt > 6))
+ final String localPattern = st1.nextToken();
+ final StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
+ if (st2.countTokens() != 5)
{
throw new RuntimeException("invalid pattern: \"" + localPattern + "\"");
}
try
{
- String minutePattern = st2.nextToken();
- String[] minutePatternParts = minutePattern.split(":");
- if (minutePatternParts.length > 1)
- {
- for (i = 0; i < (minutePatternParts.length - 1); ++i)
- {
- if (minutePatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (minutePatternParts[i].startsWith("~"))
- {
- _minuteAdderRnd.put(_matcherSize, Integer.parseInt(minutePatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + minutePatternParts[i] + "\"");
- }
- minutePattern = minutePatternParts[minutePatternParts.length - 1];
- }
- _minuteMatchers.add(buildValueMatcher(minutePattern, MINUTE_VALUE_PARSER));
+ _minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
}
catch (Exception e)
{
@@ -121,35 +246,9 @@ public class SchedulingPattern implements NextTime
try
{
- String hourPattern = st2.nextToken();
- String[] hourPatternParts = hourPattern.split(":");
- if (hourPatternParts.length > 1)
- {
- for (i = 0; i < (hourPatternParts.length - 1); ++i)
- {
- if (hourPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (hourPatternParts[i].startsWith("+"))
- {
- _hourAdder.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- if (hourPatternParts[i].startsWith("~"))
- {
- _hourAdderRnd.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + hourPatternParts[i] + "\"");
- }
- hourPattern = hourPatternParts[hourPatternParts.length - 1];
- }
- _hourMatchers.add(buildValueMatcher(hourPattern, HOUR_VALUE_PARSER));
+ _hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
}
+
catch (Exception e)
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
@@ -157,28 +256,7 @@ public class SchedulingPattern implements NextTime
try
{
- String dayOfMonthPattern = st2.nextToken();
- String[] dayOfMonthPatternParts = dayOfMonthPattern.split(":");
- if (dayOfMonthPatternParts.length > 1)
- {
- for (i = 0; i < (dayOfMonthPatternParts.length - 1); ++i)
- {
- if (dayOfMonthPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (dayOfMonthPatternParts[i].startsWith("+"))
- {
- _dayOfYearAdder.put(_matcherSize, Integer.parseInt(dayOfMonthPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown day modifier \"" + dayOfMonthPatternParts[i] + "\"");
- }
- dayOfMonthPattern = dayOfMonthPatternParts[dayOfMonthPatternParts.length - 1];
- }
- _dayOfMonthMatchers.add(buildValueMatcher(dayOfMonthPattern, DAY_OF_MONTH_VALUE_PARSER));
+ _dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
}
catch (Exception e)
{
@@ -202,28 +280,17 @@ public class SchedulingPattern implements NextTime
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
}
-
- if (st2.hasMoreTokens())
- {
- try
- {
- String weekOfYearAdderText = st2.nextToken();
- if (weekOfYearAdderText.charAt(0) != '+')
- {
- throw new RuntimeException("Unknown week of year addition in pattern \"" + localPattern + "\".");
- }
- weekOfYearAdderText = weekOfYearAdderText.substring(1);
- _weekOfYearAdder.put(_matcherSize, Integer.parseInt(weekOfYearAdderText));
- }
- catch (Exception e)
- {
- throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
- }
- }
- ++_matcherSize;
+ _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("*"))
@@ -231,12 +298,12 @@ public class SchedulingPattern implements NextTime
return new AlwaysTrueValueMatcher();
}
- ArrayList values = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(str, ",");
+ final List values = new ArrayList<>();
+ final StringTokenizer st = new StringTokenizer(str, ",");
while (st.hasMoreTokens())
{
+ final String element = st.nextToken();
List local;
- String element = st.nextToken();
try
{
local = parseListElement(element, parser);
@@ -245,15 +312,13 @@ public class SchedulingPattern implements NextTime
{
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
}
-
- for (Integer value : local)
+ for (Iterator i = local.iterator(); i.hasNext();)
{
- if (values.contains(value))
+ final Object value = i.next();
+ if (!values.contains(value))
{
- continue;
+ values.add((Integer) value);
}
-
- values.add(value);
}
}
@@ -270,16 +335,23 @@ public class SchedulingPattern implements NextTime
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 List parseListElement(String str, ValueParser parser) throws Exception
{
- List values;
- StringTokenizer st = new StringTokenizer(str, "/");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "/");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
+ List values;
try
{
values = parseRange(st.nextToken(), parser);
@@ -291,9 +363,8 @@ public class SchedulingPattern implements NextTime
if (size == 2)
{
+ final String dStr = st.nextToken();
int div;
- String dStr = st.nextToken();
-
try
{
div = Integer.parseInt(dStr);
@@ -308,41 +379,47 @@ public class SchedulingPattern implements NextTime
throw new Exception("non positive divisor \"" + div + "\"");
}
- ArrayList values2 = new ArrayList<>();
+ final List 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 List parseRange(String str, ValueParser parser) throws Exception
{
- int v2;
- int v1;
if (str.equals("*"))
{
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- ArrayList values = new ArrayList<>();
- for (int i = min; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ final List values = new ArrayList<>();
+ for (int i = min; i <= max; i++)
{
values.add(i);
}
return values;
}
- StringTokenizer st = new StringTokenizer(str, "-");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "-");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
- String v1Str = st.nextToken();
+ final String v1Str = st.nextToken();
+ int v1;
try
{
v1 = parser.parse(v1Str);
@@ -354,12 +431,13 @@ public class SchedulingPattern implements NextTime
if (size == 1)
{
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
values.add(v1);
return values;
}
- String v2Str = st.nextToken();
+ final String v2Str = st.nextToken();
+ int v2;
try
{
v2 = parser.parse(v2Str);
@@ -369,91 +447,90 @@ public class SchedulingPattern implements NextTime
throw new Exception("invalid value \"" + v2Str + "\", " + e.getMessage());
}
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
if (v1 < v2)
{
- for (int i = v1; i <= v2; ++i)
+ for (int i = v1; i <= v2; i++)
{
values.add(i);
}
}
else if (v1 > v2)
{
- int i;
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- for (i = v1; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ for (int i = v1; i <= max; i++)
{
values.add(i);
}
- for (i = min; i <= v2; ++i)
+ for (int i = min; i <= v2; i++)
{
values.add(i);
}
}
else
{
+ // v1 == v2
values.add(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(timezone);
+ final GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(millis);
- gc.set(13, 0);
- gc.set(14, 0);
- for (int i = 0; i < _matcherSize; ++i)
+ gc.setTimeZone(timezone);
+ final int minute = gc.get(Calendar.MINUTE);
+ final int hour = gc.get(Calendar.HOUR_OF_DAY);
+ final int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH);
+ final int month = gc.get(Calendar.MONTH) + 1;
+ final int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1;
+ final int year = gc.get(Calendar.YEAR);
+ for (int i = 0; i < _matcherSize; i++)
{
- boolean eval = false;
- if (_weekOfYearAdder.containsKey(i))
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+ if (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))
{
- gc.add(3, -_weekOfYearAdder.get(i).intValue());
+ return true;
}
- if (_dayOfYearAdder.containsKey(i))
- {
- gc.add(6, -_dayOfYearAdder.get(i).intValue());
- }
- if (_hourAdder.containsKey(i))
- {
- gc.add(10, -_hourAdder.get(i).intValue());
- }
- int minute = gc.get(MONTH_MAX_VALUE);
- int hour = gc.get(11);
- int dayOfMonth = gc.get(5);
- int month = gc.get(2) + 1;
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
- int year = gc.get(1);
- 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);
- @SuppressWarnings("unused")
- boolean bl = 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) ? true : (eval = false);
- if (!eval)
- {
- continue;
- }
- 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);
}
+ /**
+ * It returns the next matching moment as a millis value.
+ * @param timezone
+ * @param millis
+ * @return The next matching moment as a millis value.
+ */
public long next(TimeZone timezone, long millis)
{
- long result = -1L;
- GregorianCalendar gc = new GregorianCalendar(timezone);
+ long result = -1;
+ final GregorianCalendar gc = new GregorianCalendar(timezone);
for (int i = 0; i < _matcherSize; ++i)
{
- long next = -1L;
+ long next = -1;
gc.setTimeInMillis(millis);
gc.set(13, 0);
gc.set(14, 0);
@@ -469,27 +546,28 @@ public class SchedulingPattern implements NextTime
{
gc.add(10, _hourAdder.get(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);
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+
SEARCH: do
{
- int year = gc.get(1);
- boolean isLeapYear = gc.isLeapYear(year);
+ final int year = gc.get(1);
+ final boolean isLeapYear = gc.isLeapYear(year);
for (int month = gc.get(2) + 1; month <= MONTH_MAX_VALUE; ++month)
{
if (monthMatcher.match(month))
{
gc.set(2, month - 1);
- int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ final int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
for (int dayOfMonth = gc.get(5); dayOfMonth <= maxDayOfMonth; ++dayOfMonth)
{
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher ? ((DayOfMonthValueMatcher) dayOfMonthMatcher).match(dayOfMonth, month, isLeapYear) : dayOfMonthMatcher.match(dayOfMonth))
{
gc.set(5, dayOfMonth);
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
+ final int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
if (dayOfWeekMatcher.match(dayOfWeek))
{
for (int hour = gc.get(11); hour <= HOUR_MAX_VALUE; ++hour)
@@ -505,13 +583,13 @@ public class SchedulingPattern implements NextTime
}
gc.set(MONTH_MAX_VALUE, minute);
- long next0 = gc.getTimeInMillis();
+ final long next0 = gc.getTimeInMillis();
if (next0 <= millis)
{
continue;
}
- if ((next != -1L) && (next0 >= next))
+ if ((next != -1) && (next0 >= next))
{
break SEARCH;
}
@@ -519,7 +597,7 @@ public class SchedulingPattern implements NextTime
next = next0;
if (_hourAdderRnd.containsKey(i))
{
- next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000L;
+ next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000;
}
if (!_minuteAdderRnd.containsKey(i))
@@ -527,7 +605,7 @@ public class SchedulingPattern implements NextTime
break SEARCH;
}
- next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000L;
+ next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000;
break SEARCH;
}
}
@@ -550,7 +628,7 @@ public class SchedulingPattern implements NextTime
}
while (true);
- if ((next <= millis) || ((result != -1L) && (next >= result)))
+ if ((next <= millis) || ((result != -1) && (next >= result)))
{
continue;
}
@@ -560,57 +638,89 @@ public class SchedulingPattern implements NextTime
return result;
}
- @Override
+ /**
+ * It returns the next matching moment as a long.
+ * @param millis
+ * @return The next matching moment as a long.
+ */
public long next(long millis)
{
return next(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.
+ */
private static int parseAlias(String value, String[] aliases, int offset) throws Exception
{
- for (int i = 0; i < aliases.length; ++i)
+ for (int i = 0; i < aliases.length; i++)
{
- if (!aliases[i].equalsIgnoreCase(value))
+ if (aliases[i].equalsIgnoreCase(value))
{
- continue;
+ return offset + i;
}
- return offset + i;
}
throw new Exception("invalid alias \"" + value + "\"");
}
+ /**
+ *
+ * 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.
+ *
+ * @author Paul Fernley
+ */
private static class DayOfMonthValueMatcher extends IntArrayValueMatcher
{
- private static final int[] lastDays = new int[]
+ private static final int[] LAST_DAYS =
{
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
28,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE
+ 31
};
+ /**
+ * 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 or the last-day-of-month setting applies.
+ */
public DayOfMonthValueMatcher(List integers)
{
super(integers);
}
+ /**
+ * 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) && DayOfMonthValueMatcher.isLastDayOfMonth(value, month, isLeapYear));
+ return (super.match(value) || ((value > 27) && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
}
public static int getLastDayOfMonth(int month, boolean isLeapYear)
@@ -620,29 +730,41 @@ public class SchedulingPattern implements NextTime
return 29;
}
- return lastDays[month - 1];
+ return LAST_DAYS[month - 1];
}
public static boolean isLastDayOfMonth(int value, int month, boolean isLeapYear)
{
- return value == DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ return value == getLastDayOfMonth(month, isLeapYear);
}
}
+ /**
+ *
+ * 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.
+ *
+ * @author Carlo Pelliccia
+ */
private static class IntArrayValueMatcher implements ValueMatcher
{
- private final int[] values;
+ /**
+ * The accepted values.
+ */
+ private final int[] _values;
+ /**
+ * Builds the ValueMatcher.
+ * @param integers a List 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(List integers)
{
- int size = integers.size();
- values = new int[size];
- for (int i = 0; i < size; ++i)
+ final int size = integers.size();
+ _values = new int[size];
+ for (int i = 0; i < size; i++)
{
try
{
- values[i] = integers.get(i);
- continue;
+ _values[i] = integers.get(i).intValue();
}
catch (Exception e)
{
@@ -651,27 +773,32 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Returns true if the given value is included in the matcher list.
+ */
@Override
public boolean match(int value)
{
- for (int i = 0; i < values.length; ++i)
+ for (int i = 0; i < _values.length; i++)
{
- if (values[i] != value)
+ if (_values[i] == value)
{
- continue;
+ return true;
}
- return true;
}
return false;
}
}
+ /**
+ * This ValueMatcher always returns true!
+ * @author Carlo Pelliccia
+ */
private static class AlwaysTrueValueMatcher implements ValueMatcher
{
- private AlwaysTrueValueMatcher()
- {
- }
-
+ /**
+ * Always true!
+ */
@Override
public boolean match(int value)
{
@@ -679,14 +806,31 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ *
+ * This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
+ *
+ * @author Carlo Pelliccia
+ */
private static interface ValueMatcher
{
- public boolean match(int var1);
+ /**
+ * 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);
}
+ /**
+ * The value parser for the day of week field.
+ */
private static class DayOfWeekValueParser extends SimpleValueParser
{
- private static String[] ALIASES = new String[]
+ /**
+ * Days of week aliases.
+ */
+ private static String[] ALIASES =
{
"sun",
"mon",
@@ -697,6 +841,9 @@ public class SchedulingPattern implements NextTime
"sat"
};
+ /**
+ * Builds the day value parser.
+ */
public DayOfWeekValueParser()
{
super(DAY_OF_WEEK_MIN_VALUE, DAY_OF_WEEK_MAX_VALUE);
@@ -707,17 +854,25 @@ public class SchedulingPattern implements NextTime
{
try
{
- return super.parse(value) % DAY_OF_WEEK_MAX_VALUE;
+ // try as a simple value
+ return super.parse(value) % 7;
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 0);
+ // try as an alias
+ return parseAlias(value, ALIASES, 0);
}
}
}
+ /**
+ * The value parser for the months field.
+ */
private static class MonthValueParser extends SimpleValueParser
{
+ /**
+ * Months of year aliases.
+ */
private static String[] ALIASES = new String[]
{
"jan",
@@ -734,6 +889,9 @@ public class SchedulingPattern implements NextTime
"dec"
};
+ /**
+ * Builds the months value parser.
+ */
public MonthValueParser()
{
super(MONTH_MIN_VALUE, MONTH_MAX_VALUE);
@@ -748,13 +906,19 @@ public class SchedulingPattern implements NextTime
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 1);
+ return parseAlias(value, ALIASES, 1);
}
}
}
+ /**
+ * The value parser for the day of month field.
+ */
private static class DayOfMonthValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public DayOfMonthValueParser()
{
super(DAY_OF_MONTH_MIN_VALUE, DAY_OF_MONTH_MAX_VALUE);
@@ -771,27 +935,54 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * The value parser for the hour field.
+ */
private static class HourValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public HourValueParser()
{
super(HOUR_MIN_VALUE, HOUR_MAX_VALUE);
}
}
+ /**
+ * The minutes value parser.
+ */
private static class MinuteValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public MinuteValueParser()
{
super(MINUTE_MIN_VALUE, MINUTE_MAX_VALUE);
}
}
+ /**
+ * 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)
{
_minValue = minValue;
@@ -830,12 +1021,29 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Definition for a value parser.
+ */
private static interface ValueParser
{
- public int parse(String var1) throws Exception;
+ /**
+ * 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 accepted by the parser.
+ * @return The minimum value accepted by the parser.
+ */
public int getMinValue();
+ /**
+ * Returns the maximum value accepted by the parser.
+ * @return The maximum value accepted by the parser.
+ */
public int getMaxValue();
}
}
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/time/AddPattern.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/time/AddPattern.java
deleted file mode 100644
index 893a279fd4..0000000000
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/time/AddPattern.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-public class AddPattern implements NextTime
-{
- private int _monthInc = -1;
- private int _monthSet = -1;
- private int _dayOfMonthInc = -1;
- private int _dayOfMonthSet = -1;
- private int _hourOfDayInc = -1;
- private int _hourOfDaySet = -1;
- private int _minuteInc = -1;
- private int _minuteSet = -1;
-
- public AddPattern(String pattern)
- {
- String[] timeparts;
- String[] parts = pattern.split("\\s+");
- if (parts.length == 2)
- {
- String datemodstr;
- String datepartsstr = parts[0];
- String[] dateparts = datepartsstr.split(":");
- if (dateparts.length == 2)
- {
- if (dateparts[0].startsWith("+"))
- {
- _monthInc = Integer.parseInt(dateparts[0].substring(1));
- }
- else
- {
- _monthSet = Integer.parseInt(dateparts[0]) - 1;
- }
- }
- if ((datemodstr = dateparts[dateparts.length - 1]).startsWith("+"))
- {
- _dayOfMonthInc = Integer.parseInt(datemodstr.substring(1));
- }
- else
- {
- _dayOfMonthSet = Integer.parseInt(datemodstr);
- }
- }
- if ((timeparts = parts[parts.length - 1].split(":"))[0].startsWith("+"))
- {
- _hourOfDayInc = Integer.parseInt(timeparts[0].substring(1));
- }
- else
- {
- _hourOfDaySet = Integer.parseInt(timeparts[0]);
- }
- if (timeparts[1].startsWith("+"))
- {
- _minuteInc = Integer.parseInt(timeparts[1].substring(1));
- }
- else
- {
- _minuteSet = Integer.parseInt(timeparts[1]);
- }
- }
-
- @Override
- public long next(long millis)
- {
- final GregorianCalendar gc = new GregorianCalendar(TimeZone.getDefault());
- gc.setTimeInMillis(millis);
- if (_monthInc >= 0)
- {
- gc.add(2, _monthInc);
- }
- if (_monthSet >= 0)
- {
- gc.set(2, _monthSet);
- }
- if (_dayOfMonthInc >= 0)
- {
- gc.add(5, _dayOfMonthInc);
- }
- if (_dayOfMonthSet >= 0)
- {
- gc.set(5, _dayOfMonthSet);
- }
- if (_hourOfDayInc >= 0)
- {
- gc.add(11, _hourOfDayInc);
- }
- if (_hourOfDaySet >= 0)
- {
- gc.set(11, _hourOfDaySet);
- }
- if (_minuteInc >= 0)
- {
- gc.add(12, _minuteInc);
- }
- if (_minuteSet >= 0)
- {
- gc.set(12, _minuteSet);
- }
- return gc.getTimeInMillis();
- }
-}
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/time/NextTime.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/time/NextTime.java
deleted file mode 100644
index d08d90164d..0000000000
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/time/NextTime.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-public interface NextTime
-{
- public long next(long var1);
-}
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/time/SchedulingPattern.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/time/SchedulingPattern.java
index d9fdd655f3..a9677e31ae 100644
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/time/SchedulingPattern.java
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/time/SchedulingPattern.java
@@ -1,23 +1,25 @@
/*
- * This file is part of the L2J Mobius project.
+ * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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.
- *
+ * it under the terms of the GNU Lesser General Public License version
+ * 2.1, as published by the Free Software Foundation.
+ *
* 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 .
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License 2.1 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License version 2.1 along with this program.
+ * If not, see .
*/
package org.l2jmobius.commons.time;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
@@ -26,7 +28,94 @@ import java.util.TreeMap;
import org.l2jmobius.commons.util.Rnd;
-public class SchedulingPattern implements NextTime
+/**
+ *
+ * A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
+ *
+ *
+ * - Minutes sub-pattern. During which minutes of the hour should the task been launched? The values range is from 0 to 59.
+ * - Hours sub-pattern. During which hours of the day should the task been launched? The values range is from 0 to 23.
+ * - Days of month sub-pattern. 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.
+ * - Months sub-pattern. 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".
+ * - Days of week sub-pattern. 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".
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * Some examples:
+ *
+ *
+ * 5 * * * *
+ * 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.).
+ *
+ *
+ * * * * * *
+ * This pattern causes a task to be launched every minute.
+ *
+ *
+ * * 12 * * Mon
+ * This pattern causes a task to be launched every minute during the 12th hour of Monday.
+ *
+ *
+ * * 12 16 * Mon
+ * 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.
+ *
+ *
+ * Every sub-pattern can contain two or more comma separated values.
+ *
+ *
+ * 59 11 * * 1,2,3,4,5
+ * This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
+ *
+ *
+ * Values intervals are admitted and defined using the minus character.
+ *
+ *
+ * 59 11 * * 1-5
+ * This pattern is equivalent to the previous one.
+ *
+ *
+ * The slash character can be used to identify step values within a range. It can be used both in the form */c and a-b/c. The subpattern is matched every c values of the range 0,maxvalue or a-b.
+ *
+ *
+ * */5 * * * *
+ * This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
+ *
+ *
+ * 3-18/5 * * * *
+ * 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).
+ *
+ *
+ * */15 9-17 * * *
+ * 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).
+ *
+ *
+ * All the fresh described syntax rules can be used together.
+ *
+ *
+ * * 12 10-16/2 * *
+ * 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.
+ *
+ *
+ * * 12 1-15,17,20-25 * *
+ * 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.
+ *
+ *
+ * Finally lets you combine more scheduling patterns into one, with the pipe character:
+ *
+ *
+ * 0 5 * * *|8 10 * * *|22 17 * * *
+ * This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
+ *
+ * @author Carlo Pelliccia
+ */
+public class SchedulingPattern
{
private static final int MINUTE_MIN_VALUE = 0;
private static final int MINUTE_MAX_VALUE = 59;
@@ -38,24 +127,78 @@ public class SchedulingPattern implements NextTime
private static final int MONTH_MAX_VALUE = 12;
private static final int DAY_OF_WEEK_MIN_VALUE = 0;
private static final int DAY_OF_WEEK_MAX_VALUE = 7;
+
+ /**
+ * 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();
+
+ /**
+ * The pattern as a string.
+ */
private final String _asString;
+
+ /**
+ * The ValueMatcher list for the "minute" field.
+ */
protected List _minuteMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "hour" field.
+ */
protected List _hourMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of month" field.
+ */
protected List _dayOfMonthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "month" field.
+ */
protected List _monthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of week" field.
+ */
protected List _dayOfWeekMatchers = new ArrayList<>();
+
+ /**
+ * How many matcher groups in this pattern?
+ */
protected int _matcherSize = 0;
+
protected Map _hourAdder = new TreeMap<>();
protected Map _hourAdderRnd = new TreeMap<>();
protected Map _dayOfYearAdder = new TreeMap<>();
protected Map _minuteAdderRnd = new TreeMap<>();
protected Map _weekOfYearAdder = new TreeMap<>();
+ /**
+ * 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
@@ -69,10 +212,15 @@ public class SchedulingPattern implements NextTime
return true;
}
+ /**
+ * Builds a SchedulingPattern parsing it from a string.
+ * @param pattern The pattern as a crontab-like string.
+ * @throws RuntimeException If the supplied string is not a valid pattern.
+ */
public SchedulingPattern(String pattern) throws RuntimeException
{
_asString = pattern;
- StringTokenizer st1 = new StringTokenizer(pattern, "|");
+ final StringTokenizer st1 = new StringTokenizer(pattern, "|");
if (st1.countTokens() < 1)
{
throw new RuntimeException("invalid pattern: \"" + pattern + "\"");
@@ -80,39 +228,16 @@ public class SchedulingPattern implements NextTime
while (st1.hasMoreTokens())
{
- int i;
- String localPattern = st1.nextToken();
- StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
- int tokCnt = st2.countTokens();
- if ((tokCnt < 5) || (tokCnt > 6))
+ final String localPattern = st1.nextToken();
+ final StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
+ if (st2.countTokens() != 5)
{
throw new RuntimeException("invalid pattern: \"" + localPattern + "\"");
}
try
{
- String minutePattern = st2.nextToken();
- String[] minutePatternParts = minutePattern.split(":");
- if (minutePatternParts.length > 1)
- {
- for (i = 0; i < (minutePatternParts.length - 1); ++i)
- {
- if (minutePatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (minutePatternParts[i].startsWith("~"))
- {
- _minuteAdderRnd.put(_matcherSize, Integer.parseInt(minutePatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + minutePatternParts[i] + "\"");
- }
- minutePattern = minutePatternParts[minutePatternParts.length - 1];
- }
- _minuteMatchers.add(buildValueMatcher(minutePattern, MINUTE_VALUE_PARSER));
+ _minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
}
catch (Exception e)
{
@@ -121,35 +246,9 @@ public class SchedulingPattern implements NextTime
try
{
- String hourPattern = st2.nextToken();
- String[] hourPatternParts = hourPattern.split(":");
- if (hourPatternParts.length > 1)
- {
- for (i = 0; i < (hourPatternParts.length - 1); ++i)
- {
- if (hourPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (hourPatternParts[i].startsWith("+"))
- {
- _hourAdder.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- if (hourPatternParts[i].startsWith("~"))
- {
- _hourAdderRnd.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + hourPatternParts[i] + "\"");
- }
- hourPattern = hourPatternParts[hourPatternParts.length - 1];
- }
- _hourMatchers.add(buildValueMatcher(hourPattern, HOUR_VALUE_PARSER));
+ _hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
}
+
catch (Exception e)
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
@@ -157,28 +256,7 @@ public class SchedulingPattern implements NextTime
try
{
- String dayOfMonthPattern = st2.nextToken();
- String[] dayOfMonthPatternParts = dayOfMonthPattern.split(":");
- if (dayOfMonthPatternParts.length > 1)
- {
- for (i = 0; i < (dayOfMonthPatternParts.length - 1); ++i)
- {
- if (dayOfMonthPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (dayOfMonthPatternParts[i].startsWith("+"))
- {
- _dayOfYearAdder.put(_matcherSize, Integer.parseInt(dayOfMonthPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown day modifier \"" + dayOfMonthPatternParts[i] + "\"");
- }
- dayOfMonthPattern = dayOfMonthPatternParts[dayOfMonthPatternParts.length - 1];
- }
- _dayOfMonthMatchers.add(buildValueMatcher(dayOfMonthPattern, DAY_OF_MONTH_VALUE_PARSER));
+ _dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
}
catch (Exception e)
{
@@ -202,28 +280,17 @@ public class SchedulingPattern implements NextTime
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
}
-
- if (st2.hasMoreTokens())
- {
- try
- {
- String weekOfYearAdderText = st2.nextToken();
- if (weekOfYearAdderText.charAt(0) != '+')
- {
- throw new RuntimeException("Unknown week of year addition in pattern \"" + localPattern + "\".");
- }
- weekOfYearAdderText = weekOfYearAdderText.substring(1);
- _weekOfYearAdder.put(_matcherSize, Integer.parseInt(weekOfYearAdderText));
- }
- catch (Exception e)
- {
- throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
- }
- }
- ++_matcherSize;
+ _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("*"))
@@ -231,12 +298,12 @@ public class SchedulingPattern implements NextTime
return new AlwaysTrueValueMatcher();
}
- ArrayList values = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(str, ",");
+ final List values = new ArrayList<>();
+ final StringTokenizer st = new StringTokenizer(str, ",");
while (st.hasMoreTokens())
{
+ final String element = st.nextToken();
List local;
- String element = st.nextToken();
try
{
local = parseListElement(element, parser);
@@ -245,15 +312,13 @@ public class SchedulingPattern implements NextTime
{
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
}
-
- for (Integer value : local)
+ for (Iterator i = local.iterator(); i.hasNext();)
{
- if (values.contains(value))
+ final Object value = i.next();
+ if (!values.contains(value))
{
- continue;
+ values.add((Integer) value);
}
-
- values.add(value);
}
}
@@ -270,16 +335,23 @@ public class SchedulingPattern implements NextTime
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 List parseListElement(String str, ValueParser parser) throws Exception
{
- List values;
- StringTokenizer st = new StringTokenizer(str, "/");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "/");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
+ List values;
try
{
values = parseRange(st.nextToken(), parser);
@@ -291,9 +363,8 @@ public class SchedulingPattern implements NextTime
if (size == 2)
{
+ final String dStr = st.nextToken();
int div;
- String dStr = st.nextToken();
-
try
{
div = Integer.parseInt(dStr);
@@ -308,41 +379,47 @@ public class SchedulingPattern implements NextTime
throw new Exception("non positive divisor \"" + div + "\"");
}
- ArrayList values2 = new ArrayList<>();
+ final List 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 List parseRange(String str, ValueParser parser) throws Exception
{
- int v2;
- int v1;
if (str.equals("*"))
{
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- ArrayList values = new ArrayList<>();
- for (int i = min; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ final List values = new ArrayList<>();
+ for (int i = min; i <= max; i++)
{
values.add(i);
}
return values;
}
- StringTokenizer st = new StringTokenizer(str, "-");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "-");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
- String v1Str = st.nextToken();
+ final String v1Str = st.nextToken();
+ int v1;
try
{
v1 = parser.parse(v1Str);
@@ -354,12 +431,13 @@ public class SchedulingPattern implements NextTime
if (size == 1)
{
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
values.add(v1);
return values;
}
- String v2Str = st.nextToken();
+ final String v2Str = st.nextToken();
+ int v2;
try
{
v2 = parser.parse(v2Str);
@@ -369,91 +447,90 @@ public class SchedulingPattern implements NextTime
throw new Exception("invalid value \"" + v2Str + "\", " + e.getMessage());
}
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
if (v1 < v2)
{
- for (int i = v1; i <= v2; ++i)
+ for (int i = v1; i <= v2; i++)
{
values.add(i);
}
}
else if (v1 > v2)
{
- int i;
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- for (i = v1; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ for (int i = v1; i <= max; i++)
{
values.add(i);
}
- for (i = min; i <= v2; ++i)
+ for (int i = min; i <= v2; i++)
{
values.add(i);
}
}
else
{
+ // v1 == v2
values.add(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(timezone);
+ final GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(millis);
- gc.set(13, 0);
- gc.set(14, 0);
- for (int i = 0; i < _matcherSize; ++i)
+ gc.setTimeZone(timezone);
+ final int minute = gc.get(Calendar.MINUTE);
+ final int hour = gc.get(Calendar.HOUR_OF_DAY);
+ final int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH);
+ final int month = gc.get(Calendar.MONTH) + 1;
+ final int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1;
+ final int year = gc.get(Calendar.YEAR);
+ for (int i = 0; i < _matcherSize; i++)
{
- boolean eval = false;
- if (_weekOfYearAdder.containsKey(i))
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+ if (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))
{
- gc.add(3, -_weekOfYearAdder.get(i).intValue());
+ return true;
}
- if (_dayOfYearAdder.containsKey(i))
- {
- gc.add(6, -_dayOfYearAdder.get(i).intValue());
- }
- if (_hourAdder.containsKey(i))
- {
- gc.add(10, -_hourAdder.get(i).intValue());
- }
- int minute = gc.get(MONTH_MAX_VALUE);
- int hour = gc.get(11);
- int dayOfMonth = gc.get(5);
- int month = gc.get(2) + 1;
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
- int year = gc.get(1);
- 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);
- @SuppressWarnings("unused")
- boolean bl = 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) ? true : (eval = false);
- if (!eval)
- {
- continue;
- }
- 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);
}
+ /**
+ * It returns the next matching moment as a millis value.
+ * @param timezone
+ * @param millis
+ * @return The next matching moment as a millis value.
+ */
public long next(TimeZone timezone, long millis)
{
- long result = -1L;
- GregorianCalendar gc = new GregorianCalendar(timezone);
+ long result = -1;
+ final GregorianCalendar gc = new GregorianCalendar(timezone);
for (int i = 0; i < _matcherSize; ++i)
{
- long next = -1L;
+ long next = -1;
gc.setTimeInMillis(millis);
gc.set(13, 0);
gc.set(14, 0);
@@ -469,27 +546,28 @@ public class SchedulingPattern implements NextTime
{
gc.add(10, _hourAdder.get(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);
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+
SEARCH: do
{
- int year = gc.get(1);
- boolean isLeapYear = gc.isLeapYear(year);
+ final int year = gc.get(1);
+ final boolean isLeapYear = gc.isLeapYear(year);
for (int month = gc.get(2) + 1; month <= MONTH_MAX_VALUE; ++month)
{
if (monthMatcher.match(month))
{
gc.set(2, month - 1);
- int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ final int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
for (int dayOfMonth = gc.get(5); dayOfMonth <= maxDayOfMonth; ++dayOfMonth)
{
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher ? ((DayOfMonthValueMatcher) dayOfMonthMatcher).match(dayOfMonth, month, isLeapYear) : dayOfMonthMatcher.match(dayOfMonth))
{
gc.set(5, dayOfMonth);
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
+ final int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
if (dayOfWeekMatcher.match(dayOfWeek))
{
for (int hour = gc.get(11); hour <= HOUR_MAX_VALUE; ++hour)
@@ -505,13 +583,13 @@ public class SchedulingPattern implements NextTime
}
gc.set(MONTH_MAX_VALUE, minute);
- long next0 = gc.getTimeInMillis();
+ final long next0 = gc.getTimeInMillis();
if (next0 <= millis)
{
continue;
}
- if ((next != -1L) && (next0 >= next))
+ if ((next != -1) && (next0 >= next))
{
break SEARCH;
}
@@ -519,7 +597,7 @@ public class SchedulingPattern implements NextTime
next = next0;
if (_hourAdderRnd.containsKey(i))
{
- next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000L;
+ next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000;
}
if (!_minuteAdderRnd.containsKey(i))
@@ -527,7 +605,7 @@ public class SchedulingPattern implements NextTime
break SEARCH;
}
- next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000L;
+ next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000;
break SEARCH;
}
}
@@ -550,7 +628,7 @@ public class SchedulingPattern implements NextTime
}
while (true);
- if ((next <= millis) || ((result != -1L) && (next >= result)))
+ if ((next <= millis) || ((result != -1) && (next >= result)))
{
continue;
}
@@ -560,57 +638,89 @@ public class SchedulingPattern implements NextTime
return result;
}
- @Override
+ /**
+ * It returns the next matching moment as a long.
+ * @param millis
+ * @return The next matching moment as a long.
+ */
public long next(long millis)
{
return next(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.
+ */
private static int parseAlias(String value, String[] aliases, int offset) throws Exception
{
- for (int i = 0; i < aliases.length; ++i)
+ for (int i = 0; i < aliases.length; i++)
{
- if (!aliases[i].equalsIgnoreCase(value))
+ if (aliases[i].equalsIgnoreCase(value))
{
- continue;
+ return offset + i;
}
- return offset + i;
}
throw new Exception("invalid alias \"" + value + "\"");
}
+ /**
+ *
+ * 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.
+ *
+ * @author Paul Fernley
+ */
private static class DayOfMonthValueMatcher extends IntArrayValueMatcher
{
- private static final int[] lastDays = new int[]
+ private static final int[] LAST_DAYS =
{
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
28,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE
+ 31
};
+ /**
+ * 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 or the last-day-of-month setting applies.
+ */
public DayOfMonthValueMatcher(List integers)
{
super(integers);
}
+ /**
+ * 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) && DayOfMonthValueMatcher.isLastDayOfMonth(value, month, isLeapYear));
+ return (super.match(value) || ((value > 27) && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
}
public static int getLastDayOfMonth(int month, boolean isLeapYear)
@@ -620,29 +730,41 @@ public class SchedulingPattern implements NextTime
return 29;
}
- return lastDays[month - 1];
+ return LAST_DAYS[month - 1];
}
public static boolean isLastDayOfMonth(int value, int month, boolean isLeapYear)
{
- return value == DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ return value == getLastDayOfMonth(month, isLeapYear);
}
}
+ /**
+ *
+ * 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.
+ *
+ * @author Carlo Pelliccia
+ */
private static class IntArrayValueMatcher implements ValueMatcher
{
- private final int[] values;
+ /**
+ * The accepted values.
+ */
+ private final int[] _values;
+ /**
+ * Builds the ValueMatcher.
+ * @param integers a List 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(List integers)
{
- int size = integers.size();
- values = new int[size];
- for (int i = 0; i < size; ++i)
+ final int size = integers.size();
+ _values = new int[size];
+ for (int i = 0; i < size; i++)
{
try
{
- values[i] = integers.get(i);
- continue;
+ _values[i] = integers.get(i).intValue();
}
catch (Exception e)
{
@@ -651,27 +773,32 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Returns true if the given value is included in the matcher list.
+ */
@Override
public boolean match(int value)
{
- for (int i = 0; i < values.length; ++i)
+ for (int i = 0; i < _values.length; i++)
{
- if (values[i] != value)
+ if (_values[i] == value)
{
- continue;
+ return true;
}
- return true;
}
return false;
}
}
+ /**
+ * This ValueMatcher always returns true!
+ * @author Carlo Pelliccia
+ */
private static class AlwaysTrueValueMatcher implements ValueMatcher
{
- private AlwaysTrueValueMatcher()
- {
- }
-
+ /**
+ * Always true!
+ */
@Override
public boolean match(int value)
{
@@ -679,14 +806,31 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ *
+ * This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
+ *
+ * @author Carlo Pelliccia
+ */
private static interface ValueMatcher
{
- public boolean match(int var1);
+ /**
+ * 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);
}
+ /**
+ * The value parser for the day of week field.
+ */
private static class DayOfWeekValueParser extends SimpleValueParser
{
- private static String[] ALIASES = new String[]
+ /**
+ * Days of week aliases.
+ */
+ private static String[] ALIASES =
{
"sun",
"mon",
@@ -697,6 +841,9 @@ public class SchedulingPattern implements NextTime
"sat"
};
+ /**
+ * Builds the day value parser.
+ */
public DayOfWeekValueParser()
{
super(DAY_OF_WEEK_MIN_VALUE, DAY_OF_WEEK_MAX_VALUE);
@@ -707,17 +854,25 @@ public class SchedulingPattern implements NextTime
{
try
{
- return super.parse(value) % DAY_OF_WEEK_MAX_VALUE;
+ // try as a simple value
+ return super.parse(value) % 7;
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 0);
+ // try as an alias
+ return parseAlias(value, ALIASES, 0);
}
}
}
+ /**
+ * The value parser for the months field.
+ */
private static class MonthValueParser extends SimpleValueParser
{
+ /**
+ * Months of year aliases.
+ */
private static String[] ALIASES = new String[]
{
"jan",
@@ -734,6 +889,9 @@ public class SchedulingPattern implements NextTime
"dec"
};
+ /**
+ * Builds the months value parser.
+ */
public MonthValueParser()
{
super(MONTH_MIN_VALUE, MONTH_MAX_VALUE);
@@ -748,13 +906,19 @@ public class SchedulingPattern implements NextTime
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 1);
+ return parseAlias(value, ALIASES, 1);
}
}
}
+ /**
+ * The value parser for the day of month field.
+ */
private static class DayOfMonthValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public DayOfMonthValueParser()
{
super(DAY_OF_MONTH_MIN_VALUE, DAY_OF_MONTH_MAX_VALUE);
@@ -771,27 +935,54 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * The value parser for the hour field.
+ */
private static class HourValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public HourValueParser()
{
super(HOUR_MIN_VALUE, HOUR_MAX_VALUE);
}
}
+ /**
+ * The minutes value parser.
+ */
private static class MinuteValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public MinuteValueParser()
{
super(MINUTE_MIN_VALUE, MINUTE_MAX_VALUE);
}
}
+ /**
+ * 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)
{
_minValue = minValue;
@@ -830,12 +1021,29 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Definition for a value parser.
+ */
private static interface ValueParser
{
- public int parse(String var1) throws Exception;
+ /**
+ * 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 accepted by the parser.
+ * @return The minimum value accepted by the parser.
+ */
public int getMinValue();
+ /**
+ * Returns the maximum value accepted by the parser.
+ * @return The maximum value accepted by the parser.
+ */
public int getMaxValue();
}
}
diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/commons/time/AddPattern.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/commons/time/AddPattern.java
deleted file mode 100644
index 893a279fd4..0000000000
--- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/commons/time/AddPattern.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-public class AddPattern implements NextTime
-{
- private int _monthInc = -1;
- private int _monthSet = -1;
- private int _dayOfMonthInc = -1;
- private int _dayOfMonthSet = -1;
- private int _hourOfDayInc = -1;
- private int _hourOfDaySet = -1;
- private int _minuteInc = -1;
- private int _minuteSet = -1;
-
- public AddPattern(String pattern)
- {
- String[] timeparts;
- String[] parts = pattern.split("\\s+");
- if (parts.length == 2)
- {
- String datemodstr;
- String datepartsstr = parts[0];
- String[] dateparts = datepartsstr.split(":");
- if (dateparts.length == 2)
- {
- if (dateparts[0].startsWith("+"))
- {
- _monthInc = Integer.parseInt(dateparts[0].substring(1));
- }
- else
- {
- _monthSet = Integer.parseInt(dateparts[0]) - 1;
- }
- }
- if ((datemodstr = dateparts[dateparts.length - 1]).startsWith("+"))
- {
- _dayOfMonthInc = Integer.parseInt(datemodstr.substring(1));
- }
- else
- {
- _dayOfMonthSet = Integer.parseInt(datemodstr);
- }
- }
- if ((timeparts = parts[parts.length - 1].split(":"))[0].startsWith("+"))
- {
- _hourOfDayInc = Integer.parseInt(timeparts[0].substring(1));
- }
- else
- {
- _hourOfDaySet = Integer.parseInt(timeparts[0]);
- }
- if (timeparts[1].startsWith("+"))
- {
- _minuteInc = Integer.parseInt(timeparts[1].substring(1));
- }
- else
- {
- _minuteSet = Integer.parseInt(timeparts[1]);
- }
- }
-
- @Override
- public long next(long millis)
- {
- final GregorianCalendar gc = new GregorianCalendar(TimeZone.getDefault());
- gc.setTimeInMillis(millis);
- if (_monthInc >= 0)
- {
- gc.add(2, _monthInc);
- }
- if (_monthSet >= 0)
- {
- gc.set(2, _monthSet);
- }
- if (_dayOfMonthInc >= 0)
- {
- gc.add(5, _dayOfMonthInc);
- }
- if (_dayOfMonthSet >= 0)
- {
- gc.set(5, _dayOfMonthSet);
- }
- if (_hourOfDayInc >= 0)
- {
- gc.add(11, _hourOfDayInc);
- }
- if (_hourOfDaySet >= 0)
- {
- gc.set(11, _hourOfDaySet);
- }
- if (_minuteInc >= 0)
- {
- gc.add(12, _minuteInc);
- }
- if (_minuteSet >= 0)
- {
- gc.set(12, _minuteSet);
- }
- return gc.getTimeInMillis();
- }
-}
diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/commons/time/NextTime.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/commons/time/NextTime.java
deleted file mode 100644
index d08d90164d..0000000000
--- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/commons/time/NextTime.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-public interface NextTime
-{
- public long next(long var1);
-}
diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/commons/time/SchedulingPattern.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/commons/time/SchedulingPattern.java
index d9fdd655f3..a9677e31ae 100644
--- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/commons/time/SchedulingPattern.java
+++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/commons/time/SchedulingPattern.java
@@ -1,23 +1,25 @@
/*
- * This file is part of the L2J Mobius project.
+ * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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.
- *
+ * it under the terms of the GNU Lesser General Public License version
+ * 2.1, as published by the Free Software Foundation.
+ *
* 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 .
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License 2.1 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License version 2.1 along with this program.
+ * If not, see .
*/
package org.l2jmobius.commons.time;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
@@ -26,7 +28,94 @@ import java.util.TreeMap;
import org.l2jmobius.commons.util.Rnd;
-public class SchedulingPattern implements NextTime
+/**
+ *
+ * A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
+ *
+ *
+ * - Minutes sub-pattern. During which minutes of the hour should the task been launched? The values range is from 0 to 59.
+ * - Hours sub-pattern. During which hours of the day should the task been launched? The values range is from 0 to 23.
+ * - Days of month sub-pattern. 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.
+ * - Months sub-pattern. 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".
+ * - Days of week sub-pattern. 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".
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * Some examples:
+ *
+ *
+ * 5 * * * *
+ * 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.).
+ *
+ *
+ * * * * * *
+ * This pattern causes a task to be launched every minute.
+ *
+ *
+ * * 12 * * Mon
+ * This pattern causes a task to be launched every minute during the 12th hour of Monday.
+ *
+ *
+ * * 12 16 * Mon
+ * 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.
+ *
+ *
+ * Every sub-pattern can contain two or more comma separated values.
+ *
+ *
+ * 59 11 * * 1,2,3,4,5
+ * This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
+ *
+ *
+ * Values intervals are admitted and defined using the minus character.
+ *
+ *
+ * 59 11 * * 1-5
+ * This pattern is equivalent to the previous one.
+ *
+ *
+ * The slash character can be used to identify step values within a range. It can be used both in the form */c and a-b/c. The subpattern is matched every c values of the range 0,maxvalue or a-b.
+ *
+ *
+ * */5 * * * *
+ * This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
+ *
+ *
+ * 3-18/5 * * * *
+ * 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).
+ *
+ *
+ * */15 9-17 * * *
+ * 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).
+ *
+ *
+ * All the fresh described syntax rules can be used together.
+ *
+ *
+ * * 12 10-16/2 * *
+ * 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.
+ *
+ *
+ * * 12 1-15,17,20-25 * *
+ * 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.
+ *
+ *
+ * Finally lets you combine more scheduling patterns into one, with the pipe character:
+ *
+ *
+ * 0 5 * * *|8 10 * * *|22 17 * * *
+ * This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
+ *
+ * @author Carlo Pelliccia
+ */
+public class SchedulingPattern
{
private static final int MINUTE_MIN_VALUE = 0;
private static final int MINUTE_MAX_VALUE = 59;
@@ -38,24 +127,78 @@ public class SchedulingPattern implements NextTime
private static final int MONTH_MAX_VALUE = 12;
private static final int DAY_OF_WEEK_MIN_VALUE = 0;
private static final int DAY_OF_WEEK_MAX_VALUE = 7;
+
+ /**
+ * 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();
+
+ /**
+ * The pattern as a string.
+ */
private final String _asString;
+
+ /**
+ * The ValueMatcher list for the "minute" field.
+ */
protected List _minuteMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "hour" field.
+ */
protected List _hourMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of month" field.
+ */
protected List _dayOfMonthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "month" field.
+ */
protected List _monthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of week" field.
+ */
protected List _dayOfWeekMatchers = new ArrayList<>();
+
+ /**
+ * How many matcher groups in this pattern?
+ */
protected int _matcherSize = 0;
+
protected Map _hourAdder = new TreeMap<>();
protected Map _hourAdderRnd = new TreeMap<>();
protected Map _dayOfYearAdder = new TreeMap<>();
protected Map _minuteAdderRnd = new TreeMap<>();
protected Map _weekOfYearAdder = new TreeMap<>();
+ /**
+ * 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
@@ -69,10 +212,15 @@ public class SchedulingPattern implements NextTime
return true;
}
+ /**
+ * Builds a SchedulingPattern parsing it from a string.
+ * @param pattern The pattern as a crontab-like string.
+ * @throws RuntimeException If the supplied string is not a valid pattern.
+ */
public SchedulingPattern(String pattern) throws RuntimeException
{
_asString = pattern;
- StringTokenizer st1 = new StringTokenizer(pattern, "|");
+ final StringTokenizer st1 = new StringTokenizer(pattern, "|");
if (st1.countTokens() < 1)
{
throw new RuntimeException("invalid pattern: \"" + pattern + "\"");
@@ -80,39 +228,16 @@ public class SchedulingPattern implements NextTime
while (st1.hasMoreTokens())
{
- int i;
- String localPattern = st1.nextToken();
- StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
- int tokCnt = st2.countTokens();
- if ((tokCnt < 5) || (tokCnt > 6))
+ final String localPattern = st1.nextToken();
+ final StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
+ if (st2.countTokens() != 5)
{
throw new RuntimeException("invalid pattern: \"" + localPattern + "\"");
}
try
{
- String minutePattern = st2.nextToken();
- String[] minutePatternParts = minutePattern.split(":");
- if (minutePatternParts.length > 1)
- {
- for (i = 0; i < (minutePatternParts.length - 1); ++i)
- {
- if (minutePatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (minutePatternParts[i].startsWith("~"))
- {
- _minuteAdderRnd.put(_matcherSize, Integer.parseInt(minutePatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + minutePatternParts[i] + "\"");
- }
- minutePattern = minutePatternParts[minutePatternParts.length - 1];
- }
- _minuteMatchers.add(buildValueMatcher(minutePattern, MINUTE_VALUE_PARSER));
+ _minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
}
catch (Exception e)
{
@@ -121,35 +246,9 @@ public class SchedulingPattern implements NextTime
try
{
- String hourPattern = st2.nextToken();
- String[] hourPatternParts = hourPattern.split(":");
- if (hourPatternParts.length > 1)
- {
- for (i = 0; i < (hourPatternParts.length - 1); ++i)
- {
- if (hourPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (hourPatternParts[i].startsWith("+"))
- {
- _hourAdder.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- if (hourPatternParts[i].startsWith("~"))
- {
- _hourAdderRnd.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + hourPatternParts[i] + "\"");
- }
- hourPattern = hourPatternParts[hourPatternParts.length - 1];
- }
- _hourMatchers.add(buildValueMatcher(hourPattern, HOUR_VALUE_PARSER));
+ _hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
}
+
catch (Exception e)
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
@@ -157,28 +256,7 @@ public class SchedulingPattern implements NextTime
try
{
- String dayOfMonthPattern = st2.nextToken();
- String[] dayOfMonthPatternParts = dayOfMonthPattern.split(":");
- if (dayOfMonthPatternParts.length > 1)
- {
- for (i = 0; i < (dayOfMonthPatternParts.length - 1); ++i)
- {
- if (dayOfMonthPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (dayOfMonthPatternParts[i].startsWith("+"))
- {
- _dayOfYearAdder.put(_matcherSize, Integer.parseInt(dayOfMonthPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown day modifier \"" + dayOfMonthPatternParts[i] + "\"");
- }
- dayOfMonthPattern = dayOfMonthPatternParts[dayOfMonthPatternParts.length - 1];
- }
- _dayOfMonthMatchers.add(buildValueMatcher(dayOfMonthPattern, DAY_OF_MONTH_VALUE_PARSER));
+ _dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
}
catch (Exception e)
{
@@ -202,28 +280,17 @@ public class SchedulingPattern implements NextTime
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
}
-
- if (st2.hasMoreTokens())
- {
- try
- {
- String weekOfYearAdderText = st2.nextToken();
- if (weekOfYearAdderText.charAt(0) != '+')
- {
- throw new RuntimeException("Unknown week of year addition in pattern \"" + localPattern + "\".");
- }
- weekOfYearAdderText = weekOfYearAdderText.substring(1);
- _weekOfYearAdder.put(_matcherSize, Integer.parseInt(weekOfYearAdderText));
- }
- catch (Exception e)
- {
- throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
- }
- }
- ++_matcherSize;
+ _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("*"))
@@ -231,12 +298,12 @@ public class SchedulingPattern implements NextTime
return new AlwaysTrueValueMatcher();
}
- ArrayList values = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(str, ",");
+ final List values = new ArrayList<>();
+ final StringTokenizer st = new StringTokenizer(str, ",");
while (st.hasMoreTokens())
{
+ final String element = st.nextToken();
List local;
- String element = st.nextToken();
try
{
local = parseListElement(element, parser);
@@ -245,15 +312,13 @@ public class SchedulingPattern implements NextTime
{
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
}
-
- for (Integer value : local)
+ for (Iterator i = local.iterator(); i.hasNext();)
{
- if (values.contains(value))
+ final Object value = i.next();
+ if (!values.contains(value))
{
- continue;
+ values.add((Integer) value);
}
-
- values.add(value);
}
}
@@ -270,16 +335,23 @@ public class SchedulingPattern implements NextTime
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 List parseListElement(String str, ValueParser parser) throws Exception
{
- List values;
- StringTokenizer st = new StringTokenizer(str, "/");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "/");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
+ List values;
try
{
values = parseRange(st.nextToken(), parser);
@@ -291,9 +363,8 @@ public class SchedulingPattern implements NextTime
if (size == 2)
{
+ final String dStr = st.nextToken();
int div;
- String dStr = st.nextToken();
-
try
{
div = Integer.parseInt(dStr);
@@ -308,41 +379,47 @@ public class SchedulingPattern implements NextTime
throw new Exception("non positive divisor \"" + div + "\"");
}
- ArrayList values2 = new ArrayList<>();
+ final List 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 List parseRange(String str, ValueParser parser) throws Exception
{
- int v2;
- int v1;
if (str.equals("*"))
{
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- ArrayList values = new ArrayList<>();
- for (int i = min; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ final List values = new ArrayList<>();
+ for (int i = min; i <= max; i++)
{
values.add(i);
}
return values;
}
- StringTokenizer st = new StringTokenizer(str, "-");
- int size = st.countTokens();
+ final StringTokenizer st = new StringTokenizer(str, "-");
+ final int size = st.countTokens();
if ((size < 1) || (size > 2))
{
throw new Exception("syntax error");
}
- String v1Str = st.nextToken();
+ final String v1Str = st.nextToken();
+ int v1;
try
{
v1 = parser.parse(v1Str);
@@ -354,12 +431,13 @@ public class SchedulingPattern implements NextTime
if (size == 1)
{
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
values.add(v1);
return values;
}
- String v2Str = st.nextToken();
+ final String v2Str = st.nextToken();
+ int v2;
try
{
v2 = parser.parse(v2Str);
@@ -369,91 +447,90 @@ public class SchedulingPattern implements NextTime
throw new Exception("invalid value \"" + v2Str + "\", " + e.getMessage());
}
- ArrayList values = new ArrayList<>();
+ final List values = new ArrayList<>();
if (v1 < v2)
{
- for (int i = v1; i <= v2; ++i)
+ for (int i = v1; i <= v2; i++)
{
values.add(i);
}
}
else if (v1 > v2)
{
- int i;
- int min = parser.getMinValue();
- int max = parser.getMaxValue();
- for (i = v1; i <= max; ++i)
+ final int min = parser.getMinValue();
+ final int max = parser.getMaxValue();
+ for (int i = v1; i <= max; i++)
{
values.add(i);
}
- for (i = min; i <= v2; ++i)
+ for (int i = min; i <= v2; i++)
{
values.add(i);
}
}
else
{
+ // v1 == v2
values.add(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(timezone);
+ final GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(millis);
- gc.set(13, 0);
- gc.set(14, 0);
- for (int i = 0; i < _matcherSize; ++i)
+ gc.setTimeZone(timezone);
+ final int minute = gc.get(Calendar.MINUTE);
+ final int hour = gc.get(Calendar.HOUR_OF_DAY);
+ final int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH);
+ final int month = gc.get(Calendar.MONTH) + 1;
+ final int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1;
+ final int year = gc.get(Calendar.YEAR);
+ for (int i = 0; i < _matcherSize; i++)
{
- boolean eval = false;
- if (_weekOfYearAdder.containsKey(i))
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+ if (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))
{
- gc.add(3, -_weekOfYearAdder.get(i).intValue());
+ return true;
}
- if (_dayOfYearAdder.containsKey(i))
- {
- gc.add(6, -_dayOfYearAdder.get(i).intValue());
- }
- if (_hourAdder.containsKey(i))
- {
- gc.add(10, -_hourAdder.get(i).intValue());
- }
- int minute = gc.get(MONTH_MAX_VALUE);
- int hour = gc.get(11);
- int dayOfMonth = gc.get(5);
- int month = gc.get(2) + 1;
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
- int year = gc.get(1);
- 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);
- @SuppressWarnings("unused")
- boolean bl = 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) ? true : (eval = false);
- if (!eval)
- {
- continue;
- }
- 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);
}
+ /**
+ * It returns the next matching moment as a millis value.
+ * @param timezone
+ * @param millis
+ * @return The next matching moment as a millis value.
+ */
public long next(TimeZone timezone, long millis)
{
- long result = -1L;
- GregorianCalendar gc = new GregorianCalendar(timezone);
+ long result = -1;
+ final GregorianCalendar gc = new GregorianCalendar(timezone);
for (int i = 0; i < _matcherSize; ++i)
{
- long next = -1L;
+ long next = -1;
gc.setTimeInMillis(millis);
gc.set(13, 0);
gc.set(14, 0);
@@ -469,27 +546,28 @@ public class SchedulingPattern implements NextTime
{
gc.add(10, _hourAdder.get(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);
+ final ValueMatcher minuteMatcher = _minuteMatchers.get(i);
+ final ValueMatcher hourMatcher = _hourMatchers.get(i);
+ final ValueMatcher dayOfMonthMatcher = _dayOfMonthMatchers.get(i);
+ final ValueMatcher monthMatcher = _monthMatchers.get(i);
+ final ValueMatcher dayOfWeekMatcher = _dayOfWeekMatchers.get(i);
+
SEARCH: do
{
- int year = gc.get(1);
- boolean isLeapYear = gc.isLeapYear(year);
+ final int year = gc.get(1);
+ final boolean isLeapYear = gc.isLeapYear(year);
for (int month = gc.get(2) + 1; month <= MONTH_MAX_VALUE; ++month)
{
if (monthMatcher.match(month))
{
gc.set(2, month - 1);
- int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ final int maxDayOfMonth = DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
for (int dayOfMonth = gc.get(5); dayOfMonth <= maxDayOfMonth; ++dayOfMonth)
{
if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher ? ((DayOfMonthValueMatcher) dayOfMonthMatcher).match(dayOfMonth, month, isLeapYear) : dayOfMonthMatcher.match(dayOfMonth))
{
gc.set(5, dayOfMonth);
- int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
+ final int dayOfWeek = gc.get(DAY_OF_WEEK_MAX_VALUE) - 1;
if (dayOfWeekMatcher.match(dayOfWeek))
{
for (int hour = gc.get(11); hour <= HOUR_MAX_VALUE; ++hour)
@@ -505,13 +583,13 @@ public class SchedulingPattern implements NextTime
}
gc.set(MONTH_MAX_VALUE, minute);
- long next0 = gc.getTimeInMillis();
+ final long next0 = gc.getTimeInMillis();
if (next0 <= millis)
{
continue;
}
- if ((next != -1L) && (next0 >= next))
+ if ((next != -1) && (next0 >= next))
{
break SEARCH;
}
@@ -519,7 +597,7 @@ public class SchedulingPattern implements NextTime
next = next0;
if (_hourAdderRnd.containsKey(i))
{
- next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000L;
+ next += Rnd.get(_hourAdderRnd.get(i)) * 60 * 60 * 1000;
}
if (!_minuteAdderRnd.containsKey(i))
@@ -527,7 +605,7 @@ public class SchedulingPattern implements NextTime
break SEARCH;
}
- next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000L;
+ next += Rnd.get(_minuteAdderRnd.get(i)) * 60 * 1000;
break SEARCH;
}
}
@@ -550,7 +628,7 @@ public class SchedulingPattern implements NextTime
}
while (true);
- if ((next <= millis) || ((result != -1L) && (next >= result)))
+ if ((next <= millis) || ((result != -1) && (next >= result)))
{
continue;
}
@@ -560,57 +638,89 @@ public class SchedulingPattern implements NextTime
return result;
}
- @Override
+ /**
+ * It returns the next matching moment as a long.
+ * @param millis
+ * @return The next matching moment as a long.
+ */
public long next(long millis)
{
return next(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.
+ */
private static int parseAlias(String value, String[] aliases, int offset) throws Exception
{
- for (int i = 0; i < aliases.length; ++i)
+ for (int i = 0; i < aliases.length; i++)
{
- if (!aliases[i].equalsIgnoreCase(value))
+ if (aliases[i].equalsIgnoreCase(value))
{
- continue;
+ return offset + i;
}
- return offset + i;
}
throw new Exception("invalid alias \"" + value + "\"");
}
+ /**
+ *
+ * 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.
+ *
+ * @author Paul Fernley
+ */
private static class DayOfMonthValueMatcher extends IntArrayValueMatcher
{
- private static final int[] lastDays = new int[]
+ private static final int[] LAST_DAYS =
{
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
28,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE,
+ 31,
30,
- DAY_OF_MONTH_MAX_VALUE
+ 31
};
+ /**
+ * 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 or the last-day-of-month setting applies.
+ */
public DayOfMonthValueMatcher(List integers)
{
super(integers);
}
+ /**
+ * 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) && DayOfMonthValueMatcher.isLastDayOfMonth(value, month, isLeapYear));
+ return (super.match(value) || ((value > 27) && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
}
public static int getLastDayOfMonth(int month, boolean isLeapYear)
@@ -620,29 +730,41 @@ public class SchedulingPattern implements NextTime
return 29;
}
- return lastDays[month - 1];
+ return LAST_DAYS[month - 1];
}
public static boolean isLastDayOfMonth(int value, int month, boolean isLeapYear)
{
- return value == DayOfMonthValueMatcher.getLastDayOfMonth(month, isLeapYear);
+ return value == getLastDayOfMonth(month, isLeapYear);
}
}
+ /**
+ *
+ * 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.
+ *
+ * @author Carlo Pelliccia
+ */
private static class IntArrayValueMatcher implements ValueMatcher
{
- private final int[] values;
+ /**
+ * The accepted values.
+ */
+ private final int[] _values;
+ /**
+ * Builds the ValueMatcher.
+ * @param integers a List 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(List integers)
{
- int size = integers.size();
- values = new int[size];
- for (int i = 0; i < size; ++i)
+ final int size = integers.size();
+ _values = new int[size];
+ for (int i = 0; i < size; i++)
{
try
{
- values[i] = integers.get(i);
- continue;
+ _values[i] = integers.get(i).intValue();
}
catch (Exception e)
{
@@ -651,27 +773,32 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Returns true if the given value is included in the matcher list.
+ */
@Override
public boolean match(int value)
{
- for (int i = 0; i < values.length; ++i)
+ for (int i = 0; i < _values.length; i++)
{
- if (values[i] != value)
+ if (_values[i] == value)
{
- continue;
+ return true;
}
- return true;
}
return false;
}
}
+ /**
+ * This ValueMatcher always returns true!
+ * @author Carlo Pelliccia
+ */
private static class AlwaysTrueValueMatcher implements ValueMatcher
{
- private AlwaysTrueValueMatcher()
- {
- }
-
+ /**
+ * Always true!
+ */
@Override
public boolean match(int value)
{
@@ -679,14 +806,31 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ *
+ * This interface describes the ValueMatcher behavior. A ValueMatcher is an object that validate an integer value against a set of rules.
+ *
+ * @author Carlo Pelliccia
+ */
private static interface ValueMatcher
{
- public boolean match(int var1);
+ /**
+ * 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);
}
+ /**
+ * The value parser for the day of week field.
+ */
private static class DayOfWeekValueParser extends SimpleValueParser
{
- private static String[] ALIASES = new String[]
+ /**
+ * Days of week aliases.
+ */
+ private static String[] ALIASES =
{
"sun",
"mon",
@@ -697,6 +841,9 @@ public class SchedulingPattern implements NextTime
"sat"
};
+ /**
+ * Builds the day value parser.
+ */
public DayOfWeekValueParser()
{
super(DAY_OF_WEEK_MIN_VALUE, DAY_OF_WEEK_MAX_VALUE);
@@ -707,17 +854,25 @@ public class SchedulingPattern implements NextTime
{
try
{
- return super.parse(value) % DAY_OF_WEEK_MAX_VALUE;
+ // try as a simple value
+ return super.parse(value) % 7;
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 0);
+ // try as an alias
+ return parseAlias(value, ALIASES, 0);
}
}
}
+ /**
+ * The value parser for the months field.
+ */
private static class MonthValueParser extends SimpleValueParser
{
+ /**
+ * Months of year aliases.
+ */
private static String[] ALIASES = new String[]
{
"jan",
@@ -734,6 +889,9 @@ public class SchedulingPattern implements NextTime
"dec"
};
+ /**
+ * Builds the months value parser.
+ */
public MonthValueParser()
{
super(MONTH_MIN_VALUE, MONTH_MAX_VALUE);
@@ -748,13 +906,19 @@ public class SchedulingPattern implements NextTime
}
catch (Exception e)
{
- return SchedulingPattern.parseAlias(value, ALIASES, 1);
+ return parseAlias(value, ALIASES, 1);
}
}
}
+ /**
+ * The value parser for the day of month field.
+ */
private static class DayOfMonthValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public DayOfMonthValueParser()
{
super(DAY_OF_MONTH_MIN_VALUE, DAY_OF_MONTH_MAX_VALUE);
@@ -771,27 +935,54 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * The value parser for the hour field.
+ */
private static class HourValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public HourValueParser()
{
super(HOUR_MIN_VALUE, HOUR_MAX_VALUE);
}
}
+ /**
+ * The minutes value parser.
+ */
private static class MinuteValueParser extends SimpleValueParser
{
+ /**
+ * Builds the value parser.
+ */
public MinuteValueParser()
{
super(MINUTE_MIN_VALUE, MINUTE_MAX_VALUE);
}
}
+ /**
+ * 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)
{
_minValue = minValue;
@@ -830,12 +1021,29 @@ public class SchedulingPattern implements NextTime
}
}
+ /**
+ * Definition for a value parser.
+ */
private static interface ValueParser
{
- public int parse(String var1) throws Exception;
+ /**
+ * 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 accepted by the parser.
+ * @return The minimum value accepted by the parser.
+ */
public int getMinValue();
+ /**
+ * Returns the maximum value accepted by the parser.
+ * @return The maximum value accepted by the parser.
+ */
public int getMaxValue();
}
}
diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/commons/time/AddPattern.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/commons/time/AddPattern.java
deleted file mode 100644
index 893a279fd4..0000000000
--- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/commons/time/AddPattern.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-public class AddPattern implements NextTime
-{
- private int _monthInc = -1;
- private int _monthSet = -1;
- private int _dayOfMonthInc = -1;
- private int _dayOfMonthSet = -1;
- private int _hourOfDayInc = -1;
- private int _hourOfDaySet = -1;
- private int _minuteInc = -1;
- private int _minuteSet = -1;
-
- public AddPattern(String pattern)
- {
- String[] timeparts;
- String[] parts = pattern.split("\\s+");
- if (parts.length == 2)
- {
- String datemodstr;
- String datepartsstr = parts[0];
- String[] dateparts = datepartsstr.split(":");
- if (dateparts.length == 2)
- {
- if (dateparts[0].startsWith("+"))
- {
- _monthInc = Integer.parseInt(dateparts[0].substring(1));
- }
- else
- {
- _monthSet = Integer.parseInt(dateparts[0]) - 1;
- }
- }
- if ((datemodstr = dateparts[dateparts.length - 1]).startsWith("+"))
- {
- _dayOfMonthInc = Integer.parseInt(datemodstr.substring(1));
- }
- else
- {
- _dayOfMonthSet = Integer.parseInt(datemodstr);
- }
- }
- if ((timeparts = parts[parts.length - 1].split(":"))[0].startsWith("+"))
- {
- _hourOfDayInc = Integer.parseInt(timeparts[0].substring(1));
- }
- else
- {
- _hourOfDaySet = Integer.parseInt(timeparts[0]);
- }
- if (timeparts[1].startsWith("+"))
- {
- _minuteInc = Integer.parseInt(timeparts[1].substring(1));
- }
- else
- {
- _minuteSet = Integer.parseInt(timeparts[1]);
- }
- }
-
- @Override
- public long next(long millis)
- {
- final GregorianCalendar gc = new GregorianCalendar(TimeZone.getDefault());
- gc.setTimeInMillis(millis);
- if (_monthInc >= 0)
- {
- gc.add(2, _monthInc);
- }
- if (_monthSet >= 0)
- {
- gc.set(2, _monthSet);
- }
- if (_dayOfMonthInc >= 0)
- {
- gc.add(5, _dayOfMonthInc);
- }
- if (_dayOfMonthSet >= 0)
- {
- gc.set(5, _dayOfMonthSet);
- }
- if (_hourOfDayInc >= 0)
- {
- gc.add(11, _hourOfDayInc);
- }
- if (_hourOfDaySet >= 0)
- {
- gc.set(11, _hourOfDaySet);
- }
- if (_minuteInc >= 0)
- {
- gc.add(12, _minuteInc);
- }
- if (_minuteSet >= 0)
- {
- gc.set(12, _minuteSet);
- }
- return gc.getTimeInMillis();
- }
-}
diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/commons/time/NextTime.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/commons/time/NextTime.java
deleted file mode 100644
index d08d90164d..0000000000
--- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/commons/time/NextTime.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 .
- */
-package org.l2jmobius.commons.time;
-
-public interface NextTime
-{
- public long next(long var1);
-}
diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/commons/time/SchedulingPattern.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/commons/time/SchedulingPattern.java
index d9fdd655f3..a9677e31ae 100644
--- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/commons/time/SchedulingPattern.java
+++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/commons/time/SchedulingPattern.java
@@ -1,23 +1,25 @@
/*
- * This file is part of the L2J Mobius project.
+ * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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.
- *
+ * it under the terms of the GNU Lesser General Public License version
+ * 2.1, as published by the Free Software Foundation.
+ *
* 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 .
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License 2.1 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License version 2.1 along with this program.
+ * If not, see .
*/
package org.l2jmobius.commons.time;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
@@ -26,7 +28,94 @@ import java.util.TreeMap;
import org.l2jmobius.commons.util.Rnd;
-public class SchedulingPattern implements NextTime
+/**
+ *
+ * A UNIX crontab-like pattern is a string split in five space separated parts. Each part is intented as:
+ *
+ *
+ * - Minutes sub-pattern. During which minutes of the hour should the task been launched? The values range is from 0 to 59.
+ * - Hours sub-pattern. During which hours of the day should the task been launched? The values range is from 0 to 23.
+ * - Days of month sub-pattern. 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.
+ * - Months sub-pattern. 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".
+ * - Days of week sub-pattern. 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".
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * Some examples:
+ *
+ *
+ * 5 * * * *
+ * 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.).
+ *
+ *
+ * * * * * *
+ * This pattern causes a task to be launched every minute.
+ *
+ *
+ * * 12 * * Mon
+ * This pattern causes a task to be launched every minute during the 12th hour of Monday.
+ *
+ *
+ * * 12 16 * Mon
+ * 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.
+ *
+ *
+ * Every sub-pattern can contain two or more comma separated values.
+ *
+ *
+ * 59 11 * * 1,2,3,4,5
+ * This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, Wednesday, Thursday and Friday.
+ *
+ *
+ * Values intervals are admitted and defined using the minus character.
+ *
+ *
+ * 59 11 * * 1-5
+ * This pattern is equivalent to the previous one.
+ *
+ *
+ * The slash character can be used to identify step values within a range. It can be used both in the form */c and a-b/c. The subpattern is matched every c values of the range 0,maxvalue or a-b.
+ *
+ *
+ * */5 * * * *
+ * This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, 0:15 and so on).
+ *
+ *
+ * 3-18/5 * * * *
+ * 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).
+ *
+ *
+ * */15 9-17 * * *
+ * 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).
+ *
+ *
+ * All the fresh described syntax rules can be used together.
+ *
+ *
+ * * 12 10-16/2 * *
+ * 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.
+ *
+ *
+ * * 12 1-15,17,20-25 * *
+ * 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.
+ *
+ *
+ * Finally lets you combine more scheduling patterns into one, with the pipe character:
+ *
+ *
+ * 0 5 * * *|8 10 * * *|22 17 * * *
+ * This pattern causes a task to be launched every day at 05:00, 10:08 and 17:22.
+ *
+ * @author Carlo Pelliccia
+ */
+public class SchedulingPattern
{
private static final int MINUTE_MIN_VALUE = 0;
private static final int MINUTE_MAX_VALUE = 59;
@@ -38,24 +127,78 @@ public class SchedulingPattern implements NextTime
private static final int MONTH_MAX_VALUE = 12;
private static final int DAY_OF_WEEK_MIN_VALUE = 0;
private static final int DAY_OF_WEEK_MAX_VALUE = 7;
+
+ /**
+ * 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();
+
+ /**
+ * The pattern as a string.
+ */
private final String _asString;
+
+ /**
+ * The ValueMatcher list for the "minute" field.
+ */
protected List _minuteMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "hour" field.
+ */
protected List _hourMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of month" field.
+ */
protected List _dayOfMonthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "month" field.
+ */
protected List _monthMatchers = new ArrayList<>();
+
+ /**
+ * The ValueMatcher list for the "day of week" field.
+ */
protected List _dayOfWeekMatchers = new ArrayList<>();
+
+ /**
+ * How many matcher groups in this pattern?
+ */
protected int _matcherSize = 0;
+
protected Map _hourAdder = new TreeMap<>();
protected Map _hourAdderRnd = new TreeMap<>();
protected Map _dayOfYearAdder = new TreeMap<>();
protected Map _minuteAdderRnd = new TreeMap<>();
protected Map _weekOfYearAdder = new TreeMap<>();
+ /**
+ * 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
@@ -69,10 +212,15 @@ public class SchedulingPattern implements NextTime
return true;
}
+ /**
+ * Builds a SchedulingPattern parsing it from a string.
+ * @param pattern The pattern as a crontab-like string.
+ * @throws RuntimeException If the supplied string is not a valid pattern.
+ */
public SchedulingPattern(String pattern) throws RuntimeException
{
_asString = pattern;
- StringTokenizer st1 = new StringTokenizer(pattern, "|");
+ final StringTokenizer st1 = new StringTokenizer(pattern, "|");
if (st1.countTokens() < 1)
{
throw new RuntimeException("invalid pattern: \"" + pattern + "\"");
@@ -80,39 +228,16 @@ public class SchedulingPattern implements NextTime
while (st1.hasMoreTokens())
{
- int i;
- String localPattern = st1.nextToken();
- StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
- int tokCnt = st2.countTokens();
- if ((tokCnt < 5) || (tokCnt > 6))
+ final String localPattern = st1.nextToken();
+ final StringTokenizer st2 = new StringTokenizer(localPattern, " \t");
+ if (st2.countTokens() != 5)
{
throw new RuntimeException("invalid pattern: \"" + localPattern + "\"");
}
try
{
- String minutePattern = st2.nextToken();
- String[] minutePatternParts = minutePattern.split(":");
- if (minutePatternParts.length > 1)
- {
- for (i = 0; i < (minutePatternParts.length - 1); ++i)
- {
- if (minutePatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (minutePatternParts[i].startsWith("~"))
- {
- _minuteAdderRnd.put(_matcherSize, Integer.parseInt(minutePatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + minutePatternParts[i] + "\"");
- }
- minutePattern = minutePatternParts[minutePatternParts.length - 1];
- }
- _minuteMatchers.add(buildValueMatcher(minutePattern, MINUTE_VALUE_PARSER));
+ _minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER));
}
catch (Exception e)
{
@@ -121,35 +246,9 @@ public class SchedulingPattern implements NextTime
try
{
- String hourPattern = st2.nextToken();
- String[] hourPatternParts = hourPattern.split(":");
- if (hourPatternParts.length > 1)
- {
- for (i = 0; i < (hourPatternParts.length - 1); ++i)
- {
- if (hourPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (hourPatternParts[i].startsWith("+"))
- {
- _hourAdder.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- if (hourPatternParts[i].startsWith("~"))
- {
- _hourAdderRnd.put(_matcherSize, Integer.parseInt(hourPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown hour modifier \"" + hourPatternParts[i] + "\"");
- }
- hourPattern = hourPatternParts[hourPatternParts.length - 1];
- }
- _hourMatchers.add(buildValueMatcher(hourPattern, HOUR_VALUE_PARSER));
+ _hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER));
}
+
catch (Exception e)
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing hours field: " + e.getMessage() + ".");
@@ -157,28 +256,7 @@ public class SchedulingPattern implements NextTime
try
{
- String dayOfMonthPattern = st2.nextToken();
- String[] dayOfMonthPatternParts = dayOfMonthPattern.split(":");
- if (dayOfMonthPatternParts.length > 1)
- {
- for (i = 0; i < (dayOfMonthPatternParts.length - 1); ++i)
- {
- if (dayOfMonthPatternParts[i].length() <= 1)
- {
- continue;
- }
-
- if (dayOfMonthPatternParts[i].startsWith("+"))
- {
- _dayOfYearAdder.put(_matcherSize, Integer.parseInt(dayOfMonthPatternParts[i].substring(1)));
- continue;
- }
-
- throw new RuntimeException("Unknown day modifier \"" + dayOfMonthPatternParts[i] + "\"");
- }
- dayOfMonthPattern = dayOfMonthPatternParts[dayOfMonthPatternParts.length - 1];
- }
- _dayOfMonthMatchers.add(buildValueMatcher(dayOfMonthPattern, DAY_OF_MONTH_VALUE_PARSER));
+ _dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER));
}
catch (Exception e)
{
@@ -202,28 +280,17 @@ public class SchedulingPattern implements NextTime
{
throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
}
-
- if (st2.hasMoreTokens())
- {
- try
- {
- String weekOfYearAdderText = st2.nextToken();
- if (weekOfYearAdderText.charAt(0) != '+')
- {
- throw new RuntimeException("Unknown week of year addition in pattern \"" + localPattern + "\".");
- }
- weekOfYearAdderText = weekOfYearAdderText.substring(1);
- _weekOfYearAdder.put(_matcherSize, Integer.parseInt(weekOfYearAdderText));
- }
- catch (Exception e)
- {
- throw new RuntimeException("invalid pattern \"" + localPattern + "\". Error parsing days of week field: " + e.getMessage() + ".");
- }
- }
- ++_matcherSize;
+ _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("*"))
@@ -231,12 +298,12 @@ public class SchedulingPattern implements NextTime
return new AlwaysTrueValueMatcher();
}
- ArrayList values = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(str, ",");
+ final List values = new ArrayList<>();
+ final StringTokenizer st = new StringTokenizer(str, ",");
while (st.hasMoreTokens())
{
+ final String element = st.nextToken();
List local;
- String element = st.nextToken();
try
{
local = parseListElement(element, parser);
@@ -245,15 +312,13 @@ public class SchedulingPattern implements NextTime
{
throw new Exception("invalid field \"" + str + "\", invalid element \"" + element + "\", " + e.getMessage());
}
-
- for (Integer value : local)
+ for (Iterator i = local.iterator(); i.hasNext();)
{
- if (values.contains(value))
+ final Object value = i.next();
+ if (!values.contains(value))
{
- continue;
+ values.add((Integer) value);
}
-
- values.add(value);
}
}
@@ -270,16 +335,23 @@ public class SchedulingPattern implements NextTime
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 List parseListElement(String str, ValueParser parser) throws Exception
{
- List