Fawkes API  Fawkes Development Version
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 #if 0 /* just to make Emacs auto-indent happy */
30 }
31 #endif
32 
33 /** @class Configuration <config/config.h>
34  * Interface for configuration handling.
35  * We know that half of robotics is about parameter tuning. The Configuration
36  * interface defines a unified way of storing parameters and other
37  * configuration options no matter of how the database is implemented.
38  * This is mainly done to allow for testing different solutions for ticket #10.
39  *
40  * @fn Configuration::~Configuration()
41  * Virtual empty destructor.
42  *
43  * @fn void Configuration::load(const char *file_path)
44  * Load configuration.
45  * Loads configuration data, or opens a file, depending on the implementation. After
46  * this call access to all other methods shall be possible.
47  * @param file_path path of the configuration file.
48  *
49  *
50  * @fn void Configuration::copy(Configuration *copyconf)
51  * Copies all values from the given configuration.
52  * All values from the given configuration are copied. Old values are not erased
53  * so that the copied values will overwrite existing values, new values are
54  * created, but values existent in current config but not in the copied config
55  * will remain unchanged.
56  * @param copyconf configuration to copy
57  *
58  * @fn bool Configuration::exists(const char *path)
59  * Check if a given value exists.
60  * @param path path to value
61  * @return true if the value exists, false otherwise
62  *
63  * @fn bool Configuration::is_float(const char *path)
64  * Check if a value is of type float
65  * @param path path to value
66  * @return true if the value exists and is of type float
67  *
68  * @fn bool Configuration::is_uint(const char *path)
69  * Check if a value is of type unsigned int
70  * @param path path to value
71  * @return true if the value exists and is of type unsigned int
72  *
73  * @fn bool Configuration::is_int(const char *path)
74  * Check if a value is of type int
75  * @param path path to value
76  * @return true if the value exists and is of type int
77  *
78  * @fn bool Configuration::is_bool(const char *path)
79  * Check if a value is of type bool
80  * @param path path to value
81  * @return true if the value exists and is of type bool
82  *
83  * @fn bool Configuration::is_string(const char *path)
84  * Check if a value is of type string
85  * @param path path to value
86  * @return true if the value exists and is of type string
87  *
88  * @fn bool Configuration::is_list(const char *path)
89  * Check if a value is a list.
90  * @param path path to value
91  * @return true if the value exists and is a list
92  *
93  * @fn bool Configuration::is_default(const char *path)
94  * Check if a value was read from the default config.
95  * @param path path to value
96  * @return true if the value exists and is only stored in the default config
97  *
98  * @fn float Configuration::get_float(const char *path)
99  * Get value from configuration which is of type float
100  * @param path path to value
101  * @return value
102  *
103  * @fn unsigned int Configuration::get_uint(const char *path)
104  * Get value from configuration which is of type unsigned int
105  * @param path path to value
106  * @return value
107  *
108  * @fn int Configuration::get_int(const char *path)
109  * Get value from configuration which is of type int
110  * @param path path to value
111  * @return value
112  *
113  * @fn bool Configuration::get_bool(const char *path)
114  * Get value from configuration which is of type bool
115  * @param path path to value
116  * @return value
117  *
118  * @fn std::string Configuration::get_string(const char *path)
119  * Get value from configuration which is of type string
120  * @param path path to value
121  * @return value
122  *
123  * @fn std::vector<float> Configuration::get_floats(const char *path)
124  * Get list of values from configuration which is of type float
125  * @param path path to value
126  * @return value
127  *
128  * @fn std::vector<unsigned int> Configuration::get_uints(const char *path)
129  * Get list of values from configuration which is of type unsigned int
130  * @param path path to value
131  * @return value
132  *
133  * @fn std::vector<int> Configuration::get_ints(const char *path)
134  * Get list of values from configuration which is of type int
135  * @param path path to value
136  * @return value
137  *
138  * @fn std::vector<bool> Configuration::get_bools(const char *path)
139  * Get list of values from configuration which is of type bool
140  * @param path path to value
141  * @return value
142  *
143  * @fn std::vector<std::string> Configuration::get_strings(const char *path)
144  * Get list of values from configuration which is of type string
145  * @param path path to value
146  * @return value
147  *
148  * @fn Configuration::ValueIterator * Configuration::get_value(const char *path)
149  * Get value from configuration.
150  * @param path path to value
151  * @return value iterator for just this one value, maybe invalid if value does not
152  * exists.
153  *
154  * @fn std::string Configuration::get_type(const char *path)
155  * Get type of value at given path.
156  * @param path path to value
157  * @return string representation of type, one of float, unsigned int, int, bool,
158  * or string
159  * @exception ConfigurationException shall be thrown if value does not exist or
160  * on any other error.
161  *
162  * @fn std::string Configuration::get_comment(const char *path)
163  * Get comment of value at given path.
164  * The value at the given path must exist in the host-specific configuration.
165  * @param path path to value
166  * @return comment
167  * @exception ConfigEntryNotFoundException shall be thrown if value does not exist
168  * @exception ConfigurationException shall be thrown on any other error
169  *
170  * @fn std::string Configuration::get_default_comment(const char *path)
171  * Get comment of value at given path.
172  * The value at the given path must exist in the default configuration.
173  * @param path path to value
174  * @return comment
175  * @exception ConfigEntryNotFoundException shall be thrown if value does not exist
176  * @exception ConfigurationException shall be thrown on any other error
177  *
178  *
179  * @fn void Configuration::set_float(const char *path, float f)
180  * Set new value in configuration of type float
181  * @param path path to value
182  * @param f new float value
183  *
184  * @fn void Configuration::set_uint(const char *path, unsigned int uint)
185  * Set new value in configuration of type unsigned int
186  * @param path path to value
187  * @param uint new unsigned int value
188  *
189  * @fn void Configuration::set_int(const char *path, int i)
190  * Set new value in configuration of type int
191  * @param path path to value
192  * @param i new int value
193  *
194  * @fn void Configuration::set_bool(const char *path, bool b)
195  * Set new value in configuration of type bool
196  * @param path path to value
197  * @param b new bool value
198  *
199  * @fn void Configuration::set_string(const char *path, std::string &s)
200  * Set new value in configuration of type string
201  * @param path path to value
202  * @param s new string value
203  *
204  * @fn void Configuration::set_string(const char *path, const char *s)
205  * Set new value in configuration of type string. Works like the aforementioned method.
206  * Just takes an good ol' char array instead of a std::string.
207  * @param path path to value
208  * @param s new string value
209  *
210  * @fn void Configuration::set_floats(const char *path, std::vector<float> &f)
211  * Set new value in configuration of type float
212  * @param path path to value
213  * @param f new float values
214  *
215  * @fn void Configuration::set_uints(const char *path, std::vector<unsigned int> &uint)
216  * Set new value in configuration of type unsigned int
217  * @param path path to value
218  * @param uint new unsigned int values
219  *
220  * @fn void Configuration::set_ints(const char *path, std::vector<int> &i)
221  * Set new value in configuration of type int
222  * @param path path to value
223  * @param i new int values
224  *
225  * @fn void Configuration::set_bools(const char *path, std::vector<bool> &b)
226  * Set new value in configuration of type bool
227  * @param path path to value
228  * @param b new bool values
229  *
230  * @fn void Configuration::set_strings(const char *path, std::vector<std::string> &s)
231  * Set new value in configuration of type string
232  * @param path path to value
233  * @param s new string values
234  *
235  * @fn void Configuration::set_strings(const char *path, std::vector<const char *> &s)
236  * Set new 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 values
240 
241  *
242  * @fn void Configuration::set_comment(const char *path, std::string &comment)
243  * Set new comment for existing value.
244  * @param path path to value
245  * @param comment new comment string
246  *
247  * @fn void Configuration::set_comment(const char *path, const char *comment)
248  * Set new comment for existing value. Works like the aforementioned method.
249  * Just takes an good ol' char array instead of a std::string.
250  * @param path path to value
251  * @param comment new comment string
252  *
253  * @fn void Configuration::erase(const char *path)
254  * Erase the given 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 void Configuration::set_default_float(const char *path, float f)
259  * Set new default value in configuration of type float
260  * @param path path to value
261  * @param f new float value
262  *
263  * @fn void Configuration::set_default_uint(const char *path, unsigned int uint)
264  * Set new default value in configuration of type unsigned int
265  * @param path path to value
266  * @param uint new unsigned int value
267  *
268  * @fn void Configuration::set_default_int(const char *path, int i)
269  * Set new default value in configuration of type int
270  * @param path path to value
271  * @param i new int value
272  *
273  * @fn void Configuration::set_default_bool(const char *path, bool b)
274  * Set new default value in configuration of type bool
275  * @param path path to value
276  * @param b new bool value
277  *
278  * @fn void Configuration::set_default_string(const char *path, std::string &s)
279  * Set new default value in configuration of type string
280  * @param path path to value
281  * @param s new string value
282  *
283  * @fn void Configuration::set_default_string(const char *path, const char *s)
284  * Set new default value in configuration of type string. Works like the aforementioned method.
285  * Just takes an good ol' char array instead of a std::string.
286  * @param path path to value
287  * @param s new string value
288  *
289  * @fn void Configuration::set_default_comment(const char *path, std::string &comment)
290  * Set new default comment for existing default configuration value.
291  * @param path path to value
292  * @param comment new comment string
293  *
294  * @fn void Configuration::set_default_comment(const char *path, const char *comment)
295  * Set new default comment for existing default configuration value.
296  * Works like the aforementioned method. Just takes an good ol' char array
297  * instead of a std::string.
298  * @param path path to value
299  * @param comment new comment string
300  *
301  * @fn void Configuration::erase_default(const char *path)
302  * Erase the given default value from the configuration. It is not an error if the value does
303  * not exists before deletion.
304  * @param path path to value
305  *
306  * @fn Configuration::ValueIterator * Configuration::iterator()
307  * Iterator for all values.
308  * Returns an iterator that can be used to iterate over all values in the current
309  * configuration, it will value the overlay. If a default and a host-specific value
310  * exists you will only see the host-specific value.
311  * @return iterator over all values
312  *
313  * @fn Configuration::ValueIterator * Configuration::search(const char *path)
314  * Iterator with search results.
315  * Returns an iterator that can be used to iterate over the search results. All values
316  * whose path start with the given strings are returned.
317  * A call like
318  * @code
319  * config->search("");
320  * @endcode
321  * is effectively the same as a call to iterator().
322  * @param path start of path
323  * @return iterator to search results
324  *
325  * @fn void Configuration::lock()
326  * Lock the config.
327  * No further changes or queries can be executed on the configuration and will block until
328  * the config is unlocked.
329  *
330  * @fn bool Configuration::try_lock()
331  * Try to lock the config.
332  * @see Configuration::lock()
333  * @return true, if the lock has been aquired, false otherwise
334  *
335  * @fn void Configuration::unlock()
336  * Unlock the config.
337  * Modifications and queries are possible again.
338  *
339  * @fn void Configuration::try_dump()
340  * Try to dump configuration.
341  * For configuration methods that transform configuration files in a binary
342  * format this can be used to write out the text representation on shutdown
343  * of Fawkes.
344  * @exception Exception thrown if dumping fails
345  *
346  */
347 
348 /** @class ConfigurationException config/config.h
349  * Generic configuration exception.
350  * Thrown if there is no other matching exception.
351  */
352 
353 
354 /** Constructor.
355  * @param msg message
356  */
358  : Exception(msg)
359 {
360 }
361 
362 
363 /** Constructor.
364  * @param prefix Put as "prefix: " before the message, can be used to have a prefix
365  * and put an error message from another API into msg.
366  * @param msg message
367  */
368 ConfigurationException::ConfigurationException(const char *prefix, const char *msg)
369  : Exception()
370 {
371  append("%s: %s", prefix, msg);
372 }
373 
374 
375 /** @class ConfigEntryNotFoundException config/config.h
376  * Thrown if a config entry could not be found.
377  */
378 
379 
380 /** Constructor.
381  * @param path path of value
382  */
384  : Exception("Config value for '%s' not found", path)
385 {
386 }
387 
388 
389 /** @class ConfigTypeMismatchException config/config.h
390  * Thrown if there a type problem was detected for example if you tried
391  * to query a float with get_int().
392  */
393 
394 /** Constructor.
395  * @param path path of value
396  * @param actual actual type
397  * @param requested requested type
398  */
400  const char *actual,
401  const char *requested)
402  : Exception()
403 {
404  append("Config value for '%s' is not of type '%s', but of type '%s'",
405  path, requested, actual);
406 }
407 
408 /** @class CouldNotOpenConfigException <config/config.h>
409  * Thrown if config could not be opened.
410  * This is most likely to happen during the constructor or load().
411  */
412 
413 /** Constructor.
414  * @param format format of message to describe cause or symptom of failure
415  */
417  : Exception()
418 {
419  va_list va;
420  va_start(va, format);
421  append_va(format, va);
422  va_end(va);
423 }
424 
425 
426 /** @class Configuration::ValueIterator <config/config.h>
427  * Iterator interface to iterate over config values. This does not implement a
428  * classic iterator interface with begin and end nodes but rather mimics a more
429  * Java-like interface where you iterate over the entries in a while loop until
430  * you covered all entries (much like a queue).
431  * If you implement this for your own configuration system you should not make
432  * the constructor publically accessible.
433  *
434  * @fn Configuration::ValueIterator::~ValueIterator()
435  * Virtual emptry destructor.
436  *
437  * @fn bool Configuration::ValueIterator::next()
438  * Check if there is another element and advance to this if possible.
439  * This advances to the next element, if there is one.
440  * @return true, if another element has been reached, false otherwise
441  *
442  * @fn bool Configuration::ValueIterator::valid() const
443  * Check if the current element is valid.
444  * This is much like the classic end element for iterators. If the iterator is
445  * invalid there all subsequent calls to next() shall fail.
446  * @return true, if the iterator is still valid, false otherwise
447  *
448  * @fn const char * Configuration::ValueIterator::path() const
449  * Path of value.
450  * @return path of value
451  *
452  * @fn const char * Configuration::ValueIterator::type() const
453  * Type of value.
454  * @return string representation of value type.
455  *
456  * @fn bool Configuration::ValueIterator::is_float() const
457  * Check if current value is a float.
458  * @return true, if value is a float, false otherwise
459  *
460  * @fn bool Configuration::ValueIterator::is_uint() const
461  * Check if current value is a unsigned int.
462  * @return true, if value is a unsigned int, false otherwise
463  *
464  * @fn bool Configuration::ValueIterator::is_int() const
465  * Check if current value is a int.
466  * @return true, if value is a int, false otherwise
467  *
468  * @fn bool Configuration::ValueIterator::is_bool() const
469  * Check if current value is a bool.
470  * @return true, if value is a bool, false otherwise
471  *
472  * @fn bool Configuration::ValueIterator::is_string() const
473  * Check if current value is a string.
474  * @return true, if value is a string, false otherwise
475  *
476  * @fn bool Configuration::ValueIterator::is_list() const
477  * Check if a value is a list.
478  * @return true if the value exists and is a list
479  *
480  * @fn size_t Configuration::ValueIterator::get_list_size() const
481  * Get number of elements in list value.
482  * @return number of elements in list value
483  * @throw Exception thrown if the element is not a list.
484  *
485  * @fn bool Configuration::ValueIterator::is_default() const
486  * Check if current value was read from the default config.
487  * @return true, if value was read from the default config, false otherwise
488  *
489  * @fn float Configuration::ValueIterator::get_float() const
490  * Get float value.
491  * @return value
492  *
493  * @fn unsigned int Configuration::ValueIterator::get_uint() const
494  * Get unsigned int value.
495  * @return value
496  *
497  * @fn int Configuration::ValueIterator::get_int() const
498  * Get int value.
499  * @return value
500  *
501  * @fn bool Configuration::ValueIterator::get_bool() const
502  * Get bool value.
503  * @return value
504  *
505  * @fn std::string Configuration::ValueIterator::get_string() const
506  * Get string value.
507  * @return value
508  *
509  * @fn std::vector<float> Configuration::ValueIterator::get_floats() const
510  * Get list of values from configuration which is of type float
511  * @return value
512  *
513  * @fn std::vector<unsigned int> Configuration::ValueIterator::get_uints() const
514  * Get list of values from configuration which is of type unsigned int
515  * @return value
516  *
517  * @fn std::vector<int> Configuration::ValueIterator::get_ints() const
518  * Get list of values from configuration which is of type int
519  * @return value
520  *
521  * @fn std::vector<bool> Configuration::ValueIterator::get_bools() const
522  * Get list of values from configuration which is of type bool
523  * @return value
524  *
525  * @fn std::vector<std::string> Configuration::ValueIterator::get_strings() const
526  * Get list of values from configuration which is of type string
527  * @return value
528  *
529  * @fn std::string Configuration::ValueIterator::get_comment() const
530  * Get comment of value.
531  * @return comment
532  *
533  * @fn std::string Configuration::ValueIterator::get_as_string() const
534  * Get value as string.
535  * @return value as string
536  *
537  */
538 
539 
540 
541 /** Add a configuration change handler.
542  * The added handler is called whenever a value changes and the handler
543  * desires to get notified for the given component.
544  * @param h configuration change handler
545  */
546 void
548 {
549  const char *c = h->config_monitor_prefix();
550  if ( c == NULL ) {
551  c = "";
552  }
553 
554  _change_handlers.insert(ChangeHandlerMultimap::value_type(c, h));
555 }
556 
557 
558 /** Remove a configuration change handler.
559  * The handler is removed from the change handler list and no longer called on
560  * config changes.
561  * @param h configuration change handler
562  */
563 void
565 {
566  const char *c = h->config_monitor_prefix();
567  if ( c == NULL ) {
568  c = "";
569  }
570  bool changed = true;
571  while (changed) {
572  changed = false;
573  for (ChangeHandlerMultimap::const_iterator j = _change_handlers.begin(); !changed && (j != _change_handlers.end()); ++j) {
574  _ch_range = _change_handlers.equal_range((*j).first);
575  for (ChangeHandlerMultimap::iterator i = _ch_range.first; !changed && (i != _ch_range.second); ++i) {
576  if ( (*i).second == h ) {
577  _change_handlers.erase(i);
578  changed = true;
579  break;
580  }
581  }
582  if ( changed) break;
583  }
584  }
585 }
586 
587 
588 /** Find handlers for given path.
589  * @param path path to get handlers for
590  * @return list with config change handlers.
591  */
594 {
596  for (ChangeHandlerMultimap::const_iterator j = _change_handlers.begin(); j != _change_handlers.end(); ++j) {
597  if ( strstr(path, (*j).first) == path ) {
598  _ch_range = _change_handlers.equal_range((*j).first);
599  for (ChangeHandlerMultimap::const_iterator i = _ch_range.first; i != _ch_range.second; ++i) {
600  rv->push_back((*i).second);
601  }
602  }
603  }
604 
605  return rv;
606 }
607 
608 
609 /** Notify handlers for given path.
610  * @param path path to notify handlers for
611  * @param comment_changed true if the change is about a comment change,
612  * false otherwise
613  */
614 void
615 Configuration::notify_handlers(const char *path, bool comment_changed)
616 {
617  ChangeHandlerList *h = find_handlers(path);
618  Configuration::ValueIterator *value = get_value(path);
619  if (value->next()) {
620  for (ChangeHandlerList::const_iterator i = h->begin(); i != h->end(); ++i) {
621  if (comment_changed) {
622  (*i)->config_comment_changed(value);
623  } else {
624  (*i)->config_value_changed(value);
625  }
626  }
627  } else {
628  for (ChangeHandlerList::const_iterator i = h->begin(); i != h->end(); ++i) {
629  (*i)->config_value_erased(path);
630  }
631  }
632  delete value;
633  delete h;
634 }
635 
636 
637 } // end namespace fawkes
Fawkes library namespace.
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:593
ConfigTypeMismatchException(const char *path, const char *actual, const char *requested)
Constructor.
Definition: config.cpp:399
virtual void rem_change_handler(ConfigurationChangeHandler *h)
Remove a configuration change handler.
Definition: config.cpp:564
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:357
void notify_handlers(const char *path, bool comment_changed=false)
Notify handlers for given path.
Definition: config.cpp:615
CouldNotOpenConfigException(const char *format,...)
Constructor.
Definition: config.cpp:416
Iterator interface to iterate over config values.
Definition: config.h:72
virtual void add_change_handler(ConfigurationChangeHandler *h)
Add a configuration change handler.
Definition: config.cpp:547
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:383
std::list< ConfigurationChangeHandler * > ChangeHandlerList
List that contains pointers to ConfigurationChangeHandler.
Definition: config.h:283