Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/consolecommands3/src/libraries/core/ConfigFileManager.cc @ 7196

Last change on this file since 7196 was 7190, checked in by landauf, 14 years ago

removed unnecessary return values from console commands "log", "error", "warning", "info", and "debug"
removed return value from console command Chat (the non-static chat functions still return a bool)
config and tconfig don't return a bool anymore but instead print an error if the config value doesn't exist.

fixed console command "printRTT" - it shouldn't crash on a standalone system.

  • Property svn:eol-style set to native
File size: 23.9 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    ConfigFileEntry* ConfigFileSection::getEntry(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 NULL;
126    }
127
128    ConfigFileEntry* ConfigFileSection::getEntry(const std::string& name, unsigned int index) const
129    {
130        for (std::list<ConfigFileEntry*>::const_iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
131        {
132            if (((*it)->getName() == name) && ((*it)->getIndex() == index))
133                return *it;
134        }
135        return NULL;
136    }
137
138    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getOrCreateEntryIterator(const std::string& name, const std::string& fallback, bool bString)
139    {
140        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
141        {
142            if ((*it)->getName() == name)
143            {
144                (*it)->setString(bString);
145                return it;
146            }
147        }
148
149        this->bUpdated_ = true;
150
151        return this->entries_.insert(this->entries_.end(), new ConfigFileEntryValue(name, fallback, bString));
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
178    const char* ConfigFile::DEFAULT_CONFIG_FOLDER = "defaultConfig";
179
180    ConfigFile::ConfigFile(const std::string& filename, bool bCopyFallbackFile)
181        : filename_(filename)
182        , bCopyFallbackFile_(bCopyFallbackFile)
183        , bUpdated_(false)
184    {
185    }
186
187    ConfigFile::~ConfigFile()
188    {
189        this->clear();
190    }
191
192    void ConfigFile::load()
193    {
194        // Be sure we start from new in the memory
195        this->clear();
196
197        boost::filesystem::path filepath(this->filename_);
198        if (!filepath.is_complete())
199        {
200            filepath = PathConfig::getConfigPath() / filepath;
201            if (this->bCopyFallbackFile_)
202            {
203                // Look for default file in the data folder
204                if (!boost::filesystem::exists(filepath))
205                {
206                    boost::filesystem::path defaultFilepath(PathConfig::getDataPath() / DEFAULT_CONFIG_FOLDER / this->filename_);
207                    if (boost::filesystem::exists(defaultFilepath))
208                    {
209                        // Try to copy default file from the data folder
210                        try
211                        {
212                            boost::filesystem::copy_file(defaultFilepath, filepath);
213                            COUT(3) << "Copied " << this->filename_ << " from the default config folder." << std::endl;
214                        }
215                        catch (const boost::filesystem::filesystem_error& ex)
216                        { COUT(1) << "Error in ConfigFile: " << ex.what() << std::endl; }
217                    }
218                }
219            }
220        }
221
222        // Open the file
223        std::ifstream file;
224        file.open(filepath.string().c_str(), std::fstream::in);
225        if (file.is_open())
226        {
227            ConfigFileSection* newsection = 0;
228
229            while (file.good() && !file.eof())
230            {
231                std::string line;
232                std::getline(file, line);
233
234                const std::string& temp = getStripped(line);
235                if (!isEmpty(temp) && !isComment(temp))
236                {
237                    size_t   pos1 = temp.find('[');
238                    if (pos1 == 0) pos1 = line.find('['); else pos1 = std::string::npos;
239                    size_t   pos2 = line.find(']');
240
241                    if (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1)
242                    {
243                        // New section
244                        const std::string& comment = line.substr(pos2 + 1);
245                        if (isComment(comment))
246                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1), comment);
247                        else
248                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1));
249                        this->sections_.insert(this->sections_.end(), newsection);
250                        continue;
251                    }
252                }
253
254                if (newsection != 0)
255                {
256                    if (isComment(line))
257                    {
258                        // New comment
259                        newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryComment(removeTrailingWhitespaces(line)));
260                        continue;
261                    }
262                    else
263                    {
264                        size_t pos1 = line.find('=');
265
266                        if (pos1 != std::string::npos && pos1 > 0)
267                        {
268                            // New entry
269                            size_t pos2 = line.find('[');
270                            size_t pos3 = line.find(']');
271                            size_t commentposition = getNextCommentPosition(line, pos1 + 1);
272                            while (isBetweenQuotes(line, commentposition))
273                            {
274                                commentposition = getNextCommentPosition(line, commentposition + 1);
275                            }
276                            std::string value, comment;
277                            if (commentposition == std::string::npos)
278                            {
279                                value = removeTrailingWhitespaces(line.substr(pos1 + 1));
280                            }
281                            else
282                            {
283                                value = removeTrailingWhitespaces(line.substr(pos1 + 1, commentposition - pos1 - 1));
284                                comment = removeTrailingWhitespaces(line.substr(commentposition));
285                            }
286
287                            if (pos2 != std::string::npos && pos3 != std::string::npos && pos3 > pos2 + 1)
288                            {
289                                // There might be an array index
290                                unsigned int index = 0;
291                                if (convertValue(&index, line.substr(pos2 + 1, pos3 - pos2 - 1)))
292                                {
293                                    // New array
294                                    std::list<ConfigFileEntry*>::iterator it = newsection->getOrCreateEntryIterator(getStripped(line.substr(0, pos2)), index, value, false);
295                                    (*it)->setValue(value);
296                                    (*it)->setComment(comment);
297                                    continue;
298                                }
299                            }
300
301                            // New value
302                            newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryValue(getStripped(line.substr(0, pos1)), value, false, comment));
303                            continue;
304                        }
305                    }
306                }
307            }
308
309            file.close();
310
311            COUT(3) << "Loaded config file \"" << this->filename_ << "\"." << std::endl;
312
313            // DO NOT save the file --> we can open supposedly read only config files
314        } // end file.is_open()
315    }
316
317    void ConfigFile::save() const
318    {
319        this->saveAs(this->filename_);
320    }
321
322    void ConfigFile::saveAs(const std::string& filename) const
323    {
324        boost::filesystem::path filepath(filename);
325        if (!filepath.is_complete())
326            filepath = PathConfig::getConfigPath() / filename;
327        std::ofstream file;
328        file.open(filepath.string().c_str(), std::fstream::out);
329        file.setf(std::ios::fixed, std::ios::floatfield);
330        file.precision(6);
331
332        if (!file.is_open())
333        {
334            COUT(1) << "Error: Couldn't open config-file \"" << filename << "\"." << std::endl;
335            return;
336        }
337
338        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
339        {
340            file << (*it)->getFileEntry() << std::endl;
341
342            for (std::list<ConfigFileEntry*>::const_iterator it_entries = (*it)->getEntriesBegin(); it_entries != (*it)->getEntriesEnd(); ++it_entries)
343                file << (*it_entries)->getFileEntry() << std::endl;
344
345            file << std::endl;
346        }
347
348        file.close();
349
350        COUT(4) << "Saved config file \"" << filename << "\"." << std::endl;
351    }
352
353    void ConfigFile::clear()
354    {
355        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); )
356            delete (*(it++));
357        this->sections_.clear();
358    }
359
360    const std::string& ConfigFile::getOrCreateValue(const std::string& section, const std::string& name, const std::string& fallback, bool bString)
361    {
362        const std::string& output = this->getOrCreateSection(section)->getOrCreateValue(name, fallback, bString);
363        this->saveIfUpdated();
364        return output;
365    }
366
367    const std::string& ConfigFile::getOrCreateValue(const std::string& section, const std::string& name, unsigned int index, const std::string& fallback, bool bString)
368    {
369        const std::string& output = this->getOrCreateSection(section)->getOrCreateValue(name, index, fallback, bString);
370        this->saveIfUpdated();
371        return output;
372    }
373
374    void ConfigFile::deleteVectorEntries(const std::string& section, const std::string& name, unsigned int startindex)
375    {
376        if (ConfigFileSection* sectionPtr = this->getSection(section))
377        {
378            sectionPtr->deleteVectorEntries(name, startindex);
379            this->save();
380        }
381    }
382
383    ConfigFileSection* ConfigFile::getSection(const std::string& section) const
384    {
385        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
386            if ((*it)->getName() == section)
387                return (*it);
388        return NULL;
389    }
390
391    ConfigFileSection* ConfigFile::getOrCreateSection(const std::string& section)
392    {
393        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
394            if ((*it)->getName() == section)
395                return (*it);
396
397        this->bUpdated_ = true;
398
399        return (*this->sections_.insert(this->sections_.end(), new ConfigFileSection(section)));
400    }
401
402    void ConfigFile::saveIfUpdated()
403    {
404        bool sectionsUpdated = false;
405
406        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
407        {
408            if ((*it)->bUpdated_)
409            {
410                sectionsUpdated = true;
411                (*it)->bUpdated_ = false;
412            }
413        }
414
415        if (this->bUpdated_ || sectionsUpdated)
416        {
417            this->bUpdated_ = false;
418            this->save();
419        }
420    }
421
422
423    ////////////////////////
424    // SettingsConfigFile //
425    ////////////////////////
426
427    SettingsConfigFile* SettingsConfigFile::singletonPtr_s = 0;
428
429    SettingsConfigFile::SettingsConfigFile(const std::string& filename)
430        : ConfigFile(filename)
431    {
432        ConsoleCommand* command = createConsoleCommand(createFunctor(&ConfigFile::load, this), "reloadSettings");
433        CommandExecutor::addConsoleCommandShortcut(command);
434        command = createConsoleCommand(createFunctor(&SettingsConfigFile::setFilename, this), "setSettingsFile");
435        CommandExecutor::addConsoleCommandShortcut(command);
436        command = createConsoleCommand(createFunctor(&SettingsConfigFile::config, this), "config");
437        CommandExecutor::addConsoleCommandShortcut(command).argumentCompleter(0, autocompletion::settingssections()).argumentCompleter(1, autocompletion::settingsentries()).argumentCompleter(2, autocompletion::settingsvalue());
438        command = createConsoleCommand(createFunctor(&SettingsConfigFile::tconfig, this), "tconfig");
439        CommandExecutor::addConsoleCommandShortcut(command).argumentCompleter(0, autocompletion::settingssections()).argumentCompleter(1, autocompletion::settingsentries()).argumentCompleter(2, autocompletion::settingsvalue());
440        command = createConsoleCommand(createFunctor(&SettingsConfigFile::getConfig, this), "getConfig");
441        CommandExecutor::addConsoleCommandShortcut(command).argumentCompleter(0, autocompletion::settingssections()).argumentCompleter(1, autocompletion::settingsentries());
442    }
443
444    SettingsConfigFile::~SettingsConfigFile()
445    {
446    }
447
448    void SettingsConfigFile::load()
449    {
450        ConfigFile::load();
451        this->updateConfigValues();
452    }
453
454    void SettingsConfigFile::setFilename(const std::string& filename)
455    {
456        ConfigFileManager::getInstance().setFilename(ConfigFileType::Settings, filename);
457    }
458
459    void SettingsConfigFile::addConfigValueContainer(ConfigValueContainer* container)
460    {
461        if (container == NULL)
462            return;
463        std::pair<std::string, ConfigValueContainer*> second(getLowercase(container->getName()), container);
464        this->containers_.insert(std::make_pair(getLowercase(container->getSectionName()), second));
465        this->sectionNames_.insert(container->getSectionName());
466    }
467
468    void SettingsConfigFile::removeConfigValueContainer(ConfigValueContainer* container)
469    {
470        if (container == NULL)
471            return;
472        const std::string& sectionLC = getLowercase(container->getSectionName());
473        ContainerMap::iterator upper = this->containers_.upper_bound(sectionLC);
474        for (ContainerMap::iterator it = this->containers_.lower_bound(sectionLC); it != upper; ++it)
475        {
476            if (it->second.second == container)
477            {
478                // Remove entry from section name set this was the last container for that section
479                if (upper == this->containers_.lower_bound(sectionLC))
480                    this->sectionNames_.erase(container->getSectionName());
481                this->containers_.erase(it);
482                break;
483            }
484        }
485    }
486
487    void SettingsConfigFile::updateConfigValues()
488    {
489        for (ContainerMap::const_iterator it = this->containers_.begin(); it != this->containers_.end(); ++it)
490        {
491            it->second.second->update();
492            it->second.second->getIdentifier()->updateConfigValues();
493        }
494    }
495
496    void SettingsConfigFile::clean(bool bCleanComments)
497    {
498        for (std::list<ConfigFileSection*>::iterator itSection = this->sections_.begin(); itSection != this->sections_.end(); )
499        {
500            const std::string& sectionLC = getLowercase((*itSection)->getName());
501            ContainerMap::const_iterator lower = this->containers_.lower_bound(sectionLC);
502            ContainerMap::const_iterator upper = this->containers_.upper_bound(sectionLC);
503            if (lower != upper)
504            {
505                // The section exists, delete comment
506                if (bCleanComments)
507                    (*itSection)->setComment("");
508                for (std::list<ConfigFileEntry*>::iterator itEntry = (*itSection)->entries_.begin(); itEntry != (*itSection)->entries_.end(); )
509                {
510                    const std::string& entryLC = getLowercase((*itEntry)->getName());
511                    bool bFound = false;
512                    for (ContainerMap::const_iterator itContainer = lower; itContainer != upper; ++itContainer)
513                    {
514                        if (itContainer->second.first == entryLC)
515                        {
516                            // The config-value exists, delete comment
517                            if (bCleanComments)
518                                (*itEntry)->setComment("");
519                            ++itEntry;
520                            bFound = true;
521                            break;
522                        }
523                    }
524                    if (!bFound)
525                    {
526                        // The config-value doesn't exist
527                        delete (*itEntry);
528                        (*itSection)->entries_.erase(itEntry++);
529                    }
530                }
531                ++itSection;
532            }
533            else
534            {
535                // The section doesn't exist
536                delete (*itSection);
537                this->sections_.erase(itSection++);
538            }
539        }
540
541        // Save the file
542        this->save();
543    }
544
545    void SettingsConfigFile::config(const std::string& section, const std::string& entry, const std::string& value)
546    {
547        if (!this->configImpl(section, entry, value, &ConfigValueContainer::set))
548            COUT(1) << "Error: Config value \"" << entry << "\" in section \"" << section << "\" doesn't exist." << std::endl;
549    }
550
551    void SettingsConfigFile::tconfig(const std::string& section, const std::string& entry, const std::string& value)
552    {
553        if (!this->configImpl(section, entry, value, &ConfigValueContainer::tset))
554            COUT(1) << "Error: Config value \"" << entry << "\" in section \"" << section << "\" doesn't exist." << std::endl;
555    }
556
557    bool SettingsConfigFile::configImpl(const std::string& section, const std::string& entry, const std::string& value, bool (ConfigValueContainer::*function)(const MultiType&))
558    {
559        const std::string& sectionLC = getLowercase(section);
560        const std::string& entryLC = getLowercase(entry);
561        ContainerMap::iterator upper = this->containers_.upper_bound(sectionLC);
562        for (ContainerMap::iterator it = this->containers_.lower_bound(sectionLC); it != upper; ++it)
563        {
564            // Note: Config value vectors cannot be supported
565            if (it->second.first == entryLC && !it->second.second->isVector())
566            {
567                return (it->second.second->*function)(value);
568            }
569        }
570        return false;
571    }
572
573    std::string SettingsConfigFile::getConfig(const std::string& section, const std::string& entry)
574    {
575        const std::string& sectionLC = getLowercase(section);
576        const std::string& entryLC = getLowercase(entry);
577        ContainerMap::iterator upper = this->containers_.upper_bound(sectionLC);
578        for (ContainerMap::iterator it = this->containers_.lower_bound(sectionLC); it != upper; ++it)
579        {
580            // Note: Config value vectors cannot be supported
581            if (it->second.first == entryLC && ! it->second.second->isVector())
582            {
583                std::string value;
584                it->second.second->getValue<std::string, OrxonoxClass>(&value, NULL);
585                return value;
586            }
587        }
588        return "";
589    }
590
591
592    ///////////////////////
593    // ConfigFileManager //
594    ///////////////////////
595
596    ConfigFileManager* ConfigFileManager::singletonPtr_s = 0;
597
598    ConfigFileManager::ConfigFileManager()
599    {
600        this->configFiles_.assign(NULL);
601    }
602
603    ConfigFileManager::~ConfigFileManager()
604    {
605        for (boost::array<ConfigFile*, 3>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
606            if (*it)
607                delete (*it);
608    }
609
610    void ConfigFileManager::setFilename(ConfigFileType::Value type, const std::string& filename)
611    {
612        if (this->getConfigFile(type))
613            delete this->configFiles_[type];
614        // Create and load config file
615        switch (type)
616        {
617        case ConfigFileType::Settings:
618            this->configFiles_[type] = new SettingsConfigFile(filename);
619            break;
620        case ConfigFileType::JoyStickCalibration:
621        case ConfigFileType::CommandHistory:
622            this->configFiles_[type] = new ConfigFile(filename);
623            break;
624        }
625        this->configFiles_[type]->load();
626    }
627}
Note: See TracBrowser for help on using the repository browser.