setupmatrix.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/src/model/setupmatrix.cpp $
00003   version : $LastChangedRevision: 1171 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2010-01-31 22:52:44 +0100 (Sun, 31 Jan 2010) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2009 by Johan De Taeye                                    *
00010  *                                                                         *
00011  * This library is free software; you can redistribute it and/or modify it *
00012  * under the terms of the GNU Lesser General Public License as published   *
00013  * by the Free Software Foundation; either version 2.1 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 GNU Lesser *
00019  * General Public License for more details.                                *
00020  *                                                                         *
00021  * You should have received a copy of the GNU Lesser General Public        *
00022  * License along with this library; if not, write to the Free Software     *
00023  * Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,*
00024  * USA                                                                     *
00025  *                                                                         *
00026  ***************************************************************************/
00027 
00028 #define FREPPLE_CORE
00029 #include "frepple/model.h"
00030 
00031 namespace frepple
00032 {
00033 
00034 template<class SetupMatrix> DECLARE_EXPORT Tree utils::HasName<SetupMatrix>::st;
00035 DECLARE_EXPORT const MetaCategory* SetupMatrix::metadata;
00036 DECLARE_EXPORT const MetaClass* SetupMatrixDefault::metadata;
00037 DECLARE_EXPORT const MetaCategory* SetupMatrix::Rule::metadata;
00038 
00039 
00040 int SetupMatrix::initialize()
00041 {
00042   // Initialize the metadata
00043   metadata = new MetaCategory("setupmatrix", "setupmatrices", reader, writer);
00044 
00045   // Initialize the Python class
00046   FreppleCategory<SetupMatrix>::getType().addMethod("addRule", addPythonRule, METH_KEYWORDS, "add a new setup rule");
00047   return FreppleCategory<SetupMatrix>::initialize()
00048     + Rule::initialize()
00049     + SetupMatrixRuleIterator::initialize();
00050 }
00051 
00052 
00053 int SetupMatrix::Rule::initialize()
00054 {
00055   // Initialize the metadata
00056   metadata = new MetaCategory("setupmatrixrule", "setupmatrixrules");
00057 
00058   // Initialize the Python class
00059   PythonType& x = PythonExtension<SetupMatrix::Rule>::getType();
00060   x.setName("setupmatrixrule");
00061   x.setDoc("frePPLe setupmatrixrule");
00062   x.supportgetattro();
00063   x.supportsetattro();
00064   const_cast<MetaCategory*>(metadata)->pythonClass = x.type_object();
00065   return x.typeReady();
00066 }
00067 
00068 
00069 int SetupMatrixDefault::initialize()
00070 {
00071   // Initialize the metadata
00072   SetupMatrixDefault::metadata = new MetaClass(
00073     "setupmatrix",
00074     "setupmatrix_default",
00075     Object::createString<SetupMatrixDefault>, true);
00076 
00077   // Initialize the Python class
00078   return FreppleClass<SetupMatrixDefault,SetupMatrix>::initialize();
00079 }
00080 
00081 
00082 DECLARE_EXPORT SetupMatrix::~SetupMatrix()
00083 {
00084   // Destroy the rules.
00085   // Note that the rule destructor updates the firstRule field.
00086   while (firstRule) delete firstRule;
00087 
00088   // Remove all references to this setup matrix from resources
00089   for (Resource::iterator m = Resource::begin(); m != Resource::end(); ++m)
00090     if (m->getSetupMatrix() == this) m->setSetupMatrix(NULL);
00091 }
00092 
00093 
00094 DECLARE_EXPORT void SetupMatrix::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
00095 {
00096   // Writing a reference
00097   if (m == REFERENCE)
00098   {
00099     o->writeElement
00100       (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00101     return;
00102   }
00103 
00104   // Write the complete object
00105   if (m != NOHEADER) o->BeginObject
00106     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00107 
00108   // Write all rules
00109   o->BeginObject (Tags::tag_rules);
00110   for (RuleIterator i = beginRules(); i != endRules(); ++i)
00111     // We use the FULL mode, to force the rules being written regardless
00112     // of the depth in the XML tree.
00113     o->writeElement(Tags::tag_rule, *i, FULL);
00114   o->EndObject(Tags::tag_rules);
00115 
00116   o->EndObject(tag);
00117 }
00118 
00119 
00120 DECLARE_EXPORT void SetupMatrix::beginElement(XMLInput& pIn, const Attribute& pAttr)
00121 {
00122   if (pAttr.isA(Tags::tag_rule)
00123       && pIn.getParentElement().first.isA(Tags::tag_rules))
00124     // A new rule
00125     pIn.readto(createRule(pIn.getAttributes()));
00126 }
00127 
00128 
00129 DECLARE_EXPORT SetupMatrix::Rule* SetupMatrix::createRule(const AttributeList& atts)
00130 {
00131   // Pick up the start, end and name attributes
00132   int priority = atts.get(Tags::tag_priority)->getInt();
00133 
00134   // Check for existence of a rule with the same priority
00135   Rule* result = firstRule;
00136   while (result && priority > result->priority)
00137     result = result->nextRule;
00138   if (result && result->priority != priority) result = NULL;
00139 
00140   // Pick up the action attribute and update the rule accordingly
00141   switch (MetaClass::decodeAction(atts))
00142   {
00143     case ADD:
00144       // Only additions are allowed
00145       if (result)
00146       {
00147         ostringstream o;
00148         o << "Rule with priority "  << priority
00149           << " already exists in setup matrix '" << getName() << "'";
00150         throw DataException(o.str());
00151       }
00152       result = new Rule(this, priority);
00153       return result;
00154     case CHANGE:
00155       // Only changes are allowed
00156       if (!result)
00157       {
00158         ostringstream o;
00159         o << "No rule with priority " << priority
00160           << " exists in setup matrix '" << getName() << "'";
00161         throw DataException(o.str());
00162       }
00163       return result;
00164     case REMOVE:
00165       // Delete the entity
00166       if (!result)
00167       {
00168         ostringstream o;
00169         o << "No rule with priority " << priority
00170           << " exists in setup matrix '" << getName() << "'";
00171         throw DataException(o.str());
00172       }
00173       else
00174       {
00175         // Delete it
00176         delete result;
00177         return NULL;
00178       }
00179     case ADD_CHANGE:
00180       if (!result)
00181         // Adding a new rule
00182         result = new Rule(this, priority);
00183       return result;
00184   }
00185 
00186   // This part of the code isn't expected not be reached
00187   throw LogicException("Unreachable code reached");
00188 }
00189 
00190 
00191 DECLARE_EXPORT void SetupMatrix::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00192 {
00193   HasName<SetupMatrix>::endElement(pIn, pAttr, pElement);
00194 }
00195 
00196 
00197 DECLARE_EXPORT PyObject* SetupMatrix::getattro(const Attribute& attr)
00198 {
00199   if (attr.isA(Tags::tag_name))
00200     return PythonObject(getName());
00201   if (attr.isA(Tags::tag_rules))
00202     return new SetupMatrixRuleIterator(this);
00203   return NULL;
00204 }
00205 
00206 
00207 DECLARE_EXPORT int SetupMatrix::setattro(const Attribute& attr, const PythonObject& field)
00208 {
00209   if (attr.isA(Tags::tag_name))
00210     setName(field.getString());
00211   return 0;
00212 }
00213 
00214 
00215 DECLARE_EXPORT PyObject* SetupMatrix::addPythonRule(PyObject* self, PyObject* args, PyObject* kwdict)
00216 {
00217   try
00218   {
00219     // Pick up the setup matrix
00220     SetupMatrix *matrix = static_cast<SetupMatrix*>(self);
00221     if (!matrix) throw LogicException("Can't add a rule to a NULL setupmatrix");
00222 
00223     // Parse the arguments
00224     int prio = 0;
00225     PyObject *pyfrom = NULL;
00226     PyObject *pyto = NULL;
00227     long duration = 0;
00228     double cost = 0;
00229     static const char *kwlist[] = {"priority", "fromsetup", "tosetup", "duration", "cost", NULL};
00230     if (!PyArg_ParseTupleAndKeywords(args, kwdict,
00231       "i|ssld:addRule",
00232       const_cast<char**>(kwlist), &prio, &pyfrom, &pyto, &duration, &cost))
00233         return NULL;
00234 
00235     // Add the new rule
00236     Rule * r = new Rule(matrix, prio);
00237     if (pyfrom) r->setFromSetup(PythonObject(pyfrom).getString());
00238     if (pyto) r->setToSetup(PythonObject(pyfrom).getString());
00239     r->setDuration(duration);
00240     r->setCost(cost);
00241     return PythonObject(r);
00242   }
00243   catch(...)
00244   {
00245     PythonType::evalException();
00246     return NULL;
00247   }
00248 }
00249 
00250 
00251 DECLARE_EXPORT SetupMatrix::Rule::Rule(SetupMatrix *s, int p)
00252   : cost(0), priority(p), matrix(s), nextRule(NULL), prevRule(NULL)
00253 {
00254   // Validate the arguments
00255   if (!matrix) throw DataException("Can't add a rule to NULL setup matrix");
00256 
00257   // Find the right place in the list
00258   Rule *next = matrix->firstRule, *prev = NULL;
00259   while (next && p > next->priority)
00260   {
00261     prev = next;
00262     next = next->nextRule;
00263   }
00264 
00265   // Duplicate priority
00266   if (next && next->priority == p)
00267     throw DataException("Multiple rules with identical priority in setup matrix");
00268 
00269   // Maintain linked list
00270   nextRule = next;
00271   prevRule = prev;
00272   if (prev) prev->nextRule = this;
00273   else matrix->firstRule = this;
00274   if (next) next->prevRule = this;
00275 
00276   // Initialize the Python type
00277   initType(metadata);
00278 }
00279 
00280 
00281 DECLARE_EXPORT SetupMatrix::Rule::~Rule()
00282 {
00283   // Maintain linked list
00284   if (nextRule) nextRule->prevRule = prevRule;
00285   if (prevRule) prevRule->nextRule = nextRule;
00286   else matrix->firstRule = nextRule;
00287 }
00288 
00289 
00290 DECLARE_EXPORT void SetupMatrix::Rule::writeElement
00291 (XMLOutput *o, const Keyword& tag, mode m) const
00292 {
00293   o->BeginObject(tag, Tags::tag_priority, priority);
00294   if (!from.empty()) o->writeElement(Tags::tag_fromsetup, from);
00295   if (!to.empty()) o->writeElement(Tags::tag_tosetup, to);
00296   if (duration) o->writeElement(Tags::tag_duration, duration);
00297   if (cost) o->writeElement(Tags::tag_cost, cost);
00298   o->EndObject(tag);
00299 }
00300 
00301 
00302 DECLARE_EXPORT void SetupMatrix::Rule::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00303 {
00304   if (pAttr.isA(Tags::tag_priority))
00305     setPriority(pElement.getInt());
00306   else if (pAttr.isA(Tags::tag_fromsetup))
00307     setFromSetup(pElement.getString());
00308   else if (pAttr.isA(Tags::tag_tosetup))
00309     setToSetup(pElement.getString());
00310   else if (pAttr.isA(Tags::tag_duration))
00311     setDuration(pElement.getTimeperiod());
00312   else if (pAttr.isA(Tags::tag_cost))
00313     setCost(pElement.getDouble());
00314 }
00315 
00316 
00317 DECLARE_EXPORT PyObject* SetupMatrix::Rule::getattro(const Attribute& attr)
00318 {
00319   if (attr.isA(Tags::tag_priority))
00320     return PythonObject(priority);
00321   if (attr.isA(Tags::tag_fromsetup))
00322     return PythonObject(from);
00323   if (attr.isA(Tags::tag_tosetup))
00324     return PythonObject(to);
00325   if (attr.isA(Tags::tag_duration))
00326     return PythonObject(duration);
00327   if (attr.isA(Tags::tag_cost))
00328     return PythonObject(cost);
00329   return NULL;
00330 }
00331 
00332 
00333 DECLARE_EXPORT int SetupMatrix::Rule::setattro(const Attribute& attr, const PythonObject& field)
00334 {
00335   if (attr.isA(Tags::tag_priority))
00336     setPriority(field.getInt());
00337   else if (attr.isA(Tags::tag_fromsetup))
00338     setFromSetup(field.getString());
00339   else if (attr.isA(Tags::tag_tosetup))
00340     setToSetup(field.getString());
00341   else if (attr.isA(Tags::tag_duration))
00342     setDuration(field.getTimeperiod());
00343   else if (attr.isA(Tags::tag_cost))
00344     setCost(field.getDouble());
00345   else
00346     return -1;  // Error
00347   return 0;  // OK
00348 }
00349 
00350 
00351 DECLARE_EXPORT void SetupMatrix::Rule::setPriority(const int n)
00352 {
00353   // Update the field
00354   priority = n;
00355 
00356   // Check ordering on the left
00357   while (prevRule && priority < prevRule->priority)
00358   {
00359     Rule* next = nextRule;
00360     Rule* prev = prevRule;
00361     if (prev && prev->prevRule) prev->prevRule->nextRule = this;
00362     else matrix->firstRule = this;
00363     if (prev) prev->nextRule = nextRule;
00364     nextRule = prev;
00365     prevRule = prev ? prev->prevRule : NULL;
00366     if (next && next->nextRule) next->nextRule->prevRule = prev;
00367     if (next) next->prevRule = prev;
00368     if (prev) prev->prevRule = this;
00369   }
00370 
00371   // Check ordering on the right
00372   while (nextRule && priority > nextRule->priority)
00373   {
00374     Rule* next = nextRule;
00375     Rule* prev = prevRule;
00376     nextRule = next->nextRule;
00377     if (next && next->nextRule) next->nextRule->prevRule = this;
00378     if (prev) prev->nextRule = next;
00379     if (next) next->nextRule = this;
00380     if (next) next->prevRule = prev;
00381     prevRule = next;
00382   }
00383 
00384   // Check for duplicate priorities
00385   if ((prevRule && prevRule->priority == priority)
00386     || (nextRule && nextRule->priority == priority))
00387   {
00388     ostringstream o;
00389     o << "Duplicate priority " << priority << " in setup matrix '"
00390       << matrix->getName() << "'";
00391     throw DataException(o.str());
00392   }
00393 }
00394 
00395 
00396 int SetupMatrixRuleIterator::initialize()
00397 {
00398   // Initialize the type
00399   PythonType& x = PythonExtension<SetupMatrixRuleIterator>::getType();
00400   x.setName("setupmatrixRuleIterator");
00401   x.setDoc("frePPLe iterator for setupmatrix rules");
00402   x.supportiter();
00403   return x.typeReady();
00404 }
00405 
00406 
00407 PyObject* SetupMatrixRuleIterator::iternext()
00408 {
00409   if (currule == matrix->endRules()) return NULL;
00410   PyObject *result = &*(currule++);
00411   Py_INCREF(result);
00412   return result;
00413 }
00414 
00415 
00416 DECLARE_EXPORT SetupMatrix::Rule* SetupMatrix::calculateSetup
00417   (const string oldsetup, const string newsetup) const
00418 {
00419   // No need to look
00420   if (oldsetup == newsetup) return NULL;
00421 
00422   // Loop through all rules
00423   for (Rule *curRule = firstRule; curRule; curRule = curRule->nextRule)
00424   {
00425     // Need a match on the fromsetup
00426     if (!curRule->getFromSetup().empty()
00427       && !matchWildcard(curRule->getFromSetup().c_str(), oldsetup.c_str()))
00428         continue;
00429     // Need a match on the tosetup
00430     if (!curRule->getToSetup().empty()
00431       && !matchWildcard(curRule->getToSetup().c_str(), newsetup.c_str()))
00432         continue;
00433     // Found a match
00434     return curRule;
00435   }
00436 
00437   // No matching rule was found
00438   logger << "Warning: Conversion from '" << oldsetup << "' to '" << newsetup 
00439     << "' undefined in setup matrix '" << getName() << endl;
00440   return NULL;
00441 }
00442 
00443 } // end namespace