setupmatrix.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/model/setupmatrix.cpp $ 00003 version : $LastChangedRevision: 1423 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2011-03-12 18:01:53 +0100 (Sat, 12 Mar 2011) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2009 by Johan De Taeye, frePPLe bvba * 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 else 00212 return -1; // Error 00213 return 0; 00214 } 00215 00216 00217 DECLARE_EXPORT PyObject* SetupMatrix::addPythonRule(PyObject* self, PyObject* args, PyObject* kwdict) 00218 { 00219 try 00220 { 00221 // Pick up the setup matrix 00222 SetupMatrix *matrix = static_cast<SetupMatrix*>(self); 00223 if (!matrix) throw LogicException("Can't add a rule to a NULL setupmatrix"); 00224 00225 // Parse the arguments 00226 int prio = 0; 00227 PyObject *pyfrom = NULL; 00228 PyObject *pyto = NULL; 00229 long duration = 0; 00230 double cost = 0; 00231 static const char *kwlist[] = {"priority", "fromsetup", "tosetup", "duration", "cost", NULL}; 00232 if (!PyArg_ParseTupleAndKeywords(args, kwdict, 00233 "i|ssld:addRule", 00234 const_cast<char**>(kwlist), &prio, &pyfrom, &pyto, &duration, &cost)) 00235 return NULL; 00236 00237 // Add the new rule 00238 Rule * r = new Rule(matrix, prio); 00239 if (pyfrom) r->setFromSetup(PythonObject(pyfrom).getString()); 00240 if (pyto) r->setToSetup(PythonObject(pyfrom).getString()); 00241 r->setDuration(duration); 00242 r->setCost(cost); 00243 return PythonObject(r); 00244 } 00245 catch(...) 00246 { 00247 PythonType::evalException(); 00248 return NULL; 00249 } 00250 } 00251 00252 00253 DECLARE_EXPORT SetupMatrix::Rule::Rule(SetupMatrix *s, int p) 00254 : cost(0), priority(p), matrix(s), nextRule(NULL), prevRule(NULL) 00255 { 00256 // Validate the arguments 00257 if (!matrix) throw DataException("Can't add a rule to NULL setup matrix"); 00258 00259 // Find the right place in the list 00260 Rule *next = matrix->firstRule, *prev = NULL; 00261 while (next && p > next->priority) 00262 { 00263 prev = next; 00264 next = next->nextRule; 00265 } 00266 00267 // Duplicate priority 00268 if (next && next->priority == p) 00269 throw DataException("Multiple rules with identical priority in setup matrix"); 00270 00271 // Maintain linked list 00272 nextRule = next; 00273 prevRule = prev; 00274 if (prev) prev->nextRule = this; 00275 else matrix->firstRule = this; 00276 if (next) next->prevRule = this; 00277 00278 // Initialize the Python type 00279 initType(metadata); 00280 } 00281 00282 00283 DECLARE_EXPORT SetupMatrix::Rule::~Rule() 00284 { 00285 // Maintain linked list 00286 if (nextRule) nextRule->prevRule = prevRule; 00287 if (prevRule) prevRule->nextRule = nextRule; 00288 else matrix->firstRule = nextRule; 00289 } 00290 00291 00292 DECLARE_EXPORT void SetupMatrix::Rule::writeElement 00293 (XMLOutput *o, const Keyword& tag, mode m) const 00294 { 00295 o->BeginObject(tag, Tags::tag_priority, priority); 00296 if (!from.empty()) o->writeElement(Tags::tag_fromsetup, from); 00297 if (!to.empty()) o->writeElement(Tags::tag_tosetup, to); 00298 if (duration) o->writeElement(Tags::tag_duration, duration); 00299 if (cost) o->writeElement(Tags::tag_cost, cost); 00300 o->EndObject(tag); 00301 } 00302 00303 00304 DECLARE_EXPORT void SetupMatrix::Rule::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00305 { 00306 if (pAttr.isA(Tags::tag_priority)) 00307 setPriority(pElement.getInt()); 00308 else if (pAttr.isA(Tags::tag_fromsetup)) 00309 setFromSetup(pElement.getString()); 00310 else if (pAttr.isA(Tags::tag_tosetup)) 00311 setToSetup(pElement.getString()); 00312 else if (pAttr.isA(Tags::tag_duration)) 00313 setDuration(pElement.getTimeperiod()); 00314 else if (pAttr.isA(Tags::tag_cost)) 00315 setCost(pElement.getDouble()); 00316 } 00317 00318 00319 DECLARE_EXPORT PyObject* SetupMatrix::Rule::getattro(const Attribute& attr) 00320 { 00321 if (attr.isA(Tags::tag_priority)) 00322 return PythonObject(priority); 00323 if (attr.isA(Tags::tag_fromsetup)) 00324 return PythonObject(from); 00325 if (attr.isA(Tags::tag_tosetup)) 00326 return PythonObject(to); 00327 if (attr.isA(Tags::tag_duration)) 00328 return PythonObject(duration); 00329 if (attr.isA(Tags::tag_cost)) 00330 return PythonObject(cost); 00331 return NULL; 00332 } 00333 00334 00335 DECLARE_EXPORT int SetupMatrix::Rule::setattro(const Attribute& attr, const PythonObject& field) 00336 { 00337 if (attr.isA(Tags::tag_priority)) 00338 setPriority(field.getInt()); 00339 else if (attr.isA(Tags::tag_fromsetup)) 00340 setFromSetup(field.getString()); 00341 else if (attr.isA(Tags::tag_tosetup)) 00342 setToSetup(field.getString()); 00343 else if (attr.isA(Tags::tag_duration)) 00344 setDuration(field.getTimeperiod()); 00345 else if (attr.isA(Tags::tag_cost)) 00346 setCost(field.getDouble()); 00347 else 00348 return -1; // Error 00349 return 0; // OK 00350 } 00351 00352 00353 DECLARE_EXPORT void SetupMatrix::Rule::setPriority(const int n) 00354 { 00355 // Update the field 00356 priority = n; 00357 00358 // Check ordering on the left 00359 while (prevRule && priority < prevRule->priority) 00360 { 00361 Rule* next = nextRule; 00362 Rule* prev = prevRule; 00363 if (prev && prev->prevRule) prev->prevRule->nextRule = this; 00364 else matrix->firstRule = this; 00365 if (prev) prev->nextRule = nextRule; 00366 nextRule = prev; 00367 prevRule = prev ? prev->prevRule : NULL; 00368 if (next && next->nextRule) next->nextRule->prevRule = prev; 00369 if (next) next->prevRule = prev; 00370 if (prev) prev->prevRule = this; 00371 } 00372 00373 // Check ordering on the right 00374 while (nextRule && priority > nextRule->priority) 00375 { 00376 Rule* next = nextRule; 00377 Rule* prev = prevRule; 00378 nextRule = next->nextRule; 00379 if (next && next->nextRule) next->nextRule->prevRule = this; 00380 if (prev) prev->nextRule = next; 00381 if (next) next->nextRule = this; 00382 if (next) next->prevRule = prev; 00383 prevRule = next; 00384 } 00385 00386 // Check for duplicate priorities 00387 if ((prevRule && prevRule->priority == priority) 00388 || (nextRule && nextRule->priority == priority)) 00389 { 00390 ostringstream o; 00391 o << "Duplicate priority " << priority << " in setup matrix '" 00392 << matrix->getName() << "'"; 00393 throw DataException(o.str()); 00394 } 00395 } 00396 00397 00398 int SetupMatrixRuleIterator::initialize() 00399 { 00400 // Initialize the type 00401 PythonType& x = PythonExtension<SetupMatrixRuleIterator>::getType(); 00402 x.setName("setupmatrixRuleIterator"); 00403 x.setDoc("frePPLe iterator for setupmatrix rules"); 00404 x.supportiter(); 00405 return x.typeReady(); 00406 } 00407 00408 00409 PyObject* SetupMatrixRuleIterator::iternext() 00410 { 00411 if (currule == matrix->endRules()) return NULL; 00412 PyObject *result = &*(currule++); 00413 Py_INCREF(result); 00414 return result; 00415 } 00416 00417 00418 DECLARE_EXPORT SetupMatrix::Rule* SetupMatrix::calculateSetup 00419 (const string oldsetup, const string newsetup) const 00420 { 00421 // No need to look 00422 if (oldsetup == newsetup) return NULL; 00423 00424 // Loop through all rules 00425 for (Rule *curRule = firstRule; curRule; curRule = curRule->nextRule) 00426 { 00427 // Need a match on the fromsetup 00428 if (!curRule->getFromSetup().empty() 00429 && !matchWildcard(curRule->getFromSetup().c_str(), oldsetup.c_str())) 00430 continue; 00431 // Need a match on the tosetup 00432 if (!curRule->getToSetup().empty() 00433 && !matchWildcard(curRule->getToSetup().c_str(), newsetup.c_str())) 00434 continue; 00435 // Found a match 00436 return curRule; 00437 } 00438 00439 // No matching rule was found 00440 logger << "Warning: Conversion from '" << oldsetup << "' to '" << newsetup 00441 << "' undefined in setup matrix '" << getName() << endl; 00442 return NULL; 00443 } 00444 00445 } // end namespace