Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2662 was 2662, checked in by rgrieder, 15 years ago

Merged presentation branch back to trunk.

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