package com.ullink.rxscheduler.cron;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Scheduler;
import rx.Subscription;
import rx.schedulers.TestScheduler;
import rx.subscriptions.Subscriptions;
import rx.util.functions.Action0;
import rx.util.functions.Func2;
import com.ullink.rxscheduler.cron.calendar.Calendar;
import com.ullink.rxscheduler.cron.calendar.CronExpression;
/**
* Using forwarding here because would like to be able to reuse {@link TestScheduler} features.
*/
public class RxCronForwardingScheduler extends Scheduler implements RxCronScheduler
{
private final Scheduler underlying;
public RxCronForwardingScheduler(Scheduler underlying)
{
this.underlying = underlying;
}
@Override
public Subscription schedule(Action0 action)
{
return underlying.schedule(action);
}
@Override
public <T> Subscription schedule(T state, Func2<? super Scheduler, ? super T, ? extends Subscription> action)
{
return underlying.schedule(state, action);
}
@Override
public Subscription schedule(Action0 action, long dueTime, TimeUnit unit)
{
return underlying.schedule(action, dueTime, unit);
}
@Override
public <T> Subscription schedule(T state, Func2<? super Scheduler, ? super T, ? extends Subscription> action, long dueTime, TimeUnit unit)
{
return underlying.schedule(state, action, dueTime, unit);
}
@Override
public Subscription schedulePeriodically(Action0 action, long initialDelay, long period, TimeUnit unit)
{
return underlying.schedulePeriodically(action, initialDelay, period, unit);
}
@Override
public <T> Subscription schedulePeriodically(T state, Func2<? super Scheduler, ? super T, ? extends Subscription> action, long initialDelay, long period, TimeUnit unit)
{
return underlying.schedulePeriodically(state, action, initialDelay, period, unit);
}
@Override
public long now()
{
return underlying.now();
}
private Date findNextExecutionTime(Date guess, CronExpression cronExpression, Calendar calendar)
{
Date fireTime = cronExpression.getNextValidTimeAfter(guess);
while (fireTime != null && calendar != null && !calendar.isTimeIncluded(fireTime.getTime()))
{
fireTime = findNextExecutionTime(fireTime, cronExpression, calendar);
}
return fireTime;
}
@Override
public Subscription schedule(Action0 action, CronExpression cronExpression)
{
return schedule(action, cronExpression, null);
}
@Override
public Subscription schedule(final Action0 action, final CronExpression cronExpression, final Calendar calendar)
{
return schedule(null, new Func2<Scheduler, Void, Subscription>()
{
@Override
public Subscription call(Scheduler scheduler, Void state)
{
action.call();
return Subscriptions.empty();
}
}, cronExpression, calendar);
}
@Override
public <T> Subscription schedule(T state, final Func2<? super Scheduler, ? super T, ? extends Subscription> action, final CronExpression cronExpression)
{
return schedule(state, action, cronExpression, null);
}
final class RecursiveAction<T> implements Func2<Scheduler, T, Subscription>
{
private final AtomicBoolean complete;
private final Func2<? super Scheduler, ? super T, ? extends Subscription> action;
private final CronExpression cronExpression;
private final Calendar calendar;
private final Date dueTime;
RecursiveAction(AtomicBoolean complete, Func2<? super Scheduler, ? super T, ? extends Subscription> action, CronExpression cronExpression, Calendar calendar, Date dueTime)
{
this.complete = complete;
this.action = action;
this.cronExpression = cronExpression;
this.calendar = calendar;
this.dueTime = dueTime;
}
@Override
public Subscription call(Scheduler scheduler, T state0)
{
if (!complete.get())
{
final Subscription sub1;
if (scheduler.now() < dueTime.getTime())
{
sub1 = Subscriptions.empty();
}
else
{
sub1 = action.call(scheduler, state0);
}
Date fireTime = findNextExecutionTime(new Date(scheduler.now()), cronExpression, calendar);
final Subscription sub2;
if (fireTime == null)
{
sub2 = Subscriptions.empty();
}
else
{
sub2 = scheduler.schedule(state0, new RecursiveAction<T>(complete, action, cronExpression, calendar, fireTime), fireTime);
}
return Subscriptions.create(new Action0()
{
@Override
public void call()
{
sub1.unsubscribe();
sub2.unsubscribe();
}
});
}
return Subscriptions.empty();
}
}
@Override
public <T> Subscription schedule(T state, final Func2<? super Scheduler, ? super T, ? extends Subscription> action, final CronExpression cronExpression, final Calendar calendar)
{
final AtomicBoolean complete = new AtomicBoolean();
Date initialfireTime = findNextExecutionTime(new Date(now()), cronExpression, calendar);
RecursiveAction<T> recursiveAction = new RecursiveAction<T>(complete, action, cronExpression, calendar, initialfireTime);
final Subscription sub;
if (initialfireTime == null)
{
sub = Subscriptions.empty();
}
else
{
sub = schedule(state, recursiveAction, initialfireTime);
}
return Subscriptions.create(new Action0()
{
@Override
public void call()
{
complete.set(true);
sub.unsubscribe();
}
});
}
}