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  doxygen