Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
config.cpp
1 
2 /***************************************************************************
3  * config.cpp - Fawkes configuration interface
4  *
5  * Created: Mon Dec 18 14:54:23 2006
6  * Copyright 2006-2008 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <config/config.h>
25 #include <config/change_handler.h>
26 #include <cstring>
27 
28 namespace fawkes {
29 
30 /** @class Configuration <config/config.h>
31  * Interface for configuration handling.
32  * We know that half of robotics is about parameter tuning. The Configuration
33  * interface defines a unified way of storing parameters and other
34  * configuration options no matter of how the database is implemented.
35  * This is mainly done to allow for testing different solutions for ticket #10.
36  *
37  * @fn Configuration::~Configuration()
38  * Virtual empty destructor.
39  *
40  * @fn void Configuration::load(const char *name, const char *defaults_name, const char *tag)
41  * Load configuration.
42  * Loads configuration data, or opens a file, depending on the implementation. After
43  * this call access to all other methods shall be possible.
44  * @param name name of the host-based configuration. If this does not exist it shall
45  * be created from the default configuration. The name depends on the implementation and
46  * could be a filename.
47  * @param defaults_name name of the default database. As for the name this depends on
48  * the actual implementation.
49  * @param tag this optional parameter can denote a specific config version to load. This
50  * will cause the host-specific database to be flushed and filled with the values for
51  * the given tag. All values that did not exist for the tag are copied over from the
52  * default database.
53  *
54  * @fn void Configuration::tag(const char *tag)
55  * Tag this configuration version.
56  * This creates a new tagged version of the current config. The tagged config can be
57  * accessed via load().
58  * @param tag tag for this version
59  *
60  * @fn void Configuration::copy(Configuration *copyconf)
61  * Copy all values from the given configuration.
62  * All values from the given configuration are copied. Old values are not erased
63  * so that the copied values will overwrite existing values, new values are
64  * created, but values existent in current config but not in the copie config
65  * will remain unchanged.
66  * @param copyconf configuration to copy
67  *
68  * @fn std::list<std::string> Configuration::tags()
69  * List of tags.
70  * @return list of tags
71  *
72  * @fn bool Configuration::exists(const char *path)
73  * Check if a given value exists.
74  * @param path path to value
75  * @return true if the value exists, false otherwise
76  *
77  * @fn bool Configuration::is_float(const char *path)
78  * Check if a value is of type float
79  * @param path path to value
80  * @return true if the value exists and is of type float
81  *
82  * @fn bool Configuration::is_uint(const char *path)
83  * Check if a value is of type unsigned int
84  * @param path path to value
85  * @return true if the value exists and is of type unsigned int
86  *
87  * @fn bool Configuration::is_int(const char *path)
88  * Check if a value is of type int
89  * @param path path to value
90  * @return true if the value exists and is of type int
91  *
92  * @fn bool Configuration::is_bool(const char *path)
93  * Check if a value is of type bool
94  * @param path path to value
95  * @return true if the value exists and is of type bool
96  *
97  * @fn bool Configuration::is_string(const char *path)
98  * Check if a value is of type string
99  * @param path path to value
100  * @return true if the value exists and is of type string
101  *
102  * @fn bool Configuration::is_default(const char *path)
103  * Check if a value was read from the default config.
104  * @param path path to value
105  * @return true if the value exists and is only stored in the default config
106  *
107  * @fn float Configuration::get_float(const char *path)
108  * Get value from configuration which is of type float
109  * @param path path to value
110  * @return value
111  *
112  * @fn unsigned int Configuration::get_uint(const char *path)
113  * Get value from configuration which is of type unsigned int
114  * @param path path to value
115  * @return value
116  *
117  * @fn int Configuration::get_int(const char *path)
118  * Get value from configuration which is of type int
119  * @param path path to value
120  * @return value
121  *
122  * @fn bool Configuration::get_bool(const char *path)
123  * Get value from configuration which is of type bool
124  * @param path path to value
125  * @return value
126  *
127  * @fn std::string Configuration::get_string(const char *path)
128  * Get value from configuration which is of type string
129  * @param path path to value
130  * @return value
131  *
132  * @fn Configuration::ValueIterator * Configuration::get_value(const char *path)
133  * Get value from configuration.
134  * @param path path to value
135  * @return value iterator for just this one value, maybe invalid if value does not
136  * exists.
137  *
138  * @fn std::string Configuration::get_type(const char *path)
139  * Get type of value at given path.
140  * @param path path to value
141  * @return string representation of type, one of float, unsigned int, int, bool,
142  * or string
143  * @exception ConfigurationException shall be thrown if value does not exist or
144  * on any other error.
145  *
146  * @fn std::string Configuration::get_comment(const char *path)
147  * Get comment of value at given path.
148  * The value at the given path must exist in the host-specific configuration.
149  * @param path path to value
150  * @return comment
151  * @exception ConfigEntryNotFoundException shall be thrown if value does not exist
152  * @exception ConfigurationException shall be thrown on any other error
153  *
154  * @fn std::string Configuration::get_default_comment(const char *path)
155  * Get comment of value at given path.
156  * The value at the given path must exist in the default configuration.
157  * @param path path to value
158  * @return comment
159  * @exception ConfigEntryNotFoundException shall be thrown if value does not exist
160  * @exception ConfigurationException shall be thrown on any other error
161  *
162  *
163  * @fn void Configuration::set_float(const char *path, float f)
164  * Set new value in configuration of type float
165  * @param path path to value
166  * @param f new float value
167  *
168  * @fn void Configuration::set_uint(const char *path, unsigned int uint)
169  * Set new value in configuration of type unsigned int
170  * @param path path to value
171  * @param uint new unsigned int value
172  *
173  * @fn void Configuration::set_int(const char *path, int i)
174  * Set new value in configuration of type int
175  * @param path path to value
176  * @param i new int value
177  *
178  * @fn void Configuration::set_bool(const char *path, bool b)
179  * Set new value in configuration of type bool
180  * @param path path to value
181  * @param b new bool value
182  *
183  * @fn void Configuration::set_string(const char *path, std::string &s)
184  * Set new value in configuration of type string
185  * @param path path to value
186  * @param s new string value
187  *
188  * @fn void Configuration::set_string(const char *path, const char *s)
189  * Set new value in configuration of type string. Works like the aforementioned method.
190  * Just takes an good ol' char array instead of a std::string.
191  * @param path path to value
192  * @param s new string value
193  *
194  * @fn void Configuration::set_comment(const char *path, std::string &comment)
195  * Set new comment for existing value.
196  * @param path path to value
197  * @param comment new comment string
198  *
199  * @fn void Configuration::set_comment(const char *path, const char *comment)
200  * Set new comment for existing value. Works like the aforementioned method.
201  * Just takes an good ol' char array instead of a std::string.
202  * @param path path to value
203  * @param comment new comment string
204  *
205  * @fn void Configuration::erase(const char *path)
206  * Erase the given value from the configuration. It is not an error if the value does
207  * not exists before deletion.
208  * @param path path to value
209  *
210  * @fn void Configuration::set_default_float(const char *path, float f)
211  * Set new default value in configuration of type float
212  * @param path path to value
213  * @param f new float value
214  *
215  * @fn void Configuration::set_default_uint(const char *path, unsigned int uint)
216  * Set new default value in configuration of type unsigned int
217  * @param path path to value
218  * @param uint new unsigned int value
219  *
220  * @fn void Configuration::set_default_int(const char *path, int i)
221  * Set new default value in configuration of type int
222  * @param path path to value
223  * @param i new int value
224  *
225  * @fn void Configuration::set_default_bool(const char *path, bool b)
226  * Set new default value in configuration of type bool
227  * @param path path to value
228  * @param b new bool value
229  *
230  * @fn void Configuration::set_default_string(const char *path, std::string &s)
231  * Set new default value in configuration of type string
232  * @param path path to value
233  * @param s new string value
234  *
235  * @fn void Configuration::set_default_string(const char *path, const char *s)
236  * Set new default value in configuration of type string. Works like the aforementioned method.
237  * Just takes an good ol' char array instead of a std::string.
238  * @param path path to value
239  * @param s new string value
240  *
241  * @fn void Configuration::set_default_comment(const char *path, std::string &comment)
242  * Set new default comment for existing default configuration value.
243  * @param path path to value
244  * @param comment new comment string
245  *
246  * @fn void Configuration::set_default_comment(const char *path, const char *comment)
247  * Set new default comment for existing default configuration value.
248  * Works like the aforementioned method. Just takes an good ol' char array
249  * instead of a std::string.
250  * @param path path to value
251  * @param comment new comment string
252  *
253  * @fn void Configuration::erase_default(const char *path)
254  * Erase the given default value from the configuration. It is not an error if the value does
255  * not exists before deletion.
256  * @param path path to value
257  *
258  * @fn Configuration::ValueIterator * Configuration::iterator()
259  * Iterator for all values.
260  * Returns an iterator that can be used to iterate over all values in the current
261  * configuration, it will value the overlay. If a default and a host-specific value
262  * exists you will only see the host-specific value.
263  * @return iterator over all values
264  *
265  * @fn Configuration::ValueIterator * Configuration::iterator_default()
266  * Iterator for all default values.
267  * Returns an iterator that can be used to iterate over all default values in
268  * the current default configuration. Note that this might return less paths than
269  * available, because the values for which no default entry exists are not
270  * returned.
271  * @return iterator over all default values
272  *
273  * @fn Configuration::ValueIterator * Configuration::iterator_hostspecific()
274  * Iterator for all host-specific values.
275  * Returns an iterator that can be used to iterate over all host-specific values
276  * in the current configuration. Note that this might return less paths than
277  * available, because the default values for which no host-specific entry exists
278  * are not returned.
279  * @return iterator over all host-specific values
280  *
281  * @fn Configuration::ValueIterator * Configuration::search(const char *path)
282  * Iterator with search results.
283  * Returns an iterator that can be used to iterate over the search results. All values
284  * whose path start with the given strings are returned.
285  * A call like
286  * @code
287  * config->search("");
288  * @endcode
289  * is effectively the same as a call to iterator().
290  * @param path start of path
291  * @return iterator to search results
292  *
293  * @fn void Configuration::lock()
294  * Lock the config.
295  * No further changes or queries can be executed on the configuration and will block until
296  * the config is unlocked.
297  *
298  * @fn bool Configuration::try_lock()
299  * Try to lock the config.
300  * @see Configuration::lock()
301  * @return true, if the lock has been aquired, false otherwise
302  *
303  * @fn void Configuration::unlock()
304  * Unlock the config.
305  * Modifications and queries are possible again.
306  *
307  */
308 
309 /** @class ConfigurationException config/config.h
310  * Generic configuration exception.
311  * Thrown if there is no other matching exception.
312  */
313 
314 
315 /** Constructor.
316  * @param msg message
317  */
319  : Exception(msg)
320 {
321 }
322 
323 
324 /** Constructor.
325  * @param prefix Put as "prefix: " before the message, can be used to have a prefix
326  * and put an error message from another API into msg.
327  * @param msg message
328  */
329 ConfigurationException::ConfigurationException(const char *prefix, const char *msg)
330  : Exception()
331 {
332  append("%s: %s", prefix, msg);
333 }
334 
335 
336 /** @class ConfigEntryNotFoundException config/config.h
337  * Thrown if a config entry could not be found.
338  */
339 
340 
341 /** Constructor.
342  * @param path path of value
343  */
345  : Exception("Config value for '%s' not found", path)
346 {
347 }
348 
349 
350 /** @class ConfigTypeMismatchException config/config.h
351  * Thrown if there a type problem was detected for example if you tried
352  * to query a float with get_int().
353  */
354 
355 /** Constructor.
356  * @param path path of value
357  * @param actual actual type
358  * @param requested requested type
359  */
361  const char *actual,
362  const char *requested)
363  : Exception()
364 {
365  append("Config value for '%s' is not of type '%s', but of type '%s'",
366  path, requested, actual);
367 }
368 
369 /** @class CouldNotOpenConfigException <config/config.h>
370  * Thrown if config could not be opened.
371  * This is most likely to happen during the constructor or load().
372  */
373 
374 /** Constructor.
375  * @param format format of message to describe cause or symptom of failure
376  */
378  : Exception()
379 {
380  va_list va;
381  va_start(va, format);
382  append_va(format, va);
383  va_end(va);
384 }
385 
386 
387 /** @class Configuration::ValueIterator <config/config.h>
388  * Iterator interface to iterate over config values. This does not implement a
389  * classic iterator interface with begin and end nodes but rather mimics a more
390  * Java-like interface where you iterate over the entries in a while loop until
391  * you covered all entries (much like a queue).
392  * If you implement this for your own configuration system you should not make
393  * the constructor publically accessible.
394  *
395  * @fn Configuration::ValueIterator::~ValueIterator()
396  * Virtual emptry destructor.
397  *
398  * @fn bool Configuration::ValueIterator::next()
399  * Check if there is another element and advance to this if possible.
400  * This advances to the next element, if there is one.
401  * @return true, if another element has been reached, false otherwise
402  *
403  * @fn bool Configuration::ValueIterator::valid() const
404  * Check if the current element is valid.
405  * This is much like the classic end element for iterators. If the iterator is
406  * invalid there all subsequent calls to next() shall fail.
407  * @return true, if the iterator is still valid, false otherwise
408  *
409  * @fn const char * Configuration::ValueIterator::path() const
410  * Path of value.
411  * @return path of value
412  *
413  * @fn const char * Configuration::ValueIterator::type() const
414  * Type of value.
415  * @return string representation of value type.
416  *
417  * @fn bool Configuration::ValueIterator::is_float() const
418  * Check if current value is a float.
419  * @return true, if value is a float, false otherwise
420  *
421  * @fn bool Configuration::ValueIterator::is_uint() const
422  * Check if current value is a unsigned int.
423  * @return true, if value is a unsigned int, false otherwise
424  *
425  * @fn bool Configuration::ValueIterator::is_int() const
426  * Check if current value is a int.
427  * @return true, if value is a int, false otherwise
428  *
429  * @fn bool Configuration::ValueIterator::is_bool() const
430  * Check if current value is a bool.
431  * @return true, if value is a bool, false otherwise
432  *
433  * @fn bool Configuration::ValueIterator::is_string() const
434  * Check if current value is a string.
435  * @return true, if value is a string, false otherwise
436  *
437  * @fn bool Configuration::ValueIterator::is_default() const
438  * Check if current value was read from the default config.
439  * @return true, if value was read from the default config, false otherwise
440  *
441  * @fn float Configuration::ValueIterator::get_float() const
442  * Get float value.
443  * @return value
444  *
445  * @fn unsigned int Configuration::ValueIterator::get_uint() const
446  * Get unsigned int value.
447  * @return value
448  *
449  * @fn int Configuration::ValueIterator::get_int() const
450  * Get int value.
451  * @return value
452  *
453  * @fn bool Configuration::ValueIterator::get_bool() const
454  * Get bool value.
455  * @return value
456  *
457  * @fn std::string Configuration::ValueIterator::get_string() const
458  * Get string value.
459  * @return value
460  *
461  * @fn std::string Configuration::ValueIterator::get_comment() const
462  * Get comment of value.
463  * @return comment
464  *
465  * @fn std::string Configuration::ValueIterator::get_as_string() const
466  * Get value as string.
467  * @return value as string
468  *
469  */
470 
471 
472 
473 /** Add a configuration change handler.
474  * The added handler is called whenever a value changes and the handler
475  * desires to get notified for the given component.
476  * @param h configuration change handler
477  */
478 void
480 {
481  const char *c = h->config_monitor_prefix();
482  if ( c == NULL ) {
483  c = "";
484  }
485 
486  _change_handlers.insert(ChangeHandlerMultimap::value_type(c, h));
487 }
488 
489 
490 /** Remove a configuration change handler.
491  * The handler is removed from the change handler list and no longer called on
492  * config changes.
493  * @param h configuration change handler
494  */
495 void
497 {
498  const char *c = h->config_monitor_prefix();
499  if ( c == NULL ) {
500  c = "";
501  }
502  bool changed = true;
503  while (changed) {
504  changed = false;
505  for (ChangeHandlerMultimap::const_iterator j = _change_handlers.begin(); !changed && (j != _change_handlers.end()); ++j) {
506  _ch_range = _change_handlers.equal_range((*j).first);
507  for (ChangeHandlerMultimap::iterator i = _ch_range.first; !changed && (i != _ch_range.second); ++i) {
508  if ( (*i).second == h ) {
509  _change_handlers.erase(i);
510  changed = true;
511  break;
512  }
513  }
514  if ( changed) break;
515  }
516  }
517 }
518 
519 
520 /** Find handlers for given path.
521  * @param path path to get handlers for
522  * @return list with config change handlers.
523  */
526 {
528  for (ChangeHandlerMultimap::const_iterator j = _change_handlers.begin(); j != _change_handlers.end(); ++j) {
529  if ( strstr(path, (*j).first) == path ) {
530  _ch_range = _change_handlers.equal_range((*j).first);
531  for (ChangeHandlerMultimap::const_iterator i = _ch_range.first; i != _ch_range.second; ++i) {
532  rv->push_back((*i).second);
533  }
534  }
535  }
536 
537  return rv;
538 }
539 
540 
541 /** Notify handlers for given path.
542  * @param path path to notify handlers for
543  * @param comment_changed true if the change is about a comment change,
544  * false otherwise
545  */
546 void
547 Configuration::notify_handlers(const char *path, bool comment_changed)
548 {
549  ChangeHandlerList *h = find_handlers(path);
551  if (value->next()) {
552  for (ChangeHandlerList::const_iterator i = h->begin(); i != h->end(); ++i) {
553  if (comment_changed) {
554  (*i)->config_comment_changed(value);
555  } else {
556  (*i)->config_value_changed(value);
557  }
558  }
559  } else {
560  for (ChangeHandlerList::const_iterator i = h->begin(); i != h->end(); ++i) {
561  (*i)->config_value_erased(path);
562  }
563  }
564  delete value;
565  delete h;
566 }
567 
568 } // end namespace fawkes
ChangeHandlerMultimapRange _ch_range
Change handler range.
Definition: config.h:182
ChangeHandlerMultimap _change_handlers
Registered change handlers.
Definition: config.h:180
Interface for configuration change handling.
virtual bool next()=0
Check if there is another element and advance to this if possible.
Base class for exceptions in Fawkes.
Definition: exception.h:36
ChangeHandlerList * find_handlers(const char *path)
Find handlers for given path.
Definition: config.cpp:525
ConfigTypeMismatchException(const char *path, const char *actual, const char *requested)
Constructor.
Definition: config.cpp:360
virtual void rem_change_handler(ConfigurationChangeHandler *h)
Remove a configuration change handler.
Definition: config.cpp:496
void append_va(const char *format, va_list va)
Append messages to the message list.
Definition: exception.cpp:361
ConfigurationException(const char *msg)
Constructor.
Definition: config.cpp:318
void notify_handlers(const char *path, bool comment_changed=false)
Notify handlers for given path.
Definition: config.cpp:547
CouldNotOpenConfigException(const char *format,...)
Constructor.
Definition: config.cpp:377
virtual ValueIterator * get_value(const char *path)=0
Get value from configuration.
Iterator interface to iterate over config values.
Definition: config.h:68
virtual void add_change_handler(ConfigurationChangeHandler *h)
Add a configuration change handler.
Definition: config.cpp:479
const char * config_monitor_prefix()
Which path prefix shall be monitored.
void append(const char *format,...)
Append messages to the message list.
Definition: exception.cpp:341
ConfigEntryNotFoundException(const char *path)
Constructor.
Definition: config.cpp:344
std::list< ConfigurationChangeHandler * > ChangeHandlerList
List that contains pointers to ConfigurationChangeHandler.
Definition: config.h:168