Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/questsystem5/src/core/ConfigFileManager.cc @ 2988

Last change on this file since 2988 was 2908, checked in by dafrick, 16 years ago

Reverted to revision 2906 (because I'm too stupid to merge correctly, 2nd try will follow shortly. ;))

  • Property svn:eol-style set to native
File size: 21.4 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                COUT(3) << "Copied " << this->filename_ << " from the defaultConfig folder." << std::endl;
237                boost::filesystem::copy_file(defaultFilepath, filepath);
238            }
239        }
240
241        // Open the file
242        std::ifstream file;
243        file.open(filepath.string().c_str(), std::fstream::in);
244        if (file.is_open())
245        {
246
247            char linearray[CONFIG_FILE_MAX_LINELENGHT];
248
249            ConfigFileSection* newsection = 0;
250
251            while (file.good() && !file.eof())
252            {
253                file.getline(linearray, CONFIG_FILE_MAX_LINELENGHT);
254
255                std::string line = std::string(linearray);
256
257                std::string temp = getStripped(line);
258                if (!isEmpty(temp) && !isComment(temp))
259                {
260                    size_t   pos1 = temp.find('[');
261                    if (pos1 == 0) pos1 = line.find('['); else pos1 = std::string::npos;
262                    size_t   pos2 = line.find(']');
263
264                    if (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1)
265                    {
266                        // New section
267                        std::string comment = line.substr(pos2 + 1);
268                        if (isComment(comment))
269                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1), comment);
270                        else
271                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1));
272                        this->sections_.insert(this->sections_.end(), newsection);
273                        continue;
274                    }
275                }
276
277                if (newsection != 0)
278                {
279                    if (isComment(line))
280                    {
281                        // New comment
282                        newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryComment(removeTrailingWhitespaces(line)));
283                        continue;
284                    }
285                    else
286                    {
287                        size_t pos1 = line.find('=');
288
289                        if (pos1 != std::string::npos && pos1 > 0)
290                        {
291                            // New entry
292                            size_t pos2 = line.find('[');
293                            size_t pos3 = line.find(']');
294                            size_t commentposition = getNextCommentPosition(line, pos1 + 1);
295                            while (isBetweenQuotes(line, commentposition))
296                            {
297                                commentposition = getNextCommentPosition(line, commentposition + 1);
298                            }
299                            std::string value = "", comment = "";
300                            if (commentposition == std::string::npos)
301                            {
302                                value = removeTrailingWhitespaces(line.substr(pos1 + 1));
303                            }
304                            else
305                            {
306                                value = removeTrailingWhitespaces(line.substr(pos1 + 1, commentposition - pos1 - 1));
307                                comment = removeTrailingWhitespaces(line.substr(commentposition));
308                            }
309
310                            if (pos2 != std::string::npos && pos3 != std::string::npos && pos3 > pos2 + 1)
311                            {
312                                // There might be an array index
313                                unsigned int index = 0;
314                                if (ConvertValue(&index, line.substr(pos2 + 1, pos3 - pos2 - 1)))
315                                {
316                                    // New array
317                                    std::list<ConfigFileEntry*>::iterator it = newsection->getEntryIterator(getStripped(line.substr(0, pos2)), index, value, false);
318                                    (*it)->setValue(value);
319                                    (*it)->setComment(comment);
320                                    continue;
321                                }
322                            }
323
324                            // New value
325                            newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryValue(getStripped(line.substr(0, pos1)), value, false, comment));
326                            continue;
327                        }
328                    }
329                }
330            }
331
332            file.close();
333
334            COUT(3) << "Loaded config file \"" << this->filename_ << "\"." << std::endl;
335
336            // Save the file in case something changed (like stripped whitespaces)
337            this->save();
338
339            // Update all ConfigValueContainers
340            this->updateConfigValues();
341        } // end file.is_open()
342    }
343
344    void ConfigFile::save() const
345    {
346        boost::filesystem::path filepath(Core::getConfigPath() / this->filename_);
347
348        std::ofstream file;
349        file.open(filepath.string().c_str(), std::fstream::out);
350        file.setf(std::ios::fixed, std::ios::floatfield);
351        file.precision(6);
352
353        if (!file.is_open())
354        {
355            COUT(1) << "An error occurred in ConfigFileManager.cc:" << std::endl;
356            COUT(1) << "Error: Couldn't open config-file \"" << this->filename_ << "\"." << std::endl;
357            return;
358        }
359
360        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
361        {
362            file << (*it)->getFileEntry() << std::endl;
363
364            for (std::list<ConfigFileEntry*>::const_iterator it_entries = (*it)->getEntriesBegin(); it_entries != (*it)->getEntriesEnd(); ++it_entries)
365            {
366                file << (*it_entries)->getFileEntry() << std::endl;
367            }
368
369            file << std::endl;
370        }
371
372        file.close();
373
374        COUT(4) << "Saved config file \"" << this->filename_ << "\"." << std::endl;
375    }
376
377    void ConfigFile::saveAs(const std::string& filename)
378    {
379        std::string temp = this->filename_;
380        this->filename_ = filename;
381        this->save();
382        this->filename_ = temp;
383    }
384
385    void ConfigFile::clean(bool bCleanComments)
386    {
387        for (std::list<ConfigFileSection*>::iterator it1 = this->sections_.begin(); it1 != this->sections_.end(); )
388        {
389            std::map<std::string, Identifier*>::const_iterator it2 = Identifier::getIdentifierMap().find((*it1)->getName());
390            if (it2 != Identifier::getIdentifierMapEnd() && (*it2).second->hasConfigValues())
391            {
392                // The section exists, delete comment
393                if (bCleanComments)
394                    (*it1)->setComment("");
395                for (std::list<ConfigFileEntry*>::iterator it3 = (*it1)->entries_.begin(); it3 != (*it1)->entries_.end(); )
396                {
397                    std::map<std::string, ConfigValueContainer*>::const_iterator it4 = (*it2).second->getConfigValueMap().find((*it3)->getName());
398                    if (it4 != (*it2).second->getConfigValueMapEnd())
399                    {
400                        // The config-value exists, delete comment
401                        if (bCleanComments)
402                            (*it3)->setComment("");
403                        ++it3;
404                    }
405                    else
406                    {
407                        // The config-value doesn't exist
408                        delete (*it3);
409                        (*it1)->entries_.erase(it3++);
410                    }
411                }
412                ++it1;
413            }
414            else
415            {
416                // The section doesn't exist
417                delete (*it1);
418                this->sections_.erase(it1++);
419            }
420        }
421
422        // Save the file
423        this->save();
424    }
425
426    void ConfigFile::clear()
427    {
428        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); )
429            delete (*(it++));
430        this->sections_.clear();
431    }
432
433    ConfigFileSection* ConfigFile::getSection(const std::string& section)
434    {
435        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
436            if ((*it)->getName() == section)
437                return (*it);
438
439        this->bUpdated_ = true;
440
441        return (*this->sections_.insert(this->sections_.end(), new ConfigFileSection(section)));
442    }
443
444    void ConfigFile::saveIfUpdated()
445    {
446        bool sectionsUpdated = false;
447
448        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
449        {
450            if ((*it)->bUpdated_)
451            {
452                sectionsUpdated = true;
453                (*it)->bUpdated_ = false;
454            }
455        }
456
457        if (this->bUpdated_ || sectionsUpdated)
458        {
459            this->bUpdated_ = false;
460            this->save();
461        }
462    }
463
464    void ConfigFile::updateConfigValues()
465    {
466        if (this->type_ == ConfigFileType::Settings)
467        {
468            for (std::map<std::string, Identifier*>::const_iterator it = Identifier::getIdentifierMapBegin(); it != Identifier::getIdentifierMapEnd(); ++it)
469            {
470                if (it->second->hasConfigValues())
471                {
472                    for (std::map<std::string, ConfigValueContainer*>::const_iterator it2 = (*it).second->getConfigValueMapBegin(); it2 != (*it).second->getConfigValueMapEnd(); ++it2)
473                        it2->second->update();
474
475                    it->second->updateConfigValues();
476                }
477            }
478        }
479    }
480
481
482    ///////////////////////
483    // ConfigFileManager //
484    ///////////////////////
485
486    ConfigFileManager::ConfigFileManager()
487         : mininmalFreeType_(ConfigFileType::numberOfReservedTypes)
488    {
489        assert(singletonRef_s == 0);
490        singletonRef_s = this;
491    }
492
493    ConfigFileManager::~ConfigFileManager()
494    {
495        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); )
496            delete (it++)->second;
497
498        assert(singletonRef_s != 0);
499        singletonRef_s = 0;
500    }
501
502    void ConfigFileManager::setFilename(ConfigFileType type, const std::string& filename)
503    {
504        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
505        if (it != this->configFiles_.end())
506        {
507            assert(it->second);
508            delete it->second;
509        }
510        this->configFiles_[type] = new ConfigFile(filename, type);
511        this->load(type);
512    }
513
514    void ConfigFileManager::load()
515    {
516        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
517            it->second->load();
518    }
519
520    void ConfigFileManager::save()
521    {
522        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
523            it->second->save();
524    }
525
526    void ConfigFileManager::clean(bool bCleanComments)
527    {
528        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
529            this->clean(it->first, bCleanComments);
530    }
531
532    void ConfigFileManager::load(ConfigFileType type)
533    {
534        this->getFile(type)->load();
535    }
536
537    void ConfigFileManager::save(ConfigFileType type)
538    {
539        this->getFile(type)->save();
540    }
541
542    void ConfigFileManager::saveAs(ConfigFileType type, const std::string& saveFilename)
543    {
544        this->getFile(type)->saveAs(saveFilename);
545    }
546
547    void ConfigFileManager::clean(ConfigFileType type, bool bCleanComments)
548    {
549        this->getFile(type)->clean(bCleanComments);
550    }
551
552    void ConfigFileManager::updateConfigValues()
553    {
554        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
555            it->second->updateConfigValues();
556    }
557
558    void ConfigFileManager::updateConfigValues(ConfigFileType type)
559    {
560        this->getFile(type)->updateConfigValues();
561    }
562
563    const std::string& ConfigFileManager::getFilename(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->getFilename();
568        else
569            return BLANKSTRING;
570    }
571
572    ConfigFile* ConfigFileManager::getFile(ConfigFileType type)
573    {
574        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
575        if (it != this->configFiles_.end())
576            return it->second;
577        else
578        {
579            COUT(1) << "ConfigFileManager: Can't find a config file for type with ID " << (int)type << std::endl;
580            COUT(1) << "Using " << DEFAULT_CONFIG_FILE << " file." << std::endl;
581            this->setFilename(type, DEFAULT_CONFIG_FILE);
582            return getFile(type);
583        }
584    }
585}
Note: See TracBrowser for help on using the repository browser.