// --------------------------------------------------------------------------- // - 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 (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 (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 (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 (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 (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 (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 (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 (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 (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); } }