Files
spark/src/lib/config/ConfigValueQueue.cpp
T
2019-01-03 16:20:15 -05:00

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