problem.cpp
Go to the documentation of this file.00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/model/problem.cpp $ 00003 version : $LastChangedRevision: 744 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2008-04-12 18:30:32 +0200 (Sat, 12 Apr 2008) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA * 00024 * * 00025 ***************************************************************************/ 00026 00027 #define FREPPLE_CORE 00028 #include "frepple/model.h" 00029 00030 namespace frepple 00031 { 00032 00033 DECLARE_EXPORT bool Plannable::anyChange = false; 00034 DECLARE_EXPORT bool Plannable::computationBusy = false; 00035 00036 00037 DECLARE_EXPORT bool Problem::operator < (const Problem& a) const 00038 { 00039 // 1. Sort based on entity 00040 assert(owner == a.owner); 00041 00042 // 2. Sort based on type 00043 if (getType() != a.getType()) return getType() < a.getType(); 00044 00045 // 3. Sort based on start date 00046 return getDateRange().getStart() < a.getDateRange().getStart(); 00047 } 00048 00049 00050 DECLARE_EXPORT void Problem::addProblem() 00051 { 00052 assert(owner); 00053 if ((owner->firstProblem && *this < *(owner->firstProblem)) 00054 || !owner->firstProblem) 00055 { 00056 // Insert as the first problem in the list 00057 nextProblem = owner->firstProblem; 00058 owner->firstProblem = this; 00059 } 00060 else 00061 { 00062 // Insert in the middle or at the end of the list 00063 Problem* curProblem = owner->firstProblem->nextProblem; 00064 Problem* prevProblem = owner->firstProblem; 00065 while (curProblem && !(*this < *curProblem)) 00066 { 00067 prevProblem = curProblem; 00068 curProblem = curProblem->nextProblem; 00069 } 00070 nextProblem = curProblem; 00071 prevProblem->nextProblem = this; 00072 } 00073 } 00074 00075 00076 DECLARE_EXPORT void Problem::removeProblem() 00077 { 00078 // Fast delete method: the code triggering this method is responsible of 00079 // maintaining the problem container 00080 if (!owner) return; 00081 00082 if (owner->firstProblem == this) 00083 // Removal from the head of the list 00084 owner->firstProblem = nextProblem; 00085 else 00086 { 00087 // Removal from the middle of the list 00088 Problem *prev = owner->firstProblem; 00089 for (Problem* cur = owner->firstProblem; cur; cur=cur->nextProblem) 00090 { 00091 if (cur == this) 00092 { 00093 // Found it! 00094 prev->nextProblem = nextProblem; 00095 return; 00096 } 00097 prev = cur; 00098 } 00099 // The problem wasn't found in the list. This shouldn't happen... 00100 throw LogicException("Corrupted problem list"); 00101 } 00102 } 00103 00104 00105 DECLARE_EXPORT void Plannable::setDetectProblems(bool b) 00106 { 00107 if (useProblemDetection && !b) 00108 // We are switching from 'yes' to 'no': delete all existing problems 00109 Problem::clearProblems(*this); 00110 else if (!useProblemDetection && b) 00111 // We are switching from 'no' to 'yes': mark as changed for the next 00112 // problem detection call 00113 setChanged(); 00114 // Update the flag 00115 useProblemDetection=b; 00116 } 00117 00118 00119 DECLARE_EXPORT void Plannable::computeProblems() 00120 { 00121 // Exit immediately if the list is up to date 00122 if (!anyChange && !computationBusy) return; 00123 00124 computationBusy = true; 00125 // Get exclusive access to this function in a multi-threaded environment. 00126 static Mutex computationbusy; 00127 { 00128 ScopeMutexLock l(computationbusy); 00129 00130 // Another thread may already have computed it while this thread was 00131 // waiting for the lock 00132 while (anyChange) 00133 { 00134 // Reset to change flag. Note that during the computation the flag 00135 // could be switched on again by some model change in a different thread. 00136 anyChange = false; 00137 00138 // Loop through all entities 00139 for (HasProblems::EntityIterator i; i!=HasProblems::endEntity(); ++i) 00140 { 00141 Plannable *e = i->getEntity(); 00142 if (e->getChanged() && e->getDetectProblems()) i->updateProblems(); 00143 } 00144 00145 // Mark the entities as unchanged 00146 for (HasProblems::EntityIterator j; j!=HasProblems::endEntity(); ++j) 00147 { 00148 Plannable *e = j->getEntity(); 00149 if (e->getChanged() && e->getDetectProblems()) e->setChanged(false); 00150 } 00151 } 00152 00153 // Unlock the exclusive access to this function 00154 computationBusy = false; 00155 } 00156 } 00157 00158 00159 DECLARE_EXPORT void Plannable::writeElement (XMLOutput* o, const Keyword& tag, mode m) const 00160 { 00161 // We don't bother about the mode, since this method is only called from 00162 // within the writeElement() method of other classes. 00163 00164 // Problem detection flag only written if different from the default value 00165 if (!getDetectProblems()) o->writeElement(Tags::tag_detectproblems, false); 00166 } 00167 00168 00169 DECLARE_EXPORT void Plannable::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00170 { 00171 if (pAttr.isA (Tags::tag_detectproblems)) 00172 { 00173 bool b = pElement.getBool(); 00174 setDetectProblems(b); 00175 } 00176 } 00177 00178 00179 DECLARE_EXPORT void Problem::clearProblems() 00180 { 00181 // Loop through all entities, and call clearProblems(i) 00182 for (HasProblems::EntityIterator i = HasProblems::beginEntity(); 00183 i != HasProblems::endEntity(); ++i) 00184 { 00185 clearProblems(*i); 00186 i->getEntity()->setChanged(true); 00187 } 00188 } 00189 00190 00191 DECLARE_EXPORT void Problem::clearProblems(HasProblems& p, bool setchanged) 00192 { 00193 // Nothing to do 00194 if (!p.firstProblem) return; 00195 00196 // Delete all problems in the list 00197 for (Problem *cur=p.firstProblem; cur; ) 00198 { 00199 Problem *del = cur; 00200 cur = cur->nextProblem; 00201 del->owner = NULL; 00202 delete del; 00203 } 00204 p.firstProblem = NULL; 00205 00206 // Mark as changed 00207 if (setchanged) p.getEntity()->setChanged(); 00208 } 00209 00210 00211 DECLARE_EXPORT void Problem::writer(const MetaCategory& c, XMLOutput* o) 00212 { 00213 const_iterator piter = begin(); 00214 if (piter != end()) 00215 { 00216 o->BeginObject(*c.grouptag); 00217 for (; piter!=end(); ++piter) 00218 // Note: not the regular write, but a fast write to speed things up. 00219 // This is possible since problems aren't nested and are never 00220 // referenced. 00221 piter->writeElement(o, *c.typetag); 00222 o->EndObject(*c.grouptag); 00223 } 00224 } 00225 00226 00227 DECLARE_EXPORT void Problem::writeElement(XMLOutput *o, const Keyword& tag, mode m) const 00228 { 00229 // We ignore the mode, and always write the complete model 00230 o->BeginObject(tag); 00231 o->writeElement(Tags::tag_name, getType().type); 00232 o->writeElement(Tags::tag_description, getDescription()); 00233 o->writeElement(Tags::tag_start, getDateRange().getStart()); 00234 o->writeElement(Tags::tag_end, getDateRange().getEnd()); 00235 o->writeElement(Tags::tag_weight, getWeight()); 00236 o->EndObject(tag); 00237 } 00238 00239 00240 DECLARE_EXPORT HasProblems::EntityIterator::EntityIterator() : type(0) 00241 { 00242 // Buffer 00243 bufIter = new Buffer::iterator(Buffer::begin()); 00244 if (*bufIter != Buffer::end()) return; 00245 00246 // Move on to resource if there are no buffers 00247 delete bufIter; 00248 type = 1; 00249 resIter = new Resource::iterator(Resource::begin()); 00250 if (*resIter != Resource::end()) return; 00251 00252 // Move on to operationplans if there are no resources either 00253 delete resIter; 00254 type = 2; 00255 operIter = new OperationPlan::iterator(OperationPlan::begin()); 00256 if (*operIter != OperationPlan::end()) return; 00257 00258 // Move on to demands if there are no operationplans either 00259 delete operIter; 00260 type = 3; 00261 demIter = new Demand::iterator(Demand::begin()); 00262 if (*demIter == Demand::end()) 00263 { 00264 // There is nothing at all in this model 00265 delete demIter; 00266 type = 4; 00267 } 00268 } 00269 00270 00271 DECLARE_EXPORT HasProblems::EntityIterator& HasProblems::EntityIterator::operator++() 00272 { 00273 switch (type) 00274 { 00275 case 0: 00276 // Buffer 00277 if (*bufIter != Buffer::end()) 00278 if (++(*bufIter) != Buffer::end()) return *this; 00279 ++type; 00280 delete bufIter; 00281 resIter = new Resource::iterator(Resource::begin()); 00282 if (*resIter != Resource::end()) return *this; 00283 // Note: no break statement 00284 case 1: 00285 // Resource 00286 if (*resIter != Resource::end()) 00287 if (++(*resIter) != Resource::end()) return *this; 00288 ++type; 00289 delete resIter; 00290 operIter = new OperationPlan::iterator(OperationPlan::begin()); 00291 if (*operIter != OperationPlan::end()) return *this; 00292 // Note: no break statement 00293 case 2: 00294 // Operationplan 00295 if (*operIter != OperationPlan::end()) 00296 if (++(*operIter) != OperationPlan::end()) return *this; 00297 ++type; 00298 delete operIter; 00299 demIter = new Demand::iterator(Demand::begin()); 00300 if (*demIter != Demand::end()) return *this; 00301 // Note: no break statement 00302 case 3: 00303 // Demand 00304 if (*demIter != Demand::end()) 00305 if (++(*demIter) != Demand::end()) return *this; 00306 // Ended recursing of all entities 00307 ++type; 00308 delete demIter; 00309 demIter = NULL; 00310 return *this; 00311 } 00312 throw LogicException("Unreachable code reached"); 00313 } 00314 00315 00316 DECLARE_EXPORT HasProblems::EntityIterator::~EntityIterator() 00317 { 00318 switch (type) 00319 { 00320 // Buffer 00321 case 0: delete bufIter; return; 00322 // Resource 00323 case 1: delete resIter; return; 00324 // Operation 00325 case 2: delete operIter; return; 00326 // Demand 00327 case 3: delete demIter; return; 00328 } 00329 } 00330 00331 00332 DECLARE_EXPORT HasProblems::EntityIterator::EntityIterator(const EntityIterator& o) 00333 { 00334 // Delete old iterator 00335 this->~EntityIterator(); 00336 // Populate new values 00337 type = o.type; 00338 if (type==0) bufIter = new Buffer::iterator(*(o.bufIter)); 00339 else if (type==1) resIter = new Resource::iterator(*(o.resIter)); 00340 else if (type==2) operIter = new OperationPlan::iterator(*(o.operIter)); 00341 else if (type==3) demIter = new Demand::iterator(*(o.demIter)); 00342 } 00343 00344 00345 DECLARE_EXPORT HasProblems::EntityIterator& 00346 HasProblems::EntityIterator::operator=(const EntityIterator& o) 00347 { 00348 // Gracefully handle self assignment 00349 if (this == &o) return *this; 00350 // Delete old iterator 00351 this->~EntityIterator(); 00352 // Populate new values 00353 type = o.type; 00354 if (type==0) bufIter = new Buffer::iterator(*(o.bufIter)); 00355 else if (type==1) resIter = new Resource::iterator(*(o.resIter)); 00356 else if (type==2) operIter = new OperationPlan::iterator(*(o.operIter)); 00357 else if (type==3) demIter = new Demand::iterator(*(o.demIter)); 00358 return *this; 00359 } 00360 00361 00362 DECLARE_EXPORT bool 00363 HasProblems::EntityIterator::operator != (const EntityIterator& t) const 00364 { 00365 // Different iterator type, thus always different and return false 00366 if (type != t.type) return true; 00367 00368 // Same iterator type, more granular comparison required 00369 switch (type) 00370 { 00371 // Buffer 00372 case 0: return *bufIter != *(t.bufIter); 00373 // Resource 00374 case 1: return *resIter != *(t.resIter); 00375 // Operationplan 00376 case 2: return *operIter != *(t.operIter); 00377 // Demand 00378 case 3: return *demIter != *(t.demIter); 00379 // Always return true for higher type numbers. This should happen only 00380 // when comparing with the end of list element. 00381 default: return false; 00382 } 00383 } 00384 00385 00386 DECLARE_EXPORT HasProblems& HasProblems::EntityIterator::operator*() const 00387 { 00388 switch (type) 00389 { 00390 // Buffer 00391 case 0: return **bufIter; 00392 // Resource 00393 case 1: return **resIter; 00394 // Operation 00395 case 2: return **operIter; 00396 // Demand 00397 case 3: return **demIter; 00398 default: throw LogicException("Unreachable code reached"); 00399 } 00400 } 00401 00402 00403 DECLARE_EXPORT HasProblems* HasProblems::EntityIterator::operator->() const 00404 { 00405 switch (type) 00406 { 00407 // Buffer 00408 case 0: return &**bufIter; 00409 // Resource 00410 case 1: return &**resIter; 00411 // Operationplan 00412 case 2: return &**operIter; 00413 // Demand 00414 case 3: return &**demIter; 00415 default: throw LogicException("Unreachable code reached"); 00416 } 00417 } 00418 00419 00420 DECLARE_EXPORT HasProblems::EntityIterator HasProblems::beginEntity() 00421 { 00422 return EntityIterator(); 00423 } 00424 00425 00426 DECLARE_EXPORT HasProblems::EntityIterator HasProblems::endEntity() 00427 { 00428 // Note that we give call a constructor with type 4, in order to allow 00429 // a fast comparison. 00430 return EntityIterator(4); 00431 } 00432 00433 00434 DECLARE_EXPORT Problem::const_iterator& Problem::const_iterator::operator++() 00435 { 00436 // Incrementing beyond the end 00437 if (!iter) return *this; 00438 00439 // Move to the next problem 00440 iter = iter->nextProblem; 00441 00442 // Move to the next entity 00443 // We need a while loop here because some entities can be without problems 00444 while (!iter && !owner && eiter!=HasProblems::endEntity()) 00445 { 00446 ++eiter; 00447 if (eiter!=HasProblems::endEntity()) iter = eiter->firstProblem; 00448 } 00449 return *this; 00450 } 00451 00452 00453 DECLARE_EXPORT Problem::const_iterator Problem::begin() 00454 { 00455 Plannable::computeProblems(); 00456 return const_iterator(); 00457 } 00458 00459 00460 DECLARE_EXPORT Problem::const_iterator Problem::begin(HasProblems* i, bool refresh) 00461 { 00462 // Null pointer passed, loop through the full list anyway 00463 if (!i) return begin(); 00464 00465 // Return an iterator for a single entity 00466 if (refresh) i->updateProblems(); 00467 return const_iterator(i); 00468 } 00469 00470 00471 DECLARE_EXPORT const Problem::const_iterator Problem::end() 00472 { 00473 return const_iterator(NULL); 00474 } 00475 00476 00477 } // End namespace
Documentation generated by
