Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/objecthierarchy/src/orxonox/core/ConfigValueContainer.cc @ 449

Last change on this file since 449 was 449, checked in by landauf, 16 years ago

improved config-file parsing - more error detection (and correction of course)

File size: 24.0 KB
Line 
1#include <fstream>
2#include <string>
3#include "ConfigValueContainer.h"
4
5//#define CONFIGFILEPATH "O:\\oh\\bin\\orxonox.ini"
6#define CONFIGFILEPATH "orxonox.ini"
7
8namespace orxonox
9{
10    std::list<std::string>* ConfigValueContainer::configFileLines_s = 0; // Set the static member variable configFileLines_s to zero
11    bool ConfigValueContainer::readConfigFile_s = false;                 // Set the static member variable readConfigFile_s to false
12
13    /**
14        @brief Constructor: Converts the default-value to a string, checks the config-file for a changed value, sets this->value_int_.
15        @param classname The name of the class the variable belongs to
16        @param varname The name of the variable
17        @param defvalue The default-value
18    */
19    ConfigValueContainer::ConfigValueContainer(const std::string& classname, const std::string& varname, int defvalue)
20    {
21        // Try to convert the default-value from int to string
22        std::ostringstream ostream;
23        if (ostream << defvalue)
24            this->defvalue_ = ostream.str();
25        else
26            this->defvalue_ = "0";
27
28        // Set the default values, then get the value-string
29        this->setDefaultValues(classname, varname);
30        std::string valueString = this->getValueString();
31
32        // Try to convert the value-string to int
33        std::istringstream istream(valueString);
34        if (!(istream >> this->value_int_))
35        {
36            // The conversion failed - use the default value and restore the entry in the config-file
37            this->value_int_ = defvalue;
38            (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
39            ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
40        }
41    }
42
43    /**
44        @brief Constructor: Converts the default-value to a string, checks the config-file for a changed value, sets this->value_double_.
45        @param classname The name of the class the variable belongs to
46        @param varname The name of the variable
47        @param defvalue The default-value
48    */
49    ConfigValueContainer::ConfigValueContainer(const std::string& classname, const std::string& varname, double defvalue)
50    {
51        // Try to convert the default-value from double to string
52        std::ostringstream ostream;
53        if (ostream << defvalue)
54            this->defvalue_ = ostream.str();
55        else
56            this->defvalue_ = "0.000000";
57
58        // Set the default values, then get the value-string
59        this->setDefaultValues(classname, varname);
60        std::string valueString = this->getValueString();
61
62        // Try to convert the value-string to double
63        std::istringstream istream(valueString);
64        if (!(istream >> this->value_double_))
65        {
66            // The conversion failed - use the default value and restore the entry in the config-file
67            this->value_double_ = defvalue;
68            (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
69            ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
70        }
71    }
72
73    /**
74        @brief Constructor: Converts the default-value to a string, checks the config-file for a changed value, sets this->value_bool_.
75        @param classname The name of the class the variable belongs to
76        @param varname The name of the variable
77        @param defvalue The default-value
78    */
79    ConfigValueContainer::ConfigValueContainer(const std::string& classname, const std::string& varname, bool defvalue)
80    {
81        // Convert the default-value from bool to string
82        if (defvalue)
83            this->defvalue_ = "true";
84        else
85            this->defvalue_ = "false";
86
87        // Set the default values, then get the value-string
88        this->setDefaultValues(classname, varname);
89        std::string valueString = this->getValueString();
90
91        // Try to parse the value-string - is it a word?
92        if (valueString.find("true") < valueString.size() || valueString.find("yes") < valueString.size())
93            this->value_bool_ = true;
94        else if (valueString.find("false") < valueString.size() || valueString.find("no") < valueString.size())
95            this->value_bool_ = false;
96        else
97        {
98            // Its not a known word - is it a number?
99            std::istringstream istream(valueString);
100            if (!(istream >> this->value_bool_))
101            {
102                // The conversion failed - use the default value and restore the entry in the config-file
103                this->value_bool_ = defvalue;
104                (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
105                ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
106            }
107        }
108    }
109
110    /**
111        @brief Constructor: Converts the default-value to a string, checks the config-file for a changed value, sets this->value_string_.
112        @param classname The name of the class the variable belongs to
113        @param varname The name of the variable
114        @param defvalue The default-value
115    */
116    ConfigValueContainer::ConfigValueContainer(const std::string& classname, const std::string& varname, const char* defvalue)
117    {
118        // Convert the string to a "config-file-string" with quotes
119        this->defvalue_ = "\"" + std::string(defvalue) + "\"";
120
121        // Set the default-values, then get the value-string
122        this->setDefaultValues(classname, varname);
123        std::string valueString = this->getValueString(false);
124
125        // Strip the quotes
126        unsigned int pos1 = valueString.find("\"") + 1;
127        unsigned int pos2 = valueString.find("\"", pos1);
128
129        // Check if the entry was correctly quoted
130        if (pos1 < valueString.length() && pos2 < valueString.length() && !(valueString.find("\"", pos2 + 1) < valueString.length()))
131        {
132            // It was - get the string between the quotes
133            valueString = valueString.substr(pos1, pos2 - pos1);
134            this->value_string_ = valueString;
135        }
136        else
137        {
138            // It wasn't - use the default-value and restore the entry in the config-file.
139            this->value_string_ = defvalue;
140            (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
141            ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
142        }
143    }
144
145    /**
146        @brief Constructor: Converts the default-value to a string, checks the config-file for a changed value, sets this->value_vector3_.
147        @param classname The name of the class the variable belongs to
148        @param varname The name of the variable
149        @param defvalue The default-value
150    */
151    ConfigValueContainer::ConfigValueContainer(const std::string& classname, const std::string& varname, Ogre::Vector3 defvalue)
152    {
153        // Try to convert the default-value from Vector3 to string
154        std::ostringstream ostream;
155        if (ostream << "(" << defvalue.x << "," << defvalue.y << "," << defvalue.z << ")")
156            this->defvalue_ = ostream.str();
157        else
158            this->defvalue_ = "(0,0,0)";
159
160        // Set the default values, then get the value-string
161        this->setDefaultValues(classname, varname);
162        std::string valueString = this->getValueString();
163
164        // Strip the value-string
165        bool bEntryIsCorrupt = false;
166        valueString = this->getStrippedLine(valueString);
167        unsigned int pos1, pos2, pos3;
168        pos1 = valueString.find("(");
169        if (pos1 == 0)
170            valueString.erase(pos1, 1);
171        else
172            bEntryIsCorrupt = true;
173
174        pos2 = valueString.find(")");
175        if (pos2 == valueString.length() - 1)
176            valueString.erase(pos2, 1);
177        else
178            bEntryIsCorrupt = true;
179
180        int count = 0;
181        while ((pos3 = valueString.find(",")) < valueString.length())
182        {
183            count++;
184            valueString.replace(pos3, 1, " ");
185            if (pos3 < pos1)
186                bEntryIsCorrupt = true;
187        }
188
189        if (count != 2)
190            bEntryIsCorrupt = true;
191
192        // Try to convert the stripped value-string to Vector3
193        if (!bEntryIsCorrupt)
194        {
195            std::istringstream istream(valueString);
196            if (!(istream >> this->value_vector3_.x))
197            {
198                // The conversion failed - use the default value and restore the entry in the config-file
199                this->value_vector3_.x = defvalue.x;
200                (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
201                ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
202            }
203            if (!(istream >> this->value_vector3_.y))
204            {
205                // The conversion failed - use the default value and restore the entry in the config-file
206                this->value_vector3_.y = defvalue.y;
207                (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
208                ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
209            }
210            if (!(istream >> this->value_vector3_.z))
211            {
212                // The conversion failed - use the default value and restore the entry in the config-file
213                this->value_vector3_.z = defvalue.z;
214                (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
215                ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
216            }
217        }
218        else
219        {
220            // The conversion failed - use the default value and restore the entry in the config-file
221            this->value_vector3_ = defvalue;
222            (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
223            ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
224        }
225    }
226
227    /**
228        @brief Constructor: Converts the default-value to a string, checks the config-file for a changed value, sets this->value_colourvalue_.
229        @param classname The name of the class the variable belongs to
230        @param varname The name of the variable
231        @param defvalue The default-value
232    */
233    ConfigValueContainer::ConfigValueContainer(const std::string& classname, const std::string& varname, Ogre::ColourValue defvalue)
234    {
235        // Try to convert the default-value from ColourValue to string
236        std::ostringstream ostream;
237        if (ostream << "(" << defvalue.r << "," << defvalue.g << "," << defvalue.b << "," << defvalue.a << ")")
238            this->defvalue_ = ostream.str();
239        else
240            this->defvalue_ = "(0,0,0,0)";
241
242        // Set the default values, then get the value-string
243        this->setDefaultValues(classname, varname);
244        std::string valueString = this->getValueString();
245
246        // Strip the value-string
247        bool bEntryIsCorrupt = false;
248        valueString = this->getStrippedLine(valueString);
249        unsigned int pos1, pos2, pos3;
250        pos1 = valueString.find("(");
251        if (pos1 == 0)
252            valueString.erase(pos1, 1);
253        else
254            bEntryIsCorrupt = true;
255
256        pos2 = valueString.find(")");
257        if (pos2 == valueString.length() - 1)
258            valueString.erase(pos2, 1);
259        else
260            bEntryIsCorrupt = true;
261
262        int count = 0;
263        while ((pos3 = valueString.find(",")) < valueString.length())
264        {
265            count++;
266            valueString.replace(pos3, 1, " ");
267            if (pos3 < pos1)
268                bEntryIsCorrupt = true;
269        }
270
271        if (count != 3)
272            bEntryIsCorrupt = true;
273
274        // Try to convert the stripped value-string to Vector3
275        if (!bEntryIsCorrupt)
276        {
277            std::istringstream istream(valueString);
278            if (!(istream >> this->value_colourvalue_.r))
279            {
280                // The conversion failed - use the default value and restore the entry in the config-file
281                this->value_colourvalue_.r = defvalue.r;
282                (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
283                ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
284            }
285            if (!(istream >> this->value_colourvalue_.g))
286            {
287                // The conversion failed - use the default value and restore the entry in the config-file
288                this->value_colourvalue_.g = defvalue.g;
289                (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
290                ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
291            }
292            if (!(istream >> this->value_colourvalue_.b))
293            {
294                // The conversion failed - use the default value and restore the entry in the config-file
295                this->value_colourvalue_.b = defvalue.b;
296                (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
297                ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
298            }
299            if (!(istream >> this->value_colourvalue_.a))
300            {
301                // The conversion failed - use the default value and restore the entry in the config-file
302                this->value_colourvalue_.a = defvalue.a;
303                (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
304                ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
305            }
306        }
307        else
308        {
309            // The conversion failed - use the default value and restore the entry in the config-file
310            this->value_colourvalue_ = defvalue;
311            (*this->configFileLine_) = this->varname_ + "=" + this->defvalue_;
312            ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
313        }
314    }
315
316    /**
317        @brief Sets the default values of the container and searches the coresponding entry in the config-file.
318        @param classname The name of the class the variable belongs to
319        @param varname The name of the variable
320    */
321    void ConfigValueContainer::setDefaultValues(const std::string& classname, const std::string& varname)
322    {
323        // Set the class and variable names
324        this->classname_ = classname;
325        this->varname_ = varname;
326
327        // Search the entry in the config-file
328        this->searchConfigFileLine();
329
330        // Set the values of all types to zero
331        this->value_int_ = 0;
332        this->value_double_ = 0.000000;
333        this->value_bool_ = false;
334        this->value_string_ = "";
335        this->value_vector3_ = Ogre::Vector3(0, 0, 0);
336        this->value_colourvalue_ = Ogre::ColourValue(0, 0, 0, 0);
337    }
338
339    /**
340        @brief Searches the corresponding entry in the config-file and creates it, if there is no entry.
341    */
342    void ConfigValueContainer::searchConfigFileLine()
343    {
344        // Read the file if needed
345        if (!ConfigValueContainer::readConfigFile_s)
346            ConfigValueContainer::readConfigFile(CONFIGFILEPATH);
347
348        // Just in case something goes wrong
349        this->configFileLine_ = 0;
350
351        // The string of the section we're searching
352        std::string section = "";
353        section.append("[");
354        section.append(this->classname_);
355        section.append("]");
356
357        // Iterate through all config-file-lines
358        bool success = false;
359        std::list<std::string>::iterator it1;
360        for(it1 = ConfigValueContainer::configFileLines_s->begin(); it1 != ConfigValueContainer::configFileLines_s->end(); ++it1)
361        {
362            // Don't try to parse comments
363            if (this->isComment(*it1))
364                continue;
365
366            if ((*it1).find(section) < (*it1).length())
367            {
368                // We found the right section
369                bool bLineIsEmpty = false;
370                std::list<std::string>::iterator positionToPutNewLineAt;
371
372                // Iterate through all lines in the section
373                std::list<std::string>::iterator it2;
374                for(it2 = ++it1; it2 != ConfigValueContainer::configFileLines_s->end(); ++it2)
375                {
376                    // Don't try to parse comments
377                    if (this->isComment(*it2))
378                        continue;
379
380                    // This if-else block is used to write a new line right after the last line of the
381                    // section but in front of the following empty lines before the next section.
382                    // (So this helps to keep a nice formatting with empty-lines between sections in the config-file)
383                    if (this->isEmpty(*it2))
384                    {
385                        if (!bLineIsEmpty)
386                        {
387                            bLineIsEmpty = true;
388                            positionToPutNewLineAt = it2;
389                        }
390                    }
391                    else
392                    {
393                        if (!bLineIsEmpty)
394                            positionToPutNewLineAt = it2;
395
396                        bLineIsEmpty = false;
397                    }
398
399                    // Look out for the beginning of the next section
400                    unsigned int open = (*it2).find("[");
401                    unsigned int close = (*it2).find("]");
402                    if ((open < (*it2).length()) && (close < (*it2).length()) && (open < close))
403                    {
404                        // The next section startet, so our line isn't yet in the file - now we add it and safe the file
405                        this->configFileLine_ = this->configFileLines_s->insert(positionToPutNewLineAt, this->varname_ + "=" + this->defvalue_);
406                        ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
407                        success = true;
408                        break;
409                    }
410
411                    // Look out for the variable-name
412                    if ((*it2).find(this->varname_) < (*it2).length())
413                    {
414                        // We found the right line - safe it and return
415                        this->configFileLine_ = it2;
416                        success = true;
417                        break;
418                    }
419                }
420
421                // Check if we succeeded
422                if (!success)
423                {
424                    // Looks like we found the right section, but the file ended without containing our variable - so we add it and safe the file
425                    this->configFileLine_ = this->configFileLines_s->insert(positionToPutNewLineAt, this->varname_ + "=" + this->defvalue_);
426                    ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);
427                    success = true;
428                }
429                break;
430            }
431        }
432
433        // Check if we succeeded
434        if (!success)
435        {
436            // We obviously didn't found the right section, so we'll create it
437            this->configFileLines_s->push_back("[" + this->classname_ + "]");           // Create the section
438            this->configFileLines_s->push_back(this->varname_ + "=" + this->defvalue_); // Create the line
439            this->configFileLine_ = --this->configFileLines_s->end();                   // Set the pointer to the last element
440            success = true;
441            this->configFileLines_s->push_back("");                                     // Add an empty line - this is needed for the algorithm in the searchConfigFileLine-function
442            ConfigValueContainer::writeConfigFile(CONFIGFILEPATH);                      // Save the changed config-file
443        }
444    }
445
446    /**
447        @brief Determines if a line in the config-file is a comment.
448        @param line The line to check
449        @return True = it's a comment
450    */
451    bool ConfigValueContainer::isComment(const std::string& line)
452    {
453        // Strip the line, whitespaces are disturbing
454        std::string teststring = getStrippedLine(line);
455
456        // There are four possible comment-symbols:
457        //  1) #comment in script-language style
458        //  2) %comment in matlab style
459        //  3) ;comment in unreal tournament config-file style
460        //  4) //comment in code style
461        if (teststring[0] == '#' || teststring[0] == '%' || teststring[0] == ';' || (teststring[0] == '/' && teststring[0] == '/'))
462            return true;
463
464        return false;
465    }
466
467    /**
468        @brief Determines if a line in the config-file is empty (contains only whitespaces).
469        @param line The line to check
470        @return True = it's empty
471    */
472    bool ConfigValueContainer::isEmpty(const std::string& line)
473    {
474        return getStrippedLine(line) == "";
475    }
476
477    /**
478        @brief Removes all whitespaces from a line.
479        @param line The line to strip
480        @return The stripped line
481    */
482    std::string ConfigValueContainer::getStrippedLine(const std::string& line)
483    {
484        std::string output = line;
485        unsigned int pos;
486        while ((pos = output.find(" ")) < output.length())
487            output.erase(pos, 1);
488        while ((pos = output.find("\t")) < output.length())
489            output.erase(pos, 1);
490
491        return output;
492    }
493
494    /**
495        @brief Returns the part in the corresponding config-file-entry of the container that defines the value.
496        @param bStripped True = strip the value-string
497        @return The value-string
498    */
499    std::string ConfigValueContainer::getValueString(bool bStripped)
500    {
501        std::string output;
502        if (bStripped)
503            output = this->getStrippedLine(*this->configFileLine_);
504        else
505            output = *this->configFileLine_;
506
507        return output.substr(output.find("=") + 1);
508    }
509
510    /**
511        @brief Reads the config-file and stores the lines in a list.
512        @param filename The name of the config-file
513    */
514    void ConfigValueContainer::readConfigFile(const std::string& filename)
515    {
516        ConfigValueContainer::readConfigFile_s = true;
517
518        // Create the list if needed
519        if (!ConfigValueContainer::configFileLines_s)
520            ConfigValueContainer::configFileLines_s = new std::list<std::string>;
521
522        // This creates the file if it's not existing
523        std::ofstream createFile;
524        createFile.open(filename.c_str(), std::fstream::app);
525        createFile.close();
526
527        // Open the file
528        std::ifstream file;
529        file.open(filename.c_str(), std::fstream::in);
530
531        char line[1024];
532
533        // Iterate through the file and add the lines into the list
534        while (file.good() && !file.eof())
535        {
536            file.getline(line, 1024);
537            ConfigValueContainer::configFileLines_s->push_back(line);
538//            std::cout << "### ->" << line << "<- : empty: " << isEmpty(line) << " comment: " << isComment(line) << std::endl;
539        }
540
541        // The last line is useless
542        ConfigValueContainer::configFileLines_s->pop_back();
543
544        // Add an empty line to the end of the file if needed
545        // this is needed for the algorithm in the searchConfigFileLine-function
546        if ((ConfigValueContainer::configFileLines_s->size() > 0) && !isEmpty(*ConfigValueContainer::configFileLines_s->rbegin()))
547        {
548//            std::cout << "### newline added" << std::endl;
549            ConfigValueContainer::configFileLines_s->push_back("");
550        }
551
552        file.close();
553    }
554
555    /**
556        @param Writes the content of the list, containing all lines of the config-file, into the config-file.
557        @param filename The name of the config-file
558    */
559    void ConfigValueContainer::writeConfigFile(const std::string& filename)
560    {
561        // Make sure we stored the config-file in the list
562        if (!ConfigValueContainer::readConfigFile_s)
563            ConfigValueContainer::readConfigFile(filename);
564
565        // Open the file
566        std::ofstream file;
567        file.open(filename.c_str(), std::fstream::out);
568
569        // Iterate through the list an write the lines into the file
570        std::list<std::string>::iterator it;
571        for(it = ConfigValueContainer::configFileLines_s->begin(); it != ConfigValueContainer::configFileLines_s->end(); ++it)
572        {
573            file << (*it) << std::endl;
574        }
575
576        file.close();
577    }
578}
Note: See TracBrowser for help on using the repository browser.