setupmatrix.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2009-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 SetupMatrix> DECLARE_EXPORT Tree utils::HasName<SetupMatrix>::st;
31 
32 
34 {
35  // Initialize the metadata
36  metadata = new MetaCategory("setupmatrix", "setupmatrices", reader, writer);
37 
38  // Initialize the Python class
39  FreppleCategory<SetupMatrix>::getType().addMethod("addRule",
40  addPythonRule, METH_VARARGS | METH_KEYWORDS, "add a new setup rule");
44 }
45 
46 
48 {
49  // Initialize the metadata
50  metadata = new MetaCategory("setupmatrixrule", "setupmatrixrules");
51 
52  // Initialize the Python class
54  x.setName("setupmatrixrule");
55  x.setDoc("frePPLe setupmatrixrule");
56  x.supportgetattro();
57  x.supportsetattro();
58  const_cast<MetaCategory*>(metadata)->pythonClass = x.type_object();
59  return x.typeReady();
60 }
61 
62 
64 {
65  // Initialize the metadata
67  "setupmatrix",
68  "setupmatrix_default",
69  Object::createString<SetupMatrixDefault>, true);
70 
71  // Initialize the Python class
73 }
74 
75 
77 {
78  // Destroy the rules.
79  // Note that the rule destructor updates the firstRule field.
80  while (firstRule) delete firstRule;
81 
82  // Remove all references to this setup matrix from resources
83  for (Resource::iterator m = Resource::begin(); m != Resource::end(); ++m)
84  if (m->getSetupMatrix() == this) m->setSetupMatrix(NULL);
85 }
86 
87 
89 {
90  // Writing a reference
91  if (m == REFERENCE)
92  {
93  o->writeElement
95  return;
96  }
97 
98  // Write the head
99  if (m != NOHEAD && m != NOHEADTAIL) o->BeginObject
101 
102  // Write all rules
104  for (RuleIterator i = beginRules(); i != endRules(); ++i)
105  // We use the FULL mode, to force the rules being written regardless
106  // of the depth in the XML tree.
109 
110  // Write the tail
111  if (m != NOHEADTAIL && m != NOTAIL) o->EndObject(tag);
112 }
113 
114 
116 {
117  if (pAttr.isA(Tags::tag_rule)
118  && pIn.getParentElement().first.isA(Tags::tag_rules))
119  // A new rule
120  pIn.readto(createRule(pIn.getAttributes()));
121 }
122 
123 
125 {
126  // Pick up the start, end and name attributes
127  int priority = atts.get(Tags::tag_priority)->getInt();
128 
129  // Check for existence of a rule with the same priority
130  Rule* result = firstRule;
131  while (result && priority > result->priority)
132  result = result->nextRule;
133  if (result && result->priority != priority) result = NULL;
134 
135  // Pick up the action attribute and update the rule accordingly
136  switch (MetaClass::decodeAction(atts))
137  {
138  case ADD:
139  // Only additions are allowed
140  if (result)
141  {
142  ostringstream o;
143  o << "Rule with priority " << priority
144  << " already exists in setup matrix '" << getName() << "'";
145  throw DataException(o.str());
146  }
147  result = new Rule(this, priority);
148  return result;
149  case CHANGE:
150  // Only changes are allowed
151  if (!result)
152  {
153  ostringstream o;
154  o << "No rule with priority " << priority
155  << " exists in setup matrix '" << getName() << "'";
156  throw DataException(o.str());
157  }
158  return result;
159  case REMOVE:
160  // Delete the entity
161  if (!result)
162  {
163  ostringstream o;
164  o << "No rule with priority " << priority
165  << " exists in setup matrix '" << getName() << "'";
166  throw DataException(o.str());
167  }
168  else
169  {
170  // Delete it
171  delete result;
172  return NULL;
173  }
174  case ADD_CHANGE:
175  if (!result)
176  // Adding a new rule
177  result = new Rule(this, priority);
178  return result;
179  }
180 
181  // This part of the code isn't expected not be reached
182  throw LogicException("Unreachable code reached");
183 }
184 
185 
186 DECLARE_EXPORT void SetupMatrix::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
187 {
188  HasName<SetupMatrix>::endElement(pIn, pAttr, pElement);
189 }
190 
191 
193 {
194  if (attr.isA(Tags::tag_name))
195  return PythonObject(getName());
196  if (attr.isA(Tags::tag_rules))
197  return new SetupMatrixRuleIterator(this);
198  return NULL;
199 }
200 
201 
203 {
204  if (attr.isA(Tags::tag_name))
205  setName(field.getString());
206  else
207  return -1; // Error
208  return 0;
209 }
210 
211 
212 DECLARE_EXPORT PyObject* SetupMatrix::addPythonRule(PyObject* self, PyObject* args, PyObject* kwdict)
213 {
214  try
215  {
216  // Pick up the setup matrix
217  SetupMatrix *matrix = static_cast<SetupMatrix*>(self);
218  if (!matrix) throw LogicException("Can't add a rule to a NULL setupmatrix");
219 
220  // Parse the arguments
221  int prio = 0;
222  PyObject *pyfrom = NULL;
223  PyObject *pyto = NULL;
224  long duration = 0;
225  double cost = 0;
226  static const char *kwlist[] = {"priority", "fromsetup", "tosetup", "duration", "cost", NULL};
227  if (!PyArg_ParseTupleAndKeywords(args, kwdict,
228  "i|ssld:addRule",
229  const_cast<char**>(kwlist), &prio, &pyfrom, &pyto, &duration, &cost))
230  return NULL;
231 
232  // Add the new rule
233  Rule * r = new Rule(matrix, prio);
234  if (pyfrom) r->setFromSetup(PythonObject(pyfrom).getString());
235  if (pyto) r->setToSetup(PythonObject(pyfrom).getString());
236  r->setDuration(duration);
237  r->setCost(cost);
238  return PythonObject(r);
239  }
240  catch(...)
241  {
242  PythonType::evalException();
243  return NULL;
244  }
245 }
246 
247 
249  : cost(0), priority(p), matrix(s), nextRule(NULL), prevRule(NULL)
250 {
251  // Validate the arguments
252  if (!matrix) throw DataException("Can't add a rule to NULL setup matrix");
253 
254  // Find the right place in the list
255  Rule *next = matrix->firstRule, *prev = NULL;
256  while (next && p > next->priority)
257  {
258  prev = next;
259  next = next->nextRule;
260  }
261 
262  // Duplicate priority
263  if (next && next->priority == p)
264  throw DataException("Multiple rules with identical priority in setup matrix");
265 
266  // Maintain linked list
267  nextRule = next;
268  prevRule = prev;
269  if (prev) prev->nextRule = this;
270  else matrix->firstRule = this;
271  if (next) next->prevRule = this;
272 
273  // Initialize the Python type
275 }
276 
277 
279 {
280  // Maintain linked list
281  if (nextRule) nextRule->prevRule = prevRule;
282  if (prevRule) prevRule->nextRule = nextRule;
283  else matrix->firstRule = nextRule;
284 }
285 
286 
288 (XMLOutput *o, const Keyword& tag, mode m) const
289 {
290  o->BeginObject(tag, Tags::tag_priority, priority);
291  if (!from.empty()) o->writeElement(Tags::tag_fromsetup, from);
292  if (!to.empty()) o->writeElement(Tags::tag_tosetup, to);
293  if (duration) o->writeElement(Tags::tag_duration, duration);
294  if (cost) o->writeElement(Tags::tag_cost, cost);
295  o->EndObject(tag);
296 }
297 
298 
300 {
301  if (pAttr.isA(Tags::tag_priority))
302  setPriority(pElement.getInt());
303  else if (pAttr.isA(Tags::tag_fromsetup))
304  setFromSetup(pElement.getString());
305  else if (pAttr.isA(Tags::tag_tosetup))
306  setToSetup(pElement.getString());
307  else if (pAttr.isA(Tags::tag_duration))
308  setDuration(pElement.getTimeperiod());
309  else if (pAttr.isA(Tags::tag_cost))
310  setCost(pElement.getDouble());
311 }
312 
313 
315 {
316  if (attr.isA(Tags::tag_priority))
317  return PythonObject(priority);
318  if (attr.isA(Tags::tag_setupmatrix))
319  return PythonObject(matrix);
320  if (attr.isA(Tags::tag_fromsetup))
321  return PythonObject(from);
322  if (attr.isA(Tags::tag_tosetup))
323  return PythonObject(to);
324  if (attr.isA(Tags::tag_duration))
325  return PythonObject(duration);
326  if (attr.isA(Tags::tag_cost))
327  return PythonObject(cost);
328  return NULL;
329 }
330 
331 
333 {
334  if (attr.isA(Tags::tag_priority))
335  setPriority(field.getInt());
336  else if (attr.isA(Tags::tag_fromsetup))
337  setFromSetup(field.getString());
338  else if (attr.isA(Tags::tag_tosetup))
339  setToSetup(field.getString());
340  else if (attr.isA(Tags::tag_duration))
341  setDuration(field.getTimeperiod());
342  else if (attr.isA(Tags::tag_cost))
343  setCost(field.getDouble());
344  else
345  return -1; // Error
346  return 0; // OK
347 }
348 
349 
351 {
352  // Update the field
353  priority = n;
354 
355  // Check ordering on the left
356  while (prevRule && priority < prevRule->priority)
357  {
358  Rule* next = nextRule;
359  Rule* prev = prevRule;
360  if (prev && prev->prevRule) prev->prevRule->nextRule = this;
361  else matrix->firstRule = this;
362  if (prev) prev->nextRule = nextRule;
363  nextRule = prev;
364  prevRule = prev ? prev->prevRule : NULL;
365  if (next && next->nextRule) next->nextRule->prevRule = prev;
366  if (next) next->prevRule = prev;
367  if (prev) prev->prevRule = this;
368  }
369 
370  // Check ordering on the right
371  while (nextRule && priority > nextRule->priority)
372  {
373  Rule* next = nextRule;
374  Rule* prev = prevRule;
375  nextRule = next->nextRule;
376  if (next && next->nextRule) next->nextRule->prevRule = this;
377  if (prev) prev->nextRule = next;
378  if (next) next->nextRule = this;
379  if (next) next->prevRule = prev;
380  prevRule = next;
381  }
382 
383  // Check for duplicate priorities
384  if ((prevRule && prevRule->priority == priority)
385  || (nextRule && nextRule->priority == priority))
386  {
387  ostringstream o;
388  o << "Duplicate priority " << priority << " in setup matrix '"
389  << matrix->getName() << "'";
390  throw DataException(o.str());
391  }
392 }
393 
394 
396 {
397  // Initialize the type
399  x.setName("setupmatrixRuleIterator");
400  x.setDoc("frePPLe iterator for setupmatrix rules");
401  x.supportiter();
402  return x.typeReady();
403 }
404 
405 
406 PyObject* SetupMatrixRuleIterator::iternext()
407 {
408  if (currule == matrix->endRules()) return NULL;
409  PyObject *result = &*(currule++);
410  Py_INCREF(result);
411  return result;
412 }
413 
414 
416 (const string oldsetup, const string newsetup) const
417 {
418  // No need to look
419  if (oldsetup == newsetup) return NULL;
420 
421  // Loop through all rules
422  for (Rule *curRule = firstRule; curRule; curRule = curRule->nextRule)
423  {
424  // Need a match on the fromsetup
425  if (!curRule->getFromSetup().empty()
426  && !matchWildcard(curRule->getFromSetup().c_str(), oldsetup.c_str()))
427  continue;
428  // Need a match on the tosetup
429  if (!curRule->getToSetup().empty()
430  && !matchWildcard(curRule->getToSetup().c_str(), newsetup.c_str()))
431  continue;
432  // Found a match
433  return curRule;
434  }
435 
436  // No matching rule was found
437  logger << "Warning: Conversion from '" << oldsetup << "' to '" << newsetup
438  << "' undefined in setup matrix '" << getName() << endl;
439  return NULL;
440 }
441 
442 } // end namespace