ConfigFile rewrite

This commit is contained in:
James Turk 2005-01-31 15:44:13 +00:00
parent 77fcb0ad25
commit b69f0daa1a
3 changed files with 421 additions and 33 deletions

View File

@ -17,8 +17,8 @@ IsCpp=1
Icon= Icon=
ExeOutput=..\lib ExeOutput=..\lib
ObjectOutput=..\devcpp ObjectOutput=..\devcpp
OverrideOutput=0 OverrideOutput=1
OverrideOutputName=photon.a OverrideOutputName=libphoton.a
HostApplication= HostApplication=
Folders=include,include/util,src,src/util Folders=include,include/util,src,src/util
CommandLine= CommandLine=
@ -27,7 +27,7 @@ CustomMakefile=
IncludeVersionInfo=0 IncludeVersionInfo=0
SupportXPThemes=0 SupportXPThemes=0
CompilerSet=0 CompilerSet=0
CompilerSettings=0000000000000000000000 CompilerSettings=0000001000000000000000
[Unit1] [Unit1]
FileName=..\include\types.h FileName=..\include\types.h

View File

@ -5,10 +5,13 @@
// James Turk (jpt2433@rit.edu) // James Turk (jpt2433@rit.edu)
// //
// Version: // Version:
// $Id: ConfigFile.h,v 1.1 2005/01/27 03:35:23 cozman Exp $ // $Id: ConfigFile.h,v 1.2 2005/01/31 15:44:13 cozman Exp $
// //
// Revisions: // Revisions:
// $Log: ConfigFile.h,v $ // $Log: ConfigFile.h,v $
// Revision 1.2 2005/01/31 15:44:13 cozman
// ConfigFile rewrite
//
// Revision 1.1 2005/01/27 03:35:23 cozman // Revision 1.1 2005/01/27 03:35:23 cozman
// initial import (exceptions,types, and logging,oh my!) // initial import (exceptions,types, and logging,oh my!)
// //
@ -17,10 +20,226 @@
#ifndef PHOTON_UTIL_CONFIGFILE_H #ifndef PHOTON_UTIL_CONFIGFILE_H
#define PHOTON_UTIL_CONFIGFILE_H #define PHOTON_UTIL_CONFIGFILE_H
namespace photon { #include <string>
namespace util { #include <list>
#include <sstream>
#include <functional>
#include <iostream>
namespace photon
{
namespace util
{
// Class: ConfigFile
// ConfigFile class, for reading/writing INI-style config files.
//
// File format is fairly flexible, whitespace and comments beginning with # or
// ; are ignored & left intact.
//
// Use []'s to denote sections.
//
// Variables are defined in var=val format.
class ConfigFile
{
public: // types used in ConfigFile
//predicate for search
template<class pairT>
class StrPairEq
{
public:
typedef pairT first_argument_type;
typedef std::string second_argument_type;
typedef bool result_type;
bool operator()(const pairT& lhs, const std::string& rhs) const;
};
typedef std::pair<std::string,std::string> Variable;
typedef std::list< Variable > Section;
typedef std::pair< std::string, std::list< Variable > > NamedSection;
typedef std::list< NamedSection > Layout;
// Group: (Con/De)structors
public:
// Function: ConfigFile
// Default constructor for ConfigFile.
ConfigFile();
// Function: ConfigFile
// Constructor for ConfigFile, calls <open>.
//
// Parameters:
// filename - Name of ConfigFile to open.
ConfigFile(const std::string& filename);
// Function: ~ConfigFile
// Calls close upon the ConfigFile.
virtual ~ConfigFile();
// Group: File Access
public:
// Function: open
// open a file, processing it as an INI-like config file.
//
// Parameters:
// filename - Name of ConfigFile to open.
void open(const std::string& filename);
// Function: flush
// Flushes the data written to the config file to disk, generally needs
// not be called.
void flush();
// Function: close
// Flushes the data and closes, open must be called again before using same
// ConfigFile.
void close();
// Function: setVariable
// Template function for setting variables in the config file.
//
// WARNING: Do not try to use this with user-defined types, numeric types
// and strings work fine, and this is all that should be contained in
// an INI.
//
// Parameters:
// sec - section name within config file
// var - variable name within section
// value - value to set variable equal to
template<class varType>
void setVariable(const std::string& sec,
const std::string& var,
varType value);
// Function: getVariable
// Template function for getting values from the config file. Supports
// returning a default value if the desired variable was not found.
//
// WARNING: Do not try to use this with user-defined types, numeric types
// and strings work fine, and this is all that should be contained in
// an INI.
//
// Parameters:
// sec - section name within config file
// var - variable name within section
// defVal - value to return if variable does not exist
//
// Returns:
// Value of variable within config file or defVal if value was not found.
template<class varType>
varType getVariable(const std::string& sec,
const std::string& var,
varType defVal) const;
private:
static std::string cleanString(const std::string& str);
static std::string bracketString(const std::string& str);
private:
Layout layout_;
std::string filename_;
};
//predicate for search
template<class pairT>
bool ConfigFile::StrPairEq<pairT>::operator()(const pairT& lhs,
const std::string& rhs) const
{
return ConfigFile::cleanString(lhs.first) == ConfigFile::cleanString(rhs);
}
//Template implementation
template<class varType>
void
ConfigFile::setVariable(const std::string& sec,
const std::string& var,
varType value)
{
std::string secBrac(bracketString(sec));
Layout::iterator secIter;
Section::iterator varIter;
std::ostringstream ss;
ss << value; //write value to string
//search for section
secIter = std::find_if( layout_.begin(),
layout_.end(),
std::bind2nd(StrPairEq<NamedSection>(), secBrac) );
// add the section if it does not exist
if(secIter == layout_.end())
{
layout_.push_back( NamedSection( secBrac, Section() ) );
//search again, assert that it now exists
secIter = std::find_if( layout_.begin(),
layout_.end(),
std::bind2nd(StrPairEq<NamedSection>(), secBrac) );
assert(secIter != layout_.end());
}
//search for variable
varIter = std::find_if( secIter->second.begin(),
secIter->second.end(),
std::bind2nd(StrPairEq<Variable>(), var) );
// add the variable if it does not exist
if(varIter == secIter->second.end())
{
secIter->second.push_back( Variable(var, ss.str()) );
}
else
{
varIter->second = ss.str();
}
}
//template specialization for setVariable<std::string>
/*template<>
void
ConfigFile::setVariable(std::string sec, std::string var, std::string value)
{
sec = "[" + sec + "]"; //add []s to section name
value = "\"" + value + "\""; //add ""s to value
layout_[sec][var] = value; //actually set it
}*/
template<class varType>
varType
ConfigFile::getVariable(const std::string& sec,
const std::string& var,
varType defVal) const
{
std::string secBrac(bracketString(sec));
std::stringstream ss;
varType ret(defVal);
Layout::const_iterator secIter;
Section::const_iterator varIter;
secIter = std::find_if( layout_.begin(),
layout_.end(),
std::bind2nd(StrPairEq<NamedSection>(), secBrac) );
if(secIter != layout_.end())
{
varIter = std::find_if( secIter->second.begin(),
secIter->second.end(),
std::bind2nd(StrPairEq<Variable>(), var) );
if(varIter != secIter->second.end())
{
ss.str(varIter->second);
ss >> ret;
}
}
return ret;
}
} }
} }

View File

@ -5,22 +5,191 @@
// James Turk (jpt2433@rit.edu) // James Turk (jpt2433@rit.edu)
// //
// Version: // Version:
// $Id: ConfigFile.cpp,v 1.1 2005/01/27 03:35:24 cozman Exp $ // $Id: ConfigFile.cpp,v 1.2 2005/01/31 15:44:13 cozman Exp $
// //
// Revisions: // Revisions:
// $Log: ConfigFile.cpp,v $ // $Log: ConfigFile.cpp,v $
// Revision 1.2 2005/01/31 15:44:13 cozman
// ConfigFile rewrite
//
// Revision 1.1 2005/01/27 03:35:24 cozman // Revision 1.1 2005/01/27 03:35:24 cozman
// initial import (exceptions,types, and logging,oh my!) // initial import (exceptions,types, and logging,oh my!)
// //
// //
#include "util/ConfigFile.h" #include "util/ConfigFile.h"
#include "exceptions.h"
#include <cctype>
#include <fstream>
namespace photon
{
namespace util
{
namespace photon {
namespace util {
//(Con/De)structors
ConfigFile::ConfigFile()
{
}
ConfigFile::ConfigFile(const std::string& filename)
{
open(filename);
}
ConfigFile::~ConfigFile()
{
close();
}
//File Access
void ConfigFile::open(const std::string& filename)
{
filename_ = filename;
if(filename_.empty())
{
throw PreconditionException("No filename in ConfigFile::open");
}
std::string section, var, val, str, clean;
std::ifstream file(filename_.c_str());
if(!file)
{
throw Error("Unable to open " + filename_ +
" for reading in ConfigFile::open");
}
layout_.clear(); //clear layout, just in case
while(file) //parses entire file
{
std::getline(file,str); //read in a line
clean = cleanString(str); //get a clean version
//if std::string is bracketed it is a section
if(clean[0] == '[' && clean[clean.length()-1] == ']')
{
section = str;
}
else if(std::isalpha(clean[0])) //variables must start with a letter
{
//split at the equals sign
var = str.substr(0,str.find('='));
val = str.substr(str.find('=')+1);
setVariable(section,var,val);
}
else if(clean[0] == '#' || clean[0] == ';') //comments
{
setVariable(section,"_comment",str);
}
else if(clean.length() == 0 && !file.eof()) //save blank lines
{
setVariable(section,"_newline",str);
}
}
file.close();
}
void ConfigFile::flush()
{
Layout::iterator sec;
Section::iterator var;
std::string secName;
//in case the filename is already cleared
if(filename_.empty())
{
throw PreconditionException("No filename in ConfigFile::flush");
}
//open the file blanked out, to not duplicate entries
std::ofstream file(filename_.c_str(), std::ios::out|std::ios::trunc);
if(!file)
{
throw Error("Unable to open " + filename_ +
" for writing in ConfigFile::flush");
}
//iteration through sections
for(sec = layout_.begin(); sec != layout_.end(); ++sec)
{
//ensure that section is valid
secName = cleanString(sec->first);
if(secName[0] == '[' && secName[secName.length()-1] == ']')
{
file << sec->first << std::endl; //write out raw section title
//for each variable in section, write out variable=value
for(var = sec->second.begin(); var != sec->second.end(); ++var)
{
if(var->first[0] == '_') //special values start with _
{
if(var->first.substr(1) == "comment")
{
file << var->second << std::endl;
}
else if(var->first.substr(1) == "newline")
{
file << std::endl;
}
}
else if(!cleanString(var->first).empty()) //a variable
{
file << var->first << '=' << var->second << std::endl;
}
}
}
}
file.close();
}
void ConfigFile::close()
{
//flush and clear out the data members
flush();
filename_ = std::string();
layout_.clear();
}
std::string ConfigFile::cleanString(const std::string& str)
{
std::string ret(str);
//convert to lower case
std::transform( ret.begin(), ret.end(), ret.begin(),
static_cast<int(*)(int)>(std::tolower) );
//remove all spaces
for(std::string::iterator i=ret.begin(); i != ret.end(); ++i)
{
if(std::isspace(*i))
{
i = ret.erase(i);
}
}
return ret;
}
std::string ConfigFile::bracketString(const std::string& str)
{
std::string ret( cleanString(str) );
//add brackets if they do not exist, unless the string is empty
if(!ret.empty() && ret[0] != '[' || ret[ret.length()-1] != ']')
{
ret = "["+ret+"]";
}
return ret;
}
} }
} }