Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Merged buildsystem3 containing buildsystem2 containing Adi's buildsystem branch back to the trunk.
Please update the media directory if you were not using buildsystem3 before.

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