Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/consolecommands/src/libraries/core/ConfigFileManager.cc @ 6741

Last change on this file since 6741 was 6197, checked in by dafrick, 15 years ago

Small adjustments, better code re-use, added getConfig() function to get values of config values.

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