Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/console/src/core/ConfigFileManager.cc @ 1436

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

cool stuff: autocompletion works with filesystem (has to be tested on other systems)

File size: 20.7 KB
RevLine 
[1006]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
[1056]3 *                    > www.orxonox.net <
[1006]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"
[1026]30#include "ConfigValueContainer.h"
[1006]31#include "ConsoleCommand.h"
32#include "Identifier.h"
33#include "util/Convert.h"
34#include "util/String.h"
35
36#define CONFIG_FILE_MAX_LINELENGHT 1024
37
38namespace orxonox
39{
[1434]40    SetConsoleCommandShortcutExtern(config).setArgumentCompleter(0, autocompletion::configvalueclasses()).setArgumentCompleter(1, autocompletion::configvalues()).setArgumentCompleter(2, autocompletion::configvalue());
[1435]41    SetConsoleCommandShortcutExtern(tconfig).setArgumentCompleter(0, autocompletion::configvalueclasses()).setArgumentCompleter(1, autocompletion::configvalues()).setArgumentCompleter(2, autocompletion::configvalue());
[1341]42    SetConsoleCommandShortcutExtern(reloadConfig);
43    SetConsoleCommandShortcutExtern(cleanConfig);
[1436]44    SetConsoleCommandShortcutExtern(loadSettings).setArgumentCompleter(0, autocompletion::files());
45    SetConsoleCommandShortcutExtern(loadKeybindings).setArgumentCompleter(0, autocompletion::files());
[1006]46
[1434]47    bool config(const std::string& classname, const std::string& varname, const std::string& value)
48    {
49        std::map<std::string, Identifier*>::const_iterator identifier = Identifier::getLowercaseIdentifierMap().find(getLowercase(classname));
50        if (identifier != Identifier::getLowercaseIdentifierMapEnd())
51        {
52            std::map<std::string, ConfigValueContainer*>::const_iterator variable = (*identifier).second->getLowercaseConfigValueMap().find(getLowercase(varname));
53            if (variable != (*identifier).second->getLowercaseConfigValueMapEnd())
[1435]54                return (*variable).second->set(value);
[1434]55        }
56        return false;
57    }
58
59    bool tconfig(const std::string& classname, const std::string& varname, const std::string& value)
60    {
61        std::map<std::string, Identifier*>::const_iterator identifier = Identifier::getLowercaseIdentifierMap().find(getLowercase(classname));
62        if (identifier != Identifier::getLowercaseIdentifierMapEnd())
63        {
64            std::map<std::string, ConfigValueContainer*>::const_iterator variable = (*identifier).second->getLowercaseConfigValueMap().find(getLowercase(varname));
65            if (variable != (*identifier).second->getLowercaseConfigValueMapEnd())
66                return (*variable).second->tset(value);
67        }
68        return false;
69    }
70
[1006]71    void reloadConfig()
72    {
[1020]73        ConfigFileManager::getSingleton()->load();
[1006]74    }
75
76    void cleanConfig()
77    {
[1030]78        ConfigFileManager::getSingleton()->clean(false);
[1006]79    }
80
81    void loadSettings(const std::string& filename)
82    {
[1027]83        ConfigFileManager::getSingleton()->setFile(CFT_Settings, filename, false);
[1006]84    }
85
86    void loadKeybindings(const std::string& filename)
87    {
[1020]88        ConfigFileManager::getSingleton()->setFile(CFT_Keybindings, filename);
[1006]89    }
90
91
92    //////////////////////////
93    // ConfigFileEntryValue //
94    //////////////////////////
[1049]95
96    void ConfigFileEntryValue::setValue(const std::string& value)
97    {
98        if (!this->bString_)
99            this->value_ = value;
100        else
[1050]101            this->value_ = "\"" + addSlashes(stripEnclosingQuotes(value)) + "\"";
[1049]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
[1006]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    ///////////////////////////////
[1030]122    // ConfigFileEntryVectorValue //
[1006]123    ///////////////////////////////
[1030]124    std::string ConfigFileEntryVectorValue::getFileEntry() const
[1006]125    {
126        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
[1030]127            return (this->name_ + "[" + getConvertedValue<unsigned int, std::string>(this->index_, "0") + "]" + "=" + this->value_);
[1006]128        else
[1030]129            return (this->name_ + "[" + getConvertedValue<unsigned int, std::string>(this->index_, "0") + "]=" + this->value_ + " " + this->additionalComment_);
[1006]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
[1030]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        return (size + 1);
166    }
167
[1006]168    std::string ConfigFileSection::getFileEntry() const
169    {
170        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
[1025]171            return ("[" + this->name_ + "]");
[1006]172        else
[1025]173            return ("[" + this->name_ + "] " + this->additionalComment_);
[1006]174    }
175
[1049]176    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getEntryIterator(const std::string& name, const std::string& fallback, bool bString)
[1006]177    {
178        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
[1049]179        {
[1020]180            if ((*it)->getName() == name)
[1049]181            {
182                (*it)->setString(bString);
[1020]183                return it;
[1049]184            }
185        }
[1020]186
187        this->bUpdated_ = true;
188
[1049]189        return this->entries_.insert(this->entries_.end(), (ConfigFileEntry*)(new ConfigFileEntryValue(name, fallback, bString)));
[1020]190    }
191
[1049]192    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getEntryIterator(const std::string& name, unsigned int index, const std::string& fallback, bool bString)
[1020]193    {
194        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
[1049]195        {
[1006]196            if (((*it)->getName() == name) && ((*it)->getIndex() == index))
[1049]197            {
198                (*it)->setString(bString);
[1006]199                return it;
[1049]200            }
201        }
[1006]202
203        this->bUpdated_ = true;
204
205        if (index == 0)
[1049]206            return this->entries_.insert(this->entries_.end(), (ConfigFileEntry*)(new ConfigFileEntryVectorValue(name, index, fallback, bString)));
[1006]207        else
[1049]208            return this->entries_.insert(++this->getEntryIterator(name, index - 1, "", bString), (ConfigFileEntry*)(new ConfigFileEntryVectorValue(name, index, fallback, bString)));
[1006]209    }
210
211
212    ////////////////
213    // ConfigFile //
214    ////////////////
215    ConfigFile::~ConfigFile()
216    {
217        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); )
218            delete (*(it++));
219    }
220
[1027]221    void ConfigFile::load(bool bCreateIfNotExisting)
[1006]222    {
[1027]223        if (bCreateIfNotExisting)
224        {
225            // This creates the default config file if it's not existing
226            std::ofstream createFile;
227            createFile.open(this->filename_.c_str(), std::fstream::app);
228            createFile.close();
229        }
[1006]230
231        // Open the file
232        std::ifstream file;
233        file.open(this->filename_.c_str(), std::fstream::in);
234
235        if (!file.is_open())
236        {
237            COUT(1) << "An error occurred in ConfigFileManager.cc:" << std::endl;
238            COUT(1) << "Error: Couldn't open config-file \"" << this->filename_ << "\"." << std::endl;
239            return;
240        }
241
242        char linearray[CONFIG_FILE_MAX_LINELENGHT];
243
244        ConfigFileSection* newsection = 0;
245
246        while (file.good() && !file.eof())
247        {
248            file.getline(linearray, CONFIG_FILE_MAX_LINELENGHT);
249
250            std::string line = std::string(linearray);
251
252            std::string temp = getStripped(line);
253            if (!isEmpty(temp) && !isComment(temp))
254            {
[1030]255                unsigned int   pos1 = temp.find('[');
256                if (pos1 == 0) pos1 = line.find('['); else pos1 = std::string::npos;
257                unsigned int   pos2 = line.find(']');
[1006]258
259                if (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1)
260                {
261                    // New section
[1030]262                    std::string comment = line.substr(pos2 + 1);
[1006]263                    if (isComment(comment))
[1025]264                        newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1), comment);
[1006]265                    else
[1025]266                        newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1));
[1006]267                    this->sections_.insert(this->sections_.end(), newsection);
268                    continue;
269                }
270            }
271
272            if (newsection != 0)
273            {
274                if (isComment(line))
275                {
276                    // New comment
[1020]277                    newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryComment(removeTrailingWhitespaces(line)));
[1006]278                    continue;
279                }
280                else
281                {
282                    unsigned int pos1 = line.find('=');
283
284                    if (pos1 != std::string::npos && pos1 > 0)
285                    {
286                        // New entry
287                        unsigned int pos2 = line.find('[');
288                        unsigned int pos3 = line.find(']');
[1049]289                        unsigned int commentposition = getNextCommentPosition(line, pos1 + 1);
290                        while (isBetweenQuotes(line, commentposition))
[1006]291                        {
[1049]292                            commentposition = getNextCommentPosition(line, commentposition + 1);
[1006]293                        }
[1049]294                        std::string value = "", comment = "";
295                        if (commentposition == std::string::npos)
296                        {
297                            value = removeTrailingWhitespaces(line.substr(pos1 + 1));
298                        }
[1006]299                        else
300                        {
[1049]301                            value = removeTrailingWhitespaces(line.substr(pos1 + 1, commentposition - pos1 - 1));
302                            comment = removeTrailingWhitespaces(line.substr(commentposition));
[1006]303                        }
304
305                        if (pos2 != std::string::npos && pos3 != std::string::npos && pos3 > pos2 + 1)
306                        {
307                            // There might be an array index
308                            unsigned int index = 0;
309                            if (ConvertValue(&index, line.substr(pos2 + 1, pos3 - pos2 - 1)))
310                            {
311                                // New array
[1049]312                                std::list<ConfigFileEntry*>::iterator it = newsection->getEntryIterator(getStripped(line.substr(0, pos2)), index, value, false);
[1030]313                                (*it)->setValue(value);
314                                (*it)->setComment(comment);
[1006]315                                continue;
316                            }
317                        }
318
319                        // New value
[1049]320                        newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryValue(getStripped(line.substr(0, pos1)), value, false, comment));
[1006]321                        continue;
322                    }
323                }
324            }
325        }
326
327        file.close();
328
[1020]329        COUT(3) << "Loaded config file \"" << this->filename_ << "\"." << std::endl;
330
[1006]331        // Save the file in case something changed (like stripped whitespaces)
332        this->save();
333    }
334
335    void ConfigFile::save() const
336    {
337        std::ofstream file;
338        file.open(this->filename_.c_str(), std::fstream::out);
339        file.setf(std::ios::fixed, std::ios::floatfield);
340        file.precision(6);
341
342        if (!file.is_open())
343        {
344            COUT(1) << "An error occurred in ConfigFileManager.cc:" << std::endl;
345            COUT(1) << "Error: Couldn't open config-file \"" << this->filename_ << "\"." << std::endl;
346            return;
347        }
348
349        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
350        {
351            file << (*it)->getFileEntry() << std::endl;
352
353            for (std::list<ConfigFileEntry*>::const_iterator it_entries = (*it)->getEntriesBegin(); it_entries != (*it)->getEntriesEnd(); ++it_entries)
354            {
355                file << (*it_entries)->getFileEntry() << std::endl;
356            }
357
358            file << std::endl;
359        }
360
361        file.close();
[1020]362
363        COUT(4) << "Saved config file \"" << this->filename_ << "\"." << std::endl;
[1006]364    }
365
[1030]366    void ConfigFile::clean(bool bCleanComments)
[1006]367    {
[1027]368        for (std::list<ConfigFileSection*>::iterator it1 = this->sections_.begin(); it1 != this->sections_.end(); )
369        {
370            std::map<std::string, Identifier*>::const_iterator it2 = Identifier::getIdentifierMap().find((*it1)->getName());
371            if (it2 != Identifier::getIdentifierMapEnd() && (*it2).second->hasConfigValues())
372            {
373                // The section exists, delete comment
[1030]374                if (bCleanComments)
375                    (*it1)->setComment("");
[1027]376                for (std::list<ConfigFileEntry*>::iterator it3 = (*it1)->entries_.begin(); it3 != (*it1)->entries_.end(); )
377                {
378                    std::map<std::string, ConfigValueContainer*>::const_iterator it4 = (*it2).second->getConfigValueMap().find((*it3)->getName());
379                    if (it4 != (*it2).second->getConfigValueMapEnd())
380                    {
381                        // The config-value exists, delete comment
[1030]382                        if (bCleanComments)
383                            (*it3)->setComment("");
[1027]384                        ++it3;
385                    }
386                    else
387                    {
388                        // The config-value doesn't exist
389                        delete (*it3);
390                        (*it1)->entries_.erase(it3++);
391                    }
392                }
393                ++it1;
394            }
395            else
396            {
397                // The section doesn't exist
398                delete (*it1);
399                this->sections_.erase(it1++);
400            }
401        }
402
403        // Save the file
404        this->save();
[1006]405    }
406
407    ConfigFileSection* ConfigFile::getSection(const std::string& section)
408    {
409        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
410            if ((*it)->getName() == section)
411                return (*it);
412
413        this->bUpdated_ = true;
414
[1030]415        return (*this->sections_.insert(this->sections_.end(), new ConfigFileSection(section)));
[1006]416    }
417
418    void ConfigFile::saveIfUpdated()
419    {
420        bool sectionsUpdated = false;
421
422        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
423        {
424            if ((*it)->bUpdated_)
425            {
426                sectionsUpdated = true;
427                (*it)->bUpdated_ = false;
428            }
429        }
430
431        if (this->bUpdated_ || sectionsUpdated)
432        {
433            this->bUpdated_ = false;
434            this->save();
435        }
436    }
437
438
439    ///////////////////////
440    // ConfigFileManager //
441    ///////////////////////
442    ConfigFileManager::ConfigFileManager()
443    {
444        this->setFile(CFT_Settings, DEFAULT_CONFIG_FILE);
445    }
446
447    ConfigFileManager::~ConfigFileManager()
448    {
449        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); )
450            delete (*(it++)).second;
451    }
452
[1020]453    ConfigFileManager* ConfigFileManager::getSingleton()
[1006]454    {
455        static ConfigFileManager instance;
456        return (&instance);
457    }
458
[1027]459    void ConfigFileManager::setFile(ConfigFileType type, const std::string& filename, bool bCreateIfNotExisting)
[1006]460    {
461        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
462        if (it != this->configFiles_.end())
463            if ((*it).second != 0)
464                delete (*it).second;
465
466        this->configFiles_[type] = new ConfigFile(this->getFilePath(filename));
[1027]467        this->load(type, bCreateIfNotExisting);
[1006]468    }
469
[1027]470    void ConfigFileManager::load(bool bCreateIfNotExisting)
[1006]471    {
472        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
[1027]473            (*it).second->load(bCreateIfNotExisting);
[1006]474
475        this->updateConfigValues();
476    }
477
478    void ConfigFileManager::save()
479    {
480        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
481            (*it).second->save();
482    }
483
[1030]484    void ConfigFileManager::clean(bool bCleanComments)
[1006]485    {
486        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
[1030]487            this->clean((*it).first, bCleanComments);
[1006]488    }
489
[1027]490    void ConfigFileManager::load(ConfigFileType type, bool bCreateIfNotExisting)
[1006]491    {
[1027]492        this->getFile(type)->load(bCreateIfNotExisting);
[1006]493        this->updateConfigValues(type);
494    }
495
496    void ConfigFileManager::save(ConfigFileType type)
497    {
498        this->getFile(type)->save();
499    }
500
[1030]501    void ConfigFileManager::clean(ConfigFileType type, bool bCleanComments)
[1006]502    {
[1030]503        this->getFile(type)->clean(bCleanComments);
[1006]504    }
505
506    void ConfigFileManager::updateConfigValues() const
507    {
508        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
509            this->updateConfigValues((*it).first);
510    }
511
512    void ConfigFileManager::updateConfigValues(ConfigFileType type) const
513    {
514        if (type == CFT_Settings)
515        {
516            for (std::map<std::string, Identifier*>::const_iterator it = Identifier::getIdentifierMapBegin(); it != Identifier::getIdentifierMapEnd(); ++it)
[1026]517            {
[1006]518                if ((*it).second->hasConfigValues() /* && (*it).second != ClassManager<KeyBinder>::getIdentifier()*/)
[1026]519                {
520                    for (std::map<std::string, ConfigValueContainer*>::const_iterator it2 = (*it).second->getConfigValueMapBegin(); it2 != (*it).second->getConfigValueMapEnd(); ++it2)
521                        (*it2).second->update();
522
[1006]523                    (*it).second->updateConfigValues();
[1026]524                }
525            }
[1006]526        }
527        else if (type == CFT_Keybindings)
528        {
529            // todo
530        }
531    }
532
533    ConfigFile* ConfigFileManager::getFile(ConfigFileType type)
534    {
535        std::map<ConfigFileType, ConfigFile*>::iterator it = this->configFiles_.find(type);
536        if (it != this->configFiles_.end())
537            return (*it).second;
538
539        if (type == CFT_Settings)
540            return this->configFiles_[type] = new ConfigFile(DEFAULT_CONFIG_FILE);
541        else
542            return this->configFiles_[type] = new ConfigFile("");
543    }
544
545    std::string ConfigFileManager::getFilePath(const std::string& name) const
546    {
547        return name;
548    }
549}
Note: See TracBrowser for help on using the repository browser.