// ---------------------------------------------------------------------------
// - Appointer.cpp -
// - afnix:pim module - appointer class implementation -
// ---------------------------------------------------------------------------
// - This program is free software; you can redistribute it and/or modify -
// - it provided that this copyright notice is kept intact. -
// - -
// - 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. In no event shall -
// - the copyright holder be liable for any direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software. -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2007 amaury darsch -
// ---------------------------------------------------------------------------
#include "Vector.hpp"
#include "Integer.hpp"
#include "Boolean.hpp"
#include "Runnable.hpp"
#include "Appointer.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
namespace afnix {
// -------------------------------------------------------------------------
// - private section -
// -------------------------------------------------------------------------
// max number of week days
static const long ATC_MAX_WDAY = 7;
static const long ATC_MAX_MDAY = 31;
static const long ATC_MAX_YMON = 12;
// the appointer rule
enum t_artype {
AR_BDAY, // blocked day
AR_SDAY, // special day
AR_MAXS, // max slot rule
AR_VBTM // valid block time
};
struct s_rule {
// the rule type
t_artype d_type;
// the rule info
long d_info;
union {
// special month day
long d_mday;
// the block begin time
t_long d_bbtm;
};
union {
// special year month
long d_ymon;
// the block end time
t_long d_betm;
};
// the next rule in chain
s_rule* p_next;
// create a rule by type
s_rule (t_artype type) {
d_type = type;
d_info = -1;
d_bbtm = 0LL;
d_betm = Time::DSEC;
p_next = nilp;
}
// copy construct this rule
s_rule (const s_rule& that) {
d_type = that.d_type;
d_info = that.d_info;
d_bbtm = that.d_bbtm;
d_betm = that.d_betm;
p_next = (that.p_next == nilp) ? nilp : new s_rule (*that.p_next);
}
// delete this rule
~s_rule (void) {
delete p_next;
}
// link a rule with the tail
void link (s_rule* rule) {
if (rule == nilp) return;
if (p_next != nilp) {
p_next->link (rule);
} else {
p_next = rule;
}
}
// check if the time is vald
bool chktime (const t_long time) const {
// the check result
bool result = true;
// process the 'and' plane first
const s_rule* rule = this;
while (rule != nilp) {
// process a blocked day rule
if (rule->d_type == AR_BDAY) {
Date date (time);
if (date.getwday () == rule->d_info) return false;
}
// process a special day rule
if (rule->d_type == AR_SDAY) {
Date date (time);
if ((date.getmday () == rule->d_mday) &&
(date.getymon () == rule->d_ymon)) return false;
}
// next rule
rule = rule->p_next;
}
// process the 'or' plane
rule = this;
while (rule != nilp) {
// process a valid block time
if (rule->d_type == AR_VBTM) {
t_long bbtm = time % Date::DSEC;
if (rule->d_bbtm <= bbtm) return true;
result = false;
}
rule = rule->p_next;
}
// default validation
return result;
}
// check if the slot is valid
bool chkslot (const t_long time, const t_long dlen) const {
// the check result
bool result = true;
// process the 'and' plane first
const s_rule* rule = this;
while (rule != nilp) {
// process a blocked day rule
if (rule->d_type == AR_BDAY) {
Date dbtm (time);
if (dbtm.getwday () == rule->d_info) return false;
Date detm (time + dlen);
if (detm.getwday () == rule->d_info) return false;
result = true;
}
// process a special day rule
if (rule->d_type == AR_SDAY) {
Date dbtm (time);
if ((dbtm.getmday () == rule->d_mday) &&
(dbtm.getymon () == rule->d_ymon)) return false;
Date detm (time + dlen);
if ((detm.getmday () == rule->d_mday) &&
(detm.getymon () == rule->d_ymon)) return false;
result = true;
}
// next rule
rule = rule->p_next;
}
// process the 'or' plane
rule = this;
while (rule != nilp) {
// process a valid block time
if (rule->d_type == AR_VBTM) {
t_long bbtm = time % Date::DSEC;
t_long betm = (time + dlen) % Date::DSEC;
if ((rule->d_bbtm <= bbtm) && (betm <= rule->d_betm)) return true;
result = false;
}
rule = rule->p_next;
}
// default validation
return result;
}
// get the next available time
t_long gettime (const t_long time, const bool nday) const {
t_long tnxt = time;
// process the 'and' plane first - the iteration limit is one
// wee controlled by the day counter
long dcnt = 0;
// loop in the rules
const s_rule* rule = this;
while (rule != nilp) {
// process a blocked day rule
if (rule->d_type == AR_BDAY) {
Date date (tnxt);
if (date.getwday () == rule->d_info) {
// move to the next day
tnxt = date.getbday () + Date::DSEC;
// check max counter
if (dcnt++ > ATC_MAX_WDAY) return time;
// reset processing rule
rule = this;
continue;
}
}
// process a special day rule
if (rule->d_type == AR_SDAY) {
Date date (tnxt);
if ((date.getmday () == rule->d_mday) &&
(date.getymon () == rule->d_ymon)) {
// move to the next day
tnxt = date.getbday () + Date::DSEC;
// check max counter
if (dcnt++ > ATC_MAX_WDAY) return time;
// reset processing rule
rule = this;
continue;
}
}
// next rule
rule = rule->p_next;
}
// process the 'or'plane by selecting the base time
rule = this;
while (rule != nilp) {
if (rule->d_type == AR_VBTM) {
t_long bbtm = tnxt % Date::DSEC;
if (bbtm < rule->d_bbtm) {
Date date (tnxt);
tnxt = date.getbday () + rule->d_bbtm;
return tnxt;
}
}
// next rule
rule = rule->p_next;
}
// check if we try the next day
if (nday == false) return time;
// move to the next day and restart
Date date (tnxt);
tnxt = date.getbday () + Date::DSEC;
return gettime (tnxt, false);
}
// get the time that adhere to the max slot rule
t_long getmaxs (const t_long time, const long dsnm) {
t_long tnxt = time;
// process the rules and check the max slot rule
const s_rule* rule = this;
while (rule != nilp) {
if (rule->d_type == AR_MAXS) {
if (dsnm >= rule->d_info) {
// move to the next day
Date date (tnxt);
tnxt = date.getbday () + Date::DSEC;
break;
}
}
rule = rule->p_next;
}
return tnxt;
}
// get the valid slot time
t_long getslot (const t_long time, const t_long dlen) const {
t_long tnxt = time;
// process the 'and' plane first - the iteration limit is one
// wee controlled by the day counter
long dcnt = 0;
// loop in the rules
const s_rule* rule = this;
while (rule != nilp) {
// process a blocked day rule
if (rule->d_type == AR_BDAY) {
// check begin time
Date dbtm (tnxt);
if (dbtm.getwday () == d_info) {
// move to the next day
tnxt = dbtm.getbday () + Date::DSEC;
// check max counter
if (dcnt++ > ATC_MAX_WDAY) return time;
// reset processing rule
rule = this;
continue;
}
// check end time
Date detm (tnxt+dlen);
if (detm.getwday () == rule->d_info) {
// move to the next day
tnxt = detm.getbday () + Date::DSEC;
// check max counter
if (dcnt++ > ATC_MAX_WDAY) return time;
// reset processing rule
rule = this;
continue;
}
}
// process a special day rule
if (rule->d_type == AR_SDAY) {
// check begin time
Date dbtm (tnxt);
if ((dbtm.getmday () == d_mday) &&
(dbtm.getymon () == d_ymon)) {
// move to the next day
tnxt = dbtm.getbday () + Date::DSEC;
// check max counter
if (dcnt++ > ATC_MAX_WDAY) return time;
// reset processing rule
rule = this;
continue;
}
// check end time
Date detm (tnxt+dlen);
if ((detm.getmday () == rule->d_mday) &&
(detm.getymon () == rule->d_ymon)) {
// move to the next day
tnxt = detm.getbday () + Date::DSEC;
// check max counter
if (dcnt++ > ATC_MAX_WDAY) return time;
// reset processing rule
rule = this;
continue;
}
}
// next rule
rule = rule->p_next;
}
// process the 'or'plane by selecting the base time
rule = this;
while (rule != nilp) {
if (rule->d_type == AR_VBTM) {
t_long bbtm = tnxt % Date::DSEC;
t_long betm = (tnxt + dlen) % Date::DSEC;
if ((rule->d_bbtm <= bbtm) && (betm <= rule->d_betm)) return tnxt;
}
// next rule
rule = rule->p_next;
}
// eventually try to get the next available time
return gettime (tnxt, true);
}
};
// check if a slot is in the slot set
static bool is_pushb_slot (const Set& pset, const Slot& slot) {
long slen = pset.length ();
for (long i = 0; i < slen; i++) {
Slot* sobj = dynamic_cast <Slot*> (pset.get (i));
if (sobj == nilp) continue;
if (sobj->match (slot) == true) return true;
}
return false;
}
// return the index of a push-backed slot that matches in duration
static long get_pushb_slot (const Set& pset, const t_long dlen) {
long slen = pset.length ();
for (long i = 0; i < slen; i++) {
Slot* sobj = dynamic_cast <Slot*> (pset.get (i));
if (sobj == nilp) continue;
if (sobj->getdlen () == dlen) return i;
}
return -1;
}
// return the index of a push-backed slot that matches in time and duration
static long get_pushb_slot (const Set& pset,
const t_long time, const t_long dlen) {
long slen = pset.length ();
for (long i = 0; i < slen; i++) {
Slot* sobj = dynamic_cast <Slot*> (pset.get (i));
if (sobj == nilp) continue;
if ((sobj->gettime () >= time) &&
(sobj->getdlen () == dlen)) return i;
}
return -1;
}
// return the pushback slot minimum time
static t_long get_pushb_amtm (const Set& pset, const t_long time) {
t_long result = time;
long slen = pset.length ();
for (long i = 0; i < slen; i++) {
Slot* sobj = dynamic_cast <Slot*> (pset.get (i));
if (sobj == nilp) continue;
t_long sltm = sobj->gettime ();
if (sltm <= result) result = sltm;
}
return result;
}
// return the pushback slot minimum time
static t_long get_pushb_amtm (const Set& pset,
const t_long time, const t_long mrtm) {
t_long result = mrtm < time ? time : mrtm;
long slen = pset.length ();
for (long i = 0; i < slen; i++) {
Slot* sobj = dynamic_cast <Slot*> (pset.get (i));
if (sobj == nilp) continue;
t_long sltm = sobj->gettime ();
if ((sltm <= result) && (mrtm <= sltm)) result = sltm;
}
return result;
}
// -------------------------------------------------------------------------
// - class section -
// -------------------------------------------------------------------------
// create a default appointer
Appointer::Appointer (void) {
d_time = 0LL;
d_dsnm = 0;
d_snum = 0;
p_rule = nilp;
}
// create a new appointer by time
Appointer::Appointer (const t_long time) {
d_time = time;
d_dsnm = 0;
d_snum = 0;
p_rule = nilp;
}
// copy construct this appointer
Appointer::Appointer (const Appointer& that) {
that.rdlock ();
d_time = that.d_time;
d_dsnm = that.d_dsnm;
d_snum = that.d_snum;
p_rule = (that.p_rule == nilp) ? nilp : new s_rule (*that.p_rule);
unlock ();
}
// destroy this appointer
Appointer::~Appointer (void) {
delete p_rule;
}
// return the appointer class name
String Appointer::repr (void) const {
return "Appointer";
}
// return a clone of this object
Object* Appointer::clone (void) const {
return new Appointer (*this);
}
// reset this appointer
void Appointer::reset (void) {
wrlock ();
d_dsnm = 0;
d_snum = 0;
d_pset.reset ();
unlock ();
}
// set the appointer time
void Appointer::settime (const t_long time) {
wrlock ();
d_time = time;
unlock ();
}
// get the appointer time
t_long Appointer::gettime (void) const {
rdlock ();
t_long result = d_time;
unlock ();
return result;
}
// get the appointer minimum time
t_long Appointer::getamtm (void) const {
rdlock ();
t_long result = get_pushb_amtm (d_pset, d_time);
unlock ();
return result;
}
// get the appointer minimum time with respect to a time
t_long Appointer::getamtm (const t_long mrtm) const {
rdlock ();
t_long result = get_pushb_amtm (d_pset, d_time, mrtm);
unlock ();
return result;
}
// set the appointer date
void Appointer::setdate (const Date& date) {
wrlock ();
d_time = date.gettime ();
unlock ();
}
// return the appointer date
Date Appointer::getdate (void) const {
rdlock ();
Date date (d_time);
unlock ();
return date;
}
// return the number of slots
long Appointer::getsnum (void) const{
rdlock ();
long result = d_snum;
unlock ();
return result;
}
// set a blocked day rule
void Appointer::setbday (const long wday) {
if ((wday < 0) || (wday >= ATC_MAX_WDAY)) {
throw Exception ("index-error", "invalid week day index to block");
}
wrlock ();
// create a new block
s_rule* rule = new s_rule (AR_BDAY);
rule->d_info = wday;
// attach the rule
if (p_rule != nilp) {
p_rule->link (rule);
} else {
p_rule = rule;
}
unlock ();
}
// set a special day rule
void Appointer::setsday (const long ymon, const long mday) {
if ((ymon < 1) || (ymon > ATC_MAX_YMON)) {
throw Exception ("index-error", "invalid special month index");
}
if ((mday < 1) || (mday >= ATC_MAX_MDAY)) {
throw Exception ("index-error", "invalid special month day index");
}
wrlock ();
// create a new rule
s_rule* rule = new s_rule (AR_SDAY);
rule->d_ymon = ymon;
rule->d_mday = mday;
// attach the rule
if (p_rule != nilp) {
p_rule->link (rule);
} else {
p_rule = rule;
}
unlock ();
}
// set a max slot rule rule
void Appointer::setmaxs (const long maxs) {
// ignore 0 or less argument
if (maxs <= 0) return;
wrlock ();
// create a new rule
s_rule* rule = new s_rule (AR_MAXS);
rule->d_info = maxs;
// attach the rule
if (p_rule != nilp) {
p_rule->link (rule);
} else {
p_rule = rule;
}
unlock ();
}
// set a valid block time rule
void Appointer::setvbtm (const t_long bbtm, const t_long betm) {
wrlock ();
// create a new block
s_rule* rule = new s_rule (AR_VBTM);
rule->d_bbtm = bbtm % Date::DSEC;
rule->d_betm = betm % Date::DSEC;
// attach the rule
if (p_rule != nilp) {
p_rule->link (rule);
} else {
p_rule = rule;
}
unlock ();
}
// get the next available slot by duration
Slot Appointer::getslot (const t_long dlen) {
wrlock ();
try {
// check in the pushback set
long sidx = get_pushb_slot (d_pset, dlen);
if (sidx != -1) {
// get the slot
Slot* slot = dynamic_cast <Slot*> (d_pset.get (sidx));
Slot result = *slot;
d_pset.remove (slot);
// unlock and return
unlock ();
return result;
}
// check without rule
if (p_rule == nilp) {
Slot result (d_time, dlen);
d_time += dlen;
d_snum++;
unlock ();
return result;
}
// check the max number of slots
t_long tnxt = p_rule->getmaxs (d_time, d_dsnm);
if (tnxt != d_time) d_dsnm = 0;
// compute the base time
if (p_rule->chkslot (tnxt, dlen) == false) {
tnxt = p_rule->gettime (tnxt, true);
if (p_rule->chktime (tnxt) == false) {
throw Exception ("appointer-error", "cannot set slot base time");
}
}
// compute the slot time
t_long time = p_rule->getslot (tnxt, dlen);
if (p_rule->chkslot (time, dlen) == false) {
throw Exception ("appointer-error", "cannot find appointer slot");
}
// allocate the result slot
Slot result (time, dlen);
// update the time
d_time = time + dlen;
d_dsnm++;
d_snum++;
unlock ();
return result;
} catch (...) {
unlock ();
throw;
}
}
// get the next available slot by time and duration
Slot Appointer::getslot (const t_long time, const t_long dlen) {
wrlock ();
try {
// check in the pushback set
long sidx = get_pushb_slot (d_pset, time, dlen);
if (sidx != -1) {
// get the slot
Slot* slot = dynamic_cast <Slot*> (d_pset.get (sidx));
Slot result = *slot;
d_pset.remove (slot);
// unlock and return
unlock ();
return result;
}
// check the time
if (d_time < time) settime (time);
// get the slot
Slot result = getslot (dlen);
unlock ();
return result;
} catch (...) {
unlock ();
throw;
}
}
// pushback a slot in the slot pool
void Appointer::pushback (const Slot& slot) {
wrlock ();
try {
if (is_pushb_slot (d_pset, slot) == false) {
d_pset.add (new Slot (slot));
}
unlock ();
} catch (...) {
unlock ();
throw;
}
}
// -------------------------------------------------------------------------
// - object section -
// -------------------------------------------------------------------------
// the quark zone
static const long QUARK_ZONE_LENGTH = 13;
static QuarkZone zone (QUARK_ZONE_LENGTH);
// the object supported quarks
static const long QUARK_RESET = zone.intern ("reset");
static const long QUARK_PUSHB = zone.intern ("pushback");
static const long QUARK_SETTIME = zone.intern ("set-time");
static const long QUARK_GETTIME = zone.intern ("get-time");
static const long QUARK_SETDATE = zone.intern ("set-date");
static const long QUARK_GETDATE = zone.intern ("get-date");
static const long QUARK_GETSLOT = zone.intern ("get-slot");
static const long QUARK_GETAMTM = zone.intern ("get-minimum-time");
static const long QUARK_GETSNUM = zone.intern ("get-slot-number");
static const long QUARK_SETBDAY = zone.intern ("set-blocked-day");
static const long QUARK_SETSDAY = zone.intern ("set-special-day");
static const long QUARK_SETMAXS = zone.intern ("set-maximum-slots");
static const long QUARK_SETVBTM = zone.intern ("set-valid-block-time");
// create a new object in a generic way
Object* Appointer::mknew (Vector* argv) {
long argc = (argv == nilp) ? 0 : argv->length ();
// create a default appointer object
if (argc == 0) return new Appointer;
// check for 1 argument
if (argc == 1) {
t_long time = argv->getint (0);
return new Appointer (time);
}
throw Exception ("argument-error",
"too many argument with appointer constructor");
}
// return true if the given quark is defined
bool Appointer::isquark (const long quark, const bool hflg) const {
rdlock ();
if (zone.exists (quark) == true) {
unlock ();
return true;
}
bool result = hflg ? Object::isquark (quark, hflg) : false;
unlock ();
return result;
}
// apply this object with a set of arguments and a quark
Object* Appointer::apply (Runnable* robj, Nameset* nset, const long quark,
Vector* argv) {
// get the number of arguments
long argc = (argv == nilp) ? 0 : argv->length ();
// check for argument
if (argc == 0) {
if (quark == QUARK_GETTIME) return new Integer (gettime ());
if (quark == QUARK_GETAMTM) return new Integer (getamtm ());
if (quark == QUARK_GETDATE) return new Date (gettime ());
if (quark == QUARK_GETSNUM) return new Integer (getsnum ());
if (quark == QUARK_RESET) {
reset ();
return nilp;
}
}
// check for 1 argument
if (argc == 1) {
if (quark == QUARK_GETSLOT) {
t_long dlen = argv->getint (0);
return new Slot (getslot (dlen));
}
if (quark == QUARK_SETBDAY) {
long wday = argv->getint (0);
setbday (wday);
return nilp;
}
if (quark == QUARK_SETMAXS) {
long maxs = argv->getint (0);
setmaxs (maxs);
return nilp;
}
if (quark == QUARK_SETTIME) {
t_long time = argv->getint (0);
settime (time);
return nilp;
}
if (quark == QUARK_GETAMTM) {
t_long mrtm = argv->getint (0);
return new Integer (getamtm (mrtm));
}
if (quark == QUARK_SETDATE) {
Object* obj = argv->get (0);
Date* date = dynamic_cast <Date*> (obj);
if (date == nilp) {
throw Exception ("type-error", "invalid object with set-date",
Object::repr (obj));
}
setdate (*date);
return nilp;
}
if (quark == QUARK_PUSHB) {
Object* obj = argv->get (0);
Slot* slot = dynamic_cast <Slot*> (obj);
if (slot == nilp) {
throw Exception ("type-error", "invalid object with pushback",
Object::repr (obj));
}
pushback (*slot);
return nilp;
}
}
// check for 2 arguments
if (argc == 2) {
if (quark == QUARK_GETSLOT) {
t_long time = argv->getint (0);
t_long dlen = argv->getint (1);
return new Slot (getslot (time, dlen));
}
if (quark == QUARK_SETVBTM) {
t_long bbtm = argv->getint (0);
t_long betm = argv->getint (1);
setvbtm (bbtm, betm);
return nilp;
}
if (quark == QUARK_SETSDAY) {
long ymon = argv->getint (0);
long mday = argv->getint (1);
setsday (ymon, mday);
return nilp;
}
}
// call the object method
return Object::apply (robj, nset, quark, argv);
}
}
syntax highlighted by Code2HTML, v. 0.9.1