Updated license
[anna.git] / source / core / util / Configuration.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #include <stdio.h>
38 #include <ctype.h>
39
40 #include <algorithm>
41
42 #include <anna/core/functions.hpp>
43 #include <anna/core/util/Configuration.hpp>
44 #include <anna/core/RuntimeException.hpp>
45 #include <anna/core/tracing/Logger.hpp>
46 #include <anna/core/tracing/TraceMethod.hpp>
47
48 using namespace std;
49 using namespace anna;
50
51 const char* Configuration::defaultSection = "@global@";
52
53 void Configuration::load(const char* configFile)
54 throw(RuntimeException) {
55   LOGMETHOD(TraceMethod tm("Configuration", "load", ANNA_FILE_LOCATION));
56   char buffer [2048];
57   FILE* file;
58   char* aux;
59   string currentSection(defaultSection);
60   int nline(1);
61   removeAll();
62
63   if((file = fopen(configFile, "r")) == NULL)
64     throw RuntimeException(configFile, errno, ANNA_FILE_LOCATION);
65
66   LOGDEBUG(Logger::write(Logger::Debug, "Configuration file", configFile, ANNA_FILE_LOCATION));
67
68   try {
69     while(fgets(buffer, sizeof(buffer) - 1, file) != NULL) {
70       if((aux = strchr(buffer, '#')) != NULL)
71         * aux = 0;
72
73       if(anna_strlen(aux = strip(buffer)) <= 0)
74         continue;
75
76       if(processSection(nline, aux, currentSection) == true)
77         continue;
78
79       processVariable(nline, aux, currentSection);
80       nline ++;
81     }
82   } catch(RuntimeException& ex) {
83     fclose(file);
84     throw;
85   }
86
87   fclose(file);
88 }
89
90 bool Configuration::exists(const char* sectionName, const char* variableName) const
91 throw() {
92   const VariableEx* var = find(string(sectionName), variableName);
93   return (var == NULL) ? false : !var->isNull();
94 }
95
96 void Configuration::setDefaultValue(const char* sectionName, const char* variableName, const char* defaultValue)
97 throw(RuntimeException) {
98   string section(sectionName);
99   VariableEx* var = find(section, variableName);
100
101   if(var == NULL)
102     var = createVariable(section, variableName);
103
104   var->setDefaultValue(defaultValue);
105   LOGINFORMATION(
106     string msg(sectionName);
107     msg += "::";
108     msg += variableName;
109     msg += " (default)";
110     msg += " | Default value: ";
111     msg += defaultValue;
112     Logger::write(Logger::Information, msg, ANNA_FILE_LOCATION);
113   )
114 }
115
116 const char* Configuration::getValue(const char* sectionName, const char* variableName, const bool strict) const
117 throw(RuntimeException) {
118   const VariableEx* variable = find(string(sectionName), variableName);
119   const char* result(NULL);
120
121   if(variable == NULL) {
122     string msg("Variable ");
123     msg += sectionName;
124     msg += "::";
125     msg += variableName;
126     msg += " is not defined";
127     throw RuntimeException(msg, ANNA_FILE_LOCATION);
128   }
129
130   if(variable->isNull() == false)
131     result = variable->getStringValue();
132   else
133     result = (strict == false) ? variable->getDefaultValue() : NULL;
134
135   return result;
136 }
137
138 int Configuration::getIntegerValue(const char* sectionName, const char* variableName, const bool strict) const
139 throw(RuntimeException) {
140   return atoi(getValue(sectionName, variableName, strict));
141 }
142
143 char* Configuration::strip(char* buffer) {
144   char* result;
145   result = buffer + (anna_strlen(buffer) - 1);
146
147   while(result >= buffer && isspace(*result))
148     result --;
149
150   result[1] = 0;
151
152   for(result = buffer; *result  && isspace(*result); result ++);
153
154   return result;
155 }
156
157 bool Configuration::processSection(const int nline, char* buffer, string& currentSection) {
158   bool result(false);
159   char* end;
160   char* section;
161
162   if(*buffer == '[') {
163     if((end = strchr(buffer, ']')) != NULL) {
164       *end = 0; // JASC, antes NULL
165
166       if(anna_strlen(section = strip(++ buffer)) > 0)
167         currentSection = section;
168       else {
169         string msg("Invalid section name at line: ");
170         msg += functions::asString(nline);
171         throw RuntimeException(msg, ANNA_FILE_LOCATION);
172       }
173
174       result = true;
175     }
176   }
177
178   return result;
179 }
180
181 void Configuration::processVariable(const int nline, char* buffer, const string& currentSection)
182 throw(RuntimeException) {
183   char* variableName;
184   char* value;
185   char* aux;
186   VariableEx* variable;
187
188   if((aux = anna_strchr(buffer , '=')) == NULL)
189     return;
190
191   *aux = 0;
192
193   if(anna_strlen(variableName = strip(buffer)) <= 0) {
194     string msg("Invalid section name at line: ");
195     msg += functions::asString(nline);
196     throw RuntimeException(msg, ANNA_FILE_LOCATION);
197   }
198
199   if(anna_strlen(value = strip(aux + 1)) <= 0)
200     return;
201
202   if((variable = find(currentSection, variableName)) != NULL) {
203     if(variable->isNull() == false) {
204       string msg("Duplicated variable | Section: ");
205       msg += currentSection;
206       msg += " | Variable: ";
207       msg += variableName;
208       msg += " | Line: ";
209       msg += functions::asString(nline);
210       throw RuntimeException(msg, ANNA_FILE_LOCATION);
211     }
212   } else
213     variable = createVariable(currentSection, variableName);
214
215   variable->setValue(value);
216
217   if(Logger::isActive(Logger::Information)) {
218     string msg(currentSection);
219     msg += "::";
220     msg += variableName;
221     msg += " | Value: ";
222     msg += value;
223     Logger::write(Logger::Information, variable->asString(), ANNA_FILE_LOCATION);
224   }
225 }
226
227 Configuration::VariableEx* Configuration::createVariable(const string& section, const char* variableName)
228 throw() {
229   map <string, VariableEx::Vector*>::iterator isection(a_sections.find(section));
230   VariableEx* result;
231   VariableEx::Vector* variables;
232   result = new VariableEx(variableName);
233
234   if(isection == a_sections.end()) {
235     variables = new VariableEx::Vector;
236     a_sections [section] = variables;
237   } else
238     variables = isection->second;
239
240   variables->push_back(result);
241   return result;
242 }
243
244 Configuration::VariableEx* Configuration::find(const string& section, const char* variableName)
245 throw() {
246   VariableEx* result(NULL);
247   map <string, VariableEx::Vector*>::iterator isection(a_sections.find(section));
248
249   if(isection == a_sections.end())
250     return NULL;
251
252   VariableEx::Vector* varVector(isection->second);
253
254   for(VariableEx::Vector::iterator vv = varVector->begin(), maxvv = varVector->end(); vv != maxvv; vv ++) {
255     if(!strcmp((*vv)->getName(), variableName)) {
256       result = *vv;
257       break;
258     }
259   }
260
261   return result;
262 }
263
264 void Configuration::removeAll()
265 throw() {
266   map <string, VariableEx::Vector*>::iterator ii(a_sections.begin());
267   map <string, VariableEx::Vector*>::iterator end(a_sections.end());
268   VariableEx::Vector::iterator vv, maxvv;
269   VariableEx* variable;
270
271   for(; ii != end; ii ++) {
272     for(vv = ii->second->begin(), maxvv = ii->second->end(); vv != maxvv; vv ++) {
273       variable = *vv;
274       delete variable;
275     }
276
277     delete ii->second;
278   }
279
280   a_sections.clear();
281 }
282