DataSet.h

Go to the documentation of this file.
00001 /***************************************************************************
00002  *
00003  * Copyright (C) 2009-2010 Cassio Neri Moreira
00004  *
00005  * This file is part of the KeyValue library.
00006  *
00007  * The KeyValue library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License as published
00009  * by the Free Software Foundation, either version 3 of the License, or (at
00010  * your option) any later version.
00011  *
00012  * The KeyValue library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
00015  * Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License along
00018  * with KeyValue. If not, see <http://www.gnu.org/licenses/>.
00019  *
00020  * If you modify this library, or any covered work, by linking or combining
00021  * it with Excel (or a modified version of that program), containing parts
00022  * covered by the terms of End-User License Agreement for Microsoft
00023  * Software, the licensors of KeyValue grant you additional permission to
00024  * convey the resulting work.
00025  *
00026  **************************************************************************/
00027 
00034 #ifndef KEYVALUE_MNGT_DATASET_H_
00035 #define KEYVALUE_MNGT_DATASET_H_
00036 
00037 #include <map>
00038 #include <typeinfo>
00039 
00040 #include <boost/any.hpp>
00041 #include <boost/utility.hpp>
00042 #include <boost/weak_ptr.hpp>
00043 
00044 #include "keyvalue/extern/String.h"
00045 #include "keyvalue/extern/SharedPtr.h"
00046 #include "keyvalue/key/Key.h"
00047 #include "keyvalue/value/Result.h"
00048 #include "keyvalue/value/TypeName.h"
00049 #include "keyvalue/value/Value.h"
00050 #include "keyvalue/sys/exception/Exception.h"
00051 
00052 #ifndef NDEBUG
00053 #include "keyvalue/sys/logger/Logger.h"
00054 #include "keyvalue/util/Global.h"
00055 #endif
00056 
00057 namespace keyvalue {
00058 
00059 // Forward declaration
00060 class Processor;
00061 
00067 class DataSet: private boost::noncopyable {
00068 
00069 public:
00070 
00076   explicit
00077   DataSet(const string& name);
00078 
00084   string
00085   getName() const;
00086 
00093   void
00094   add(const string& key, const value::Value& value);
00095 
00129   template<typename KeyType>
00130   typename KeyType::OutputType_*
00131   find(const KeyType& key) const;
00132 
00142   template<typename KeyType>
00143   typename KeyType::OutputType_
00144   getValue(const KeyType& key) const;
00145 
00158   value::Result
00159   process() const;
00160 
00178   value::Result
00179   process(const Processor& processor) const;
00180 
00198   bool
00199   mustUpdate() const;
00200 
00204   friend std::ostream&
00205   operator<<(std::ostream& os, const DataSet& dataSet);
00206 
00207 private:
00208 
00216   struct Record {
00217 
00218     typedef boost::weak_ptr<Record> WeakPtr;
00219 
00220     explicit
00221     Record(const value::Value& rawValue);
00222 
00223     Record(WeakPtr next, WeakPtr driver);
00224 
00225     value::Value    rawValue_;
00226     bool            isBusy_;
00227     WeakPtr         next_;
00228     WeakPtr         driver_;
00229     boost::any      cache_;
00230     std::type_info* keyType_;
00231   };
00232 
00241   struct Graph {
00242 
00243     Graph();
00244 
00245     Graph(shared_ptr<Record> first, shared_ptr<Record> last,
00246       bool hasChanged);
00247 
00248     shared_ptr<Record>  first_;
00249     shared_ptr<Record>  last_;
00250     bool                hasChanged_;
00251   };
00252 
00253   class CircularReference : public RuntimeError {
00254   };
00255 
00273   Graph
00274   getGraph(const string& key, bool start = true) const;
00275 
00276   Graph
00277   getGraph(shared_ptr<Record> first, const string& key) const;
00278 
00279   value::Result
00280   doProcess(const Processor& processor) const;
00281 
00282   typedef std::map<string, shared_ptr<Record> > MapType_;
00283   typedef MapType_::value_type                  ValueType_;
00284   string                                        name_;
00285 
00286   mutable MapType_                              map_;
00287   mutable bool                                  mustUpdate_;
00288   mutable value::Result                         result_;
00289 
00290 }; // class DataSet
00291 
00292 /*--------------------------------------------------------------------------
00293  * DataSet::find()
00294  *------------------------------------------------------------------------*/
00295 
00296 template<typename KeyType>
00297 typename KeyType::OutputType_*
00298 DataSet::find(const KeyType& key) const {
00299 
00300   typedef typename KeyType::OutputType_ OutputType;
00301 
00306   Graph graph(getGraph(key.getName()));
00307   if (!graph.last_)
00308     return static_cast<OutputType*>(0);
00309 
00310   // Syntactic sugar.
00311   shared_ptr<Record>& last(graph.last_);
00312 
00317   if (KeyType::isBasicOrEnum) {
00318     if (!graph.hasChanged_ && &typeid(KeyType) == last->keyType_) {
00319 
00320       #ifndef NDEBUG
00321       util::Global<logger::Logger>::get()->log(Debug(10) & "Returning "
00322         "cached value for key '" & key.getName() & "'.");
00323       #endif
00324 
00325       return boost::any_cast<OutputType>(&last->cache_);
00326     }
00327     mustUpdate_ = true;
00328   }
00329 
00334   typedef typename KeyType::InputType_ InputType;
00335 
00336   const InputType* container(last->rawValue_.get<InputType>());
00337 
00338   if (!container)
00339     throw RuntimeError() & "Value for key '" & key.getName() & "' in "
00340       "data set '" & name_ & "' must be a " &
00341       value::TypeName<InputType>::value_ & "!";
00342 
00347   key.checkInput(*container);
00348 
00349   // If execution gets here, then the memoization above didn't work. In
00350   // particular:
00351   //
00352   // (1) If the output container element type is a basic type, then the
00353   // cached value (if any) is out of date and must be recalculated.
00354   //
00355   // (2) If the output container element type is an object type, then even
00356   // if the raw value (a container of object names) haven't changed since
00357   // last time this key was processed, the objects themselves could have
00358   // been rebuilt. Hence, the cached value (if any) might be out of date.
00359   // To check that, we provide the cached value to the container converter
00360   // which compares old objects with new ones.
00361 
00362   OutputType* cache(0);
00363   if (!KeyType::isBasicOrEnum && &typeid(KeyType) == last->keyType_)
00364     cache = boost::any_cast<OutputType>(&last->cache_);
00365 
00366   typename KeyType::ConverterType_ converter(*container, cache);
00367 
00368   while (!converter.isEmpty()) {
00369     converter.insert(key.map(converter.pop()));
00370     key.checkSoFar(converter);
00371   }
00372 
00373   OutputType output(converter.getOutput());
00374   key.checkOutput(output);
00375 
00376   mustUpdate_ = mustUpdate_ || converter.mustUpdate();
00377 
00382   last->cache_   = output;
00383   last->keyType_ = const_cast<std::type_info*>(&typeid(KeyType));
00384 
00385   return boost::any_cast<OutputType>(&last->cache_);
00386 }
00387 
00388 /*--------------------------------------------------------------------------
00389  * DataSet::getValue()
00390  *------------------------------------------------------------------------*/
00391 
00392 template<typename KeyType>
00393 typename KeyType::OutputType_
00394 DataSet::getValue(const KeyType& key) const {
00395 
00396   typename KeyType::OutputType_* result(find(key));
00397 
00398   if (result)
00399     return *result;
00400 
00401   throw RuntimeError() & "Cannot resolve key '" & key.getName() & "' for "
00402     "data set '" & name_ & "'!";
00403 }
00404 
00405 } // namespace keyvalue
00406 
00407 #endif // KEYVALUE_MNGT_DATASET_H_