296 lines
9.6 KiB
C++
Executable File
296 lines
9.6 KiB
C++
Executable File
|
|
#include "ConfigValueQueue.h"
|
|
#include "ConfigurationException.h"
|
|
|
|
#include "lib/utils/proto_helper.h"
|
|
#include <google/protobuf/descriptor.h>
|
|
#include <boost/lexical_cast.hpp>
|
|
#include "config.h"
|
|
|
|
namespace bts {
|
|
namespace config {
|
|
namespace internal {
|
|
|
|
using namespace ::google::protobuf;
|
|
using namespace bts::expression;
|
|
using namespace bts::util::proto;
|
|
using std::vector;
|
|
|
|
ConfigValueQueue::ConfigValueQueue(IPropertyEvaluator& evaluator) :
|
|
_eval(evaluator),
|
|
_vals(),
|
|
_current_file()
|
|
{
|
|
}
|
|
|
|
ConfigValueQueue::~ConfigValueQueue()
|
|
{
|
|
}
|
|
|
|
void ConfigValueQueue::clear() {
|
|
_vals.clear();
|
|
}
|
|
|
|
void ConfigValueQueue::process() {
|
|
try {
|
|
process_fields();
|
|
} catch(...) {
|
|
clear();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void ConfigValueQueue::defer_value(proto::Message* msg,
|
|
const proto::Reflection* reflection,
|
|
const proto::FieldDescriptor* field,
|
|
int index, int line, int col,
|
|
const std::string& source,
|
|
const std::string& destination,
|
|
const proto::Message* root) {
|
|
|
|
bts_assert(msg);
|
|
bts_assert(reflection);
|
|
bts_assert(field);
|
|
bts_assert(!source.empty());
|
|
//bts_assert(!destination.empty());
|
|
//bts_assert_d(root != nullptr);
|
|
DeferredValue dv = {msg, reflection, field, index, line, col, _current_file, source, destination, root};
|
|
_vals.push_back(dv);
|
|
}
|
|
|
|
void ConfigValueQueue::process_fields() {
|
|
std::vector<DeferredValue>::iterator iter = _vals.begin();
|
|
std::vector<DeferredValue> temp;
|
|
size_t len;
|
|
do {
|
|
temp.clear();
|
|
len = _vals.size();
|
|
for (size_t x = 0; x < len; ++x) {
|
|
DeferredValue& dv = _vals[x];
|
|
if (can_process(dv.sourceval)) {
|
|
process_field(dv);
|
|
} else {
|
|
temp.push_back(dv);
|
|
}
|
|
}
|
|
_vals = temp;
|
|
// Continue as long as progress is being made
|
|
} while(_vals.size() > 0 && _vals.size() < len);
|
|
|
|
// Error condition
|
|
if (!_vals.empty()) {
|
|
std::ostringstream msg;
|
|
msg << "Unable to processes config value references. " << _vals.size() << " errors detected.\n\n";
|
|
for (iter = _vals.begin(); iter != _vals.end(); iter++) {
|
|
msg << "Cannot write " << iter->sourceval << " due to circular reference. ";
|
|
msg << iter->filename << ":" << iter->line << "," << iter->col << std::endl;
|
|
}
|
|
std::string error = msg.str();
|
|
throw new RecursiveReferenceException(error.c_str());
|
|
}
|
|
}
|
|
|
|
bool ConfigValueQueue::can_process(const std::string& source) {
|
|
// Determine if source has a pending write
|
|
size_t len = _vals.size();
|
|
for(size_t x = 0; x < len; ++x) {
|
|
// If there is a destination assignment underneath the source path
|
|
// Then we cannot copy the value yet
|
|
const string& dest = _vals[x].dest_key;
|
|
if (dest.compare(0, source.size(), source) == 0) {
|
|
if (dest.size() <= source.size() || dest[source.size()] == '.') {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool is_relevant(const proto::Message* message) {
|
|
if (message) {
|
|
const proto::Descriptor* desc = message->GetDescriptor();
|
|
bts_assert_d(desc);
|
|
const proto::Message& result = config::get_message(desc);
|
|
return &result == message;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ConfigValueQueue::process_field(const DeferredValue& dv) {
|
|
Value val;
|
|
if (_eval.get_value(dv.sourceval, &val)) {
|
|
assign_value_to_field(dv.message, dv.reflection, dv.field, val, dv.index);
|
|
} else {
|
|
if (is_relevant(dv.root_message)) {
|
|
std::string message = "Error retrieving value for " + dv.sourceval;
|
|
throw SelfReferenceException(message.c_str(), dv.filename.c_str(), dv.line, dv.col);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename TProto, typename TValue>
|
|
void ConfigValueQueue::assign(
|
|
proto::Message* message,
|
|
const proto::Reflection* reflection,
|
|
const proto::FieldDescriptor* field,
|
|
const Value& value, int& index) {
|
|
if (value.which() == ValueType::list) {
|
|
vector<Value> v = boost::get<vector<Value> >(value);
|
|
if (field->is_repeated()) {
|
|
for (vector<Value>::iterator i = v.begin(); i != v.end(); ++i) {
|
|
int size = reflection->FieldSize(*message, field);
|
|
add<TProto>(message, reflection, field, (TProto)boost::get<TValue>(*i));
|
|
if (index != size) {
|
|
for (int i = index; i < size; ++i) {
|
|
reflection->SwapElements(message, field, i, size);
|
|
}
|
|
}
|
|
index++;
|
|
}
|
|
bump_indexes(message, field, v.size());
|
|
}
|
|
else if (field->type() == proto::FieldDescriptor::Type::TYPE_STRING) {
|
|
reflection->SetString(message, field, value_to_string(value));
|
|
}
|
|
else if (field->type() == proto::FieldDescriptor::Type::TYPE_BOOL) {
|
|
reflection->SetBool(message, field, value_to_bool(value));
|
|
}
|
|
else {
|
|
string error = "Invalid type conversion in assignment to " + message->GetTypeName() + "." + field->name();
|
|
throw ConfigurationException(error.c_str());
|
|
}
|
|
} else {
|
|
set_field<TProto>(message, reflection, field, (TProto)boost::get<TValue>(value), index);
|
|
if (field->is_repeated()) {
|
|
bump_indexes(message, field, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename TProto, typename TValue>
|
|
void ConfigValueQueue::lexical_assign(
|
|
proto::Message* message,
|
|
const proto::Reflection* reflection,
|
|
const proto::FieldDescriptor* field,
|
|
const Value& value, int& index) {
|
|
if (value.which() == ValueType::list) {
|
|
vector<Value> v = boost::get<vector<Value> >(value);
|
|
bts_assert(field->is_repeated());
|
|
for (vector<Value>::iterator i = v.begin(); i != v.end(); ++i) {
|
|
int size = reflection->FieldSize(*message, field);
|
|
TProto result = boost::lexical_cast<TProto>(boost::get<TValue>(*i));
|
|
add<TProto>(message, reflection, field, result);
|
|
if (index != size) {
|
|
for (int i = index; i < size; ++i) {
|
|
reflection->SwapElements(message, field, i, size);
|
|
}
|
|
}
|
|
index++;
|
|
}
|
|
bump_indexes(message, field, v.size());
|
|
} else {
|
|
TProto result = boost::lexical_cast<TProto>(boost::get<TValue>(value));
|
|
set_field<TProto>(message, reflection, field, result, index);
|
|
if (field->is_repeated()) {
|
|
bump_indexes(message, field, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConfigValueQueue::assign_value_to_field(proto::Message* message,
|
|
const proto::Reflection* reflection,
|
|
const proto::FieldDescriptor* field,
|
|
const Value& value, int index) throw() {
|
|
#define MY_ARGS message, reflection, field, value, index
|
|
|
|
// Note: ConfigPropertyEvaluator puts every numeric type in as double so the right hand side must be double
|
|
switch(field->type()) {
|
|
case FieldDescriptor::TYPE_DOUBLE:
|
|
assign<double>(MY_ARGS); break;
|
|
case FieldDescriptor::TYPE_FLOAT:
|
|
assign<float, double>(MY_ARGS); break;
|
|
case FieldDescriptor::TYPE_FIXED64:
|
|
case FieldDescriptor::TYPE_INT64:
|
|
assign<int64_t, double>(MY_ARGS); break;
|
|
case FieldDescriptor::TYPE_UINT64:
|
|
assign<uint64_t, double>(MY_ARGS); break;
|
|
case FieldDescriptor::TYPE_FIXED32:
|
|
case FieldDescriptor::TYPE_INT32:
|
|
if (value.which() == ValueType::string) {
|
|
lexical_assign<int, string>(MY_ARGS);
|
|
}
|
|
else {
|
|
assign<int, double>(MY_ARGS);
|
|
}
|
|
break;
|
|
case FieldDescriptor::TYPE_UINT32:
|
|
assign<uint32_t, double>(MY_ARGS); break;
|
|
case FieldDescriptor::TYPE_BOOL:
|
|
assign<bool>(MY_ARGS); break;
|
|
case FieldDescriptor::TYPE_STRING:
|
|
if (value.which() == ValueType::integer || value.which() == ValueType::real) {
|
|
lexical_assign<string, double>(MY_ARGS);
|
|
}
|
|
else {
|
|
assign<string>(MY_ARGS);
|
|
}
|
|
break;
|
|
case FieldDescriptor::TYPE_MESSAGE:
|
|
assign<const Message*, void*>(MY_ARGS); break;
|
|
case FieldDescriptor::TYPE_ENUM: {
|
|
if (value.which() == ValueType::list) {
|
|
vector<Value> v = boost::get<vector<Value> >(value);
|
|
for (vector<Value>::iterator iter = v.begin(); iter != v.end(); iter++) {
|
|
int size = reflection->FieldSize(*message, field);
|
|
const EnumDescriptor* enum_type = field->enum_type();
|
|
const EnumValueDescriptor* enum_value = enum_type->FindValueByName(boost::get<string>(value));
|
|
reflection->AddEnum(message, field, enum_value);
|
|
if (index != size) {
|
|
for (int i = index; i < size; ++i) {
|
|
reflection->SwapElements(message, field, i, size);
|
|
}
|
|
}
|
|
index++;
|
|
}
|
|
bump_indexes(message, field, v.size());
|
|
}
|
|
else {
|
|
const EnumDescriptor* enum_type = field->enum_type();
|
|
const EnumValueDescriptor* enum_value = enum_type->FindValueByName(boost::get<string>(value));
|
|
set_field(message, reflection, field, enum_value, index);
|
|
if (field->is_repeated()) {
|
|
bump_indexes(message, field, 1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case FieldDescriptor::TYPE_SFIXED32:
|
|
case FieldDescriptor::TYPE_SFIXED64:
|
|
case FieldDescriptor::TYPE_SINT32:
|
|
case FieldDescriptor::TYPE_SINT64:
|
|
case FieldDescriptor::TYPE_BYTES:
|
|
case FieldDescriptor::TYPE_GROUP:
|
|
default:
|
|
bts_assert_msg(false, "Unsupported assignment destination");
|
|
}
|
|
|
|
#undef MY_ARGS
|
|
}
|
|
|
|
void ConfigValueQueue::bump_indexes(proto::Message* message,
|
|
const proto::FieldDescriptor* field,
|
|
int count)
|
|
{
|
|
std::vector<DeferredValue>::iterator iter = _vals.begin();
|
|
for (;iter != _vals.end(); iter++) {
|
|
DeferredValue& dv = *iter;
|
|
if (dv.message == message && dv.field == field) {
|
|
dv.index += count;
|
|
}
|
|
}
|
|
}
|
|
|
|
}}}//namespace
|
|
|