setupmatrix.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2009 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_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 complete object
99  if (m != NOHEADER) 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  o->EndObject(tag);
111 }
112 
113 
115 {
116  if (pAttr.isA(Tags::tag_rule)
117  && pIn.getParentElement().first.isA(Tags::tag_rules))
118  // A new rule
119  pIn.readto(createRule(pIn.getAttributes()));
120 }
121 
122 
124 {
125  // Pick up the start, end and name attributes
126  int priority = atts.get(Tags::tag_priority)->getInt();
127 
128  // Check for existence of a rule with the same priority
129  Rule* result = firstRule;
130  while (result && priority > result->priority)
131  result = result->nextRule;
132  if (result && result->priority != priority) result = NULL;
133 
134  // Pick up the action attribute and update the rule accordingly
135  switch (MetaClass::decodeAction(atts))
136  {
137  case ADD:
138  // Only additions are allowed
139  if (result)
140  {
141  ostringstream o;
142  o << "Rule with priority " << priority
143  << " already exists in setup matrix '" << getName() << "'";
144  throw DataException(o.str());
145  }
146  result = new Rule(this, priority);
147  return result;
148  case CHANGE:
149  // Only changes are allowed
150  if (!result)
151  {
152  ostringstream o;
153  o << "No rule with priority " << priority
154  << " exists in setup matrix '" << getName() << "'";
155  throw DataException(o.str());
156  }
157  return result;
158  case REMOVE:
159  // Delete the entity
160  if (!result)
161  {
162  ostringstream o;
163  o << "No rule with priority " << priority
164  << " exists in setup matrix '" << getName() << "'";
165  throw DataException(o.str());
166  }
167  else
168  {
169  // Delete it
170  delete result;
171  return NULL;
172  }
173  case ADD_CHANGE:
174  if (!result)
175  // Adding a new rule
176  result = new Rule(this, priority);
177  return result;
178  }
179 
180  // This part of the code isn't expected not be reached
181  throw LogicException("Unreachable code reached");
182 }
183 
184 
185 DECLARE_EXPORT void SetupMatrix::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
186 {
187  HasName<SetupMatrix>::endElement(pIn, pAttr, pElement);
188 }
189 
190 
192 {
193  if (attr.isA(Tags::tag_name))
194  return PythonObject(getName());
195  if (attr.isA(Tags::tag_rules))
196  return new SetupMatrixRuleIterator(this);
197  return NULL;
198 }
199 
200 
202 {
203  if (attr.isA(Tags::tag_name))
204  setName(field.getString());
205  else
206  return -1; // Error
207  return 0;
208 }
209 
210 
211 DECLARE_EXPORT PyObject* SetupMatrix::addPythonRule(PyObject* self, PyObject* args, PyObject* kwdict)
212 {
213  try
214  {
215  // Pick up the setup matrix
216  SetupMatrix *matrix = static_cast<SetupMatrix*>(self);
217  if (!matrix) throw LogicException("Can't add a rule to a NULL setupmatrix");
218 
219  // Parse the arguments
220  int prio = 0;
221  PyObject *pyfrom = NULL;
222  PyObject *pyto = NULL;
223  long duration = 0;
224  double cost = 0;
225  static const char *kwlist[] = {"priority", "fromsetup", "tosetup", "duration", "cost", NULL};
226  if (!PyArg_ParseTupleAndKeywords(args, kwdict,
227  "i|ssld:addRule",
228  const_cast<char**>(kwlist), &prio, &pyfrom, &pyto, &duration, &cost))
229  return NULL;
230 
231  // Add the new rule
232  Rule * r = new Rule(matrix, prio);
233  if (pyfrom) r->setFromSetup(PythonObject(pyfrom).getString());
234  if (pyto) r->setToSetup(PythonObject(pyfrom).getString());
235  r->setDuration(duration);
236  r->setCost(cost);
237  return PythonObject(r);
238  }
239  catch(...)
240  {
241  PythonType::evalException();
242  return NULL;
243  }
244 }
245 
246 
248  : cost(0), priority(p), matrix(s), nextRule(NULL), prevRule(NULL)
249 {
250  // Validate the arguments
251  if (!matrix) throw DataException("Can't add a rule to NULL setup matrix");
252 
253  // Find the right place in the list
254  Rule *next = matrix->firstRule, *prev = NULL;
255  while (next && p > next->priority)
256  {
257  prev = next;
258  next = next->nextRule;
259  }
260 
261  // Duplicate priority
262  if (next && next->priority == p)
263  throw DataException("Multiple rules with identical priority in setup matrix");
264 
265  // Maintain linked list
266  nextRule = next;
267  prevRule = prev;
268  if (prev) prev->nextRule = this;
269  else matrix->firstRule = this;
270  if (next) next->prevRule = this;
271 
272  // Initialize the Python type
274 }
275 
276 
278 {
279  // Maintain linked list
280  if (nextRule) nextRule->prevRule = prevRule;
281  if (prevRule) prevRule->nextRule = nextRule;
282  else matrix->firstRule = nextRule;
283 }
284 
285 
287 (XMLOutput *o, const Keyword& tag, mode m) const
288 {
289  o->BeginObject(tag, Tags::tag_priority, priority);
290  if (!from.empty()) o->writeElement(Tags::tag_fromsetup, from);
291  if (!to.empty()) o->writeElement(Tags::tag_tosetup, to);
292  if (duration) o->writeElement(Tags::tag_duration, duration);
293  if (cost) o->writeElement(Tags::tag_cost, cost);
294  o->EndObject(tag);
295 }
296 
297 
299 {
300  if (pAttr.isA(Tags::tag_priority))
301  setPriority(pElement.getInt());
302  else if (pAttr.isA(Tags::tag_fromsetup))
303  setFromSetup(pElement.getString());
304  else if (pAttr.isA(Tags::tag_tosetup))
305  setToSetup(pElement.getString());
306  else if (pAttr.isA(Tags::tag_duration))
307  setDuration(pElement.getTimeperiod());
308  else if (pAttr.isA(Tags::tag_cost))
309  setCost(pElement.getDouble());
310 }
311 
312 
314 {
315  if (attr.isA(Tags::tag_priority))
316  return PythonObject(priority);
317  if (attr.isA(Tags::tag_setupmatrix))
318  return PythonObject(matrix);
319  if (attr.isA(Tags::tag_fromsetup))
320  return PythonObject(from);
321  if (attr.isA(Tags::tag_tosetup))
322  return PythonObject(to);
323  if (attr.isA(Tags::tag_duration))
324  return PythonObject(duration);
325  if (attr.isA(Tags::tag_cost))
326  return PythonObject(cost);
327  return NULL;
328 }
329 
330 
332 {
333  if (attr.isA(Tags::tag_priority))
334  setPriority(field.getInt());
335  else if (attr.isA(Tags::tag_fromsetup))
336  setFromSetup(field.getString());
337  else if (attr.isA(Tags::tag_tosetup))
338  setToSetup(field.getString());
339  else if (attr.isA(Tags::tag_duration))
340  setDuration(field.getTimeperiod());
341  else if (attr.isA(Tags::tag_cost))
342  setCost(field.getDouble());
343  else
344  return -1; // Error
345  return 0; // OK
346 }
347 
348 
350 {
351  // Update the field
352  priority = n;
353 
354  // Check ordering on the left
355  while (prevRule && priority < prevRule->priority)
356  {
357  Rule* next = nextRule;
358  Rule* prev = prevRule;
359  if (prev && prev->prevRule) prev->prevRule->nextRule = this;
360  else matrix->firstRule = this;
361  if (prev) prev->nextRule = nextRule;
362  nextRule = prev;
363  prevRule = prev ? prev->prevRule : NULL;
364  if (next && next->nextRule) next->nextRule->prevRule = prev;
365  if (next) next->prevRule = prev;
366  if (prev) prev->prevRule = this;
367  }
368 
369  // Check ordering on the right
370  while (nextRule && priority > nextRule->priority)
371  {
372  Rule* next = nextRule;
373  Rule* prev = prevRule;
374  nextRule = next->nextRule;
375  if (next && next->nextRule) next->nextRule->prevRule = this;
376  if (prev) prev->nextRule = next;
377  if (next) next->nextRule = this;
378  if (next) next->prevRule = prev;
379  prevRule = next;
380  }
381 
382  // Check for duplicate priorities
383  if ((prevRule && prevRule->priority == priority)
384  || (nextRule && nextRule->priority == priority))
385  {
386  ostringstream o;
387  o << "Duplicate priority " << priority << " in setup matrix '"
388  << matrix->getName() << "'";
389  throw DataException(o.str());
390  }
391 }
392 
393 
395 {
396  // Initialize the type
398  x.setName("setupmatrixRuleIterator");
399  x.setDoc("frePPLe iterator for setupmatrix rules");
400  x.supportiter();
401  return x.typeReady();
402 }
403 
404 
406 {
407  if (currule == matrix->endRules()) return NULL;
408  PyObject *result = &*(currule++);
409  Py_INCREF(result);
410  return result;
411 }
412 
413 
415 (const string oldsetup, const string newsetup) const
416 {
417  // No need to look
418  if (oldsetup == newsetup) return NULL;
419 
420  // Loop through all rules
421  for (Rule *curRule = firstRule; curRule; curRule = curRule->nextRule)
422  {
423  // Need a match on the fromsetup
424  if (!curRule->getFromSetup().empty()
425  && !matchWildcard(curRule->getFromSetup().c_str(), oldsetup.c_str()))
426  continue;
427  // Need a match on the tosetup
428  if (!curRule->getToSetup().empty()
429  && !matchWildcard(curRule->getToSetup().c_str(), newsetup.c_str()))
430  continue;
431  // Found a match
432  return curRule;
433  }
434 
435  // No matching rule was found
436  logger << "Warning: Conversion from '" << oldsetup << "' to '" << newsetup
437  << "' undefined in setup matrix '" << getName() << endl;
438  return NULL;
439 }
440 
441 } // end namespace