/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.scheduler.limit;
import java.util.BitSet;
import org.joda.time.DateTime;
import org.joda.time.LocalTime;
import divconq.lang.CoreLocalTime;
import divconq.util.TimeUtil;
import divconq.xml.XElement;
public class DayWindow {
protected DayWindow parent = null;
protected BitSet excluded = new BitSet(1440);
protected BitSet included = new BitSet(1440);
protected boolean useDefaultWindow = true;
public void setParent(DayWindow v) {
this.parent = v;
}
public DayWindow getParent() {
return this.parent;
}
public void init(XElement config) {
if (config != null) {
// get all the exclude windows
for (XElement el : config.selectAll("ExcludeWindow")) {
CoreLocalTime from = TimeUtil.parseCoreLocalTime(el.getAttribute("From"));
CoreLocalTime to = TimeUtil.parseCoreLocalTime(el.getAttribute("To"));
this.setBits(false, from, to);
}
// then add all the include windows
for (XElement el : config.selectAll("IncludeWindow")) {
CoreLocalTime from = TimeUtil.parseCoreLocalTime(el.getAttribute("From"));
CoreLocalTime to = TimeUtil.parseCoreLocalTime(el.getAttribute("To"));
this.setBits(true, from, to);
}
// True - use default of 24 hours open if there are no windows given
// False - do not include 24 hour open if there are no windows given
// Parent - use only the parent (if present) if there are no windows given
// serves as a hint to the parser to include parent
String defaultWindow = config.getAttribute("DefaultWindow");
if ("False".equals(defaultWindow) || "Parent".equals(defaultWindow))
this.useDefaultWindow = false;
}
// if using default window and no values were found then fill all as though included
if (this.useDefaultWindow && (this.included.cardinality() == 0)) {
this.setBits(true, null, null);
}
}
public boolean excludeAll() {
if ((this.excluded.cardinality() == 1440) || (this.included.cardinality() == 0)) {
if (this.parent != null)
return this.parent.excludeAll();
return true;
}
return false;
}
/*
* search to see if there are any minutes open for this date after or at the current minute
*
* @param si
* @return
*/
public LocalTime nextTimeOn(DateTime si) {
// check that not completely excluded
if (this.excludeAll())
return null;
int addMinutes = 1;
LocalTime sil = si.toLocalTime();
// start at the next minute
int sidx = sil.getHourOfDay() * 60 + sil.getMinuteOfHour() + 1;
// if any minute is open, return it
for (int i = sidx; i < 1440; i++) {
if (this.check(i) == CheckLimitResult.Pass)
return sil.plusMinutes(addMinutes);
addMinutes++;
}
// nothing open today
return null;
}
public CheckLimitResult check(DateTime si) {
if (this.excludeAll())
return CheckLimitResult.Fail;
LocalTime sil = si.toLocalTime();
return this.check(sil.getHourOfDay() * 60 + sil.getMinuteOfHour());
}
// our setting take preference over the parents
public CheckLimitResult check(int idx) {
if (this.excluded.get(idx))
return CheckLimitResult.Fail;
if (this.included.get(idx))
return CheckLimitResult.Pass;
if (this.parent != null)
return this.parent.check(idx);
return CheckLimitResult.NA;
}
public void setBits(boolean include, CoreLocalTime from, CoreLocalTime to) {
int fidx = (from == null) ? 0 : from.getMinuteStamp();
if (fidx < 0)
fidx = 0;
if (fidx > 1439)
return;
int tidx = (to == null) ? 1440 : to.getMinuteStamp();
if (tidx <= 1)
return;
if (tidx > 1440)
tidx = 1440;
if (include)
this.included.set(fidx, tidx);
else
this.excluded.set(fidx, tidx);
}
}