#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 destruction
m_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());
}
}