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