332ecfbe11135f0839005fa879c767628fddc3bd
[libi2ncommon] / configlib / i2n_global_config.cpp
1 /** @file
2  *
3  * @author Reinhard Pfau \<Reinhard.Pfau@intra2net.com\>
4  * 
5  * @copyright &copy; Copyright 2008 by Intra2net AG
6  * @license commercial
7  * @contact info@intra2net.com
8  */
9
10 #include "i2n_global_config.hpp"
11
12
13 #include <set>
14 #include <map>
15
16 #include <filefunc.hxx>
17 #include <stringfunc.hxx>
18 #include <i2n_configdata.hpp>
19 #include <i2n_configfile.hpp>
20 #include <logfunc.hpp>
21 #include <tracefunc.hpp>
22
23 //#define NOISEDEBUG
24 #undef NOISEDEBUG
25
26 #ifdef NOISEDEBUG
27 #include <iostream>
28 #include <iomanip>
29 #define DOUT(msg) std::cout << msg << std::endl
30 #else
31 #define DOUT(msg) do {} while (0)
32 #endif
33
34
35 namespace I2n
36 {
37
38 namespace Config
39 {
40
41
42 namespace Detail
43 {
44
45
46 } // eo namespace Detail
47
48
49
50 namespace
51 {
52
53
54 Logger::PartLogger& module_logger()
55 {
56     static Logger::PartLogger _logger(HERE);
57     return _logger;
58 } // eo module_logger()
59
60
61
62 std::string config_file_name;
63
64 ConfigData config_data;
65
66
67 /*
68 **
69 */
70
71
72 struct HideOut
73 : public Detail::Thing
74 {
75     HideOut(
76         const std::string& section,
77         const std::string& key,
78         const Detail::FeedFunction& feed,
79         const Detail::FeedFunction2& feed2);
80
81     virtual ~HideOut();
82
83     Detail::FeedFunction m_feed;
84     Detail::FeedFunction2 m_feed2;
85     std::string  m_section;
86     std::string  m_key;
87 }; // eo struct HideOut
88
89 typedef boost::shared_ptr< HideOut > HideOutPtr;
90
91
92 class HideOutMap
93 : public GroupedKeyValueData<
94     std::string,
95     std::string,
96     HideOut*
97 >
98 {
99     public:
100         HideOutMap();
101         virtual ~HideOutMap();
102
103     public:
104
105         static int Instances;
106 }; // eo class HideOutMap
107
108 int HideOutMap::Instances= 0;
109
110 HideOutMap::HideOutMap()
111 {
112     DOUT("HideOutMap::HideOutMap()");
113     ++Instances;
114 } // eo HideOutMap::HideOutMap()
115
116
117 HideOutMap::~HideOutMap()
118 {
119     DOUT("HideOutMap::~HideOutMap()");
120     --Instances;
121 } // eo HideOutMap::~HideOutMap()
122
123
124 HideOutMap& hideout_map()
125 {
126     static HideOutMap the_hideout_map;
127     DOUT("hideout_map()");
128     return the_hideout_map;
129 } // eo hideout_map()
130
131
132 // implementation of HideOut:
133
134
135 HideOut::HideOut(
136     const std::string& section,
137     const std::string& key,
138     const Detail::FeedFunction& feed,
139     const Detail::FeedFunction2& feed2)
140 : m_section(section)
141 , m_key(key)
142 , m_feed(feed)
143 , m_feed2(feed2)
144 {
145     hideout_map()[section][key]+= this;
146 } // eo HideOut::HideOut(const FeedFunction&)
147
148
149
150 HideOut::~HideOut()
151 {
152     if (HideOutMap::Instances)
153     {
154         hideout_map()[m_section].removeValue(m_key,this);
155     }
156 } // eo HideOut::~HideOut()
157
158
159
160 /*
161 **
162 */
163
164
165 /**
166  * @brief distributes (new) values to the active config variables.
167  * @param old_config_data reference to the old config data (for determining the deltas).
168  * @return @a true if new values could be converted without an error.
169  */
170 bool feedEm( ConfigData& old_config_data )
171 {
172     SCOPETRACKER();
173     bool result= true;
174     HideOutMap::KeyListType section_list;
175     hideout_map().getKeys(section_list);
176     for(HideOutMap::KeyListType::const_iterator section_it = section_list.begin();
177         section_it != section_list.end();
178         ++section_it)
179     {
180         std::string section( *section_it );
181         DOUT("process section \"" << section << "\"");
182         HideOutMap::GroupDataType::KeyListType key_list;
183         hideout_map()[section].getKeys(key_list);
184         for(HideOutMap::GroupDataType::KeyListType::const_iterator key_it= key_list.begin();
185             key_it != key_list.end();
186             ++key_it)
187         {
188             std::string key( *key_it );
189             DOUT("process key \"" << key << "\"");
190             bool has_new_data= config_data.hasKey(section, key);
191             bool has_old_data= old_config_data.hasKey(section, key);
192             if (not has_new_data and not has_old_data)
193             {
194                 // we don't have data and we didn't had data... well, nothing to do:
195                 DOUT("no data");
196                 continue;
197             }
198             if (has_new_data)
199             {
200                 DOUT("has (new) data");
201                 StringList values;
202                 config_data[section].getValues(key, values);
203                 std::string value( config_data[section][key] );
204                 if (has_old_data)
205                 {
206                     StringList old_values;
207                     old_config_data[section].getValues(key, old_values);
208                     if (values == old_values)
209                     {
210                         // value didn't change, so we can skip this case.
211                         DOUT("data is same as before");
212                         continue;
213                     }
214                 }
215                 // at this point we need to publish (new or changed) value:
216                 HideOutMap::GroupDataType::ValueListType func_list;
217                 hideout_map()[section].getValues(key, func_list);
218                 for(HideOutMap::GroupDataType::ValueListType::iterator func_it= func_list.begin();
219                     func_it != func_list.end();
220                     ++func_it)
221                 {
222                     DOUT("  feed iteration");
223                     if (not (*func_it)) //paranoia
224                     {
225                         continue;
226                     }
227                     if ( (*func_it)->m_feed)
228                     {
229                         if ( not (*func_it)->m_feed( value ) )
230                         {
231                             module_logger().error() << "unable to publish [" <<section << "]"
232                                 << " \"" << key << "\"  : \"" << value << "\""
233                             ;
234                             result= false;
235                         }
236                         continue;
237                     }
238                     if ( (*func_it)->m_feed2)
239                     {
240                         if ( not (*func_it)->m_feed2( values ) )
241                         {
242                             module_logger().error() << "unable to publish [" <<section << "]"
243                                 << " \"" << key << "\"  : ...\"" << value << "\""
244                             ;
245                             result= false;
246                         }
247                         continue;
248                     }
249                     module_logger().error() << "no feeder";
250                 }
251                 continue;
252             }
253             // at this point we don't have (new) data, but we had old data...
254             {
255                 DOUT("had old data");
256                 StringList values;
257
258                 // at this point we need to publish (vanished) value:
259                 HideOutMap::GroupDataType::ValueListType func_list;
260                 hideout_map()[section].getValues(key, func_list);
261                 for(HideOutMap::GroupDataType::ValueListType::iterator func_it= func_list.begin();
262                     func_it != func_list.end();
263                     ++func_it)
264                 {
265                     DOUT("  feed iteration");
266                     if (not (*func_it)) //paranoia
267                     {
268                         continue;
269                     }
270                     if ( (*func_it)->m_feed)
271                     {
272                         //TODO reset the data (to initial value) somehow?
273                         // for now; we just keep the last value...
274                         continue;
275                     }
276                     if ( (*func_it)->m_feed2)
277                     {
278                         if ( not (*func_it)->m_feed2( values ) )
279                         {
280                             module_logger().error() << "unable to publish [" <<section << "]"
281                                 << " \"" << key << "\"  : <empty> "
282                             ;
283                             result= false;
284                         }
285                         continue;
286                     }
287                     module_logger().error() << "no feeder";
288                 }
289                 continue;
290             }
291         }
292     }
293     return result;
294 } // eo feedEm(ConfigData&,ConfigData&)
295
296
297 /*
298 **
299 */
300
301
302 } // eo namespace <anonymous>
303
304
305 /************************************************************************\
306 \************************************************************************/
307
308
309 namespace Detail
310 {
311
312
313 FlowerShop seymour(
314     const std::string& section,
315     const std::string& key,
316     const FeedFunction& feed)
317 {
318     DOUT("seymor(\"" << section << "\", \"" << key << "\",FeedFunction)");
319     FlowerShop result( new HideOut( section, key, feed, 0 ) );
320     if (not config_data.empty())
321     {
322         if (config_data.hasKey(section, key))
323         {
324             feed( config_data[section][key] );
325         }
326     }
327     return result;
328 } // eo seymour(const std::string&,const std::string&,const FeedFunction&)
329
330
331 FlowerShop seymour(
332     const std::string& section,
333     const std::string& key,
334     const FeedFunction2& feed)
335 {
336     DOUT("seymor(\"" << section << "\", \"" << key << "\",FeedFunction2)");
337     FlowerShop result( new HideOut( section, key, 0, feed ) );
338     if (not config_data.empty())
339     {
340         if (config_data.hasKey(section, key))
341         {
342             StringList values;
343             config_data[section].getValues(key, values);
344             feed( values );
345         }
346     }
347     return result;
348 } // eo seymour(const std::string&,const std::string&,const FeedFunction2&)
349
350
351 FlowerShop seymour(FlowerShop shop, const FeedFunction& feed)
352 {
353     if (!shop)
354     {
355         // no shop; no coordinates; no useful stuff to do...
356         return shop;
357     }
358     HideOutPtr ptr = boost::shared_dynamic_cast< HideOut >(shop);
359     if (!ptr)
360     {
361         // again, no coordinates...
362         return ptr;
363     }
364     FlowerShop result( new HideOut( ptr->m_section, ptr->m_key, feed, 0 ) );
365     if (not config_data.empty())
366     {
367         if (config_data.hasKey(ptr->m_section, ptr->m_key))
368         {
369             feed( config_data[ptr->m_section][ptr->m_key] );
370         }
371     }
372     return result;
373 } // eo seymour(FlowerShop,const FeedFunction&)
374
375
376 FlowerShop seymour(FlowerShop shop, const FeedFunction2& feed)
377 {
378     if (!shop)
379     {
380         // no shop; no coordinates; no useful stuff to do...
381         return shop;
382     }
383     HideOutPtr ptr = boost::shared_dynamic_cast< HideOut >(shop);
384     if (!ptr)
385     {
386         // again, no coordinates...
387         return ptr;
388     }
389     FlowerShop result( new HideOut( ptr->m_section, ptr->m_key, 0, feed ) );
390     if (not config_data.empty())
391     {
392         if (config_data.hasKey(ptr->m_section, ptr->m_key))
393         {
394             StringList values;
395             config_data[ptr->m_section].getValues(ptr->m_key, values);
396             feed( values );
397         }
398     }
399     return result;
400 } // eo seymour(FlowerShop,const FeedFunction&)
401
402
403 } // eo namespace Detail
404
405
406
407 /**
408  * @brief reloads the configuration from the last (known) config file.
409  * @return @a true if config was succesfully reloaded.
410  */
411 bool reload()
412 {
413     ConfigData old_config_data;
414     ConfigData new_config_data;
415     if (config_file_name.empty())
416     {
417         return false;
418     }
419     bool loaded= load_ini_config_file(config_file_name, new_config_data);
420     bool publish_successful= true;
421     if (loaded)
422     {
423         module_logger().debug() << "new_config_data.empty(): " << new_config_data.empty();
424         // save old data
425         old_config_data.swap( config_data );
426         // and install the new data
427         new_config_data.swap( config_data );
428
429         // react on the config: publish the values:
430         publish_successful = feedEm( old_config_data );
431     }
432     return loaded and publish_successful;
433 } // reload()
434
435
436
437
438 /**
439  * @brief sets the path of the config file and loads it.
440  * @param path the path to the config file.
441  * @return @a true iff the config could be loaded and all values pushed into their config vars.
442  */
443 bool set_config_file(const std::string& path)
444 {
445     if (not config_file_name.empty())
446     {
447         // WTF?!...
448         // a new config file.. well... why not?
449     }
450     Stat stat(path);
451     if (not stat or not stat.is_regular_file())
452     {
453         return false;
454     }
455     config_file_name= path;
456     return reload();
457 } // eo set_config_file(const std::string&)
458
459
460 /**
461  * @brief returns the current config file path.
462  * @return the current path.
463  */
464 std::string get_config_file()
465 {
466     return config_file_name;
467 } // eo get_config_file()
468
469
470 } // eo namespace config
471
472 } // eo namespace I2n