Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gamestate/src/libraries/core/ConfigFileManager.cc @ 6432

Last change on this file since 6432 was 6432, checked in by rgrieder, 14 years ago

Changed the way config values associated with general settings (ConfigFileType::Settings) are handled:

  • ConfigFileManager only handles config files listed in the ConfigFileType enum (normal enum again)
  • ConfigFileManager only takes care of ConfigFiles and returns a pointer to the right one, just two functions left. —> use like: ConfigFileManager::getInstance().getConfigFile(myType)→doSomething();
  • Moved all code (except for the argument completion functions) relating to ConfigFileType::Settings to a new class: SettingsConfigFile, which is a Singleton (it doesn't make sense to have multiple instances unless you start coding a lot more)
  • SettingsConfigFile handles config value containers according to their section and entry in the ini file, not according to class and variables names. (In most cases it will be class and variable names though)
  • SettingsConfigFile supports:
    • clear() (removes any file entries not associated to a config value container)
    • updateConfigValues() (does exactly that through the identifier)
    • config, tconfig and getConfig
    • commands listed above are exported to tolua, and tconfig, config and getConfig were given shortcuts in Lua (e.g. orxonox.config)
  • If you need to organise ConfigFiles yourself, just do it without the ConfigFileManager, like the KeyBinder does.
  • All getValue() functions have been split into getOrCreateValue() and getValue(), which is const
  • Removed obsolete config value management code in the Identifier (it still stores and destroys them and provides access to them)

All of that leads to one HUGE advantage:
"config OutputHandler softDebugLevelInGameConsole"
works now :D (any further implications are up to the reader…)
(it didn't work before because the actual config value container is in the InGameConsole singleton)

  • Property svn:eol-style set to native
File size: 23.0 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 <boost/filesystem.hpp>
32
33#include "util/Convert.h"
34#include "util/Math.h"
35#include "util/StringUtils.h"
36#include "ConsoleCommand.h"
37#include "ConfigValueContainer.h"
38#include "PathConfig.h"
39
40namespace orxonox
41{
42    //////////////////////////
43    // ConfigFileEntryValue //
44    //////////////////////////
45
46    void ConfigFileEntryValue::update()
47    {
48        // Make sure we remove the quotes when bString changes
49        if (this->bString_)
50            this->value_ = stripEnclosingQuotes(this->value_);
51        // Assemble the entry line
52        this->fileEntry_ = this->getKeyString() + " = ";
53        if (this->bString_ && !this->value_.empty())
54            this->fileEntry_ += '"' + addSlashes(this->value_) + '"';
55        else
56            this->fileEntry_ += this->value_;
57        if (!this->additionalComment_.empty())
58            this->fileEntry_ += ' ' + this->additionalComment_;
59    }
60
61
62    ////////////////////////////////
63    // ConfigFileEntryVectorValue //
64    ////////////////////////////////
65    void ConfigFileEntryVectorValue::update()
66    {
67        this->keyString_ = this->name_ + '[' + multi_cast<std::string>(this->index_) + ']';
68        ConfigFileEntryValue::update();
69    }
70
71
72    ///////////////////////
73    // ConfigFileSection //
74    ///////////////////////
75    ConfigFileSection::~ConfigFileSection()
76    {
77        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); )
78            delete (*(it++));
79    }
80
81    void ConfigFileSection::deleteVectorEntries(const std::string& name, unsigned int startindex)
82    {
83        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); )
84        {
85            if (((*it)->getName() == name) && ((*it)->getIndex() >= startindex))
86            {
87                delete (*it);
88                this->entries_.erase(it++);
89            }
90            else
91            {
92                ++it;
93            }
94        }
95    }
96
97    unsigned int ConfigFileSection::getVectorSize(const std::string& name) const
98    {
99        unsigned int size = 0;
100        for (std::list<ConfigFileEntry*>::const_iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
101            if ((*it)->getName() == name)
102                if ((*it)->getIndex() > size)
103                    size = (*it)->getIndex();
104        if (size == 0)
105            return 0;
106        else
107            return (size + 1);
108    }
109
110    std::string ConfigFileSection::getFileEntry() const
111    {
112        if (this->additionalComment_.empty())
113            return ('[' + this->name_ + ']');
114        else
115            return ('[' + this->name_ + "] " + this->additionalComment_);
116    }
117
118    std::list<ConfigFileEntry*>::const_iterator ConfigFileSection::getEntryIterator(const std::string& name) const
119    {
120        for (std::list<ConfigFileEntry*>::const_iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
121        {
122            if ((*it)->getName() == name)
123                return it;
124        }
125        return this->entries_.end();
126    }
127
128    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getOrCreateEntryIterator(const std::string& name, const std::string& fallback, bool bString)
129    {
130        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
131        {
132            if ((*it)->getName() == name)
133            {
134                (*it)->setString(bString);
135                return it;
136            }
137        }
138
139        this->bUpdated_ = true;
140
141        return this->entries_.insert(this->entries_.end(), new ConfigFileEntryValue(name, fallback, bString));
142    }
143
144    std::list<ConfigFileEntry*>::const_iterator ConfigFileSection::getEntryIterator(const std::string& name, unsigned int index) const
145    {
146        for (std::list<ConfigFileEntry*>::const_iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
147        {
148            if (((*it)->getName() == name) && ((*it)->getIndex() == index))
149                return it;
150        }
151        return this->entries_.end();
152    }
153
154    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getOrCreateEntryIterator(const std::string& name, unsigned int index, const std::string& fallback, bool bString)
155    {
156        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
157        {
158            if (((*it)->getName() == name) && ((*it)->getIndex() == index))
159            {
160                (*it)->setString(bString);
161                return it;
162            }
163        }
164
165        this->bUpdated_ = true;
166
167        if (index == 0)
168            return this->entries_.insert(this->entries_.end(), new ConfigFileEntryVectorValue(name, index, fallback, bString));
169        else
170            return this->entries_.insert(++this->getOrCreateEntryIterator(name, index - 1, "", bString), new ConfigFileEntryVectorValue(name, index, fallback, bString));
171    }
172
173
174    ////////////////
175    // ConfigFile //
176    ////////////////
177    ConfigFile::ConfigFile(const std::string& filename)
178        : filename_(filename)
179        , bUpdated_(false)
180    {
181    }
182
183    ConfigFile::~ConfigFile()
184    {
185        this->clear();
186    }
187
188    void ConfigFile::load()
189    {
190        // Be sure we start from new in the memory
191        this->clear();
192
193        // Get default file if necessary and available
194        boost::filesystem::path filepath(PathConfig::getConfigPath() / this->filename_);
195        if (!boost::filesystem::exists(filepath))
196        {
197            // Try to get default one from the data folder
198            boost::filesystem::path defaultFilepath(PathConfig::getDataPath() / "defaultConfig" / this->filename_);
199            if (boost::filesystem::exists(defaultFilepath))
200            {
201                COUT(3) << "Copied " << this->filename_ << " from the defaultConfig folder." << std::endl;
202                boost::filesystem::copy_file(defaultFilepath, filepath);
203            }
204        }
205
206        // Open the file
207        std::ifstream file;
208        file.open(filepath.string().c_str(), std::fstream::in);
209        if (file.is_open())
210        {
211            ConfigFileSection* newsection = 0;
212
213            while (file.good() && !file.eof())
214            {
215                std::string line;
216                std::getline(file, line);
217
218                const std::string& temp = getStripped(line);
219                if (!isEmpty(temp) && !isComment(temp))
220                {
221                    size_t   pos1 = temp.find('[');
222                    if (pos1 == 0) pos1 = line.find('['); else pos1 = std::string::npos;
223                    size_t   pos2 = line.find(']');
224
225                    if (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1)
226                    {
227                        // New section
228                        const std::string& comment = line.substr(pos2 + 1);
229                        if (isComment(comment))
230                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1), comment);
231                        else
232                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1));
233                        this->sections_.insert(this->sections_.end(), newsection);
234                        continue;
235                    }
236                }
237
238                if (newsection != 0)
239                {
240                    if (isComment(line))
241                    {
242                        // New comment
243                        newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryComment(removeTrailingWhitespaces(line)));
244                        continue;
245                    }
246                    else
247                    {
248                        size_t pos1 = line.find('=');
249
250                        if (pos1 != std::string::npos && pos1 > 0)
251                        {
252                            // New entry
253                            size_t pos2 = line.find('[');
254                            size_t pos3 = line.find(']');
255                            size_t commentposition = getNextCommentPosition(line, pos1 + 1);
256                            while (isBetweenQuotes(line, commentposition))
257                            {
258                                commentposition = getNextCommentPosition(line, commentposition + 1);
259                            }
260                            std::string value, comment;
261                            if (commentposition == std::string::npos)
262                            {
263                                value = removeTrailingWhitespaces(line.substr(pos1 + 1));
264                            }
265                            else
266                            {
267                                value = removeTrailingWhitespaces(line.substr(pos1 + 1, commentposition - pos1 - 1));
268                                comment = removeTrailingWhitespaces(line.substr(commentposition));
269                            }
270
271                            if (pos2 != std::string::npos && pos3 != std::string::npos && pos3 > pos2 + 1)
272                            {
273                                // There might be an array index
274                                unsigned int index = 0;
275                                if (convertValue(&index, line.substr(pos2 + 1, pos3 - pos2 - 1)))
276                                {
277                                    // New array
278                                    std::list<ConfigFileEntry*>::iterator it = newsection->getOrCreateEntryIterator(getStripped(line.substr(0, pos2)), index, value, false);
279                                    (*it)->setValue(value);
280                                    (*it)->setComment(comment);
281                                    continue;
282                                }
283                            }
284
285                            // New value
286                            newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryValue(getStripped(line.substr(0, pos1)), value, false, comment));
287                            continue;
288                        }
289                    }
290                }
291            }
292
293            file.close();
294
295            COUT(3) << "Loaded config file \"" << this->filename_ << "\"." << std::endl;
296
297            // Save the file in case something changed (like stripped white space)
298            this->save();
299        } // end file.is_open()
300    }
301
302    void ConfigFile::save() const
303    {
304        this->saveAs(this->filename_);
305    }
306
307    void ConfigFile::saveAs(const std::string& filename) const
308    {
309        std::ofstream file;
310        file.open((PathConfig::getConfigPathString() + filename).c_str(), std::fstream::out);
311        file.setf(std::ios::fixed, std::ios::floatfield);
312        file.precision(6);
313
314        if (!file.is_open())
315        {
316            COUT(1) << "Error: Couldn't open config-file \"" << filename << "\"." << std::endl;
317            return;
318        }
319
320        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
321        {
322            file << (*it)->getFileEntry() << std::endl;
323
324            for (std::list<ConfigFileEntry*>::const_iterator it_entries = (*it)->getEntriesBegin(); it_entries != (*it)->getEntriesEnd(); ++it_entries)
325                file << (*it_entries)->getFileEntry() << std::endl;
326
327            file << std::endl;
328        }
329
330        file.close();
331
332        COUT(4) << "Saved config file \"" << filename << "\"." << std::endl;
333    }
334
335    void ConfigFile::clear()
336    {
337        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); )
338            delete (*(it++));
339        this->sections_.clear();
340    }
341
342    const std::string& ConfigFile::getOrCreateValue(const std::string& section, const std::string& name, const std::string& fallback, bool bString)
343    {
344        const std::string& output = this->getOrCreateSection(section)->getOrCreateValue(name, fallback, bString);
345        this->saveIfUpdated();
346        return output;
347    }
348
349    const std::string& ConfigFile::getOrCreateValue(const std::string& section, const std::string& name, unsigned int index, const std::string& fallback, bool bString)
350    {
351        const std::string& output = this->getOrCreateSection(section)->getOrCreateValue(name, index, fallback, bString);
352        this->saveIfUpdated();
353        return output;
354    }
355
356    void ConfigFile::deleteVectorEntries(const std::string& section, const std::string& name, unsigned int startindex)
357    {
358        if (ConfigFileSection* sectionPtr = this->getSection(section))
359        {
360            sectionPtr->deleteVectorEntries(name, startindex);
361            this->save();
362        }
363    }
364
365    ConfigFileSection* ConfigFile::getSection(const std::string& section) const
366    {
367        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
368            if ((*it)->getName() == section)
369                return (*it);
370        return NULL;
371    }
372
373    ConfigFileSection* ConfigFile::getOrCreateSection(const std::string& section)
374    {
375        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
376            if ((*it)->getName() == section)
377                return (*it);
378
379        this->bUpdated_ = true;
380
381        return (*this->sections_.insert(this->sections_.end(), new ConfigFileSection(section)));
382    }
383
384    void ConfigFile::saveIfUpdated()
385    {
386        bool sectionsUpdated = false;
387
388        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
389        {
390            if ((*it)->bUpdated_)
391            {
392                sectionsUpdated = true;
393                (*it)->bUpdated_ = false;
394            }
395        }
396
397        if (this->bUpdated_ || sectionsUpdated)
398        {
399            this->bUpdated_ = false;
400            this->save();
401        }
402    }
403
404
405    ////////////////////////
406    // SettingsConfigFile //
407    ////////////////////////
408
409    SettingsConfigFile* SettingsConfigFile::singletonPtr_s = 0;
410
411    SettingsConfigFile::SettingsConfigFile(const std::string& filename)
412        : ConfigFile(filename)
413    {
414        ConsoleCommand* command = createConsoleCommand(createFunctor(&ConfigFile::load, this), "reloadSettings");
415        CommandExecutor::addConsoleCommandShortcut(command);
416        command = createConsoleCommand(createFunctor(&SettingsConfigFile::setFilename, this), "setSettingsFile");
417        CommandExecutor::addConsoleCommandShortcut(command);
418        command = createConsoleCommand(createFunctor(&SettingsConfigFile::config, this), "config");
419        CommandExecutor::addConsoleCommandShortcut(command).argumentCompleter(0, autocompletion::settingssections()).argumentCompleter(1, autocompletion::settingsentries()).argumentCompleter(2, autocompletion::settingsvalue());
420        command = createConsoleCommand(createFunctor(&SettingsConfigFile::tconfig, this), "tconfig");
421        CommandExecutor::addConsoleCommandShortcut(command).argumentCompleter(0, autocompletion::settingssections()).argumentCompleter(1, autocompletion::settingsentries()).argumentCompleter(2, autocompletion::settingsvalue());
422        command = createConsoleCommand(createFunctor(&SettingsConfigFile::getConfig, this), "getConfig");
423        CommandExecutor::addConsoleCommandShortcut(command).argumentCompleter(0, autocompletion::settingssections()).argumentCompleter(1, autocompletion::settingsentries());
424    }
425
426    SettingsConfigFile::~SettingsConfigFile()
427    {
428    }
429
430    void SettingsConfigFile::load()
431    {
432        ConfigFile::load();
433        this->updateConfigValues();
434    }
435
436    void SettingsConfigFile::setFilename(const std::string& filename)
437    {
438        ConfigFileManager::getInstance().setFilename(ConfigFileType::Settings, filename);
439    }
440
441    void SettingsConfigFile::addConfigValueContainer(ConfigValueContainer* container)
442    {
443        if (container == NULL)
444            return;
445        std::pair<std::string, ConfigValueContainer*> second(getLowercase(container->getName()), container);
446        this->containers_.insert(std::make_pair(getLowercase(container->getSectionName()), second));
447        this->sectionNames_.insert(container->getSectionName());
448    }
449
450    void SettingsConfigFile::removeConfigValueContainer(ConfigValueContainer* container)
451    {
452        if (container == NULL)
453            return;
454        const std::string& sectionLC = getLowercase(container->getSectionName());
455        ContainerMap::iterator upper = this->containers_.upper_bound(sectionLC);
456        for (ContainerMap::iterator it = this->containers_.lower_bound(sectionLC); it != upper; ++it)
457        {
458            if (it->second.second == container)
459            {
460                // Remove entry from section name set this was the last container for that section
461                if (upper == this->containers_.lower_bound(sectionLC))
462                    this->sectionNames_.erase(container->getSectionName());
463                this->containers_.erase(it);
464                break;
465            }
466        }
467    }
468
469    void SettingsConfigFile::updateConfigValues()
470    {
471        for (ContainerMap::const_iterator it = this->containers_.begin(); it != this->containers_.end(); ++it)
472        {
473            it->second.second->update();
474            it->second.second->getIdentifier()->updateConfigValues();
475        }
476    }
477
478    void SettingsConfigFile::clean(bool bCleanComments)
479    {
480        for (std::list<ConfigFileSection*>::iterator itSection = this->sections_.begin(); itSection != this->sections_.end(); )
481        {
482            const std::string& sectionLC = getLowercase((*itSection)->getName());
483            ContainerMap::const_iterator lower = this->containers_.lower_bound(sectionLC);
484            ContainerMap::const_iterator upper = this->containers_.upper_bound(sectionLC);
485            if (lower != upper)
486            {
487                // The section exists, delete comment
488                if (bCleanComments)
489                    (*itSection)->setComment("");
490                for (std::list<ConfigFileEntry*>::iterator itEntry = (*itSection)->entries_.begin(); itEntry != (*itSection)->entries_.end(); )
491                {
492                    const std::string& entryLC = getLowercase((*itEntry)->getName());
493                    bool bFound = false;
494                    for (ContainerMap::const_iterator itContainer = lower; itContainer != upper; ++itContainer)
495                    {
496                        if (itContainer->second.first == entryLC)
497                        {
498                            // The config-value exists, delete comment
499                            if (bCleanComments)
500                                (*itEntry)->setComment("");
501                            ++itEntry;
502                            bFound = true;
503                            break;
504                        }
505                    }
506                    if (!bFound)
507                    {
508                        // The config-value doesn't exist
509                        delete (*itEntry);
510                        (*itSection)->entries_.erase(itEntry++);
511                    }
512                }
513                ++itSection;
514            }
515            else
516            {
517                // The section doesn't exist
518                delete (*itSection);
519                this->sections_.erase(itSection++);
520            }
521        }
522
523        // Save the file
524        this->save();
525    }
526
527    bool SettingsConfigFile::config(const std::string& section, const std::string& entry, const std::string& value)
528    {
529        return this->configImpl(section, entry, value, &ConfigValueContainer::set);
530    }
531
532    bool SettingsConfigFile::tconfig(const std::string& section, const std::string& entry, const std::string& value)
533    {
534        return this->configImpl(section, entry, value, &ConfigValueContainer::tset);
535    }
536
537    bool SettingsConfigFile::configImpl(const std::string& section, const std::string& entry, const std::string& value, bool (ConfigValueContainer::*function)(const MultiType&))
538    {
539        const std::string& sectionLC = getLowercase(section);
540        const std::string& entryLC = getLowercase(entry);
541        ContainerMap::iterator upper = this->containers_.upper_bound(sectionLC);
542        for (ContainerMap::iterator it = this->containers_.lower_bound(sectionLC); it != upper; ++it)
543        {
544            // Note: Config value vectors cannot be supported
545            if (it->second.first == entryLC && !it->second.second->isVector())
546            {
547                return (it->second.second->*function)(value);
548            }
549        }
550        return false;
551    }
552
553    std::string SettingsConfigFile::getConfig(const std::string& section, const std::string& entry)
554    {
555        const std::string& sectionLC = getLowercase(section);
556        const std::string& entryLC = getLowercase(entry);
557        ContainerMap::iterator upper = this->containers_.upper_bound(sectionLC);
558        for (ContainerMap::iterator it = this->containers_.lower_bound(sectionLC); it != upper; ++it)
559        {
560            // Note: Config value vectors cannot be supported
561            if (it->second.first == entryLC && ! it->second.second->isVector())
562            {
563                std::string value;
564                it->second.second->getValue<std::string, OrxonoxClass>(&value, NULL);
565                return value;
566            }
567        }
568        return "";
569    }
570
571
572    ///////////////////////
573    // ConfigFileManager //
574    ///////////////////////
575
576    ConfigFileManager* ConfigFileManager::singletonPtr_s = 0;
577
578    ConfigFileManager::ConfigFileManager()
579    {
580        this->configFiles_.assign(NULL);
581    }
582
583    ConfigFileManager::~ConfigFileManager()
584    {
585        for (boost::array<ConfigFile*, 3>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
586            if (*it)
587                delete (*it);
588    }
589
590    void ConfigFileManager::setFilename(ConfigFileType::Value type, const std::string& filename)
591    {
592        if (this->getConfigFile(type))
593            delete this->configFiles_[type];
594        // Create and load config file
595        switch (type)
596        {
597        case ConfigFileType::Settings:
598            this->configFiles_[type] = new SettingsConfigFile(filename);
599            break;
600        case ConfigFileType::JoyStickCalibration:
601        case ConfigFileType::CommandHistory:
602            this->configFiles_[type] = new ConfigFile(filename);
603            break;
604        }
605        this->configFiles_[type]->load();
606    }
607}
Note: See TracBrowser for help on using the repository browser.