#ifndef SH_CONFIG_LOADER_H__#define SH_CONFIG_LOADER_H__#include <map>#include <vector>#include <cassert>#include <string>
namespace sh
{class ConfigNode;
class ConfigLoader
{public:
static void loadAllFiles(ConfigLoader* c, const std::string& path);ConfigLoader(const std::string& fileEnding);virtual ~ConfigLoader();
std::string m_fileEnding;
// For a line like
// entity animals/dog
// {
// ...
// }
// The type is "entity" and the name is "animals/dog"
// Or if animal/dog was not there then name is ""
virtual ConfigNode *getConfigScript (const std::string &name);virtual std::map <std::string, ConfigNode*> getAllConfigScripts ();virtual void parseScript(std::ifstream &stream);protected:
float m_LoadOrder;
// like "*.object"
std::map <std::string, ConfigNode*> m_scriptList;
enum Token
{TOKEN_Text,TOKEN_NewLine,TOKEN_OpenBrace,TOKEN_CloseBrace,TOKEN_EOF,};Token tok, lastTok;std::string tokVal, lastTokVal;
void _parseNodes(std::ifstream &stream, ConfigNode *parent);
void _nextToken(std::ifstream &stream);
void _skipNewLines(std::ifstream &stream);
virtual void clearScriptList();};class ConfigNode
{public:
ConfigNode(ConfigNode *parent, const std::string &name = "untitled");~ConfigNode();inline void setName(const std::string &name){this->m_name = name;
}inline std::string &getName(){return m_name;
}inline void addValue(const std::string &value){m_values.push_back(value);}inline void clearValues(){m_values.clear();}inline std::vector<std::string> &getValues(){return m_values;
}inline const std::string &getValue(unsigned int index = 0){assert(index < m_values.size());return m_values[index];
}ConfigNode *addChild(const std::string &name = "untitled", bool replaceExisting = false);ConfigNode *findChild(const std::string &name, bool recursive = false);inline std::vector<ConfigNode*> &getChildren()
{return m_children;
}inline ConfigNode *getChild(unsigned int index = 0){assert(index < m_children.size());return m_children[index];
}void setParent(ConfigNode *newParent);
inline ConfigNode *getParent()
{return m_parent;
}private:
std::string m_name;
std::vector<std::string> m_values;
std::vector<ConfigNode*> m_children;ConfigNode *m_parent;int m_lastChildFound; //The last child node's index found with a call to findChild()std::vector<ConfigNode*>::iterator _iter;bool _removeSelf;
};}#endif
//cpp
#include "ConfigLoader.hpp"
#include <vector>#include <map>#include <exception>#include <fstream>#include <boost/filesystem.hpp>namespace sh
{void ConfigLoader::loadAllFiles(ConfigLoader* c, const std::string& path){for ( boost::filesystem::recursive_directory_iterator end, dir(path); dir != end; ++dir )
{boost::filesystem::path p(*dir);if(p.extension() == c->m_fileEnding)
{std::ifstream in((*dir).path().string().c_str(), std::ios::binary);
c->parseScript(in);}}}ConfigLoader::ConfigLoader(const std::string& fileEnding){//Register as a ScriptLoader
m_fileEnding = fileEnding;}ConfigLoader::~ConfigLoader(){clearScriptList();}void ConfigLoader::clearScriptList()
{std::map <std::string, ConfigNode *>::iterator i;
for (i = m_scriptList.begin(); i != m_scriptList.end(); i++)
{delete i->second;
}m_scriptList.clear();}ConfigNode *ConfigLoader::getConfigScript(const std::string &name){std::map <std::string, ConfigNode*>::iterator i;
std::string key = name;
i = m_scriptList.find(key);//If found..
if (i != m_scriptList.end())
{return i->second;
}else
{return NULL;
}}std::map <std::string, ConfigNode*> ConfigLoader::getAllConfigScripts ()
{return m_scriptList;
}void ConfigLoader::parseScript(std::ifstream &stream)
{//Get first token
_nextToken(stream);if (tok == TOKEN_EOF)
{stream.close();return;
}//Parse the script
_parseNodes(stream, 0);stream.close();}void ConfigLoader::_nextToken(std::ifstream &stream)
{lastTok = tok;lastTokVal = tokVal;//EOF token
if (stream.eof())
{tok = TOKEN_EOF;return;
}//(Get next character)
int ch = stream.get();
if (ch == -1)
{tok = TOKEN_EOF;return;
}while ((ch == ' ' || ch == 9) && !stream.eof())
{ //Skip leading spaces / tabs
ch = stream.get();}if (stream.eof())
{tok = TOKEN_EOF;return;
}//Newline token
if (ch == '\r' || ch == '\n')
{do
{ch = stream.get();} while ((ch == '\r' || ch == '\n') && !stream.eof());
stream.unget();tok = TOKEN_NewLine;return;
}//Open brace token
else if (ch == '{'){tok = TOKEN_OpenBrace;return;
}//Close brace token
else if (ch == '}'){tok = TOKEN_CloseBrace;return;
}//Text token
if (ch < 32 || ch > 122) //Verify valid char{throw std::runtime_error("Parse Error: Invalid character, ConfigLoader::load()");}tokVal = "";tok = TOKEN_Text;do
{//Skip comments
if (ch == '/')
{int ch2 = stream.peek();
//C++ style comment (//)
if (ch2 == '/')
{stream.get();do
{ch = stream.get();} while (ch != '\r' && ch != '\n' && !stream.eof());
tok = TOKEN_NewLine;return;
}}//Add valid char to tokVal
tokVal += (char)ch;
//Next char
ch = stream.get();} while (ch > 32 && ch <= 122 && !stream.eof());
stream.unget();return;
}void ConfigLoader::_skipNewLines(std::ifstream &stream)
{while (tok == TOKEN_NewLine)
{_nextToken(stream);}}void ConfigLoader::_parseNodes(std::ifstream &stream, ConfigNode *parent)
{typedef std::pair<std::string, ConfigNode*> ScriptItem;while (true){switch (tok)
{//Node
case TOKEN_Text:
//Add the new node
ConfigNode *newNode;if (parent)
{newNode = parent->addChild(tokVal);}else
{newNode = new ConfigNode(0, tokVal);
}//Get values
_nextToken(stream);while (tok == TOKEN_Text)
{newNode->addValue(tokVal);_nextToken(stream);}//Add root nodes to scriptList
if (!parent){
std::string key;
if (newNode->getValues().empty())
{key = newNode->getName() + ' ';}else
{key = newNode->getName() + ' ' + newNode->getValues().front();}m_scriptList.insert(ScriptItem(key, newNode));}_skipNewLines(stream);//Add any sub-nodes
if (tok == TOKEN_OpenBrace)
{//Parse nodes
_nextToken(stream);_parseNodes(stream, newNode);//Check for matching closing brace
if (tok != TOKEN_CloseBrace)
{throw std::runtime_error("Parse Error: Expecting closing brace");}_nextToken(stream);_skipNewLines(stream);}break;
//Out of place brace
case TOKEN_OpenBrace:
throw std::runtime_error("Parse Error: Opening brace out of plane");break;
//Return if end of nodes have been reached
case TOKEN_CloseBrace:
return;
//Return if reached end of file
case TOKEN_EOF:
return;
case TOKEN_NewLine:
_nextToken(stream);break;
}};}ConfigNode::ConfigNode(ConfigNode *parent, const std::string &name){m_name = name;m_parent = parent;_removeSelf = true; //For proper destructionm_lastChildFound = -1;//Add self to parent's child list (unless this is the root node being created)
if (parent != NULL)
{m_parent->m_children.push_back(this);
_iter = --(m_parent->m_children.end());}}ConfigNode::~ConfigNode(){//Delete all children
std::vector<ConfigNode*>::iterator i;for (i = m_children.begin(); i != m_children.end(); i++)
{ConfigNode *node = *i;node->_removeSelf = false;
delete node;
}m_children.clear();//Remove self from parent's child list
if (_removeSelf && m_parent != NULL)
{m_parent->m_children.erase(_iter);}}ConfigNode *ConfigNode::addChild(const std::string &name, bool replaceExisting){if (replaceExisting)
{ConfigNode *node = findChild(name, false);
if (node)
{return node;
}}return new ConfigNode(this, name);}ConfigNode *ConfigNode::findChild(const std::string &name, bool recursive){int indx, prevC, nextC;
int childCount = (int)m_children.size();if (m_lastChildFound != -1)
{//If possible, try checking the nodes neighboring the last successful search
//(often nodes searched for in sequence, so this will boost search speeds).
prevC = m_lastChildFound-1; if (prevC < 0) prevC = 0; else if (prevC >= childCount) prevC = childCount-1;nextC = m_lastChildFound+1; if (nextC < 0) nextC = 0; else if (nextC >= childCount) nextC = childCount-1;for (indx = prevC; indx <= nextC; ++indx)
{ConfigNode *node = m_children[indx];if (node->m_name == name)
{m_lastChildFound = indx;return node;
}}//If not found that way, search for the node from start to finish, avoiding the
//already searched area above.
for (indx = nextC + 1; indx < childCount; ++indx)
{ConfigNode *node = m_children[indx];if (node->m_name == name) {
m_lastChildFound = indx;return node;
}}for (indx = 0; indx < prevC; ++indx)
{ConfigNode *node = m_children[indx];if (node->m_name == name) {
m_lastChildFound = indx;return node;
}}}else
{//Search for the node from start to finish
for (indx = 0; indx < childCount; ++indx){
ConfigNode *node = m_children[indx];if (node->m_name == name) {
m_lastChildFound = indx;return node;
}}}//If not found, search child nodes (if recursive == true)
if (recursive)
{for (indx = 0; indx < childCount; ++indx)
{m_children[indx]->findChild(name, recursive);}}//Not found anywhere
return NULL;
}void ConfigNode::setParent(ConfigNode *newParent)
{//Remove self from current parent
m_parent->m_children.erase(_iter);//Set new parent
m_parent = newParent;//Add self to new parent
m_parent->m_children.push_back(this);
_iter = --(m_parent->m_children.end());}}