Forked from
jlab / hallc / analyzer_software / hcana
144 commits behind the upstream repository.
-
Chao Peng authored16736e1a
ConfigObject.cpp 10.00 KiB
//============================================================================//
// A class based on the support from ConfigParser and ConfigValue //
// It provides a simple way to read text file as configuration file, and read //
// or modify a parameter in the inherited class //
// The Configure() function should be overloaded according to specialized //
// requirements, and be called after the parameters being configured //
// //
// Chao Peng //
// 10/31/2016 //
//============================================================================//
#include <fstream>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include "ConfigObject.h"
//============================================================================//
// Constructor, Destructor //
//============================================================================//
// constructor
ConfigObject::ConfigObject(const std::string &splitter, const std::string &ignore, bool case_ins)
: split_chars(splitter), ignore_chars(ignore), case_insensitive(case_ins), __empty_value("")
{
// set default replace bracket
replace_pair = std::make_pair("{", "}");
}
// destructor
ConfigObject::~ConfigObject()
{
// place holder
}
//============================================================================//
// Public Member Function //
//============================================================================//
// configure the cluster method
void ConfigObject::Configure(const std::string &path)
{
// save the path
config_path = path;
// clear the map
config_map.clear();
// read configuration file in
ReadConfigFile(path);
}
// clear all the loaded configuration values
void ConfigObject::ClearConfig()
{
config_path = "";
config_map.clear();
}
// read configuration file and build the configuration map
bool ConfigObject::ReadConfigFile(const std::string &path)
{
ConfigParser c_parser;
c_parser.SetSplitters(split_chars); // self-defined splitters
if (c_parser.ReadFile(path)) {
// current directory
parserProcess(c_parser, path);
return true;
} else {
std::cerr << "Cannot open configuration file "
<< "\"" << path << "\""
<< std::endl;
return false;
}
}
// read the configuration string directly
void ConfigObject::ReadConfigString(const std::string &content)
{
ConfigParser c_parser;
c_parser.SetSplitters(split_chars);
c_parser.ReadBuffer(content.c_str());
parserProcess(c_parser, "buffer_string");
}
// continue parse the terms
void ConfigObject::parserProcess(ConfigParser &c_parser, const std::string &source)
{
std::string cur_dir = ConfigParser::decompose_path(source).dir;
while (c_parser.ParseLine()) {
// possible control words
if (c_parser.NbofElements() == 1) {
std::string control = c_parser.TakeFirst();
size_t pos = control.find("{THIS_DIR}");
if(pos != std::string::npos)
control.replace(pos, 10, cur_dir);
parseControl(control);
// var_name and var_value
} else if (c_parser.NbofElements() == 2) {
std::string var_name, key, var_value;
c_parser >> var_name >> var_value;
size_t pos = var_value.find("{THIS_DIR}");
if(pos != std::string::npos)
var_value.replace(pos, 10, cur_dir);
parseTerm(std::move(var_name), std::move(var_value));
// unsupported format
} else {
std::cout << "Warning: Unsupported format at line "
<< c_parser.LineNumber() << " from " << source << "\n"
<< "\"" << c_parser.CurrentLine() << "\""
<< std::endl;
}
}
}
// check if a certain term is configured
bool ConfigObject::HasKey(const std::string &var_name)
const
{
auto key = formKey(var_name);
if (config_map.find(key) != config_map.end())
return true;
return false;
}
// list all the existing configuration keys
void ConfigObject::ListKeys()
const
{
for (auto &it : config_map) {
std::cout << it.first << std::endl;
}
}
// get all the existing configuration keys
std::vector<std::string> ConfigObject::GetKeyList()
const
{
std::vector<std::string> res;
for (auto &it : config_map) {
res.push_back(it.first);
}
return res;
}
// save current configuration into a file
void ConfigObject::SaveConfig(const std::string &path)
const
{
std::string save_path;
if (path.empty())
save_path = config_path;
if(save_path.empty())
save_path = "latest.conf";
std::ofstream save(save_path);
for (auto &it : config_map) {
save << it.first
<< " " << split_chars.front() << " "
<< it.second
<< std::endl;
}
}
// get configuration value by its name/key
ConfigValue ConfigObject::GetConfigValue(const std::string &var_name)
const
{
// convert to lower case and remove uninterested characters
auto key = formKey(var_name);
auto it = config_map.find(key);
if (it == config_map.end()) {
return __empty_value;
} else {
ConfigValue result(it->second);
reform(result._value, replace_pair.first, replace_pair.second);
return result;
}
}
// set configuration value by its name/key
void ConfigObject::SetConfigValue(const std::string &var_name, const ConfigValue &c_value)
{
// convert to lower case and remove uninterested characters
auto key = formKey(var_name);
config_map[key] = c_value;
}
// get configuration value from the map
// if no such config value exists, it will fill the default value in
ConfigValue ConfigObject::GetConfigValue(const std::string &var_name, const ConfigValue &def_value, bool verbose)
{
auto key = formKey(var_name);
auto it = config_map.find(key);
if (it == config_map.end()) {
if (def_value.IsEmpty())
return __empty_value;
if (verbose) {
std::cout << var_name << " (key: " << key << ")"
<< " not defined in configuration file, set to default value "
<< def_value
<< std::endl;
}
config_map[key] = def_value;
return def_value;
}
ConfigValue result(it->second);
reform(result._value, replace_pair.first, replace_pair.second);
return result;
}
//============================================================================//
// Protected Member Function //
//============================================================================//
// build the key
std::string ConfigObject::formKey(const std::string &rawKey)
const
{
std::string key = ConfigParser::str_remove(rawKey, ignore_chars);
if (case_insensitive) {
key = ConfigParser::str_lower(key);
}
return key;
}
// replace the contents inside replace_pair with the configuration value
void ConfigObject::reform(std::string &input, const std::string &op, const std::string &cl)
const
{
// loop until no pair found
while (true) {
auto rpair = ConfigParser::find_pair(input, op, cl);
if (rpair.first != std::string::npos && rpair.second != std::string::npos) {
// get content inside the bracket
std::string var = input.substr(rpair.first + op.size(),
rpair.second - op.size() - rpair.first);
// deal with nested structure
reform(var, op, cl);
// replace content
std::string val;
// environment variable
if (rpair.first > 0 && input.at(rpair.first - 1) == '$') {
val = std::getenv(var.c_str());
// replace $ mark also
rpair.first--;
// ConfigObject variable
} else {
val = GetConfigValue(var)._value;
}
// replace variable with configuration value
input.replace(rpair.first, rpair.second - rpair.first + cl.size(), val);
} else {
// no pair found any more
return;
}
}
}
//============================================================================//
// Private Member Function //
//============================================================================//
// parse the control word and respond
void ConfigObject::parseControl(const std::string &word)
{
if (ConfigParser::str_upper(word.substr(0, 7)) == "INCLUDE") {
// need the most outer pair
auto p = ConfigParser::find_pair(word, "(", ")");
// not find pair
if (p.first == std::string::npos || p.second == std::string::npos) {
std::cout << "Unsupported control word format: " << word << "."
<< "Expected: INCLUDE(path)"
<< std::endl;
return;
}
int begin = p.first + 1;
int length = p.second - begin;
std::string new_path = word.substr(begin, length);
reform(new_path, replace_pair.first, replace_pair.second);
ReadConfigFile(new_path);
}
else {
std::cout << "Unsupported control word: " << word << std::endl;
}
}
// parse the configuration term
void ConfigObject::parseTerm(std::string &&var_name, std::string &&var_value)
{
// convert to lower case and remove uninterested characters
auto key = formKey(var_name);
if (key.back() == '+') {
key.pop_back();
auto it = config_map.find(key);
if (it != config_map.end())
it->second += var_value;
else
config_map[key] = var_value;
} else {
config_map[key] = var_value;
}
}