tizen 2.4 release
[framework/convergence/service/service-plugin-client.git] / src / pluginConfig.cpp
1 /*
2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3 *
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /**
18  * @file pluginConfig.hpp
19  * @author
20  * @date
21  *
22  * @author
23  *
24  * @brief Plugin configuration parser done in C++.
25  */
26
27 #include <fstream>
28 #include <string>
29 #include <map>
30 #include <vector>
31 //
32 // // threads
33 #include <glib.h>
34 //
35 // // internal
36 #include <algorithm>
37
38 #include "pluginConfig.h"
39 #include "pluginConfig.hpp"
40
41 #define UNUSED(x) (void)(x)
42
43 ////////////////////////////////////////////////////////////////////////////////
44 // Trimming functions
45 ////////////////////////////////////////////////////////////////////////////////
46
47 /**
48  * @brief Namespace for all content related to plugin.
49  **/
50 namespace plugin
51 {
52
53     /**
54     * @brief Namespace for internal code related to configuration.
55     **/
56     namespace internal
57     {
58
59 // trimming functions taken from:
60 // https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
61
62         /**
63         * @brief trim from start, i.e. left side
64         *
65         * @param s ...
66         * @return :string&
67         **/
68         static inline std::string &ltrim(std::string &s)
69         {
70             s.erase(s.begin(), std::find_if(s.begin(), s.end(),
71                                             std::not1(std::ptr_fun<int, int>(std::isspace))));
72             return s;
73         }
74
75         /**
76         * @brief trim from end, i.e. right side
77         *
78         * @param s ...
79         * @return :string&
80         **/
81         static inline std::string &rtrim(std::string &s)
82         {
83             s.erase(std::find_if(s.rbegin(), s.rend(),
84                                  std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
85             return s;
86         }
87
88         /**
89         * @brief trim from both ends
90         *
91         * @param s ...
92         * @return :string&
93         **/
94         static inline std::string &trim(std::string &s)
95         {
96             return ltrim(rtrim(s));
97         }
98     } // end of namespace internal
99 } // endof plugin
100
101 ////////////////////////////////////////////////////////////////////////////////
102 //
103 ////////////////////////////////////////////////////////////////////////////////
104 /**
105  * @brief Namespace for all content related to plugin.
106  **/
107 namespace plugin
108 {
109     /**
110     * @brief Namespace for internal code related to configuration.
111     **/
112     namespace internal
113     {
114         /**
115         * @brief Typedef for configuration key-value map.
116         * @typedef MapStrStr
117         * Section data
118         * |---------------------------|----------------------------------------|
119         * | KEY : char*               | VALUE : char*                          |
120         * |---------------------------|----------------------------------------|
121         * |         KEY1              |    VAL1                                |
122         * |---------------------------|----------------------------------------|
123         * |         KEY2              |    VAL2                                |
124         * |---------------------------|----------------------------------------|
125         * |         KEY3              |    VAL3                                |
126         * |---------------------------|----------------------------------------|
127         */
128         typedef std::map<std::string, std::string> MapStrStr;
129
130         typedef std::pair<std::string, std::string> MapStrStrEntry;
131
132         /**
133         * @typedef ListSection
134         * Sections
135         * |------------------------------------------|
136         * | SECTION PTR : MapStrStr *                |
137         * |------------------------------------------|
138         * |         SP1                              |
139         * |------------------------------------------|
140         * |         SP2                              |
141         * |------------------------------------------|
142         * |         SN3                              |
143         * |------------------------------------------|
144         */
145         typedef std::vector<MapStrStr> ListSection;
146
147
148         /**
149         * @brief Typedef for configuration key-value map.
150         * @typedef plugin_internal_MapStrSection
151         * Sections
152         * |---------------------------|----------------------------------------|
153         * | SECTION NAME : char*      | SECTION PTR      : MapStrStr *         |
154         * |---------------------------|----------------------------------------|
155         * |         SN1               |    SP1                                 |
156         * |---------------------------|----------------------------------------|
157         * |         SN2               |    SP2                                 |
158         * |---------------------------|----------------------------------------|
159         * |         SN3               |    SP3                                 |
160         * |---------------------------|----------------------------------------|
161         */
162         typedef std::map<std::string, MapStrStr *> MapStrSection;
163
164         typedef std::pair<std::string, MapStrStr *> MapStrSectionEntry;
165
166     } // end of namespace internal
167 } // end of namespace plugin
168
169 ////////////////////////////////////////////////////////////////////////////////
170 // ConfigParserState class
171 ////////////////////////////////////////////////////////////////////////////////
172
173 /**
174  * @brief Namespace for all content related to plugin.
175  **/
176 namespace plugin
177 {
178     /**
179     * @brief Namespace for internal code related to configuration.
180     **/
181     namespace internal
182     {
183         /**
184         * @brief For storing state of the parser between running line-parsing functions.
185         **/
186         class ConfigParserState
187         {
188         public:
189             std::string current_section_name;
190             MapStrStr *current_section;
191         };
192     } // end of namespace internal
193 } // end of namespace plugin
194
195 /**
196  * @brief Namespace for all content related to plugin.
197  **/
198 namespace plugin
199 {
200     /**
201     * @brief Namespace for internal code related to configuration.
202     **/
203     namespace internal
204     {
205         ////////////////////////////////////////////////////////////////////////
206         /**
207         * @brief Internal backing storage for configuration.
208         *
209         * Getters and assignment operator are thread safe, constructors and destructor are not.
210         *
211         **/
212         class ConfigData
213         {
214         private:
215
216             GMutex *data_mutex;
217
218             /**
219             * @brief Filepath to the read configuration file.
220             **/
221             std::string filepath;
222
223             /**
224             * @brief Describes layout of the data in the file.
225             **/
226             PluginConfigType type;
227
228             /**
229             * @brief Variable that stores the all sections of configuration,
230             * meant to be accessed through the field 'configuration'.
231             **/
232             ListSection sections;
233
234             /**
235             * @brief Variable that stores the whole configuration.
236             **/
237             MapStrSection configuration;
238
239         public:
240             ConfigData()
241                 : data_mutex(new GMutex), filepath()
242             {
243                 g_mutex_init(data_mutex);
244             }
245
246             /**
247             * @brief Loads configuration from the specified file.
248             *
249             * @param filepath a path to the configuration file
250             * @param type expected type of configuration file, this value
251             * determines how the file is parsed
252             **/
253             ConfigData(const std::string &filepath, PluginConfigType type)
254                 : data_mutex(new GMutex), filepath(filepath), type(type)
255             {
256                 g_mutex_init(data_mutex);
257                 sections.reserve(128);
258                 std::ifstream in(filepath);
259                 ConfigParserState state;
260
261                 while (in.good())
262                 {
263                     std::string line;
264                     std::getline(in, line);
265
266                     if (line.length() == 0)
267                         continue;
268
269                     switch (type)
270                     {
271                         case PluginConfigType::CCT_INI:
272                             parseLineIni(state, line);
273                             break;
274                         case PluginConfigType::CCT_GIT:
275                             parseLineGit(state, line);
276                             break;
277                         case PluginConfigType::CCT_CSV_COMMA:
278                             parseLineCsv(state, line, ',');
279                             break;
280                         case PluginConfigType::CCT_CSV_TAB:
281                             parseLineCsv(state, line, '\t');
282                             break;
283                         case PluginConfigType::CCT_CSV_COLON:
284                             parseLineCsv(state, line, ':');
285                             break;
286                         case PluginConfigType::CCT_CSV_SEMICOLON:
287                             parseLineCsv(state, line, ';');
288                             break;
289                         default:
290                             break;
291                     }
292                 }
293
294                 if (in.is_open())
295                 {
296 #ifdef DEBUG
297                     std::cout << "* configuration file " << filepath << " was _loaded" << std::endl;
298 #endif
299                     in.close();
300                 }
301             }
302
303             /**
304             * @brief Copy constructor.
305             *
306             * @param source ...
307             **/
308             ConfigData(const plugin::internal::ConfigData &source)
309                 : data_mutex(new GMutex), filepath(), type(), sections(), configuration()
310             {
311                 g_mutex_init(data_mutex);
312                 g_mutex_lock(source.data_mutex);
313                 filepath = source.filepath;
314                 type = source.type;
315                 sections = source.sections;
316
317                 for (auto it = source.configuration.begin(); it != source.configuration.end(); ++it)
318                 {
319                     size_t index = 0;
320                     bool found = false;
321
322                     for (auto sit = source.sections.begin(); sit != source.sections.end(); ++sit, ++index)
323                         if (it->second == &(*sit))
324                         {
325                             found = true;
326                             break;
327                         }
328
329                     if (!found)
330                         continue;
331
332                     configuration[it->first] = &(sections.data()[index]);
333                 }
334
335                 g_mutex_unlock(source.data_mutex);
336             }
337
338             /**
339             * @brief Assignment operator.
340             *
341             * @param source ...
342             * @return :internal::ConfigData&
343             **/
344             plugin::internal::ConfigData &operator=(const plugin::internal::ConfigData &source)
345             {
346                 if (this == &source)
347                     return *this;
348
349                 if (this < &source)
350                 {
351                     g_mutex_lock(data_mutex);
352                     g_mutex_lock(source.data_mutex);
353                 }
354                 else
355                 {
356                     g_mutex_lock(source.data_mutex);
357                     g_mutex_lock(data_mutex);
358                 }
359
360                 filepath = source.filepath;
361                 type = source.type;
362                 sections = source.sections;
363                 configuration = MapStrSection();
364
365                 for (auto it = source.configuration.begin(); it != source.configuration.end(); ++it)
366                 {
367                     size_t index = 0;
368                     bool found = false;
369
370                     for (auto sit = source.sections.begin(); sit != source.sections.end(); ++sit, ++index)
371                         if (it->second == &(*sit))
372                         {
373                             found = true;
374                             break;
375                         }
376
377                     if (!found)
378                         continue;
379
380                     configuration[it->first] = &(sections.data()[index]);
381                 }
382
383                 if (this < &source)
384                 {
385                     g_mutex_unlock(source.data_mutex);
386                     g_mutex_unlock(data_mutex);
387                 }
388                 else
389                 {
390                     g_mutex_unlock(data_mutex);
391                     g_mutex_unlock(source.data_mutex);
392                 }
393
394                 return *this;
395             }
396
397             /**
398             * @brief Destructor.
399             *
400             **/
401             ~ConfigData()
402             {
403                 clear();
404                 g_mutex_clear(data_mutex);
405                 delete data_mutex;
406             }
407
408         private:
409             void parseLineIni(ConfigParserState &state, const std::string &line)
410             {
411                 if (line[0] == ';')
412                     return;
413
414                 if (line[0] == '[')
415                 {
416                     std::string section_name = line.substr(1, line.length() - 2);
417                     sections.push_back(MapStrStr());
418                     MapStrStr *section = &(sections[sections.size() - 1]);
419                     configuration[section_name] = section;
420                     state.current_section_name = section_name;
421                     state.current_section = section;
422                 }
423                 else
424                 {
425                     size_t pos = line.find('=');
426                     std::string key = line.substr(0, pos);
427                     std::string value = line.substr(pos + 1);
428                     state.current_section->insert(MapStrStrEntry(key, value));
429                 }
430             }
431
432             void parseLineGit(ConfigParserState &state, const std::string &line)
433             {
434                 if (line[0] == ';' || line[0] == '#')
435                     return;
436
437                 if (line[0] == '[')
438                 {
439                     std::string section_name = line.substr(1, line.length() - 2);
440                     sections.push_back(MapStrStr());
441                     MapStrStr *section = &(sections[sections.size() - 1]);
442                     configuration[section_name] = section;
443                     state.current_section_name = section_name;
444                     state.current_section = section;
445                 }
446                 else
447                 {
448                     size_t pos = line.find(" = ");
449                     std::string key = line.substr(0, pos);
450                     std::string value = line.substr(pos + 3);
451                     ltrim(key);
452                     state.current_section->insert(MapStrStrEntry(key, value));
453                 }
454             }
455
456             void parseLineCsv(ConfigParserState &state, const std::string &line, char separator)
457             {
458                 UNUSED(state);
459                 size_t pos1 = line.find_first_of(separator);
460                 size_t pos2 = line.find_last_of(separator);
461                 std::string section = line.substr(0, pos1);
462                 std::string key = line.substr(pos1 + 1, pos2 - pos1 - 1);
463                 std::string value = line.substr(pos2 + 1);
464                 auto it = configuration.find(section);
465
466                 if (it == configuration.end())
467                 {
468                     sections.push_back(MapStrStr());
469                     configuration[section] = &(sections[sections.size() - 1]);
470                     configuration[section]->insert(MapStrStrEntry(key, value));
471                 }
472                 else
473                     it->second->insert(MapStrStrEntry(key, value));
474             }
475
476             /**
477             * @brief Removes configuration from memory, rendering this configuration invalid.
478             *
479             * @return void
480             **/
481             void clear()
482             {
483                 filepath = std::string();
484                 //for(auto it = configuration.begin(); it != configuration.end(), ++it)
485                 //  delete *it;
486                 type = PluginConfigType::CCT_INVALID;
487                 configuration.clear();
488                 sections.clear();
489             }
490
491         public:
492             inline bool hasSection(const std::string &section) const
493             {
494                 g_mutex_lock(data_mutex);
495                 bool result = configuration.find(section) != configuration.end();
496                 g_mutex_unlock(data_mutex);
497                 return result;
498             }
499
500
501             /**
502             * @brief This method assumes that the given section exists. Use hasSection()
503             * first, or use hasSectionAndKey() instead.
504             *
505             * @param section name of the configuration section
506             * @param key name of the key within that section
507             * @return bool true if such key exists
508             **/
509             inline bool hasKey(const std::string &section, const std::string &key) const
510             {
511                 g_mutex_lock(data_mutex);
512                 bool result = configuration.at(section)->find(key) != configuration.at(section)->end();
513                 g_mutex_unlock(data_mutex);
514                 return result;
515             }
516
517             inline bool hasSectionAndKey(const std::string &section, const std::string &key) const
518             {
519                 g_mutex_lock(data_mutex);
520                 bool result = configuration.find(section) != configuration.end()
521                               && configuration.at(section)->find(key) != configuration.at(section)->end();
522                 g_mutex_unlock(data_mutex);
523                 return result;
524             }
525
526             inline const std::string &getFilepath() const
527             {
528                 g_mutex_lock(data_mutex);
529                 const std::string &result = filepath;
530                 g_mutex_unlock(data_mutex);
531                 return result;
532             }
533
534             inline PluginConfigType getType() const
535             {
536                 g_mutex_lock(data_mutex);
537                 PluginConfigType result = type;
538                 g_mutex_unlock(data_mutex);
539                 return result;
540             }
541
542             // not thread-safe
543             //inline const MapStrStr& get_section(const std::string& section) const;
544
545             inline const std::string &getEntry(const std::string &section, const std::string &key) const
546             {
547                 g_mutex_lock(data_mutex);
548                 const std::string &result = configuration.at(section)->at(key);
549                 g_mutex_unlock(data_mutex);
550                 return result;
551             }
552
553         public:
554             friend std::ostream &operator<<(std::ostream &out, const ConfigData &configuration)
555             {
556                 out << "sections: " << configuration.configuration.size() << std::endl;
557
558                 for (auto it = configuration.configuration.begin(); it != configuration.configuration.end(); ++it)
559                 {
560                     out << "'" << it->first << "'" << std::endl;
561
562 #ifdef __GNUC__
563 #include <features.h>
564 #if __GNUC_PREREQ(4,7)
565
566                     //      If  gcc_version >= 4.7
567                     if (it->second == nullptr)
568                     {
569 #else
570
571                     if (it->second == NULL)
572                     {
573 #endif
574 #else
575
576                     //    If not gcc
577                     if (it->second == NULL)
578                     {
579 #endif
580                         out << "  nullptr!" << std::endl;
581                     }
582                     else
583                     {
584                         out << "  entries: " << it->second->size() << std::endl;
585
586                         for (auto sit = it->second->begin(); sit != it->second->end(); ++sit)
587                             out << "  '" << sit->first << "' -> '" << sit->second << "'" << std::endl;
588                     }
589                 }
590
591                 return out;
592             }
593
594         }; // end of class ConfigData
595     } // end of namespace internal
596 } // end of namespace plugin
597
598 ////////////////////////////////////////////////////////////////////////////////
599 // C++ API implementation
600 ////////////////////////////////////////////////////////////////////////////////
601
602 /**
603  * @brief Namespace for all content related to plugin.
604  **/
605 namespace plugin
606 {
607     Config::Config()
608     {
609         _loaded = false;
610         _configuration = NULL;
611     }
612
613     Config::~Config()
614     {
615         if (_configuration != NULL)
616         {
617             delete _configuration;
618             _configuration = NULL;
619         }
620     }
621
622     std::string Config::_getRaw(const std::string &section, const std::string &key)
623     {
624         if (NULL == _configuration)
625         {
626             return std::string();
627         }
628
629         if (!_configuration->hasSectionAndKey(section, key))
630             return std::string();
631
632         return _configuration->getEntry(section, key);
633     }
634
635     const std::string *Config::_getRawPtr(const std::string &section, const std::string &key)
636     {
637         if (NULL == _configuration)
638         {
639             return NULL;
640         }
641
642         if (!_configuration->hasSectionAndKey(section, key))
643             return NULL;
644
645         return &(_configuration->getEntry(section, key));
646     }
647
648     bool Config::load(const std::string &filepath, PluginConfigType type)
649     {
650         if (NULL != _configuration)
651         {
652             delete _configuration;
653             _configuration = NULL;
654         }
655
656         if (filepath.length() > 0)
657         {
658             _configuration = new internal::ConfigData(filepath, type);
659             _loaded = true;
660             return true;
661         }
662
663         return false;
664     }
665
666     void Config::unload()
667     {
668         if (NULL != _configuration)
669         {
670             delete _configuration;
671             _configuration = NULL;
672             _loaded = false;
673         }
674     }
675
676     std::string Config::getString(const std::string &section, const std::string &key)
677     {
678         return _getRaw(section, key);
679     }
680
681     const std::string *Config::getStringPtr(const std::string &section, const std::string &key)
682     {
683         return _getRawPtr(section, key);
684     }
685
686     int Config::getInt(const std::string &section, const std::string &key)
687     {
688         return std::atoi(_getRaw(section, key).c_str());
689     }
690
691     double Config::getDouble(const std::string &section, const std::string &key)
692     {
693         return std::atof(_getRaw(section, key).c_str());
694     }
695
696     bool Config::isLoaded()
697     {
698         return _loaded;
699     }
700
701 } // end of namespace plugin
702
703 ////////////////////////////////////////////////////////////////////////////////
704 // C API implementation
705 ////////////////////////////////////////////////////////////////////////////////
706
707 #ifdef __cplusplus
708 extern "C" {
709 #endif
710
711     ConfigHandle plugin_config_create()
712     {
713         return reinterpret_cast<ConfigHandle>(new plugin::Config());
714     }
715
716     void plugin_config_delete(ConfigHandle config_handle)
717     {
718         delete reinterpret_cast<plugin::Config *>(config_handle);
719     }
720
721     void plugin_config_load(ConfigHandle config_handle, const char *filepath,
722                               PluginConfigType type)
723     {
724         if (config_handle)
725         {
726             plugin::Config *pConfig = reinterpret_cast<plugin::Config *>(config_handle);
727             pConfig->load(filepath, type);
728         }
729     }
730
731     void plugin_config_unload(ConfigHandle config_handle)
732     {
733         if (config_handle)
734         {
735             plugin::Config *pConfig =
736                 reinterpret_cast<plugin::Config *>(config_handle);
737             pConfig->unload();
738         }
739     }
740
741     const char *plugin_config_get_string(ConfigHandle config_handle,
742                                            const char *section, const char *key)
743     {
744         if (config_handle)
745         {
746             plugin::Config *pConfig = reinterpret_cast<plugin::Config *>(config_handle);
747
748             const std::string *val = pConfig->getStringPtr(section, key);
749             if (val)
750                 return val->c_str();
751         }
752
753         return "";
754     }
755
756     int plugin_config_get_int(ConfigHandle config_handle, const char *section, const char *key)
757     {
758         if (config_handle)
759         {
760             plugin::Config *pConfig = reinterpret_cast<plugin::Config *>(config_handle);
761
762             return pConfig->getInt(section, key);
763         }
764
765         return 0;
766     }
767
768     double plugin_config_get_double(ConfigHandle config_handle, const char *section, const char *key)
769     {
770         if (config_handle)
771         {
772             plugin::Config *pConfig = reinterpret_cast<plugin::Config *>(config_handle);
773
774             return pConfig->getDouble(section, key);
775         }
776
777         return 0;
778     }
779
780     bool plugin_config_is_loaded(ConfigHandle config_handle)
781     {
782         if (config_handle)
783         {
784             plugin::Config *pConfig = reinterpret_cast<plugin::Config *>(config_handle);
785
786             return pConfig->isLoaded();
787         }
788
789         return false;
790     }
791
792 #ifdef __cplusplus
793 }
794 #endif