Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network/src/core/ConfigFileManager.cc @ 1494

Last change on this file since 1494 was 1494, checked in by rgrieder, 16 years ago
  • set the svn:eol-style property to all files so, that where ever you check out, you'll get the right line endings (had to change every file with mixed endings to windows in order to set the property)
  • Property svn:eol-style set to native
File size: 20.5 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#include "ConfigValueContainer.h"
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{
40    SetConsoleCommandShortcutExtern(config).setArgumentCompleter(0, autocompletion::configvalueclasses()).setArgumentCompleter(1, autocompletion::configvalues()).setArgumentCompleter(2, autocompletion::configvalue());
41    SetConsoleCommandShortcutExtern(tconfig).setArgumentCompleter(0, autocompletion::configvalueclasses()).setArgumentCompleter(1, autocompletion::configvalues()).setArgumentCompleter(2, autocompletion::configvalue());
42    SetConsoleCommandShortcutExtern(reloadConfig);
43    SetConsoleCommandShortcutExtern(cleanConfig);
44    SetConsoleCommandShortcutExtern(loadSettings).setArgumentCompleter(0, autocompletion::files());
45    SetConsoleCommandShortcutExtern(loadKeybindings).setArgumentCompleter(0, autocompletion::files());
46
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())
54                return (*variable).second->set(value);
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
71    void reloadConfig()
72    {
73        ConfigFileManager::getSingleton()->load();
74    }
75
76    void cleanConfig()
77    {
78        ConfigFileManager::getSingleton()->clean(false);
79    }
80
81    void loadSettings(const std::string& filename)
82    {
83        ConfigFileManager::getSingleton()->setFile(CFT_Settings, filename, false);
84    }
85
86    void loadKeybindings(const std::string& filename)
87    {
88        ConfigFileManager::getSingleton()->setFile(CFT_Keybindings, filename);
89    }
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_ + "[" + getConvertedValue<unsigned int, std::string>(this->index_, "0") + "]" + "=" + this->value_);
128        else
129            return (this->name_ + "[" + getConvertedValue<unsigned int, std::string>(this->index_, "0") + "]=" + 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        return (size + 1);
166    }
167
168    std::string ConfigFileSection::getFileEntry() const
169    {
170        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
171            return ("[" + this->name_ + "]");
172        else
173            return ("[" + this->name_ + "] " + this->additionalComment_);
174    }
175
176    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getEntryIterator(const std::string& name, const std::string& fallback, bool bString)
177    {
178        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
179        {
180            if ((*it)->getName() == name)
181            {
182                (*it)->setString(bString);
183                return it;
184            }
185        }
186
187        this->bUpdated_ = true;
188
189        return this->entries_.insert(this->entries_.end(), (ConfigFileEntry*)(new ConfigFileEntryValue(name, fallback, bString)));
190    }
191
192    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getEntryIterator(const std::string& name, unsigned int index, const std::string& fallback, bool bString)
193    {
194        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
195        {
196            if (((*it)->getName() == name) && ((*it)->getIndex() == index))
197            {
198                (*it)->setString(bString);
199                return it;
200            }
201        }
202
203        this->bUpdated_ = true;
204
205        if (index == 0)
206            return this->entries_.insert(this->entries_.end(), (ConfigFileEntry*)(new ConfigFileEntryVectorValue(name, index, fallback, bString)));
207        else
208            return this->entries_.insert(++this->getEntryIterator(name, index - 1, "", bString), (ConfigFileEntry*)(new ConfigFileEntryVectorValue(name, index, fallback, bString)));
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
221    void ConfigFile::load(bool bCreateIfNotExisting)
222    {
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        }
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            {
255                unsigned int   pos1 = temp.find('[');
256                if (pos1 == 0) pos1 = line.find('['); else pos1 = std::string::npos;
257                unsigned int   pos2 = line.find(']');
258
259                if (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1)
260                {
261                    // New section
262                    std::string comment = line.substr(pos2 + 1);
263                    if (isComment(comment))
264                        newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1), comment);
265                    else
266                        newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1));
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
277                    newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryComment(removeTrailingWhitespaces(line)));
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(']');
289                        unsigned int commentposition = getNextCommentPosition(line, pos1 + 1);
290                        while (isBetweenQuotes(line, commentposition))
291                        {
292                            commentposition = getNextCommentPosition(line, commentposition + 1);
293                        }
294                        std::string value = "", comment = "";
295                        if (commentposition == std::string::npos)
296                        {
297                            value = removeTrailingWhitespaces(line.substr(pos1 + 1));
298                        }
299                        else
300                        {
301                            value = removeTrailingWhitespaces(line.substr(pos1 + 1, commentposition - pos1 - 1));
302                            comment = removeTrailingWhitespaces(line.substr(commentposition));
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
312                                std::list<ConfigFileEntry*>::iterator it = newsection->getEntryIterator(getStripped(line.substr(0, pos2)), index, value, false);
313                                (*it)->setValue(value);
314                                (*it)->setComment(comment);
315                                continue;
316                            }
317                        }
318
319                        // New value
320                        newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryValue(getStripped(line.substr(0, pos1)), value, false, comment));
321                        continue;
322                    }
323                }
324            }
325        }
326
327        file.close();
328
329        COUT(3) << "Loaded config file \"" << this->filename_ << "\"." << std::endl;
330
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();
362
363        COUT(4) << "Saved config file \"" << this->filename_ << "\"." << std::endl;
364    }
365
366    void ConfigFile::save(const std::string& filename)
367    {
368        std::string temp = this->filename_;
369        this->filename_ = filename;
370        this->save();
371        this->filename_ = temp;
372    }
373
374    void ConfigFile::clean(bool bCleanComments)
375    {
376        for (std::list<ConfigFileSection*>::iterator it1 = this->sections_.begin(); it1 != this->sections_.end(); )
377        {
378            std::map<std::string, Identifier*>::const_iterator it2 = Identifier::getIdentifierMap().find((*it1)->getName());
379            if (it2 != Identifier::getIdentifierMapEnd() && (*it2).second->hasConfigValues())
380            {
381                // The section exists, delete comment
382                if (bCleanComments)
383                    (*it1)->setComment("");
384                for (std::list<ConfigFileEntry*>::iterator it3 = (*it1)->entries_.begin(); it3 != (*it1)->entries_.end(); )
385                {
386                    std::map<std::string, ConfigValueContainer*>::const_iterator it4 = (*it2).second->getConfigValueMap().find((*it3)->getName());
387                    if (it4 != (*it2).second->getConfigValueMapEnd())
388                    {
389                        // The config-value exists, delete comment
390                        if (bCleanComments)
391                            (*it3)->setComment("");
392                        ++it3;
393                    }
394                    else
395                    {
396                        // The config-value doesn't exist
397                        delete (*it3);
398                        (*it1)->entries_.erase(it3++);
399                    }
400                }
401                ++it1;
402            }
403            else
404            {
405                // The section doesn't exist
406                delete (*it1);
407                this->sections_.erase(it1++);
408            }
409        }
410
411        // Save the file
412        this->save();
413    }
414
415    ConfigFileSection* ConfigFile::getSection(const std::string& section)
416    {
417        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
418            if ((*it)->getName() == section)
419                return (*it);
420
421        this->bUpdated_ = true;
422
423        return (*this->sections_.insert(this->sections_.end(), new ConfigFileSection(section)));
424    }
425
426    void ConfigFile::saveIfUpdated()
427    {
428        bool sectionsUpdated = false;
429
430        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
431        {
432            if ((*it)->bUpdated_)
433            {
434                sectionsUpdated = true;
435                (*it)->bUpdated_ = false;
436            }
437        }
438
439        if (this->bUpdated_ || sectionsUpdated)
440        {
441            this->bUpdated_ = false;
442            this->save();
443        }
444    }
445
446
447    ///////////////////////
448    // ConfigFileManager //
449    ///////////////////////
450    ConfigFileManager::ConfigFileManager()
451    {
452        this->setFile(CFT_Settings, DEFAULT_CONFIG_FILE);
453    }
454
455    ConfigFileManager::~ConfigFileManager()
456    {
457        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); )
458            delete (*(it++)).second;
459    }
460
461    ConfigFileManager* ConfigFileManager::getSingleton()
462    {
463        static ConfigFileManager instance;
464        return (&instance);
465    }
466
467    void ConfigFileManager::setFile(ConfigFileType type, const std::string& filename, bool bCreateIfNotExisting)
468    {
469        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
470        if (it != this->configFiles_.end())
471            if ((*it).second != 0)
472                delete (*it).second;
473
474        this->configFiles_[type] = new ConfigFile(this->getFilePath(filename));
475        this->load(type, bCreateIfNotExisting);
476    }
477
478    void ConfigFileManager::load(bool bCreateIfNotExisting)
479    {
480        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
481            (*it).second->load(bCreateIfNotExisting);
482
483        this->updateConfigValues();
484    }
485
486    void ConfigFileManager::save()
487    {
488        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
489            (*it).second->save();
490    }
491
492    void ConfigFileManager::clean(bool bCleanComments)
493    {
494        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
495            this->clean((*it).first, bCleanComments);
496    }
497
498    void ConfigFileManager::load(ConfigFileType type, bool bCreateIfNotExisting)
499    {
500        this->getFile(type)->load(bCreateIfNotExisting);
501        this->updateConfigValues(type);
502    }
503
504    void ConfigFileManager::save(ConfigFileType type)
505    {
506        this->getFile(type)->save();
507    }
508
509    void ConfigFileManager::save(ConfigFileType type, const std::string& filename)
510    {
511        this->getFile(type)->save(filename);
512    }
513
514    void ConfigFileManager::clean(ConfigFileType type, bool bCleanComments)
515    {
516        this->getFile(type)->clean(bCleanComments);
517    }
518
519    void ConfigFileManager::updateConfigValues() const
520    {
521        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
522            this->updateConfigValues((*it).first);
523    }
524
525    void ConfigFileManager::updateConfigValues(ConfigFileType type) const
526    {
527        if (type == CFT_Settings)
528        {
529            for (std::map<std::string, Identifier*>::const_iterator it = Identifier::getIdentifierMapBegin(); it != Identifier::getIdentifierMapEnd(); ++it)
530            {
531                if ((*it).second->hasConfigValues() /* && (*it).second != ClassManager<KeyBinder>::getIdentifier()*/)
532                {
533                    for (std::map<std::string, ConfigValueContainer*>::const_iterator it2 = (*it).second->getConfigValueMapBegin(); it2 != (*it).second->getConfigValueMapEnd(); ++it2)
534                        (*it2).second->update();
535
536                    (*it).second->updateConfigValues();
537                }
538            }
539        }
540        else if (type == CFT_Keybindings)
541        {
542            // todo
543        }
544    }
545
546    ConfigFile* ConfigFileManager::getFile(ConfigFileType type)
547    {
548        std::map<ConfigFileType, ConfigFile*>::iterator it = this->configFiles_.find(type);
549        if (it != this->configFiles_.end())
550            return (*it).second;
551
552        if (type == CFT_Settings)
553            return this->configFiles_[type] = new ConfigFile(DEFAULT_CONFIG_FILE);
554        else
555            return this->configFiles_[type] = new ConfigFile("");
556    }
557
558    std::string ConfigFileManager::getFilePath(const std::string& name) const
559    {
560        return name;
561    }
562}
Note: See TracBrowser for help on using the repository browser.