提交 67c0edd0 编写于 作者: J Jesse Glick

Support skips with hash syntax.

上级 a689c472
......@@ -108,13 +108,13 @@ throws ANTLRException
{
bits = doRange(d,field);
}
| ("H" "(")=> "H" "(" s=token "-" e=token ")"
| ("H" "(")=> "H" "(" s=token "-" e=token ")" ( "/" d=token )?
{
bits = doHash(s,e);
bits = doHash(s,e,d);
}
| "H"
| "H" ( "/" d=token )?
{
bits = doHash(field);
bits = doHash(d,field);
}
;
......
......@@ -88,16 +88,31 @@ abstract class BaseParser extends LLkParser {
/**
* Uses {@link Hash} to choose a random (but stable) value from within this field.
*/
protected long doHash( int field ) {
protected long doHash(int step, int field) throws ANTLRException {
int u = UPPER_BOUNDS[field];
if (field==2) u = 28; // day of month can vary depending on month, so to make life simpler, just use [1,28] that's always safe
if (field==4) u = 6; // Both 0 and 7 of day of week are Sunday. For better distribution, limit upper bound to 6
int h = hash.next(u+1 - LOWER_BOUNDS[field]); // upper bound is inclusive
return 1L << (h+LOWER_BOUNDS[field]);
return doHash(LOWER_BOUNDS[field], u, step);
}
protected long doHash( int s, int e ) {
return 1L << (s+hash.next(e+1-s));
protected long doHash(int s, int e, int step) throws ANTLRException {
if (step > e - s + 1) {
error(Messages.BaseParser_OutOfRange(step, 1, e - s + 1));
throw new AssertionError();
} else if (step > 1) {
long bits = 0;
for (int i = hash.next(step) + s; i <= e; i += step) {
bits |= 1L << i;
}
assert bits != 0;
return bits;
} else if (step <=0) {
error(Messages.BaseParser_MustBePositive(step));
throw new AssertionError();
} else {
// step=1 (i.e. omitted) in the case of hash is actually special; means pick one value, not step by 1
return 1L << (s+hash.next(e+1-s));
}
}
protected void rangeCheck(int value, int field) throws ANTLRException {
......
......@@ -45,6 +45,9 @@
</p><p>
The 'H' token can be used with a range. For example, '<tt>H H(0-7) * * *</tt>'
means some time between midnight to 7:59am.
You can also use skips with 'H', with or without ranges.
For example, '<tt>H/15 * * * *</tt>' means every fifteen minutes (perhaps at :07, :22, :37, :52);
'<tt>H(0-29)/10 * * * *</tt>' means every ten minutes in the first half of every hour (perhaps at :04, :14, :24).
</p><p>
The '<tt>H</tt>' token can be thought of as a random value over a range,
but it actually is a hash of the job name, not a random function, so that
......
......@@ -188,7 +188,7 @@ public class CronTabTest {
@Test
public void testHash1() throws Exception {
CronTab x = new CronTab("H H(5-8) * * *",new Hash() {
CronTab x = new CronTab("H H(5-8) H/3 H(1-10)/4 *",new Hash() {
public int next(int n) {
return n-1;
}
......@@ -196,6 +196,8 @@ public class CronTabTest {
assertEquals("59;", bitset(x.bits[0]));
assertEquals("8;", bitset(x.bits[1]));
assertEquals("3;6;9;12;15;18;21;24;27;", bitset(x.bits[2]));
assertEquals("4;8;", bitset(x.bits[3]));
}
private static String bitset(long bits) {
......@@ -210,7 +212,7 @@ public class CronTabTest {
@Test
public void testHash2() throws Exception {
CronTab x = new CronTab("H H(5-8) * * *",new Hash() {
CronTab x = new CronTab("H H(5-8) H/3 H(1-10)/4 *",new Hash() {
public int next(int n) {
return 1;
}
......@@ -218,6 +220,8 @@ public class CronTabTest {
assertEquals("1;", bitset(x.bits[0]));
assertEquals("6;", bitset(x.bits[1]));
assertEquals("2;5;8;11;14;17;20;23;26;", bitset(x.bits[2]));
assertEquals("2;6;10;", bitset(x.bits[3]));
}
@Test public void hashedMinute() throws Exception {
......@@ -229,4 +233,20 @@ public class CronTabTest {
compare(new GregorianCalendar(2013, 2, 22, 13, 56), new CronTab("H H(12-13) * * *", Hash.from("stuff")).ceil(t));
}
@Test public void hashSkips() throws Exception {
compare(new GregorianCalendar(2013, 2, 21, 16, 26), new CronTab("H/15 * * * *", Hash.from("stuff")).ceil(new GregorianCalendar(2013, 2, 21, 16, 21)));
compare(new GregorianCalendar(2013, 2, 21, 16, 41), new CronTab("H/15 * * * *", Hash.from("stuff")).ceil(new GregorianCalendar(2013, 2, 21, 16, 31)));
compare(new GregorianCalendar(2013, 2, 21, 16, 56), new CronTab("H/15 * * * *", Hash.from("stuff")).ceil(new GregorianCalendar(2013, 2, 21, 16, 42)));
compare(new GregorianCalendar(2013, 2, 21, 17, 11), new CronTab("H/15 * * * *", Hash.from("stuff")).ceil(new GregorianCalendar(2013, 2, 21, 16, 59)));
compare(new GregorianCalendar(2013, 2, 21, 0, 2), new CronTab("H(0-15)/3 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 0)));
compare(new GregorianCalendar(2013, 2, 21, 0, 2), new CronTab("H(0-3)/4 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 0)));
compare(new GregorianCalendar(2013, 2, 21, 1, 2), new CronTab("H(0-3)/4 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 5)));
try {
compare(new GregorianCalendar(2013, 2, 21, 0, 0), new CronTab("H(0-3)/15 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 0)));
fail();
} catch (ANTLRException x) {
// good
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册