Fawkes API  Fawkes Development Version
config.cpp
00001 
00002 /***************************************************************************
00003  *  config.cpp - Fawkes configuration interface
00004  *
00005  *  Created: Mon Dec 18 14:54:23 2006
00006  *  Copyright  2006-2008  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program 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 Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <config/config.h>
00025 #include <config/change_handler.h>
00026 #include <cstring>
00027 
00028 namespace fawkes {
00029 
00030 /** @class Configuration <config/config.h>
00031  * Interface for configuration handling.
00032  * We know that half of robotics is about parameter tuning. The Configuration
00033  * interface defines a unified way of storing parameters and other
00034  * configuration options no matter of how the database is implemented.
00035  * This is mainly done to allow for testing different solutions for ticket #10.
00036  *
00037  * @fn Configuration::~Configuration()
00038  * Virtual empty destructor.
00039  *
00040  * @fn void Configuration::load(const char *name, const char *defaults_name, const char *tag)
00041  * Load configuration.
00042  * Loads configuration data, or opens a file, depending on the implementation. After
00043  * this call access to all other methods shall be possible.
00044  * @param name name of the host-based configuration. If this does not exist it shall
00045  * be created from the default configuration. The name depends on the implementation and
00046  * could be a filename.
00047  * @param defaults_name name of the default database. As for the name this depends on
00048  * the actual implementation.
00049  * @param tag this optional parameter can denote a specific config version to load. This
00050  * will cause the host-specific database to be flushed and filled with the values for
00051  * the given tag. All values that did not exist for the tag are copied over from the
00052  * default database.
00053  * 
00054  * @fn void Configuration::tag(const char *tag)
00055  * Tag this configuration version.
00056  * This creates a new tagged version of the current config. The tagged config can be
00057  * accessed via load().
00058  * @param tag tag for this version
00059  *
00060  * @fn void Configuration::copy(Configuration *copyconf)
00061  * Copy all values from the given configuration.
00062  * All values from the given configuration are copied. Old values are not erased
00063  * so that the copied values will overwrite existing values, new values are
00064  * created, but values existent in current config but not in the copie config
00065  * will remain unchanged.
00066  * @param copyconf configuration to copy
00067  * 
00068  * @fn std::list<std::string> Configuration::tags()
00069  * List of tags.
00070  * @return list of tags
00071  * 
00072  * @fn bool Configuration::exists(const char *path)
00073  * Check if a given value exists.
00074  * @param path path to value
00075  * @return true if the value exists, false otherwise
00076  * 
00077  * @fn bool Configuration::is_float(const char *path)
00078  * Check if a value is of type float
00079  * @param path path to value
00080  * @return true if the value exists and is of type float
00081  * 
00082  * @fn bool Configuration::is_uint(const char *path)
00083  * Check if a value is of type unsigned int
00084  * @param path path to value
00085  * @return true if the value exists and is of type unsigned int
00086  * 
00087  * @fn bool Configuration::is_int(const char *path)
00088  * Check if a value is of type int
00089  * @param path path to value
00090  * @return true if the value exists and is of type int
00091  * 
00092  * @fn bool Configuration::is_bool(const char *path)
00093  * Check if a value is of type bool
00094  * @param path path to value
00095  * @return true if the value exists and is of type bool
00096  * 
00097  * @fn bool Configuration::is_string(const char *path)
00098  * Check if a value is of type string
00099  * @param path path to value
00100  * @return true if the value exists and is of type string
00101  * 
00102  * @fn bool Configuration::is_default(const char *path)
00103  * Check if a value was read from the default config.
00104  * @param path path to value
00105  * @return true if the value exists and is only stored in the default config
00106  * 
00107  * @fn float Configuration::get_float(const char *path)
00108  * Get value from configuration which is of type float
00109  * @param path path to value
00110  * @return value
00111  * 
00112  * @fn unsigned int Configuration::get_uint(const char *path)
00113  * Get value from configuration which is of type unsigned int
00114  * @param path path to value
00115  * @return value
00116  * 
00117  * @fn int Configuration::get_int(const char *path)
00118  * Get value from configuration which is of type int
00119  * @param path path to value
00120  * @return value
00121  * 
00122  * @fn bool Configuration::get_bool(const char *path)
00123  * Get value from configuration which is of type bool
00124  * @param path path to value
00125  * @return value
00126  * 
00127  * @fn std::string Configuration::get_string(const char *path)
00128  * Get value from configuration which is of type string
00129  * @param path path to value
00130  * @return value
00131  *
00132  * @fn Configuration::ValueIterator * Configuration::get_value(const char *path)
00133  * Get value from configuration.
00134  * @param path path to value
00135  * @return value iterator for just this one value, maybe invalid if value does not
00136  * exists.
00137  *
00138  * @fn std::string Configuration::get_type(const char *path)
00139  * Get type of value at given path.
00140  * @param path path to value
00141  * @return string representation of type, one of float, unsigned int, int, bool,
00142  * or string
00143  * @exception ConfigurationException shall be thrown if value does not exist or
00144  * on any other error.
00145  *
00146  * @fn std::string Configuration::get_comment(const char *path)
00147  * Get comment of value at given path.
00148  * The value at the given path must exist in the host-specific configuration.
00149  * @param path path to value
00150  * @return comment
00151  * @exception ConfigEntryNotFoundException shall be thrown if value does not exist
00152  * @exception ConfigurationException shall be thrown on any other error
00153  *
00154  * @fn std::string Configuration::get_default_comment(const char *path)
00155  * Get comment of value at given path.
00156  * The value at the given path must exist in the default configuration.
00157  * @param path path to value
00158  * @return comment
00159  * @exception ConfigEntryNotFoundException shall be thrown if value does not exist
00160  * @exception ConfigurationException shall be thrown on any other error
00161  *
00162  * 
00163  * @fn void Configuration::set_float(const char *path, float f)
00164  * Set new value in configuration of type float
00165  * @param path path to value
00166  * @param f new float value
00167  * 
00168  * @fn void Configuration::set_uint(const char *path, unsigned int uint)
00169  * Set new value in configuration of type unsigned int
00170  * @param path path to value
00171  * @param uint new unsigned int value
00172  * 
00173  * @fn void Configuration::set_int(const char *path, int i)
00174  * Set new value in configuration of type int
00175  * @param path path to value
00176  * @param i new int value
00177  * 
00178  * @fn void Configuration::set_bool(const char *path, bool b)
00179  * Set new value in configuration of type bool
00180  * @param path path to value
00181  * @param b new bool value
00182  * 
00183  * @fn void Configuration::set_string(const char *path, std::string &s)
00184  * Set new value in configuration of type string
00185  * @param path path to value
00186  * @param s new string value
00187  *
00188  * @fn void Configuration::set_string(const char *path, const char *s)
00189  * Set new value in configuration of type string. Works like the aforementioned method.
00190  * Just takes an good ol' char array instead of a std::string.
00191  * @param path path to value
00192  * @param s new string value
00193  *
00194  * @fn void Configuration::set_comment(const char *path, std::string &comment)
00195  * Set new comment for existing value.
00196  * @param path path to value
00197  * @param comment new comment string
00198  *
00199  * @fn void Configuration::set_comment(const char *path, const char *comment)
00200  * Set new comment for existing value. Works like the aforementioned method.
00201  * Just takes an good ol' char array instead of a std::string.
00202  * @param path path to value
00203  * @param comment new comment string
00204  *
00205  * @fn void Configuration::erase(const char *path)
00206  * Erase the given value from the configuration. It is not an error if the value does
00207  * not exists before deletion.
00208  * @param path path to value
00209  *
00210  * @fn void Configuration::set_default_float(const char *path, float f)
00211  * Set new default value in configuration of type float
00212  * @param path path to value
00213  * @param f new float value
00214  * 
00215  * @fn void Configuration::set_default_uint(const char *path, unsigned int uint)
00216  * Set new default value in configuration of type unsigned int
00217  * @param path path to value
00218  * @param uint new unsigned int value
00219  * 
00220  * @fn void Configuration::set_default_int(const char *path, int i)
00221  * Set new default value in configuration of type int
00222  * @param path path to value
00223  * @param i new int value
00224  * 
00225  * @fn void Configuration::set_default_bool(const char *path, bool b)
00226  * Set new default value in configuration of type bool
00227  * @param path path to value
00228  * @param b new bool value
00229  * 
00230  * @fn void Configuration::set_default_string(const char *path, std::string &s)
00231  * Set new default value in configuration of type string
00232  * @param path path to value
00233  * @param s new string value
00234  *
00235  * @fn void Configuration::set_default_string(const char *path, const char *s)
00236  * Set new default value in configuration of type string. Works like the aforementioned method.
00237  * Just takes an good ol' char array instead of a std::string.
00238  * @param path path to value
00239  * @param s new string value
00240  *
00241  * @fn void Configuration::set_default_comment(const char *path, std::string &comment)
00242  * Set new default comment for existing default configuration value.
00243  * @param path path to value
00244  * @param comment new comment string
00245  *
00246  * @fn void Configuration::set_default_comment(const char *path, const char *comment)
00247  * Set new default comment for existing default configuration value.
00248  * Works like the aforementioned method. Just takes an good ol' char array
00249  * instead of a std::string.
00250  * @param path path to value
00251  * @param comment new comment string
00252  *
00253  * @fn void Configuration::erase_default(const char *path)
00254  * Erase the given default value from the configuration. It is not an error if the value does
00255  * not exists before deletion.
00256  * @param path path to value
00257  *
00258  * @fn Configuration::ValueIterator * Configuration::iterator()
00259  * Iterator for all values.
00260  * Returns an iterator that can be used to iterate over all values in the current
00261  * configuration, it will value the overlay. If a default and a host-specific value
00262  * exists you will only see the host-specific value.
00263  * @return iterator over all values
00264  *
00265  * @fn Configuration::ValueIterator * Configuration::iterator_default()
00266  * Iterator for all default values.
00267  * Returns an iterator that can be used to iterate over all default values in
00268  * the current default configuration. Note that this might return less paths than
00269  * available, because the values for which no default entry exists are not
00270  * returned.
00271  * @return iterator over all default values
00272  *
00273  * @fn Configuration::ValueIterator * Configuration::iterator_hostspecific()
00274  * Iterator for all host-specific values.
00275  * Returns an iterator that can be used to iterate over all host-specific values
00276  * in the current configuration. Note that this might return less paths than
00277  * available, because the default values for which no host-specific entry exists
00278  * are not returned.
00279  * @return iterator over all host-specific values
00280  *
00281  * @fn Configuration::ValueIterator * Configuration::search(const char *path)
00282  * Iterator with search results.
00283  * Returns an iterator that can be used to iterate over the search results. All values
00284  * whose path start with the given strings are returned.
00285  * A call like
00286  * @code
00287  *   config->search("");
00288  * @endcode
00289  * is effectively the same as a call to iterator().
00290  * @param path start of path
00291  * @return iterator to search results
00292  *
00293  * @fn void Configuration::lock()
00294  * Lock the config.
00295  * No further changes or queries can be executed on the configuration and will block until
00296  * the config is unlocked.
00297  *
00298  * @fn bool Configuration::try_lock()
00299  * Try to lock the config.
00300  * @see Configuration::lock()
00301  * @return true, if the lock has been aquired, false otherwise
00302  *
00303  * @fn void Configuration::unlock()
00304  * Unlock the config.
00305  * Modifications and queries are possible again.
00306  *
00307  */
00308 
00309 /** @class ConfigurationException config/config.h
00310  * Generic configuration exception.
00311  * Thrown if there is no other matching exception.
00312  */
00313 
00314 
00315 /** Constructor.
00316  * @param msg message
00317  */
00318 ConfigurationException::ConfigurationException(const char *msg)
00319   : Exception(msg)
00320 {
00321 }
00322 
00323 
00324 /** Constructor.
00325  * @param prefix Put as "prefix: " before the message, can be used to have a prefix
00326  * and put an error message from another API into msg.
00327  * @param msg message
00328  */
00329 ConfigurationException::ConfigurationException(const char *prefix, const char *msg)
00330   : Exception()
00331 {
00332   append("%s: %s", prefix, msg);
00333 }
00334 
00335 
00336 /** @class ConfigEntryNotFoundException config/config.h
00337  * Thrown if a config entry could not be found.
00338  */
00339 
00340 
00341 /** Constructor.
00342  * @param path path of value
00343  */
00344 ConfigEntryNotFoundException::ConfigEntryNotFoundException( const char *path)
00345   : Exception("Config value for '%s' not found", path)
00346 {
00347 }
00348 
00349 
00350 /** @class ConfigTypeMismatchException config/config.h
00351  * Thrown if there a type problem was detected for example if you tried
00352  * to query a float with get_int().
00353  */
00354 
00355 /** Constructor.
00356  * @param path path of value
00357  * @param actual actual type
00358  * @param requested requested type
00359  */
00360 ConfigTypeMismatchException::ConfigTypeMismatchException(const char *path,
00361                                                          const char *actual,
00362                                                          const char *requested)
00363   : Exception()
00364 {
00365   append("Config value for '%s' is not of type '%s', but of type '%s'",
00366          path, requested, actual);
00367 }
00368 
00369 /** @class CouldNotOpenConfigException <config/config.h>
00370  * Thrown if config could not be opened.
00371  * This is most likely to happen during the constructor or load().
00372  */
00373 
00374 /** Constructor.
00375  * @param format format of message to describe cause or symptom of failure
00376  */
00377 CouldNotOpenConfigException::CouldNotOpenConfigException(const char *format, ...)
00378   : Exception()
00379 {
00380   va_list va;
00381   va_start(va, format);
00382   append_va(format, va);
00383   va_end(va);
00384 }
00385 
00386 
00387 /** @class Configuration::ValueIterator <config/config.h>
00388  * Iterator interface to iterate over config values. This does not implement a
00389  * classic iterator interface with begin and end nodes but rather mimics a more
00390  * Java-like interface where you iterate over the entries in a while loop until
00391  * you covered all entries (much like a queue).
00392  * If you implement this for your own configuration system you should not make
00393  * the constructor publically accessible.
00394  *
00395  * @fn Configuration::ValueIterator::~ValueIterator()
00396  * Virtual emptry destructor.
00397  *
00398  * @fn bool Configuration::ValueIterator::next()
00399  * Check if there is another element and advance to this if possible.
00400  * This advances to the next element, if there is one.
00401  * @return true, if another element has been reached, false otherwise
00402  *
00403  * @fn bool Configuration::ValueIterator::valid() const
00404  * Check if the current element is valid.
00405  * This is much like the classic end element for iterators. If the iterator is
00406  * invalid there all subsequent calls to next() shall fail.
00407  * @return true, if the iterator is still valid, false otherwise
00408  *
00409  * @fn const char * Configuration::ValueIterator::path() const
00410  * Path of value.
00411  * @return path of value
00412  *
00413  * @fn const char * Configuration::ValueIterator::type() const
00414  * Type of value.
00415  * @return string representation of value type.
00416  *
00417  * @fn bool Configuration::ValueIterator::is_float() const
00418  * Check if current value is a float.
00419  * @return true, if value is a float, false otherwise
00420  *
00421  * @fn bool Configuration::ValueIterator::is_uint() const
00422  * Check if current value is a unsigned int.
00423  * @return true, if value is a unsigned int, false otherwise
00424  *
00425  * @fn bool Configuration::ValueIterator::is_int() const
00426  * Check if current value is a int.
00427  * @return true, if value is a int, false otherwise
00428  *
00429  * @fn bool Configuration::ValueIterator::is_bool() const
00430  * Check if current value is a bool.
00431  * @return true, if value is a bool, false otherwise
00432  *
00433  * @fn bool Configuration::ValueIterator::is_string() const
00434  * Check if current value is a string.
00435  * @return true, if value is a string, false otherwise
00436  *
00437  * @fn bool Configuration::ValueIterator::is_default() const
00438  * Check if current value was read from the default config.
00439  * @return true, if value was read from the default config, false otherwise
00440  *
00441  * @fn float Configuration::ValueIterator::get_float() const
00442  * Get float value.
00443  * @return value
00444  *
00445  * @fn unsigned int Configuration::ValueIterator::get_uint() const
00446  * Get unsigned int value.
00447  * @return value
00448  *
00449  * @fn int Configuration::ValueIterator::get_int() const
00450  * Get int value.
00451  * @return value
00452  *
00453  * @fn bool Configuration::ValueIterator::get_bool() const
00454  * Get bool value.
00455  * @return value
00456  *
00457  * @fn std::string Configuration::ValueIterator::get_string() const
00458  * Get string value.
00459  * @return value
00460  *
00461  * @fn std::string Configuration::ValueIterator::get_comment() const
00462  * Get comment of value.
00463  * @return comment
00464  *
00465  * @fn std::string Configuration::ValueIterator::get_as_string() const
00466  * Get value as string.
00467  * @return value as string
00468  *
00469  */
00470 
00471 
00472 
00473 /** Add a configuration change handler.
00474  * The added handler is called whenever a value changes and the handler
00475  * desires to get notified for the given component.
00476  * @param h configuration change handler
00477  */
00478 void
00479 Configuration::add_change_handler(ConfigurationChangeHandler *h)
00480 {
00481   const char *c = h->config_monitor_prefix();
00482   if ( c == NULL ) {
00483     c = "";
00484   }
00485 
00486   _change_handlers.insert(ChangeHandlerMultimap::value_type(c, h));
00487 }
00488 
00489 
00490 /** Remove a configuration change handler.
00491  * The handler is removed from the change handler list and no longer called on
00492  * config changes.
00493  * @param h configuration change handler
00494  */
00495 void
00496 Configuration::rem_change_handler(ConfigurationChangeHandler *h)
00497 {
00498   const char *c = h->config_monitor_prefix();
00499   if ( c == NULL ) {
00500     c = "";
00501   }
00502   bool changed = true;
00503   while (changed) {
00504     changed = false;
00505     for (ChangeHandlerMultimap::const_iterator j = _change_handlers.begin(); !changed && (j != _change_handlers.end()); ++j) {
00506       _ch_range = _change_handlers.equal_range((*j).first);
00507       for (ChangeHandlerMultimap::iterator i = _ch_range.first; !changed && (i != _ch_range.second); ++i) {
00508         if ( (*i).second == h ) {
00509           _change_handlers.erase(i);
00510           changed = true;
00511           break;
00512         }
00513       }
00514       if ( changed)  break;
00515     }
00516   }
00517 }
00518 
00519 
00520 /** Find handlers for given path.
00521  * @param path path to get handlers for
00522  * @return list with config change handlers.
00523  */
00524 Configuration::ChangeHandlerList *
00525 Configuration::find_handlers(const char *path)
00526 {
00527   ChangeHandlerList *rv = new ChangeHandlerList();
00528   for (ChangeHandlerMultimap::const_iterator j = _change_handlers.begin(); j != _change_handlers.end(); ++j) {
00529     if ( strstr(path, (*j).first) == path ) {
00530       _ch_range = _change_handlers.equal_range((*j).first);
00531       for (ChangeHandlerMultimap::const_iterator i = _ch_range.first; i != _ch_range.second; ++i) {
00532         rv->push_back((*i).second);
00533       }
00534     }
00535   }
00536 
00537   return rv;
00538 }
00539 
00540 
00541 /** Notify handlers for given path.
00542  * @param path path to notify handlers for
00543  * @param comment_changed true if the change is about a comment change,
00544  * false otherwise
00545  */
00546 void
00547 Configuration::notify_handlers(const char *path, bool comment_changed)
00548 {
00549   ChangeHandlerList *h = find_handlers(path);
00550   Configuration::ValueIterator *value = get_value(path);
00551   if (value->next()) {
00552     for (ChangeHandlerList::const_iterator i = h->begin(); i != h->end(); ++i) {
00553       if (comment_changed) {
00554         (*i)->config_comment_changed(value);
00555       } else {
00556         (*i)->config_value_changed(value);
00557       }
00558     }
00559   } else {
00560     for (ChangeHandlerList::const_iterator i = h->begin(); i != h->end(); ++i) {
00561       (*i)->config_value_erased(path);
00562     }
00563   }
00564   delete value;
00565   delete h;
00566 }
00567 
00568 } // end namespace fawkes