calendar.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: http://svn.code.sf.net/p/frepple/code/trunk/src/model/calendar.cpp $
00003   version : $LastChangedRevision: 1764 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2012-08-27 19:07:52 +0200 (Mon, 27 Aug 2012) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba                 *
00010  *                                                                         *
00011  * This library is free software; you can redistribute it and/or modify it *
00012  * under the terms of the GNU Affero General Public License as published   *
00013  * by the Free Software Foundation; either version 3 of the License, or    *
00014  * (at your option) any later version.                                     *
00015  *                                                                         *
00016  * This library is distributed in the hope that it will be useful,         *
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
00019  * GNU Affero General Public License for more details.                     *
00020  *                                                                         *
00021  * You should have received a copy of the GNU Affero General Public        *
00022  * License along with this program.                                        *
00023  * If not, see <http://www.gnu.org/licenses/>.                             *
00024  *                                                                         *
00025  ***************************************************************************/
00026 
00027 #define FREPPLE_CORE
00028 #include "frepple/model.h"
00029 
00030 namespace frepple
00031 {
00032 
00033 template<class Calendar> DECLARE_EXPORT Tree utils::HasName<Calendar>::st;
00034 DECLARE_EXPORT const MetaCategory* Calendar::metadata;
00035 DECLARE_EXPORT const MetaCategory* Calendar::Bucket::metadata;
00036 DECLARE_EXPORT const MetaClass *CalendarDouble::metadata;
00037 DECLARE_EXPORT const MetaClass *CalendarDouble::BucketDouble::metadata;
00038 
00039 
00040 int Calendar::initialize()
00041 {
00042   // Initialize the metadata
00043   metadata = new MetaCategory("calendar", "calendars", reader, writer);
00044 
00045   // Initialize the Python class
00046   FreppleCategory<Calendar>::getType().addMethod("addBucket", addPythonBucket, METH_KEYWORDS, "find a bucket or create a new one");
00047   int ok = Calendar::Bucket::initialize();
00048   ok +=  FreppleCategory<Calendar>::initialize();
00049   ok += CalendarBucketIterator::initialize();
00050   ok += CalendarEventIterator::initialize();
00051   ok += CalendarDouble::BucketDouble::initialize();
00052   return ok;
00053 }
00054 
00055 
00056 int Calendar::Bucket::initialize()
00057 {
00058   // Initialize the metadata
00059   metadata = new MetaCategory("bucket", "buckets");
00060 
00061   // Initialize the Python class
00062   PythonType& x = FreppleCategory<Calendar::Bucket>::getType();
00063   x.setName(metadata->type);
00064   x.setDoc("frePPLe " + metadata->type);
00065   x.supportgetattro();
00066   x.supportsetattro();
00067   x.supportstr();
00068   x.supportcompare();
00069   const_cast<MetaCategory*>(metadata)->pythonClass = x.type_object();
00070   return x.typeReady();
00071 }
00072 
00073 
00074 int CalendarDouble::initialize()
00075 {
00076   // Initialize the metadata
00077   metadata = new MetaClass("calendar", "calendar_double",
00078       Object::createString<CalendarDouble>, true);
00079 
00080   // Initialize the Python class
00081   FreppleClass<CalendarDouble,Calendar>::getType().addMethod("setValue", setPythonValue, METH_KEYWORDS, "update the value in a date range");
00082   FreppleClass<CalendarDouble,Calendar>::getType().addMethod("events", getEvents, METH_VARARGS, "return an event iterator");
00083   return FreppleClass<CalendarDouble,Calendar>::initialize();
00084 }
00085 
00086 
00087 int CalendarDouble::BucketDouble::initialize()
00088 {
00089   // Initialize the metadata
00090   metadata = new MetaClass("bucket", "bucket_double"); // xxx, NULL, true);
00091 
00092   // Initialize the Python class
00093   PythonType& x = FreppleClass<CalendarDouble::BucketDouble,Calendar::Bucket>::getType();
00094   x.setName(metadata->type);
00095   x.setDoc("frePPLe " + metadata->type);
00096   x.supportgetattro();
00097   x.supportsetattro();
00098   x.supportstr();
00099   x.supportcompare();
00100   x.setBase(Calendar::Bucket::metadata->pythonClass);
00101   x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation");
00102   const_cast<MetaClass*>(metadata)->pythonClass = x.type_object();
00103   return x.typeReady();
00104 }
00105 
00106 
00107 /** Updates the value in a certain date range.<br>
00108   * This will create a new bucket if required. */
00109 void CalendarDouble::setValue(Date start, Date end, const double v)
00110 {
00111   BucketDouble* x = static_cast<BucketDouble*>(findBucket(start));
00112   if (x && x->getStart() == start && x->getEnd() <= end)
00113     // We can update an existing bucket: it has the same start date
00114     // and ends before the new effective period ends.
00115     x->setEnd(end);
00116   else
00117     // Creating a new bucket
00118     x = static_cast<BucketDouble*>(addBucket(start,end));
00119   x->setValue(v);
00120   x->setPriority(lowestPriority()-1);
00121 }
00122 
00123 
00124 void CalendarDouble::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
00125 {
00126   // Writing a reference
00127   if (m == REFERENCE)
00128   {
00129     o->writeElement(tag, Tags::tag_name, getName());
00130     return;
00131   }
00132 
00133   // Write the complete object
00134   if (m != NOHEADER) o->BeginObject(tag, Tags::tag_name, XMLEscape(getName()));
00135 
00136   // Write the default value
00137   if (getDefault()) o->writeElement(Tags::tag_default, getDefault());
00138 
00139   // Write all buckets
00140   o->BeginObject (Tags::tag_buckets);
00141   for (BucketIterator i = beginBuckets(); i != endBuckets(); ++i)
00142     // We use the FULL mode, to force the buckets being written regardless
00143     // of the depth in the XML tree.
00144     o->writeElement(Tags::tag_bucket, *i, FULL);
00145   o->EndObject(Tags::tag_buckets);
00146 
00147   o->EndObject(tag);
00148 }
00149 
00150 
00151 void CalendarDouble::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00152 {
00153   if (pAttr.isA(Tags::tag_default))
00154     pElement >> defaultValue;
00155   else
00156     Calendar::endElement(pIn, pAttr, pElement);
00157 }
00158 
00159 
00160 DECLARE_EXPORT Calendar::~Calendar()
00161 {
00162   // De-allocate all the dynamic memory used for the bucket objects
00163   while (firstBucket)
00164   {
00165     Bucket* tmp = firstBucket;
00166     firstBucket = firstBucket->nextBucket;
00167     delete tmp;
00168   }
00169 
00170   // Remove all references from locations
00171   for (Location::iterator l = Location::begin(); l != Location::end(); ++l)
00172   {
00173     if (l->getAvailable() == this)
00174       l->setAvailable(NULL);
00175   }
00176 }
00177 
00178 
00179 DECLARE_EXPORT CalendarDouble::~CalendarDouble()
00180 {
00181   // Remove all references from buffers
00182   for (Buffer::iterator b = Buffer::begin(); b != Buffer::end(); ++b)
00183   {
00184     if (b->getMinimumCalendar()==this) b->setMinimumCalendar(NULL);
00185     if (b->getMaximumCalendar()==this) b->setMaximumCalendar(NULL);
00186   }
00187 
00188   // Remove all references from resources
00189   for (Resource::iterator r = Resource::begin(); r != Resource::end(); ++r)
00190     if (r->getMaximumCalendar()==this) r->setMaximumCalendar(NULL);
00191 }
00192 
00193 
00194 DECLARE_EXPORT Calendar::Bucket* Calendar::addBucket
00195 (Date start, Date end, int id)
00196 {
00197   // Assure the start is before the end.
00198   if (start > end)
00199     return createNewBucket(end, start, id);
00200   else
00201     return createNewBucket(start, end, id);
00202 }
00203 
00204 
00205 DECLARE_EXPORT void Calendar::removeBucket(Calendar::Bucket* bkt)
00206 {
00207   // Verify the bucket is on this calendar indeed
00208   Bucket *b = firstBucket;
00209   while (b && b != bkt) b = b->nextBucket;
00210 
00211   // Error
00212   if (!b)
00213     throw DataException("Trying to remove unavailable bucket from calendar '"
00214         + getName() + "'");
00215 
00216   // Update the list
00217   if (bkt->prevBucket)
00218     // Previous bucket links to a new next bucket
00219     bkt->prevBucket->nextBucket = bkt->nextBucket;
00220   else
00221     // New head for the bucket list
00222     firstBucket = bkt->nextBucket;
00223   if (bkt->nextBucket)
00224     // Update the reference prevBucket of the next bucket
00225     bkt->nextBucket->prevBucket = bkt->prevBucket;
00226 
00227   // Delete the bucket
00228   delete bkt;
00229 }
00230 
00231 
00232 DECLARE_EXPORT void Calendar::Bucket::setEnd(const Date d)
00233 {
00234   // Check
00235   if (d < startdate) 
00236     throw DataException("Calendar bucket end must be later than its start");
00237 
00238   // Update
00239   enddate = d;
00240 }
00241 
00242 
00243 DECLARE_EXPORT void Calendar::Bucket::setStart(const Date d) 
00244 {
00245   // Check
00246   if (d > enddate) 
00247     throw DataException("Calendar bucket start must be earlier than its end");
00248 
00249   // Update the field
00250   startdate = d;
00251 
00252   // Keep the list in sorted order
00253   updateSort();
00254 }
00255 
00256 
00257 DECLARE_EXPORT void Calendar::Bucket::updateSort()
00258 {
00259   // Update the position in the list
00260   bool ok = true;
00261   do
00262   {
00263     ok = true;
00264     if (nextBucket && (
00265       nextBucket->startdate < startdate ||
00266       (nextBucket->startdate == startdate && nextBucket->priority < priority)
00267       ))
00268     {
00269       // Move a position later in the list
00270       if (nextBucket->nextBucket)
00271         nextBucket->nextBucket->prevBucket = this;
00272       if (prevBucket)
00273         prevBucket->nextBucket = nextBucket;
00274       else
00275         cal->firstBucket = nextBucket;
00276       nextBucket->prevBucket = prevBucket;
00277       prevBucket = nextBucket;
00278       Calendar::Bucket* tmp = nextBucket->nextBucket;
00279       nextBucket->nextBucket = this;
00280       nextBucket = tmp;
00281       ok = false;
00282     }
00283     else if (prevBucket && (
00284       prevBucket->startdate > startdate ||
00285       (prevBucket->startdate == startdate && prevBucket->priority > priority)
00286       ))
00287     {
00288       // Move a position earlier in the list
00289       if (prevBucket->prevBucket)
00290         prevBucket->prevBucket->nextBucket = this;      
00291       if (nextBucket)
00292         nextBucket->prevBucket = prevBucket;
00293       prevBucket->nextBucket = nextBucket;
00294       nextBucket = prevBucket;
00295       Calendar::Bucket* tmp = prevBucket->prevBucket;
00296       prevBucket->prevBucket = this;
00297       prevBucket = tmp;
00298       ok = false;
00299     }
00300   }
00301   while (!ok); // Repeat till in place
00302 }
00303 
00304 
00305 DECLARE_EXPORT Calendar::Bucket* Calendar::findBucket(Date d, bool fwd) const
00306 {
00307   Calendar::Bucket *curBucket = NULL;
00308   double curPriority = DBL_MAX;
00309   long timeInWeek = INT_MIN;  
00310   for (Bucket *b = firstBucket; b; b = b->nextBucket)
00311   {
00312     if (b->getStart() > d)
00313       // Buckets are sorted by the start date. Other entries definitely
00314       // won't be effective.
00315       break;
00316     else if (curPriority > b->getPriority()
00317         && ( (fwd && d >= b->getStart() && d < b->getEnd()) ||
00318             (!fwd && d > b->getStart() && d <= b->getEnd())
00319            ))
00320     {
00321       if (!b->offsetcounter)
00322       {
00323         // Continuously effective
00324         curPriority = b->getPriority();
00325         curBucket = &*b;
00326       }
00327       else
00328       {
00329         // There are ineffective periods during the week 
00330         if (timeInWeek == INT_MIN) 
00331         {
00332           // Lazy initialization
00333           timeInWeek = d.getSecondsWeek();
00334           // Special case: asking backward while at first second of the week
00335           if (!fwd && timeInWeek == 0L) timeInWeek = 604800L;
00336         }
00337         // Check all intervals
00338         for (short i=0; i<b->offsetcounter; i+=2)
00339           if ((fwd && timeInWeek >= b->offsets[i] && timeInWeek < b->offsets[i+1]) ||
00340               (!fwd && timeInWeek > b->offsets[i] && timeInWeek <= b->offsets[i+1]))
00341           {
00342             // All conditions are met!
00343             curPriority = b->getPriority();
00344             curBucket = &*b;
00345             break;
00346           }
00347       }
00348     }
00349   }
00350   return curBucket;
00351 }
00352 
00353 
00354 DECLARE_EXPORT Calendar::Bucket* Calendar::findBucket(int ident) const
00355 {
00356   for (Bucket *b = firstBucket; b; b = b->nextBucket)
00357     if (b->id == ident) return b;
00358   return NULL;
00359 }
00360 
00361 
00362 DECLARE_EXPORT void Calendar::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
00363 {
00364   // Writing a reference
00365   if (m == REFERENCE)
00366   {
00367     o->writeElement
00368     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00369     return;
00370   }
00371 
00372   // Write the complete object
00373   if (m != NOHEADER) o->BeginObject
00374     (tag, Tags::tag_name, XMLEscape(getName()), Tags::tag_type, getType().type);
00375 
00376   // Write all buckets
00377   o->BeginObject (Tags::tag_buckets);
00378   for (BucketIterator i = beginBuckets(); i != endBuckets(); ++i)
00379     // We use the FULL mode, to force the buckets being written regardless
00380     // of the depth in the XML tree.
00381     o->writeElement(Tags::tag_bucket, *i, FULL);
00382   o->EndObject(Tags::tag_buckets);
00383 
00384   o->EndObject(tag);
00385 }
00386 
00387 
00388 DECLARE_EXPORT Calendar::Bucket* Calendar::createBucket(const AttributeList& atts)
00389 {
00390   // Pick up the start, end and name attributes
00391   const DataElement* d = atts.get(Tags::tag_start);
00392   Date startdate = *d ? d->getDate() : Date::infinitePast;
00393   d = atts.get(Tags::tag_end);
00394   Date enddate = *d ? d->getDate() : Date::infiniteFuture;
00395   d = atts.get(Tags::tag_id);
00396   int id = *d ? d->getInt() : INT_MIN;
00397 
00398   // Check for existence of the bucket with the same identifier
00399   Calendar::Bucket* result = findBucket(id);
00400 
00401   // Pick up the action attribute and update the bucket accordingly
00402   switch (MetaClass::decodeAction(atts))
00403   {
00404     case ADD:
00405       // Only additions are allowed
00406       if (result)
00407       {
00408         ostringstream o;
00409         o << "Bucket " << id << " already exists in calendar '" << getName() << "'";
00410         throw DataException(o.str());
00411       }
00412       result = addBucket(startdate, enddate, id);
00413       return result;
00414     case CHANGE:
00415       // Only changes are allowed
00416       if (!result)
00417       {
00418         ostringstream o;
00419         o << "Bucket " << id << " doesn't exist in calendar '" << getName() << "'";
00420         throw DataException(o.str());
00421       }
00422       return result;
00423     case REMOVE:
00424       // Delete the entity
00425       if (!result)
00426       {
00427         ostringstream o;
00428         o << "Bucket " << id << " doesn't exist in calendar '" << getName() << "'";
00429         throw DataException(o.str());
00430       }
00431       else
00432       {
00433         // Delete it
00434         removeBucket(result);
00435         return NULL;
00436       }
00437     case ADD_CHANGE:
00438       if (!result)
00439         // Adding a new bucket
00440         result = addBucket(startdate, enddate, id);
00441       return result;
00442   }
00443 
00444   // This part of the code isn't expected not be reached
00445   throw LogicException("Unreachable code reached");
00446 }
00447 
00448 
00449 DECLARE_EXPORT void Calendar::beginElement(XMLInput& pIn, const Attribute& pAttr)
00450 {
00451   if (pAttr.isA (Tags::tag_bucket)
00452       && pIn.getParentElement().first.isA(Tags::tag_buckets))
00453     // A new bucket
00454     pIn.readto(createBucket(pIn.getAttributes()));
00455 }
00456 
00457 
00458 DECLARE_EXPORT void Calendar::Bucket::writeHeader(XMLOutput *o, const Keyword& tag) const
00459 {
00460   // The header line has a variable number of attributes: start, end and/or name
00461   if (startdate != Date::infinitePast)
00462   {
00463     if (enddate != Date::infiniteFuture)
00464       o->BeginObject(tag, Tags::tag_id, id, Tags::tag_start, startdate, Tags::tag_end, enddate);
00465     else
00466       o->BeginObject(tag, Tags::tag_id, id, Tags::tag_start, startdate);
00467   }
00468   else
00469   {
00470     if (enddate != Date::infiniteFuture)
00471       o->BeginObject(tag, Tags::tag_id, id, Tags::tag_end, enddate);
00472     else
00473       o->BeginObject(tag, Tags::tag_id, id);
00474   }
00475 }
00476 
00477 
00478 DECLARE_EXPORT void Calendar::Bucket::writeElement
00479 (XMLOutput *o, const Keyword& tag, mode m) const
00480 {
00481   assert(m == DEFAULT || m == FULL);
00482   writeHeader(o,tag);
00483   if (priority) o->writeElement(Tags::tag_priority, priority);
00484   if (days != 127) o->writeElement(Tags::tag_days, days);
00485   if (starttime)
00486     o->writeElement(Tags::tag_starttime, starttime);
00487   if (endtime != TimePeriod(86400L))
00488     o->writeElement(Tags::tag_endtime, endtime);
00489   o->EndObject(tag);
00490 }
00491 
00492 
00493 DECLARE_EXPORT void Calendar::Bucket::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00494 {
00495   if (pAttr.isA(Tags::tag_priority))
00496     pElement >> priority;
00497   else if (pAttr.isA(Tags::tag_days))
00498     setDays(pElement.getInt());
00499   else if (pAttr.isA(Tags::tag_starttime))
00500     setStartTime(pElement.getTimeperiod());
00501   else if (pAttr.isA(Tags::tag_endtime))
00502     setEndTime(pElement.getTimeperiod());
00503 }
00504 
00505 
00506 DECLARE_EXPORT void Calendar::Bucket::setId(int ident)
00507 {
00508   // Check non-null calendar
00509   if (!cal) 
00510     throw LogicException("Generating calendar bucket without calendar");
00511 
00512   if (ident == INT_MIN)
00513   {
00514     // Force generation of a new identifier.
00515     // This is done by taking the highest existing id and adding 1.
00516     for (BucketIterator i = cal->beginBuckets(); i != cal->endBuckets(); ++i)
00517       if (i->id >= ident) ident = i->id + 1;
00518     if (ident == INT_MIN) ident = 1;
00519   }
00520   else
00521   {
00522     // Check & enforce uniqueness of the argument identifier  
00523     bool unique;
00524     do
00525     {
00526       unique = true;
00527       for (BucketIterator i = cal->beginBuckets(); i != cal->endBuckets(); ++i)
00528         if (i->id == ident && &(*i) != this)
00529         {
00530           // Update the indentifier to avoid violating the uniqueness
00531           unique = false;
00532           ++ident;
00533           break;
00534         }
00535     }
00536     while (!unique);
00537   }
00538 
00539   // Update the identifier
00540   id = ident;
00541 }
00542 
00543 
00544 DECLARE_EXPORT Calendar::EventIterator& Calendar::EventIterator::operator++()
00545 {
00546   if (!theCalendar)
00547     throw LogicException("Can't walk forward on event iterator of NULL calendar.");
00548 
00549   // Go over all entries and ask them to update the iterator
00550   Date d = curDate;
00551   curDate = Date::infiniteFuture;
00552   for (const Calendar::Bucket *b = theCalendar->firstBucket; b; b = b->nextBucket)
00553     b->nextEvent(this, d); 
00554 
00555   // Remember the bucket that won the evaluation
00556   lastBucket = curBucket;
00557   lastPriority = curPriority;
00558   return *this;
00559 }
00560 
00561 
00562 DECLARE_EXPORT Calendar::EventIterator& Calendar::EventIterator::operator--()
00563 {
00564   if (!theCalendar)
00565     throw LogicException("Can't walk backward on event iterator of NULL calendar.");
00566 
00567   // Go over all entries and ask them to update the iterator
00568   Date d = curDate;
00569   curDate = Date::infinitePast;
00570   for (const Calendar::Bucket *b = theCalendar->firstBucket; b; b = b->nextBucket)
00571     b->prevEvent(this, d);
00572   
00573   // Remember the bucket that won the evaluation
00574   lastBucket = curBucket;
00575   lastPriority = curPriority;
00576   return *this;
00577 }
00578 
00579 
00580 DECLARE_EXPORT void Calendar::Bucket::nextEvent(EventIterator* iter, Date refDate) const
00581 {
00582   // FIRST CASE: Bucket that is continuously effective
00583   if (!offsetcounter)
00584   {
00585     // Evaluate the start date of the bucket
00586     if (refDate < startdate && priority <= iter->lastPriority && (
00587       startdate < iter->curDate ||
00588       (startdate == iter->curDate && priority <= iter->curPriority)
00589       ))
00590     {
00591       iter->curDate = startdate;
00592       iter->curBucket = this;
00593       iter->curPriority = priority;
00594       return;
00595     }
00596 
00597     // Next evaluate the end date of the bucket
00598     if (refDate < enddate && enddate <= iter->curDate && iter->lastBucket == this)
00599     {
00600       iter->curDate = enddate;
00601       iter->curBucket = iter->theCalendar->findBucket(enddate);
00602       iter->curPriority = iter->curBucket ? iter->curBucket->priority : INT_MAX;
00603       return;
00604     }
00605 
00606     // End function: this bucket won't create next event
00607     return;
00608   }
00609 
00610   // SECOND CASE: Interruptions in effectivity.
00611 
00612   // Jump to the start date
00613   bool allowEqualAtStart = false;
00614   if (refDate < startdate && (
00615     startdate < iter->curDate ||
00616     (startdate == iter->curDate && priority <= iter->curPriority) 
00617     ))
00618   {
00619     refDate = startdate;
00620     allowEqualAtStart = true;
00621   }
00622 
00623   // Find position in the week
00624   long timeInWeek = refDate.getSecondsWeek();
00625 
00626   // Loop over all effective days in the week in which refDate falls
00627   for (short i=0; i<offsetcounter; i+=2)
00628   {
00629     // Start and end date of this effective period
00630     Date st = refDate + TimePeriod(offsets[i] - timeInWeek);
00631     Date nd = refDate + TimePeriod(offsets[i+1] - timeInWeek);
00632 
00633     // Move to next week if required
00634     bool canReturn = true;
00635     if (refDate >= nd)
00636     {
00637       st += TimePeriod(86400L*7);
00638       nd += TimePeriod(86400L*7);
00639       canReturn = false;
00640     }
00641 
00642     // Check enddate and startdate are not violated
00643     if (st < startdate)
00644       if (nd < startdate)
00645         continue;  // No overlap with overall effective dates
00646       else
00647         st = startdate;
00648     if (nd >= enddate)
00649       if (st >= enddate)
00650         continue;  // No overlap with effective range
00651       else
00652         nd = enddate;
00653 
00654     if ((refDate < st || (allowEqualAtStart && refDate == st)) && priority <= iter->lastPriority)
00655     {
00656       if (st > iter->curDate || (st == iter->curDate && priority > iter->curPriority))
00657       {
00658         // Another bucket is doing better already
00659         if (canReturn) break;
00660         else continue;
00661       }
00662       // The effective start on this weekday qualifies as the next event
00663       iter->curDate = st;
00664       iter->curBucket = this;
00665       iter->curPriority = priority;
00666       if (canReturn) return;
00667     }
00668     if (refDate < nd && iter->lastBucket == this)
00669     {
00670       if (nd > iter->curDate || (nd == iter->curDate && priority > iter->curPriority))
00671       {
00672         // Another bucket is doing better already
00673         if (canReturn) break;
00674         else continue;
00675       }
00676       // This bucket is currently effective.
00677       // The effective end on this weekday qualifies as the next event.
00678       iter->curDate = nd;
00679       iter->curBucket = iter->theCalendar->findBucket(nd); 
00680       iter->curPriority = iter->curBucket ? iter->curBucket->priority : INT_MAX;
00681       if (canReturn) return;
00682     }
00683   }
00684 }
00685 
00686 
00687 DECLARE_EXPORT void Calendar::Bucket::prevEvent(EventIterator* iter, Date refDate) const
00688 {
00689   // FIRST CASE: Bucket that is continuously effective
00690   if (!offsetcounter)
00691   {
00692     // First evaluate the end date of the bucket
00693     if (refDate > enddate && priority <= iter->lastPriority && (
00694        enddate > iter->curDate ||
00695        (enddate == iter->curDate && priority < iter->curPriority)
00696       ))
00697     {      
00698       iter->curDate = enddate;
00699       iter->curBucket = this;
00700       iter->curPriority = priority;
00701       return;
00702     }
00703 
00704     // Next evaluate the start date of the bucket
00705     if (refDate > startdate && startdate > iter->curDate && iter->lastBucket == this)
00706     {
00707       iter->curDate = startdate;
00708       iter->curBucket = iter->theCalendar->findBucket(startdate, false);
00709       iter->curPriority = iter->curBucket ? iter->curBucket->priority : INT_MAX;
00710       return;
00711     }
00712 
00713     // End function: this bucket won't create the previous event
00714     return;
00715   }
00716 
00717   // SECOND CASE: Interruptions in effectivity.
00718 
00719   // Jump to the end date
00720   bool allowEqualAtEnd = false;
00721   if (refDate > enddate && (
00722     enddate > iter->curDate ||
00723     (enddate == iter->curDate && priority < iter->curPriority)
00724     )) 
00725   {
00726     refDate = enddate;
00727     allowEqualAtEnd = true;
00728   }
00729 
00730   // Find position in the week
00731   long timeInWeek = refDate.getSecondsWeek();
00732 
00733   // Loop over all effective days in the week in which refDate falls
00734   for (short i=offsetcounter-1; i>=0; i-=2)
00735   {
00736     // Start and end date of this effective period
00737     Date st = refDate + TimePeriod(offsets[i] - timeInWeek);
00738     Date nd = refDate + TimePeriod(offsets[i+1] - timeInWeek);
00739 
00740     // Move to previous week if required
00741     bool canReturn = true;
00742     if (refDate <= st)
00743     {
00744       st -= TimePeriod(86400L*7);
00745       nd -= TimePeriod(86400L*7);
00746       canReturn = false;
00747     }
00748 
00749     // Check enddate and startdate are not violated
00750     if (st <= startdate)
00751       if (nd <= startdate)
00752         continue;  // No overlap with overall effective dates
00753       else
00754         st = startdate;
00755     if (nd > enddate)
00756       if (st > enddate)
00757         continue;  // No overlap with effective range
00758       else
00759         nd = enddate;
00760 
00761     if ((refDate > nd || (allowEqualAtEnd && refDate == nd))
00762       && priority <= iter->lastPriority)
00763     {
00764       if (nd < iter->curDate || (nd == iter->curDate && priority <= iter->curPriority))
00765       {
00766         // Another bucket is doing better already
00767         if (canReturn) break;
00768         else continue;
00769       }
00770       // The effective end on this weekday qualifies as the next event
00771       iter->curDate = nd;
00772       iter->curBucket = this;
00773       if (canReturn) return;
00774     }
00775     if (refDate > st && iter->lastBucket == this)
00776     {
00777       if (st < iter->curDate || (st == iter->curDate && priority <= iter->curPriority))
00778       {
00779         // Another bucket is doing better already
00780         if (canReturn) break;
00781         else continue;
00782       }
00783       // This bucket is currently effective.
00784       // The effective end on this weekday qualifies as the next event.
00785       iter->curDate = st;
00786       iter->curBucket = iter->theCalendar->findBucket(st, false);
00787       iter->curPriority = iter->curBucket ? iter->curBucket->priority : INT_MAX;
00788       if (canReturn) return;
00789     }
00790   }
00791 }
00792 
00793 
00794 DECLARE_EXPORT PyObject* Calendar::getattro(const Attribute& attr)
00795 {
00796   if (attr.isA(Tags::tag_name))
00797     return PythonObject(getName());
00798   if (attr.isA(Tags::tag_buckets))
00799     return new CalendarBucketIterator(this);
00800   return NULL;
00801 }
00802 
00803 
00804 DECLARE_EXPORT int Calendar::setattro(const Attribute& attr, const PythonObject& field)
00805 {
00806   if (attr.isA(Tags::tag_name))
00807     setName(field.getString());
00808   else
00809     return -1;  // Error
00810   return 0;  // OK
00811 }
00812 
00813 
00814 DECLARE_EXPORT PyObject* CalendarDouble::getattro(const Attribute& attr)
00815 {
00816   if (attr.isA(Tags::tag_default))
00817     return PythonObject(getDefault());
00818   return Calendar::getattro(attr);
00819 }
00820 
00821 
00822 DECLARE_EXPORT int CalendarDouble::setattro(const Attribute& attr, const PythonObject& field)
00823 {
00824   if (attr.isA(Tags::tag_default))
00825     setDefault(field.getDouble());
00826   else
00827     return Calendar::setattro(attr, field);
00828   return 0;
00829 }
00830 
00831 
00832 DECLARE_EXPORT PyObject* CalendarDouble::setPythonValue(PyObject* self, PyObject* args, PyObject* kwdict)
00833 {
00834   try
00835   {
00836     // Pick up the calendar
00837     CalendarDouble *cal = static_cast<CalendarDouble*>(self);
00838     if (!cal) throw LogicException("Can't set value of a NULL calendar");
00839 
00840     // Parse the arguments
00841     PyObject *pystart, *pyend, *pyval;
00842     if (!PyArg_ParseTuple(args, "OOO:setValue", &pystart, &pyend, &pyval))
00843       return NULL;
00844 
00845     // Update the calendar
00846     PythonObject start(pystart), end(pyend), val(pyval);
00847     cal->setValue(start.getDate(), end.getDate(), val.getDouble());
00848   }
00849   catch(...)
00850   {
00851     PythonType::evalException();
00852     return NULL;
00853   }
00854   return Py_BuildValue("");
00855 }
00856 
00857 
00858 DECLARE_EXPORT PyObject* Calendar::addPythonBucket(PyObject* self, PyObject* args, PyObject* kwdict)
00859 {
00860   try
00861   {
00862     // Pick up the calendar
00863     Calendar* cal = static_cast<Calendar*>(self);
00864     if (!cal) throw LogicException("Can't set value of a NULL calendar");
00865 
00866     // Parse the arguments
00867     int id = 1;
00868     if (!PyArg_ParseTuple(args, "|i:addBucket", &id))
00869       return NULL;
00870 
00871     // See if the bucket exists, or create it
00872     Bucket * b = cal->findBucket(id);
00873     if (!b) b = cal->addBucket(Date::infinitePast, Date::infiniteFuture, id);
00874 
00875     // Return a reference
00876     Py_INCREF(b);
00877     return b;
00878   }
00879   catch(...)
00880   {
00881     PythonType::evalException();
00882     return NULL;
00883   }
00884   return Py_BuildValue("");
00885 }
00886 
00887 
00888 int CalendarBucketIterator::initialize()
00889 {
00890   // Initialize the type
00891   PythonType& x = PythonExtension<CalendarBucketIterator>::getType();
00892   x.setName("calendarBucketIterator");
00893   x.setDoc("frePPLe iterator for calendar buckets");
00894   x.supportiter();
00895   return x.typeReady();
00896 }
00897 
00898 
00899 PyObject* CalendarBucketIterator::iternext()
00900 {
00901   if (i == cal->endBuckets()) return NULL;
00902   PyObject *result = &*(i++);
00903   Py_INCREF(result);
00904   return result;
00905 }
00906 
00907 
00908 DECLARE_EXPORT PyObject* Calendar::Bucket::getattro(const Attribute& attr)
00909 {
00910   if (attr.isA(Tags::tag_start))
00911     return PythonObject(getStart());
00912   if (attr.isA(Tags::tag_end))
00913     return PythonObject(getEnd());
00914   if (attr.isA(Tags::tag_value))
00915   {
00916     if (cal->getType() == *CalendarDouble::metadata)   
00917       return PythonObject(dynamic_cast< CalendarDouble::BucketDouble* >(this)->getValue());
00918     PyErr_SetString(PythonLogicException, "calendar type not recognized");
00919     return NULL;
00920   }
00921   if (attr.isA(Tags::tag_priority))
00922     return PythonObject(getPriority());
00923   if (attr.isA(Tags::tag_days))
00924     return PythonObject(getDays());
00925   if (attr.isA(Tags::tag_starttime))
00926     return PythonObject(getStartTime());
00927   if (attr.isA(Tags::tag_endtime))
00928     return PythonObject(getEndTime());
00929   if (attr.isA(Tags::tag_id))
00930     return PythonObject(getId());
00931   if (attr.isA(Tags::tag_calendar))
00932     return PythonObject(getCalendar());
00933   return NULL;
00934 }
00935 
00936 
00937 DECLARE_EXPORT int Calendar::Bucket::setattro(const Attribute& attr, const PythonObject& field)
00938 {
00939   if (attr.isA(Tags::tag_id))
00940     setId(field.getInt());
00941   else if (attr.isA(Tags::tag_start))
00942     setStart(field.getDate());
00943   else if (attr.isA(Tags::tag_end))
00944     setEnd(field.getDate());
00945   else if (attr.isA(Tags::tag_priority))
00946     setPriority(field.getInt());
00947   else if (attr.isA(Tags::tag_days))
00948     setDays(field.getInt());
00949   else if (attr.isA(Tags::tag_starttime))
00950     setStartTime(field.getTimeperiod());
00951   else if (attr.isA(Tags::tag_endtime))
00952     setEndTime(field.getTimeperiod());
00953   else if (attr.isA(Tags::tag_value))
00954   {
00955     if (cal->getType() == *CalendarDouble::metadata)
00956       dynamic_cast< CalendarDouble::BucketDouble* >(this)->setValue(field.getDouble());
00957     else
00958     {
00959       PyErr_SetString(PythonLogicException, "calendar type not recognized");
00960       return -1;
00961     }
00962   }
00963   else
00964     return -1;
00965   return 0;
00966 }
00967 
00968 
00969 DECLARE_EXPORT PyObject* Calendar::getEvents(
00970   PyObject* self, PyObject* args, PyObject* kwdict
00971 )
00972 {
00973   try
00974   {
00975     // Pick up the calendar
00976     Calendar *cal = NULL;
00977     PythonObject c(self);
00978     if (c.check(CalendarDouble::metadata))
00979       cal = static_cast<CalendarDouble*>(self);
00980     else
00981       throw LogicException("Invalid calendar type");
00982 
00983     // Parse the arguments
00984     PyObject* pystart = NULL;
00985     PyObject* pydirection = NULL;
00986     if (!PyArg_ParseTuple(args, "|OO:setvalue", &pystart, &pydirection))
00987       return NULL;
00988     Date startdate = pystart ? PythonObject(pystart).getDate() : Date::infinitePast;
00989     bool forward = pydirection ? PythonObject(pydirection).getBool() : true;
00990 
00991     // Return the iterator
00992     return new CalendarEventIterator(cal, startdate, forward);
00993   }
00994   catch(...)
00995   {
00996     PythonType::evalException();
00997     return NULL;
00998   }
00999 }
01000 
01001 
01002 int CalendarEventIterator::initialize()
01003 {
01004   // Initialize the type
01005   PythonType& x = PythonExtension<CalendarEventIterator>::getType();
01006   x.setName("calendarEventIterator");
01007   x.setDoc("frePPLe iterator for calendar events");
01008   x.supportiter();
01009   return x.typeReady();
01010 }
01011 
01012 
01013 PyObject* CalendarEventIterator::iternext()
01014 {
01015   if ((forward && eventiter.getDate() == Date::infiniteFuture)
01016       || (!forward && eventiter.getDate() == Date::infinitePast))
01017     return NULL;
01018   PythonObject x;
01019   if (dynamic_cast<CalendarDouble*>(cal))
01020   {
01021     if (eventiter.getBucket())
01022       x = PythonObject(dynamic_cast<const CalendarDouble::BucketDouble*>(eventiter.getBucket())->getValue());
01023     else
01024       x = PythonObject(dynamic_cast<CalendarDouble*>(cal)->getDefault());
01025   }
01026   else 
01027     // Unknown calendar type we can't iterate
01028     return NULL; 
01029   PyObject* result = Py_BuildValue("(N,N)",
01030       static_cast<PyObject*>(PythonObject(eventiter.getDate())),
01031       static_cast<PyObject*>(x)
01032       );
01033   if (forward)
01034     ++eventiter;
01035   else
01036     --eventiter;
01037   return result;
01038 }
01039 
01040 
01041 DECLARE_EXPORT void Calendar::Bucket::updateOffsets()
01042 {
01043   if (days==127 && !starttime && endtime==TimePeriod(86400L))
01044   {
01045     // Bucket is effective continuously. No need to update the structure.
01046     offsetcounter = 0;
01047     return;
01048   }
01049 
01050   offsetcounter = -1;
01051   short tmp = days;
01052   for (short i=0; i<=6; ++i)
01053   {
01054     // Loop over all days in the week
01055     if (tmp & 1)
01056     {
01057       if (offsetcounter>=1 && (offsets[offsetcounter] == 86400*i + starttime))
01058         // Special case: the start date of todays offset entry
01059         // is the end date yesterdays entry. We can just update the
01060         // end date of that entry.
01061         offsets[offsetcounter] = 86400*i + endtime;
01062       else
01063       {
01064         // New offset pair
01065         offsets[++offsetcounter] = 86400*i + starttime; 
01066         offsets[++offsetcounter] = 86400*i + endtime; 
01067       }
01068     }
01069     tmp = tmp>>1; // Shift to the next bit
01070   }
01071 
01072   // Special case: there is no gap between the end of the last event in the 
01073   // week and the next event in the following week.
01074   if (offsetcounter >= 1 && offsets[0]==0 && offsets[offsetcounter]==86400*7)
01075   {
01076     offsets[0] = offsets[offsetcounter-1] - 86400*7;
01077     offsets[offsetcounter] = 86400*7 + offsets[1]; 
01078   }
01079 }
01080 
01081 } // end namespace

Documentation generated for frePPLe by  doxygen