Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/ConfigFileManager.cc @ 2520

Last change on this file since 2520 was 2103, checked in by rgrieder, 16 years ago

Merged r2101 (objecthierarchy) to trunk.

  • Property svn:eol-style set to native
File size: 20.6 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "ConfigFileManager.h"
30
31#include <cassert>
32#include "util/Convert.h"
33#include "util/String.h"
34#include "ConsoleCommand.h"
35#include "ConfigValueContainer.h"
36
37namespace orxonox
38{
39    const int CONFIG_FILE_MAX_LINELENGHT  = 1024;
40    const char* const DEFAULT_CONFIG_FILE = "default.ini";
41
42    ConfigFileManager* ConfigFileManager::singletonRef_s = 0;
43
44    SetConsoleCommandShortcutExtern(config).argumentCompleter(0, autocompletion::configvalueclasses()).argumentCompleter(1, autocompletion::configvalues()).argumentCompleter(2, autocompletion::configvalue());
45    SetConsoleCommandShortcutExtern(tconfig).argumentCompleter(0, autocompletion::configvalueclasses()).argumentCompleter(1, autocompletion::configvalues()).argumentCompleter(2, autocompletion::configvalue());
46    SetConsoleCommandShortcutExtern(reloadConfig);
47    SetConsoleCommandShortcutExtern(cleanConfig);
48    SetConsoleCommandShortcutExtern(loadSettings).argumentCompleter(0, autocompletion::files());
49
50    bool config(const std::string& classname, const std::string& varname, const std::string& value)
51    {
52        std::map<std::string, Identifier*>::const_iterator identifier = Identifier::getLowercaseIdentifierMap().find(getLowercase(classname));
53        if (identifier != Identifier::getLowercaseIdentifierMapEnd())
54        {
55            std::map<std::string, ConfigValueContainer*>::const_iterator variable = (*identifier).second->getLowercaseConfigValueMap().find(getLowercase(varname));
56            if (variable != (*identifier).second->getLowercaseConfigValueMapEnd())
57                return (*variable).second->set(value);
58        }
59        return false;
60    }
61
62    bool tconfig(const std::string& classname, const std::string& varname, const std::string& value)
63    {
64        std::map<std::string, Identifier*>::const_iterator identifier = Identifier::getLowercaseIdentifierMap().find(getLowercase(classname));
65        if (identifier != Identifier::getLowercaseIdentifierMapEnd())
66        {
67            std::map<std::string, ConfigValueContainer*>::const_iterator variable = (*identifier).second->getLowercaseConfigValueMap().find(getLowercase(varname));
68            if (variable != (*identifier).second->getLowercaseConfigValueMapEnd())
69                return (*variable).second->tset(value);
70        }
71        return false;
72    }
73
74    void reloadConfig()
75    {
76        ConfigFileManager::getInstance().load();
77    }
78
79    void cleanConfig()
80    {
81        ConfigFileManager::getInstance().clean(false);
82    }
83
84    void loadSettings(const std::string& filename)
85    {
86        ConfigFileManager::getInstance().setFilename(ConfigFileType::Settings, filename);
87    }
88
89    //////////////////////////
90    // ConfigFileEntryValue //
91    //////////////////////////
92
93    void ConfigFileEntryValue::setValue(const std::string& value)
94    {
95        if (!this->bString_)
96            this->value_ = value;
97        else
98            this->value_ = "\"" + addSlashes(stripEnclosingQuotes(value)) + "\"";
99    }
100
101    std::string ConfigFileEntryValue::getValue() const
102    {
103        if (!this->bString_)
104            return this->value_;
105        else
106            return removeSlashes(stripEnclosingQuotes(this->value_));
107    }
108
109    std::string ConfigFileEntryValue::getFileEntry() const
110    {
111        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
112            return (this->name_ + "=" + this->value_);
113        else
114            return (this->name_ + "=" + this->value_ + " " + this->additionalComment_);
115    }
116
117
118    ////////////////////////////////
119    // ConfigFileEntryVectorValue //
120    ////////////////////////////////
121    std::string ConfigFileEntryVectorValue::getFileEntry() const
122    {
123        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
124            return (this->name_ + "[" + getConvertedValue<unsigned int, std::string>(this->index_, "0") + "]" + "=" + this->value_);
125        else
126            return (this->name_ + "[" + getConvertedValue<unsigned int, std::string>(this->index_, "0") + "]=" + this->value_ + " " + this->additionalComment_);
127    }
128
129
130    ///////////////////////
131    // ConfigFileSection //
132    ///////////////////////
133    ConfigFileSection::~ConfigFileSection()
134    {
135        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); )
136            delete (*(it++));
137    }
138
139    void ConfigFileSection::deleteVectorEntries(const std::string& name, unsigned int startindex)
140    {
141        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); )
142        {
143            if (((*it)->getName() == name) && ((*it)->getIndex() >= startindex))
144            {
145                delete (*it);
146                this->entries_.erase(it++);
147            }
148            else
149            {
150                ++it;
151            }
152        }
153    }
154
155    unsigned int ConfigFileSection::getVectorSize(const std::string& name)
156    {
157        unsigned int size = 0;
158        for (std::list<ConfigFileEntry*>::const_iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
159            if ((*it)->getName() == name)
160                if ((*it)->getIndex() > size)
161                    size = (*it)->getIndex();
162        return (size + 1);
163    }
164
165    std::string ConfigFileSection::getFileEntry() const
166    {
167        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
168            return ("[" + this->name_ + "]");
169        else
170            return ("[" + this->name_ + "] " + this->additionalComment_);
171    }
172
173    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getEntryIterator(const std::string& name, const std::string& fallback, bool bString)
174    {
175        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
176        {
177            if ((*it)->getName() == name)
178            {
179                (*it)->setString(bString);
180                return it;
181            }
182        }
183
184        this->bUpdated_ = true;
185
186        return this->entries_.insert(this->entries_.end(), (ConfigFileEntry*)(new ConfigFileEntryValue(name, fallback, bString)));
187    }
188
189    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getEntryIterator(const std::string& name, unsigned int index, const std::string& fallback, bool bString)
190    {
191        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
192        {
193            if (((*it)->getName() == name) && ((*it)->getIndex() == index))
194            {
195                (*it)->setString(bString);
196                return it;
197            }
198        }
199
200        this->bUpdated_ = true;
201
202        if (index == 0)
203            return this->entries_.insert(this->entries_.end(), (ConfigFileEntry*)(new ConfigFileEntryVectorValue(name, index, fallback, bString)));
204        else
205            return this->entries_.insert(++this->getEntryIterator(name, index - 1, "", bString), (ConfigFileEntry*)(new ConfigFileEntryVectorValue(name, index, fallback, bString)));
206    }
207
208
209    ////////////////
210    // ConfigFile //
211    ////////////////
212    ConfigFile::~ConfigFile()
213    {
214        this->clear();
215    }
216
217    void ConfigFile::load(bool bCreateIfNotExisting)
218    {
219        // Be sure we start from new
220        this->clear();
221
222        // This creates the config file if it's not existing
223        std::ofstream createFile;
224        createFile.open(this->filename_.c_str(), std::fstream::app);
225        createFile.close();
226
227        // Open the file
228        std::ifstream file;
229        file.open(this->filename_.c_str(), std::fstream::in);
230
231        if (!file.is_open())
232        {
233            COUT(1) << "An error occurred in ConfigFileManager.cc:" << std::endl;
234            COUT(1) << "Error: Couldn't open config-file \"" << this->filename_ << "\"." << std::endl;
235            return;
236        }
237
238        char linearray[CONFIG_FILE_MAX_LINELENGHT];
239
240        ConfigFileSection* newsection = 0;
241
242        while (file.good() && !file.eof())
243        {
244            file.getline(linearray, CONFIG_FILE_MAX_LINELENGHT);
245
246            std::string line = std::string(linearray);
247
248            std::string temp = getStripped(line);
249            if (!isEmpty(temp) && !isComment(temp))
250            {
251                size_t   pos1 = temp.find('[');
252                if (pos1 == 0) pos1 = line.find('['); else pos1 = std::string::npos;
253                size_t   pos2 = line.find(']');
254
255                if (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1)
256                {
257                    // New section
258                    std::string comment = line.substr(pos2 + 1);
259                    if (isComment(comment))
260                        newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1), comment);
261                    else
262                        newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1));
263                    this->sections_.insert(this->sections_.end(), newsection);
264                    continue;
265                }
266            }
267
268            if (newsection != 0)
269            {
270                if (isComment(line))
271                {
272                    // New comment
273                    newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryComment(removeTrailingWhitespaces(line)));
274                    continue;
275                }
276                else
277                {
278                    size_t pos1 = line.find('=');
279
280                    if (pos1 != std::string::npos && pos1 > 0)
281                    {
282                        // New entry
283                        size_t pos2 = line.find('[');
284                        size_t pos3 = line.find(']');
285                        size_t commentposition = getNextCommentPosition(line, pos1 + 1);
286                        while (isBetweenQuotes(line, commentposition))
287                        {
288                            commentposition = getNextCommentPosition(line, commentposition + 1);
289                        }
290                        std::string value = "", comment = "";
291                        if (commentposition == std::string::npos)
292                        {
293                            value = removeTrailingWhitespaces(line.substr(pos1 + 1));
294                        }
295                        else
296                        {
297                            value = removeTrailingWhitespaces(line.substr(pos1 + 1, commentposition - pos1 - 1));
298                            comment = removeTrailingWhitespaces(line.substr(commentposition));
299                        }
300
301                        if (pos2 != std::string::npos && pos3 != std::string::npos && pos3 > pos2 + 1)
302                        {
303                            // There might be an array index
304                            unsigned int index = 0;
305                            if (ConvertValue(&index, line.substr(pos2 + 1, pos3 - pos2 - 1)))
306                            {
307                                // New array
308                                std::list<ConfigFileEntry*>::iterator it = newsection->getEntryIterator(getStripped(line.substr(0, pos2)), index, value, false);
309                                (*it)->setValue(value);
310                                (*it)->setComment(comment);
311                                continue;
312                            }
313                        }
314
315                        // New value
316                        newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryValue(getStripped(line.substr(0, pos1)), value, false, comment));
317                        continue;
318                    }
319                }
320            }
321        }
322
323        file.close();
324
325        COUT(3) << "Loaded config file \"" << this->filename_ << "\"." << std::endl;
326
327        // Save the file in case something changed (like stripped whitespaces)
328        this->save();
329
330        // Update all ConfigValueContainers
331        this->updateConfigValues();
332    }
333
334    void ConfigFile::save() const
335    {
336        std::ofstream file;
337        file.open(this->filename_.c_str(), std::fstream::out);
338        file.setf(std::ios::fixed, std::ios::floatfield);
339        file.precision(6);
340
341        if (!file.is_open())
342        {
343            COUT(1) << "An error occurred in ConfigFileManager.cc:" << std::endl;
344            COUT(1) << "Error: Couldn't open config-file \"" << this->filename_ << "\"." << std::endl;
345            return;
346        }
347
348        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
349        {
350            file << (*it)->getFileEntry() << std::endl;
351
352            for (std::list<ConfigFileEntry*>::const_iterator it_entries = (*it)->getEntriesBegin(); it_entries != (*it)->getEntriesEnd(); ++it_entries)
353            {
354                file << (*it_entries)->getFileEntry() << std::endl;
355            }
356
357            file << std::endl;
358        }
359
360        file.close();
361
362        COUT(4) << "Saved config file \"" << this->filename_ << "\"." << std::endl;
363    }
364
365    void ConfigFile::saveAs(const std::string& filename)
366    {
367        std::string temp = this->filename_;
368        this->filename_ = filename;
369        this->save();
370        this->filename_ = temp;
371    }
372
373    void ConfigFile::clean(bool bCleanComments)
374    {
375        for (std::list<ConfigFileSection*>::iterator it1 = this->sections_.begin(); it1 != this->sections_.end(); )
376        {
377            std::map<std::string, Identifier*>::const_iterator it2 = Identifier::getIdentifierMap().find((*it1)->getName());
378            if (it2 != Identifier::getIdentifierMapEnd() && (*it2).second->hasConfigValues())
379            {
380                // The section exists, delete comment
381                if (bCleanComments)
382                    (*it1)->setComment("");
383                for (std::list<ConfigFileEntry*>::iterator it3 = (*it1)->entries_.begin(); it3 != (*it1)->entries_.end(); )
384                {
385                    std::map<std::string, ConfigValueContainer*>::const_iterator it4 = (*it2).second->getConfigValueMap().find((*it3)->getName());
386                    if (it4 != (*it2).second->getConfigValueMapEnd())
387                    {
388                        // The config-value exists, delete comment
389                        if (bCleanComments)
390                            (*it3)->setComment("");
391                        ++it3;
392                    }
393                    else
394                    {
395                        // The config-value doesn't exist
396                        delete (*it3);
397                        (*it1)->entries_.erase(it3++);
398                    }
399                }
400                ++it1;
401            }
402            else
403            {
404                // The section doesn't exist
405                delete (*it1);
406                this->sections_.erase(it1++);
407            }
408        }
409
410        // Save the file
411        this->save();
412    }
413
414    void ConfigFile::clear()
415    {
416        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); )
417            delete (*(it++));
418        this->sections_.clear();
419    }
420
421    ConfigFileSection* ConfigFile::getSection(const std::string& section)
422    {
423        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
424            if ((*it)->getName() == section)
425                return (*it);
426
427        this->bUpdated_ = true;
428
429        return (*this->sections_.insert(this->sections_.end(), new ConfigFileSection(section)));
430    }
431
432    void ConfigFile::saveIfUpdated()
433    {
434        bool sectionsUpdated = false;
435
436        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
437        {
438            if ((*it)->bUpdated_)
439            {
440                sectionsUpdated = true;
441                (*it)->bUpdated_ = false;
442            }
443        }
444
445        if (this->bUpdated_ || sectionsUpdated)
446        {
447            this->bUpdated_ = false;
448            this->save();
449        }
450    }
451
452    void ConfigFile::updateConfigValues()
453    {
454        if (this->type_ == ConfigFileType::Settings)
455        {
456            for (std::map<std::string, Identifier*>::const_iterator it = Identifier::getIdentifierMapBegin(); it != Identifier::getIdentifierMapEnd(); ++it)
457            {
458                if (it->second->hasConfigValues())
459                {
460                    for (std::map<std::string, ConfigValueContainer*>::const_iterator it2 = (*it).second->getConfigValueMapBegin(); it2 != (*it).second->getConfigValueMapEnd(); ++it2)
461                        it2->second->update();
462
463                    it->second->updateConfigValues();
464                }
465            }
466        }
467    }
468
469
470    ///////////////////////
471    // ConfigFileManager //
472    ///////////////////////
473
474    ConfigFileManager::ConfigFileManager()
475         : mininmalFreeType_(ConfigFileType::numberOfReservedTypes)
476    {
477        assert(singletonRef_s == 0);
478        singletonRef_s = this;
479    }
480
481    ConfigFileManager::~ConfigFileManager()
482    {
483        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); )
484            delete (it++)->second;
485
486        assert(singletonRef_s != 0);
487        singletonRef_s = 0;
488    }
489
490    void ConfigFileManager::setFilename(ConfigFileType type, const std::string& filename)
491    {
492        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
493        if (it != this->configFiles_.end())
494        {
495            assert(it->second);
496            delete it->second;
497        }
498        this->configFiles_[type] = new ConfigFile(filename, type);
499        this->load(type);
500    }
501
502    void ConfigFileManager::load()
503    {
504        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
505            it->second->load();
506    }
507
508    void ConfigFileManager::save()
509    {
510        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
511            it->second->save();
512    }
513
514    void ConfigFileManager::clean(bool bCleanComments)
515    {
516        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
517            this->clean(it->first, bCleanComments);
518    }
519
520    void ConfigFileManager::load(ConfigFileType type)
521    {
522        this->getFile(type)->load();
523    }
524
525    void ConfigFileManager::save(ConfigFileType type)
526    {
527        this->getFile(type)->save();
528    }
529
530    void ConfigFileManager::saveAs(ConfigFileType type, const std::string& saveFilename)
531    {
532        this->getFile(type)->saveAs(saveFilename);
533    }
534
535    void ConfigFileManager::clean(ConfigFileType type, bool bCleanComments)
536    {
537        this->getFile(type)->clean(bCleanComments);
538    }
539
540    void ConfigFileManager::updateConfigValues()
541    {
542        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
543            it->second->updateConfigValues();
544    }
545
546    void ConfigFileManager::updateConfigValues(ConfigFileType type)
547    {
548        this->getFile(type)->updateConfigValues();
549    }
550
551    const std::string& ConfigFileManager::getFilename(ConfigFileType type)
552    {
553        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
554        if (it != this->configFiles_.end())
555            return it->second->getFilename();
556        else
557            return BLANKSTRING;
558    }
559
560    ConfigFile* ConfigFileManager::getFile(ConfigFileType type)
561    {
562        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
563        if (it != this->configFiles_.end())
564            return it->second;
565        else
566        {
567            COUT(1) << "ConfigFileManager: Can't find a config file for type with ID " << (int)type << std::endl;
568            COUT(1) << "Using " << DEFAULT_CONFIG_FILE << " file." << std::endl;
569            this->setFilename(type, DEFAULT_CONFIG_FILE);
570            return getFile(type);
571        }
572    }
573}
Note: See TracBrowser for help on using the repository browser.