calendar.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba *
4  * *
5  * This library is free software; you can redistribute it and/or modify it *
6  * under the terms of the GNU Affero General Public License as published *
7  * by the Free Software Foundation; either version 3 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This library is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU Affero General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU Affero General Public *
16  * License along with this program. *
17  * If not, see <http://www.gnu.org/licenses/>. *
18  * *
19  ***************************************************************************/
20 
21 #define FREPPLE_CORE
22 #include "frepple/model.h"
23 
24 namespace frepple
25 {
26 
27 template<class Calendar> DECLARE_EXPORT Tree utils::HasName<Calendar>::st;
32 
33 
35 {
36  // Initialize the metadata
37  metadata = new MetaCategory("calendar", "calendars", reader, writer);
38 
39  // Initialize the Python class
40  FreppleCategory<Calendar>::getType().addMethod("addBucket", addPythonBucket, METH_KEYWORDS, "find a bucket or create a new one");
46  return ok;
47 }
48 
49 
51 {
52  // Initialize the metadata
53  metadata = new MetaCategory("bucket", "buckets");
54 
55  // Initialize the Python class
57  x.setName(metadata->type);
58  x.setDoc("frePPLe " + metadata->type);
59  x.supportgetattro();
60  x.supportsetattro();
61  x.supportstr();
62  x.supportcompare();
63  const_cast<MetaCategory*>(metadata)->pythonClass = x.type_object();
64  return x.typeReady();
65 }
66 
67 
69 {
70  // Initialize the metadata
71  metadata = new MetaClass("calendar", "calendar_double",
72  Object::createString<CalendarDouble>, true);
73 
74  // Initialize the Python class
75  FreppleClass<CalendarDouble,Calendar>::getType().addMethod("setValue",
76  setPythonValue, METH_KEYWORDS, "update the value in a date range");
78  getEvents, METH_VARARGS, "return an event iterator");
80 }
81 
82 
84 {
85  // Initialize the metadata
86  metadata = new MetaClass("bucket", "bucket_double");
87 
88  // Initialize the Python class
90  x.setName(metadata->type);
91  x.setDoc("frePPLe " + metadata->type);
92  x.supportgetattro();
93  x.supportsetattro();
94  x.supportstr();
95  x.supportcompare();
96  x.setBase(Calendar::Bucket::metadata->pythonClass);
97  x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation");
98  const_cast<MetaClass*>(metadata)->pythonClass = x.type_object();
99  return x.typeReady();
100 }
101 
102 
103 /** Updates the value in a certain date range.<br>
104  * This will create a new bucket if required. */
105 void CalendarDouble::setValue(Date start, Date end, const double v)
106 {
107  BucketDouble* x = static_cast<BucketDouble*>(findBucket(start));
108  if (x && x->getStart() == start && x->getEnd() <= end)
109  // We can update an existing bucket: it has the same start date
110  // and ends before the new effective period ends.
111  x->setEnd(end);
112  else
113  // Creating a new bucket
114  x = static_cast<BucketDouble*>(addBucket(start,end));
115  x->setValue(v);
116  x->setPriority(lowestPriority()-1);
117 }
118 
119 
120 void CalendarDouble::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
121 {
122  // Writing a reference
123  if (m == REFERENCE)
124  {
125  o->writeElement(tag, Tags::tag_name, getName());
126  return;
127  }
128 
129  // Write the complete object
130  if (m != NOHEADER) o->BeginObject(tag, Tags::tag_name, XMLEscape(getName()));
131 
132  // Write the default value
133  if (getDefault()) o->writeElement(Tags::tag_default, getDefault());
134 
135  // Write all buckets
137  for (BucketIterator i = beginBuckets(); i != endBuckets(); ++i)
138  // We use the FULL mode, to force the buckets being written regardless
139  // of the depth in the XML tree.
142 
143  o->EndObject(tag);
144 }
145 
146 
147 void CalendarDouble::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
148 {
149  if (pAttr.isA(Tags::tag_default))
150  pElement >> defaultValue;
151  else
152  Calendar::endElement(pIn, pAttr, pElement);
153 }
154 
155 
157 {
158  // De-allocate all the dynamic memory used for the bucket objects
159  while (firstBucket)
160  {
161  Bucket* tmp = firstBucket;
162  firstBucket = firstBucket->nextBucket;
163  delete tmp;
164  }
165 
166  // Remove all references from locations
167  for (Location::iterator l = Location::begin(); l != Location::end(); ++l)
168  {
169  if (l->getAvailable() == this)
170  l->setAvailable(NULL);
171  }
172 }
173 
174 
176 {
177  // Remove all references from buffers
178  for (Buffer::iterator b = Buffer::begin(); b != Buffer::end(); ++b)
179  {
180  if (b->getMinimumCalendar()==this) b->setMinimumCalendar(NULL);
181  if (b->getMaximumCalendar()==this) b->setMaximumCalendar(NULL);
182  }
183 
184  // Remove all references from resources
185  for (Resource::iterator r = Resource::begin(); r != Resource::end(); ++r)
186  if (r->getMaximumCalendar()==this) r->setMaximumCalendar(NULL);
187 }
188 
189 
191 (Date start, Date end, int id)
192 {
193  // Assure the start is before the end.
194  if (start > end)
195  return createNewBucket(end, start, id);
196  else
197  return createNewBucket(start, end, id);
198 }
199 
200 
202 {
203  // Verify the bucket is on this calendar indeed
204  Bucket *b = firstBucket;
205  while (b && b != bkt) b = b->nextBucket;
206 
207  // Error
208  if (!b)
209  throw DataException("Trying to remove unavailable bucket from calendar '"
210  + getName() + "'");
211 
212  // Update the list
213  if (bkt->prevBucket)
214  // Previous bucket links to a new next bucket
215  bkt->prevBucket->nextBucket = bkt->nextBucket;
216  else
217  // New head for the bucket list
218  firstBucket = bkt->nextBucket;
219  if (bkt->nextBucket)
220  // Update the reference prevBucket of the next bucket
221  bkt->nextBucket->prevBucket = bkt->prevBucket;
222 
223  // Delete the bucket
224  delete bkt;
225 }
226 
227 
229 {
230  // Check
231  if (d < startdate)
232  throw DataException("Calendar bucket end must be later than its start");
233 
234  // Update
235  enddate = d;
236 }
237 
238 
240 {
241  // Check
242  if (d > enddate)
243  throw DataException("Calendar bucket start must be earlier than its end");
244 
245  // Update the field
246  startdate = d;
247 
248  // Keep the list in sorted order
249  updateSort();
250 }
251 
252 
253 DECLARE_EXPORT void Calendar::Bucket::updateSort()
254 {
255  // Update the position in the list
256  bool ok = true;
257  do
258  {
259  ok = true;
260  if (nextBucket && (
261  nextBucket->startdate < startdate ||
262  (nextBucket->startdate == startdate && nextBucket->priority < priority)
263  ))
264  {
265  // Move a position later in the list
266  if (nextBucket->nextBucket)
267  nextBucket->nextBucket->prevBucket = this;
268  if (prevBucket)
269  prevBucket->nextBucket = nextBucket;
270  else
271  cal->firstBucket = nextBucket;
272  nextBucket->prevBucket = prevBucket;
273  prevBucket = nextBucket;
274  Calendar::Bucket* tmp = nextBucket->nextBucket;
275  nextBucket->nextBucket = this;
276  nextBucket = tmp;
277  ok = false;
278  }
279  else if (prevBucket && (
280  prevBucket->startdate > startdate ||
281  (prevBucket->startdate == startdate && prevBucket->priority > priority)
282  ))
283  {
284  // Move a position earlier in the list
285  if (prevBucket->prevBucket)
286  prevBucket->prevBucket->nextBucket = this;
287  if (nextBucket)
288  nextBucket->prevBucket = prevBucket;
289  prevBucket->nextBucket = nextBucket;
290  nextBucket = prevBucket;
291  Calendar::Bucket* tmp = prevBucket->prevBucket;
292  prevBucket->prevBucket = this;
293  prevBucket = tmp;
294  ok = false;
295  }
296  }
297  while (!ok); // Repeat till in place
298 }
299 
300 
302 {
303  Calendar::Bucket *curBucket = NULL;
304  double curPriority = DBL_MAX;
305  long timeInWeek = INT_MIN;
306  for (Bucket *b = firstBucket; b; b = b->nextBucket)
307  {
308  if (b->getStart() > d)
309  // Buckets are sorted by the start date. Other entries definitely
310  // won't be effective.
311  break;
312  else if (curPriority > b->getPriority()
313  && ( (fwd && d >= b->getStart() && d < b->getEnd()) ||
314  (!fwd && d > b->getStart() && d <= b->getEnd())
315  ))
316  {
317  if (!b->offsetcounter)
318  {
319  // Continuously effective
320  curPriority = b->getPriority();
321  curBucket = &*b;
322  }
323  else
324  {
325  // There are ineffective periods during the week
326  if (timeInWeek == INT_MIN)
327  {
328  // Lazy initialization
329  timeInWeek = d.getSecondsWeek();
330  // Special case: asking backward while at first second of the week
331  if (!fwd && timeInWeek == 0L) timeInWeek = 604800L;
332  }
333  // Check all intervals
334  for (short i=0; i<b->offsetcounter; i+=2)
335  if ((fwd && timeInWeek >= b->offsets[i] && timeInWeek < b->offsets[i+1]) ||
336  (!fwd && timeInWeek > b->offsets[i] && timeInWeek <= b->offsets[i+1]))
337  {
338  // All conditions are met!
339  curPriority = b->getPriority();
340  curBucket = &*b;
341  break;
342  }
343  }
344  }
345  }
346  return curBucket;
347 }
348 
349 
351 {
352  for (Bucket *b = firstBucket; b; b = b->nextBucket)
353  if (b->id == ident) return b;
354  return NULL;
355 }
356 
357 
359 {
360  // Writing a reference
361  if (m == REFERENCE)
362  {
363  o->writeElement
365  return;
366  }
367 
368  // Write the complete object
369  if (m != NOHEADER) o->BeginObject
371 
372  // Write all buckets
374  for (BucketIterator i = beginBuckets(); i != endBuckets(); ++i)
375  // We use the FULL mode, to force the buckets being written regardless
376  // of the depth in the XML tree.
379 
380  o->EndObject(tag);
381 }
382 
383 
385 {
386  // Pick up the start, end and name attributes
387  const DataElement* d = atts.get(Tags::tag_start);
388  Date startdate = *d ? d->getDate() : Date::infinitePast;
389  d = atts.get(Tags::tag_end);
390  Date enddate = *d ? d->getDate() : Date::infiniteFuture;
391  d = atts.get(Tags::tag_id);
392  int id = *d ? d->getInt() : INT_MIN;
393 
394  // Check for existence of the bucket with the same identifier
395  Calendar::Bucket* result = findBucket(id);
396 
397  // Pick up the action attribute and update the bucket accordingly
398  switch (MetaClass::decodeAction(atts))
399  {
400  case ADD:
401  // Only additions are allowed
402  if (result)
403  {
404  ostringstream o;
405  o << "Bucket " << id << " already exists in calendar '" << getName() << "'";
406  throw DataException(o.str());
407  }
408  result = addBucket(startdate, enddate, id);
409  return result;
410  case CHANGE:
411  // Only changes are allowed
412  if (!result)
413  {
414  ostringstream o;
415  o << "Bucket " << id << " doesn't exist in calendar '" << getName() << "'";
416  throw DataException(o.str());
417  }
418  return result;
419  case REMOVE:
420  // Delete the entity
421  if (!result)
422  {
423  ostringstream o;
424  o << "Bucket " << id << " doesn't exist in calendar '" << getName() << "'";
425  throw DataException(o.str());
426  }
427  else
428  {
429  // Delete it
430  removeBucket(result);
431  return NULL;
432  }
433  case ADD_CHANGE:
434  if (!result)
435  // Adding a new bucket
436  result = addBucket(startdate, enddate, id);
437  return result;
438  }
439 
440  // This part of the code isn't expected not be reached
441  throw LogicException("Unreachable code reached");
442 }
443 
444 
446 {
447  if (pAttr.isA (Tags::tag_bucket)
448  && pIn.getParentElement().first.isA(Tags::tag_buckets))
449  // A new bucket
450  pIn.readto(createBucket(pIn.getAttributes()));
451 }
452 
453 
455 {
456  // The header line has a variable number of attributes: start, end and/or name
457  if (startdate != Date::infinitePast)
458  {
459  if (enddate != Date::infiniteFuture)
460  o->BeginObject(tag, Tags::tag_id, id, Tags::tag_start, startdate, Tags::tag_end, enddate);
461  else
462  o->BeginObject(tag, Tags::tag_id, id, Tags::tag_start, startdate);
463  }
464  else
465  {
466  if (enddate != Date::infiniteFuture)
467  o->BeginObject(tag, Tags::tag_id, id, Tags::tag_end, enddate);
468  else
469  o->BeginObject(tag, Tags::tag_id, id);
470  }
471 }
472 
473 
475 (XMLOutput *o, const Keyword& tag, mode m) const
476 {
477  assert(m == DEFAULT || m == FULL);
478  writeHeader(o,tag);
479  if (priority) o->writeElement(Tags::tag_priority, priority);
480  if (days != 127) o->writeElement(Tags::tag_days, days);
481  if (starttime)
482  o->writeElement(Tags::tag_starttime, starttime);
483  if (endtime != TimePeriod(86400L))
484  o->writeElement(Tags::tag_endtime, endtime);
485  o->EndObject(tag);
486 }
487 
488 
490 {
491  if (pAttr.isA(Tags::tag_priority))
492  pElement >> priority;
493  else if (pAttr.isA(Tags::tag_days))
494  setDays(pElement.getInt());
495  else if (pAttr.isA(Tags::tag_starttime))
496  setStartTime(pElement.getTimeperiod());
497  else if (pAttr.isA(Tags::tag_endtime))
498  setEndTime(pElement.getTimeperiod());
499 }
500 
501 
503 {
504  // Check non-null calendar
505  if (!cal)
506  throw LogicException("Generating calendar bucket without calendar");
507 
508  if (ident == INT_MIN)
509  {
510  // Force generation of a new identifier.
511  // This is done by taking the highest existing id and adding 1.
512  for (BucketIterator i = cal->beginBuckets(); i != cal->endBuckets(); ++i)
513  if (i->id >= ident) ident = i->id + 1;
514  if (ident == INT_MIN) ident = 1;
515  }
516  else
517  {
518  // Check & enforce uniqueness of the argument identifier
519  bool unique;
520  do
521  {
522  unique = true;
523  for (BucketIterator i = cal->beginBuckets(); i != cal->endBuckets(); ++i)
524  if (i->id == ident && &(*i) != this)
525  {
526  // Update the indentifier to avoid violating the uniqueness
527  unique = false;
528  ++ident;
529  break;
530  }
531  }
532  while (!unique);
533  }
534 
535  // Update the identifier
536  id = ident;
537 }
538 
539 
541 {
542  if (!theCalendar)
543  throw LogicException("Can't walk forward on event iterator of NULL calendar.");
544 
545  // Go over all entries and ask them to update the iterator
546  Date d = curDate;
547  curDate = Date::infiniteFuture;
548  for (const Calendar::Bucket *b = theCalendar->firstBucket; b; b = b->nextBucket)
549  b->nextEvent(this, d);
550 
551  // Remember the bucket that won the evaluation
552  lastBucket = curBucket;
553  lastPriority = curPriority;
554  return *this;
555 }
556 
557 
559 {
560  if (!theCalendar)
561  throw LogicException("Can't walk backward on event iterator of NULL calendar.");
562 
563  // Go over all entries and ask them to update the iterator
564  Date d = curDate;
565  curDate = Date::infinitePast;
566  for (const Calendar::Bucket *b = theCalendar->firstBucket; b; b = b->nextBucket)
567  b->prevEvent(this, d);
568 
569  // Remember the bucket that won the evaluation
570  lastBucket = curBucket;
571  lastPriority = curPriority;
572  return *this;
573 }
574 
575 
576 DECLARE_EXPORT void Calendar::Bucket::nextEvent(EventIterator* iter, Date refDate) const
577 {
578  // FIRST CASE: Bucket that is continuously effective
579  if (!offsetcounter)
580  {
581  // Evaluate the start date of the bucket
582  if (refDate < startdate && priority <= iter->lastPriority && (
583  startdate < iter->curDate ||
584  (startdate == iter->curDate && priority <= iter->curPriority)
585  ))
586  {
587  iter->curDate = startdate;
588  iter->curBucket = this;
589  iter->curPriority = priority;
590  return;
591  }
592 
593  // Next evaluate the end date of the bucket
594  if (refDate < enddate && enddate <= iter->curDate && iter->lastBucket == this)
595  {
596  iter->curDate = enddate;
597  iter->curBucket = iter->theCalendar->findBucket(enddate);
598  iter->curPriority = iter->curBucket ? iter->curBucket->priority : INT_MAX;
599  return;
600  }
601 
602  // End function: this bucket won't create next event
603  return;
604  }
605 
606  // SECOND CASE: Interruptions in effectivity.
607 
608  // Jump to the start date
609  bool allowEqualAtStart = false;
610  if (refDate < startdate && (
611  startdate < iter->curDate ||
612  (startdate == iter->curDate && priority <= iter->curPriority)
613  ))
614  {
615  refDate = startdate;
616  allowEqualAtStart = true;
617  }
618 
619  // Find position in the week
620  long timeInWeek = refDate.getSecondsWeek();
621 
622  // Loop over all effective days in the week in which refDate falls
623  for (short i=0; i<offsetcounter; i+=2)
624  {
625  // Start and end date of this effective period
626  Date st = refDate + TimePeriod(offsets[i] - timeInWeek);
627  Date nd = refDate + TimePeriod(offsets[i+1] - timeInWeek);
628 
629  // Move to next week if required
630  bool canReturn = true;
631  if (refDate >= nd)
632  {
633  st += TimePeriod(86400L*7);
634  nd += TimePeriod(86400L*7);
635  canReturn = false;
636  }
637 
638  // Check enddate and startdate are not violated
639  if (st < startdate)
640  {
641  if (nd < startdate)
642  continue; // No overlap with overall effective dates
643  else
644  st = startdate;
645  }
646  if (nd >= enddate)
647  {
648  if (st >= enddate)
649  continue; // No overlap with effective range
650  else
651  nd = enddate;
652  }
653 
654  if ((refDate < st || (allowEqualAtStart && refDate == st)) && priority <= iter->lastPriority)
655  {
656  if (st > iter->curDate || (st == iter->curDate && priority > iter->curPriority))
657  {
658  // Another bucket is doing better already
659  if (canReturn) break;
660  else continue;
661  }
662  // The effective start on this weekday qualifies as the next event
663  iter->curDate = st;
664  iter->curBucket = this;
665  iter->curPriority = priority;
666  if (canReturn) return;
667  }
668  if (refDate < nd && iter->lastBucket == this)
669  {
670  if (nd > iter->curDate || (nd == iter->curDate && priority > iter->curPriority))
671  {
672  // Another bucket is doing better already
673  if (canReturn) break;
674  else continue;
675  }
676  // This bucket is currently effective.
677  // The effective end on this weekday qualifies as the next event.
678  iter->curDate = nd;
679  iter->curBucket = iter->theCalendar->findBucket(nd);
680  iter->curPriority = iter->curBucket ? iter->curBucket->priority : INT_MAX;
681  if (canReturn) return;
682  }
683  }
684 }
685 
686 
687 DECLARE_EXPORT void Calendar::Bucket::prevEvent(EventIterator* iter, Date refDate) const
688 {
689  // FIRST CASE: Bucket that is continuously effective
690  if (!offsetcounter)
691  {
692  // First evaluate the end date of the bucket
693  if (refDate > enddate && priority <= iter->lastPriority && (
694  enddate > iter->curDate ||
695  (enddate == iter->curDate && priority < iter->curPriority)
696  ))
697  {
698  iter->curDate = enddate;
699  iter->curBucket = this;
700  iter->curPriority = priority;
701  return;
702  }
703 
704  // Next evaluate the start date of the bucket
705  if (refDate > startdate && startdate > iter->curDate && iter->lastBucket == this)
706  {
707  iter->curDate = startdate;
708  iter->curBucket = iter->theCalendar->findBucket(startdate, false);
709  iter->curPriority = iter->curBucket ? iter->curBucket->priority : INT_MAX;
710  return;
711  }
712 
713  // End function: this bucket won't create the previous event
714  return;
715  }
716 
717  // SECOND CASE: Interruptions in effectivity.
718 
719  // Jump to the end date
720  bool allowEqualAtEnd = false;
721  if (refDate > enddate && (
722  enddate > iter->curDate ||
723  (enddate == iter->curDate && priority < iter->curPriority)
724  ))
725  {
726  refDate = enddate;
727  allowEqualAtEnd = true;
728  }
729 
730  // Find position in the week
731  long timeInWeek = refDate.getSecondsWeek();
732 
733  // Loop over all effective days in the week in which refDate falls
734  for (short i=offsetcounter-1; i>=0; i-=2)
735  {
736  // Start and end date of this effective period
737  Date st = refDate + TimePeriod(offsets[i] - timeInWeek);
738  Date nd = refDate + TimePeriod(offsets[i+1] - timeInWeek);
739 
740  // Move to previous week if required
741  bool canReturn = true;
742  if (refDate <= st)
743  {
744  st -= TimePeriod(86400L*7);
745  nd -= TimePeriod(86400L*7);
746  canReturn = false;
747  }
748 
749  // Check enddate and startdate are not violated
750  if (st <= startdate)
751  {
752  if (nd <= startdate)
753  continue; // No overlap with overall effective dates
754  else
755  st = startdate;
756  }
757  if (nd > enddate)
758  {
759  if (st > enddate)
760  continue; // No overlap with effective range
761  else
762  nd = enddate;
763  }
764 
765  if ((refDate > nd || (allowEqualAtEnd && refDate == nd))
766  && priority <= iter->lastPriority)
767  {
768  if (nd < iter->curDate || (nd == iter->curDate && priority <= iter->curPriority))
769  {
770  // Another bucket is doing better already
771  if (canReturn) break;
772  else continue;
773  }
774  // The effective end on this weekday qualifies as the next event
775  iter->curDate = nd;
776  iter->curBucket = this;
777  if (canReturn) return;
778  }
779  if (refDate > st && iter->lastBucket == this)
780  {
781  if (st < iter->curDate || (st == iter->curDate && priority <= iter->curPriority))
782  {
783  // Another bucket is doing better already
784  if (canReturn) break;
785  else continue;
786  }
787  // This bucket is currently effective.
788  // The effective end on this weekday qualifies as the next event.
789  iter->curDate = st;
790  iter->curBucket = iter->theCalendar->findBucket(st, false);
791  iter->curPriority = iter->curBucket ? iter->curBucket->priority : INT_MAX;
792  if (canReturn) return;
793  }
794  }
795 }
796 
797 
799 {
800  if (attr.isA(Tags::tag_name))
801  return PythonObject(getName());
802  if (attr.isA(Tags::tag_buckets))
803  return new CalendarBucketIterator(this);
804  return NULL;
805 }
806 
807 
809 {
810  if (attr.isA(Tags::tag_name))
811  setName(field.getString());
812  else
813  return -1; // Error
814  return 0; // OK
815 }
816 
817 
819 {
820  if (attr.isA(Tags::tag_default))
821  return PythonObject(getDefault());
822  return Calendar::getattro(attr);
823 }
824 
825 
827 {
828  if (attr.isA(Tags::tag_default))
829  setDefault(field.getDouble());
830  else
831  return Calendar::setattro(attr, field);
832  return 0;
833 }
834 
835 
836 DECLARE_EXPORT PyObject* CalendarDouble::setPythonValue(PyObject* self, PyObject* args, PyObject* kwdict)
837 {
838  try
839  {
840  // Pick up the calendar
841  CalendarDouble *cal = static_cast<CalendarDouble*>(self);
842  if (!cal) throw LogicException("Can't set value of a NULL calendar");
843 
844  // Parse the arguments
845  PyObject *pystart, *pyend, *pyval;
846  if (!PyArg_ParseTuple(args, "OOO:setValue", &pystart, &pyend, &pyval))
847  return NULL;
848 
849  // Update the calendar
850  PythonObject start(pystart), end(pyend), val(pyval);
851  cal->setValue(start.getDate(), end.getDate(), val.getDouble());
852  }
853  catch(...)
854  {
855  PythonType::evalException();
856  return NULL;
857  }
858  return Py_BuildValue("");
859 }
860 
861 
862 DECLARE_EXPORT PyObject* Calendar::addPythonBucket(PyObject* self, PyObject* args, PyObject* kwdict)
863 {
864  try
865  {
866  // Pick up the calendar
867  Calendar* cal = static_cast<Calendar*>(self);
868  if (!cal) throw LogicException("Can't set value of a NULL calendar");
869 
870  // Parse the arguments
871  int id = 1;
872  if (!PyArg_ParseTuple(args, "|i:addBucket", &id))
873  return NULL;
874 
875  // See if the bucket exists, or create it
876  Bucket * b = cal->findBucket(id);
877  if (!b) b = cal->addBucket(Date::infinitePast, Date::infiniteFuture, id);
878 
879  // Return a reference
880  Py_INCREF(b);
881  return b;
882  }
883  catch(...)
884  {
885  PythonType::evalException();
886  return NULL;
887  }
888  return Py_BuildValue("");
889 }
890 
891 
893 {
894  // Initialize the type
896  x.setName("calendarBucketIterator");
897  x.setDoc("frePPLe iterator for calendar buckets");
898  x.supportiter();
899  return x.typeReady();
900 }
901 
902 
904 {
905  if (i == cal->endBuckets()) return NULL;
906  PyObject *result = &*(i++);
907  Py_INCREF(result);
908  return result;
909 }
910 
911 
913 {
914  if (attr.isA(Tags::tag_start))
915  return PythonObject(getStart());
916  if (attr.isA(Tags::tag_end))
917  return PythonObject(getEnd());
918  if (attr.isA(Tags::tag_value))
919  {
920  if (cal->getType() == *CalendarDouble::metadata)
921  return PythonObject(dynamic_cast< CalendarDouble::BucketDouble* >(this)->getValue());
922  PyErr_SetString(PythonLogicException, "calendar type not recognized");
923  return NULL;
924  }
925  if (attr.isA(Tags::tag_priority))
926  return PythonObject(getPriority());
927  if (attr.isA(Tags::tag_days))
928  return PythonObject(getDays());
929  if (attr.isA(Tags::tag_starttime))
930  return PythonObject(getStartTime());
931  if (attr.isA(Tags::tag_endtime))
932  return PythonObject(getEndTime());
933  if (attr.isA(Tags::tag_id))
934  return PythonObject(getId());
935  if (attr.isA(Tags::tag_calendar))
936  return PythonObject(getCalendar());
937  return NULL;
938 }
939 
940 
942 {
943  if (attr.isA(Tags::tag_id))
944  setId(field.getInt());
945  else if (attr.isA(Tags::tag_start))
946  setStart(field.getDate());
947  else if (attr.isA(Tags::tag_end))
948  setEnd(field.getDate());
949  else if (attr.isA(Tags::tag_priority))
950  setPriority(field.getInt());
951  else if (attr.isA(Tags::tag_days))
952  setDays(field.getInt());
953  else if (attr.isA(Tags::tag_starttime))
954  setStartTime(field.getTimeperiod());
955  else if (attr.isA(Tags::tag_endtime))
956  setEndTime(field.getTimeperiod());
957  else if (attr.isA(Tags::tag_value))
958  {
959  if (cal->getType() == *CalendarDouble::metadata)
960  dynamic_cast< CalendarDouble::BucketDouble* >(this)->setValue(field.getDouble());
961  else
962  {
963  PyErr_SetString(PythonLogicException, "calendar type not recognized");
964  return -1;
965  }
966  }
967  else
968  return -1;
969  return 0;
970 }
971 
972 
974  PyObject* self, PyObject* args
975 )
976 {
977  try
978  {
979  // Pick up the calendar
980  Calendar *cal = NULL;
981  PythonObject c(self);
983  cal = static_cast<CalendarDouble*>(self);
984  else
985  throw LogicException("Invalid calendar type");
986 
987  // Parse the arguments
988  PyObject* pystart = NULL;
989  PyObject* pydirection = NULL;
990  if (!PyArg_ParseTuple(args, "|OO:setvalue", &pystart, &pydirection))
991  return NULL;
992  Date startdate = pystart ? PythonObject(pystart).getDate() : Date::infinitePast;
993  bool forward = pydirection ? PythonObject(pydirection).getBool() : true;
994 
995  // Return the iterator
996  return new CalendarEventIterator(cal, startdate, forward);
997  }
998  catch(...)
999  {
1000  PythonType::evalException();
1001  return NULL;
1002  }
1003 }
1004 
1005 
1007 {
1008  // Initialize the type
1010  x.setName("calendarEventIterator");
1011  x.setDoc("frePPLe iterator for calendar events");
1012  x.supportiter();
1013  return x.typeReady();
1014 }
1015 
1016 
1018 {
1019  if ((forward && eventiter.getDate() == Date::infiniteFuture)
1020  || (!forward && eventiter.getDate() == Date::infinitePast))
1021  return NULL;
1022  PythonObject x;
1023  if (dynamic_cast<CalendarDouble*>(cal))
1024  {
1025  if (eventiter.getBucket())
1026  x = PythonObject(dynamic_cast<const CalendarDouble::BucketDouble*>(eventiter.getBucket())->getValue());
1027  else
1028  x = PythonObject(dynamic_cast<CalendarDouble*>(cal)->getDefault());
1029  }
1030  else
1031  // Unknown calendar type we can't iterate
1032  return NULL;
1033  PyObject* result = Py_BuildValue("(N,N)",
1034  static_cast<PyObject*>(PythonObject(eventiter.getDate())),
1035  static_cast<PyObject*>(x)
1036  );
1037  if (forward)
1038  ++eventiter;
1039  else
1040  --eventiter;
1041  return result;
1042 }
1043 
1044 
1045 DECLARE_EXPORT void Calendar::Bucket::updateOffsets()
1046 {
1047  if (days==127 && !starttime && endtime==TimePeriod(86400L))
1048  {
1049  // Bucket is effective continuously. No need to update the structure.
1050  offsetcounter = 0;
1051  return;
1052  }
1053 
1054  offsetcounter = -1;
1055  short tmp = days;
1056  for (short i=0; i<=6; ++i)
1057  {
1058  // Loop over all days in the week
1059  if (tmp & 1)
1060  {
1061  if (offsetcounter>=1 && (offsets[offsetcounter] == 86400*i + starttime))
1062  // Special case: the start date of todays offset entry
1063  // is the end date yesterdays entry. We can just update the
1064  // end date of that entry.
1065  offsets[offsetcounter] = 86400*i + endtime;
1066  else
1067  {
1068  // New offset pair
1069  offsets[++offsetcounter] = 86400*i + starttime;
1070  offsets[++offsetcounter] = 86400*i + endtime;
1071  }
1072  }
1073  tmp = tmp>>1; // Shift to the next bit
1074  }
1075 
1076  // Special case: there is no gap between the end of the last event in the
1077  // week and the next event in the following week.
1078  if (offsetcounter >= 1 && offsets[0]==0 && offsets[offsetcounter]==86400*7)
1079  {
1080  offsets[0] = offsets[offsetcounter-1] - 86400*7;
1081  offsets[offsetcounter] = 86400*7 + offsets[1];
1082  }
1083 }
1084 
1085 } // end namespace