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