Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: data/Media/src/core/ConfigFileManager.cc @ 5181

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