You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

600 lines
16 KiB
C++

/***************************************************************************
* Copyright (C) gempa GmbH *
* All rights reserved. *
* Contact: gempa GmbH (seiscomp-dev@gempa.de) *
* *
* GNU Affero General Public License Usage *
* This file may be used under the terms of the GNU Affero *
* Public License version 3.0 as published by the Free Software Foundation *
* and appearing in the file LICENSE included in the packaging of this *
* file. Please review the following information to ensure the GNU Affero *
* Public License version 3.0 requirements will be met: *
* https://www.gnu.org/licenses/agpl-3.0.html. *
* *
* Other Usage *
* Alternatively, this file may be used in accordance with the terms and *
* conditions contained in a signed written agreement between you and *
* gempa GmbH. *
***************************************************************************/
#ifndef SEISCOMP_SYSTEM_SETTINGS_H
#define SEISCOMP_SYSTEM_SETTINGS_H
#include <seiscomp/core/strings.h>
#include <seiscomp/config/config.h>
#include <iostream>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <map>
#include <boost/type_traits.hpp>
#include <boost/mpl/if.hpp>
namespace Seiscomp {
namespace System {
namespace Generic {
namespace Detail {
class No { };
class Yes { No no[2]; };
template <typename Type, Type Ptr>
struct MemberHelperClass;
template <typename T, typename Type>
Yes MemberHelper_accept(MemberHelperClass<Type, &T::accept> *);
template<typename T,typename Type>
No MemberHelper_accept(...);
template<typename T, typename PARAM>
struct HasMemberAccept {
enum {
value = sizeof(MemberHelper_accept<T, void (T::*)(PARAM &)>(0)) == sizeof(Yes)
};
};
template <typename T>
struct IsClassType {
enum {
value = boost::is_same<int,T>::value ?
0
:
(
boost::is_same<unsigned int,T>::value ?
0
:
(
boost::is_same<double,T>::value ?
0
:
(
boost::is_same<bool,T>::value ?
0
:
1
)
)
)
};
};
template <typename C, typename T>
struct MustMatch {
static T get(const C &value) {
throw std::runtime_error("key attribute must be a string");
}
};
template <typename C>
struct MustMatch<C,C> {
static C get(const C &value) {
return value;
}
};
template <typename T>
T getConfig(const Config::Config *cfg, const std::string &symbol, bool asPath);
}
template <template <typename> class VisitedItem, class Proc>
class SettingsVisitor {
public:
typedef SettingsVisitor<VisitedItem, Proc> SelfType;
SettingsVisitor() : _success(true) {}
// ----------------------------------------------------------------------
// Public interface for visited classes
// ----------------------------------------------------------------------
public:
//! Bind a value
template <typename T>
SettingsVisitor &operator&(VisitedItem<T> visitedItem);
//! Bind a value
template <typename T>
SettingsVisitor &operator&(VisitedItem< std::vector<T> > visitedItem);
// ----------------------------------------------------------------------
// Public state management
// ----------------------------------------------------------------------
public:
bool success() const;
const std::string lastError() const;
//! Resets the success flag and the error message
void reset();
//! Returns the processor
Proc &proc();
public:
operator bool() const;
// ----------------------------------------------------------------------
// Interface to be used in processor
// ----------------------------------------------------------------------
public:
template <typename T>
void push(const VisitedItem<T> &visitedItem);
void pop();
void setError(const std::string &msg);
// ----------------------------------------------------------------------
// Private methods and helpers
// ----------------------------------------------------------------------
private:
template <typename T, int IS_CLASS>
struct VisitHelper {};
// Non-class types never have member functions. This includes vectors
// of non-class types
template <typename T>
struct VisitHelper<T, 0> {
static SettingsVisitor &process(SettingsVisitor &visitor, VisitedItem<T> &visitedItem) {
visitor.handleSingle(visitedItem);
return visitor;
}
};
// Class type
template <typename T>
struct VisitHelper<T, 1> {
static SettingsVisitor &process(SettingsVisitor &visitor, VisitedItem<T> &visitedItem) {
return VisitCompositeHelper<T, Detail::HasMemberAccept<T, SelfType>::value>::dispatch(visitor, visitedItem);
}
};
// Vector with class type
template <typename T>
struct VisitHelper<std::vector<T>, 1> {
static SettingsVisitor &process(SettingsVisitor &visitor, VisitedItem< std::vector<T> > &visitedItem) {
return VisitCompositeHelper<std::vector<T>, Detail::HasMemberAccept<T, SelfType>::value>::dispatch(visitor, visitedItem);
}
};
template <typename T, int HAS_VISIT>
struct VisitCompositeHelper {};
// No bind method
template <typename T>
struct VisitCompositeHelper<T,0> {
static SettingsVisitor &dispatch(SettingsVisitor &visitor, VisitedItem<T> &visitedItem) {
visitor.handleSingle(visitedItem);
return visitor;
}
};
// Bind method => descent
template <typename T>
struct VisitCompositeHelper<T,1> {
static SettingsVisitor &dispatch(SettingsVisitor &visitor, VisitedItem<T> &visitedItem) {
visitor.push(visitedItem);
visitedItem.value.accept(visitor);
visitor.pop();
return visitor;
}
};
// Vector of elements without bind method
template <typename T>
struct VisitCompositeHelper< std::vector<T> ,0> {
static SettingsVisitor &dispatch(SettingsVisitor &visitor, VisitedItem< std::vector<T> > &visitedItem) {
visitor.handleSingle(visitedItem);
return visitor;
}
};
// Vector with elements with bind method
template <typename T>
struct VisitCompositeHelper< std::vector<T> ,1> {
static SettingsVisitor &dispatch(SettingsVisitor &visitor, VisitedItem< std::vector<T> > &visitedItem) {
visitor.handleMultiple(visitedItem);
return visitor;
}
};
// ----------------------------------------------------------------------
// Callbacks which delegate calls to the processor
// ----------------------------------------------------------------------
private:
template <typename T>
void handleSingle(VisitedItem<T> &visitedItem);
template <typename T>
void handleMultiple(VisitedItem< std::vector<T> > &visitedItem);
// ----------------------------------------------------------------------
// Members
// ----------------------------------------------------------------------
public:
std::string indent; //!< Indentation for each tree level in two-space steps
std::string configPrefix;
protected:
std::deque<std::string> _prefixStack;
bool _success;
std::string _errorMessage;
private:
Proc _proc;
};
}
template <typename T>
struct ConfigOptionBinding {
enum Flags {
NoFlags = 0x00,
IsKey = 0x01,
InterpretAsPath = 0x02
};
ConfigOptionBinding(T &value, int flags, const char *configFileRelativeSymbol)
: value(value)
, flags(flags)
, configFileRelativeSymbol(configFileRelativeSymbol) {}
bool isKey() { return flags & IsKey; }
T &value;
int flags;
const char *configFileRelativeSymbol;
};
class ConfigOptionLinker {
public:
ConfigOptionLinker()
: _stage(None) {}
public:
void get(const Config::Config *cfg) {
setStage(GetCfg);
_external.cfg = cfg;
}
void get(const Config::Config &cfg) {
get(&cfg);
}
void dump(std::ostream &os) {
setStage(Print);
_external.os = &os;
}
public:
// A single non-array option
template <typename T, typename V>
void visitSingle(V &visitor, ConfigOptionBinding<T> &visitedItem) {
switch ( _stage ) {
case GetCfg:
if ( !visitedItem.isKey() && !visitedItem.configFileRelativeSymbol )
return;
if ( !CfgLinkHelper<T, IsNativelySupported<T>::value>::process(*this, visitedItem, visitor.configPrefix) )
visitor.setError("Invalid configuration value for " + visitor.configPrefix + visitedItem.configFileRelativeSymbol);
break;
break;
case Print:
if ( visitedItem.configFileRelativeSymbol )
*_external.os << visitor.configPrefix << visitedItem.configFileRelativeSymbol;
else if ( visitedItem.isKey() )
*_external.os << "*KEY*";
else
return;
*_external.os << ": ";
PrintHelper<T, IsNativelySupported<T>::value>::process(*_external.os, visitedItem.value);
*_external.os << std::endl;
break;
default:
break;
}
}
// A single array option
template <typename T, typename V>
void visitSingle(V &visitor, ConfigOptionBinding< std::vector<T> > &visitedItem) {
switch ( _stage ) {
case GetCfg:
if ( !visitedItem.configFileRelativeSymbol )
return;
if ( !CfgLinkHelper<std::vector<T>, IsNativelySupported<T>::value>::process(*this, visitedItem, visitor.configPrefix) )
visitor.setError("Invalid configuration value for " + visitor.configPrefix + visitedItem.configFileRelativeSymbol);
break;
case Print:
if ( visitedItem.configFileRelativeSymbol )
*_external.os << visitor.configPrefix << visitedItem.configFileRelativeSymbol;
else if ( visitedItem.isKey() )
*_external.os << "*KEY*";
else
return;
*_external.os << ": ";
PrintHelper<std::vector<T>, IsNativelySupported<T>::value>::process(*_external.os, visitedItem.value);
*_external.os << std::endl;
break;
default:
break;
}
}
// An array option consisting of composites
template <typename T, typename V>
void visitMultiple(V &visitor, ConfigOptionBinding< std::vector<T> > &visitedItem) {
switch ( _stage ) {
case GetCfg:
try {
std::vector<std::string> items;
items = Generic::Detail::getConfig< std::vector<std::string> >(_external.cfg, visitor.configPrefix + visitedItem.configFileRelativeSymbol, false);
std::string oldKey = _key;
visitor.push(visitedItem);
for ( size_t i = 0; i < items.size(); ++i ) {
T value;
ConfigOptionBinding<T> item(value, false, items[i].c_str());
std::string oldKey = _key;
visitor.push(item);
_key = items[i];
item.value.accept(visitor);
if ( visitor.success() )
visitedItem.value.push_back(value);
visitor.pop();
_key = oldKey;
}
visitor.pop();
_key = oldKey;
}
catch ( ... ) {}
break;
case Print:
if ( visitedItem.configFileRelativeSymbol ) {
*_external.os << visitor.configPrefix << visitedItem.configFileRelativeSymbol;
*_external.os << ": ";
if ( visitedItem.value.empty() )
*_external.os << "[]" << std::endl;
else {
*_external.os << "[" << visitedItem.value.size() << "]" << std::endl;
std::string oldKey = _key;
visitor.push(visitedItem);
for ( size_t i = 0; i < visitedItem.value.size(); ++i ) {
*_external.os << "{" << std::endl;
visitedItem.value[i].accept(visitor);
*_external.os << "}" << std::endl;
}
visitor.pop();
_key = oldKey;
}
}
break;
default:
break;
}
}
// Helpers
private:
template <typename T>
T key() const {
return Generic::Detail::MustMatch<std::string,T>::get(_key);
}
template <typename T>
struct IsNativelySupported {
enum {
value = Generic::Detail::IsClassType<T>::value ?
(
boost::is_same<std::string,T>::value ?
1
:
0
)
:
1
};
};
template <typename T, int IS_SUPPORTED>
struct CfgLinkHelper {};
template <typename T>
struct CfgLinkHelper<T,0> {
template <typename P>
static bool process(P &proc,
ConfigOptionBinding<T> &visitedItem,
const std::string &prefix) {
try {
std::string tmp;
if ( visitedItem.isKey() )
tmp = proc.template key<std::string>();
else
tmp = Generic::Detail::getConfig<std::string>(proc._external.cfg, prefix + visitedItem.configFileRelativeSymbol, visitedItem.flags & ConfigOptionBinding< std::vector<T> >::InterpretAsPath);
return fromString(visitedItem.value, tmp);
}
catch ( ... ) {}
return true;
}
};
template <typename T>
struct CfgLinkHelper<std::vector<T>,0> {
template <typename P>
static bool process(P &proc,
ConfigOptionBinding< std::vector<T> > &visitedItem,
const std::string &prefix) {
try {
std::vector<std::string> tmp;
if ( visitedItem.isKey() )
tmp = proc.template key<std::vector<std::string> >();
else
tmp = Generic::Detail::getConfig<std::vector<std::string> >(proc._external.cfg, prefix + visitedItem.configFileRelativeSymbol, visitedItem.flags & ConfigOptionBinding< std::vector<T> >::InterpretAsPath);
visitedItem.value.resize(tmp.size());
for ( size_t i = 0; i < tmp.size(); ++i ) {
if ( !fromString(visitedItem.value[i], tmp[i]) )
return false;
}
}
catch ( ... ) {}
return true;
}
};
template <typename T>
struct CfgLinkHelper<T,1> {
template <typename P>
static bool process(P &proc,
ConfigOptionBinding<T> &visitedItem,
const std::string &prefix) {
if ( visitedItem.isKey() )
visitedItem.value = proc.template key<T>();
else {
try {
visitedItem.value = Generic::Detail::getConfig<T>(proc._external.cfg, prefix + visitedItem.configFileRelativeSymbol, visitedItem.flags & ConfigOptionBinding< std::vector<T> >::InterpretAsPath);
}
catch ( ... ) {}
}
return true;
}
};
template <typename T, int IS_SUPPORTED>
struct PrintHelper {};
template <typename T>
struct PrintHelper<T,0> {
static void process(std::ostream &os, const T &value) {
os << toString(value);
}
};
template <typename T>
struct PrintHelper<std::vector<T>,0> {
static void process(std::ostream &os, const std::vector<T> &value) {
if ( value.empty() ) {
os << "[]";
return;
}
for ( size_t i = 0; i < value.size(); ++i ) {
if ( i ) os << ", ";
PrintHelper<T,0>::process(os, value[i]);
}
}
};
template <typename T>
struct PrintHelper<T,1> {
static void process(std::ostream &os, const T &value) {
os << value;
}
};
template <typename T>
struct PrintHelper<std::vector<T>,1> {
static void process(std::ostream &os, const std::vector<T> &value) {
if ( value.empty() )
os << "[]";
else
os << Core::toString(value);
}
};
private:
enum Stage {
None,
GetCfg,
Print
};
void setStage(Stage s) {
_stage = s;
}
Stage _stage;
std::string _key; //!< The current array item key value
// Output structures depending on the stage
union {
std::ostream *os;
const Config::Config *cfg;
} _external;
};
struct ConfigSettingsLinker : Generic::SettingsVisitor<ConfigOptionBinding, ConfigOptionLinker> {
template <typename T>
static ConfigOptionBinding<T> key(T &boundValue) {
return ConfigOptionBinding<T>(boundValue, ConfigOptionBinding<T>::IsKey, nullptr);
}
template <typename T>
static ConfigOptionBinding<T> cfg(T &boundValue, const char *name) {
return ConfigOptionBinding<T>(boundValue, 0, name);
}
template <typename T>
static ConfigOptionBinding<T> cfgAsPath(T &boundValue, const char *name) {
return ConfigOptionBinding<T>(boundValue, ConfigOptionBinding<T>::InterpretAsPath, name);
}
};
}
}
#include <seiscomp/system/settings.ipp>
#endif