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