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