#include "ConfigValueQueue.h" #include "ConfigurationException.h" #include "lib/utils/proto_helper.h" #include #include #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::iterator iter = _vals.begin(); std::vector 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 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 v = boost::get >(value); if (field->is_repeated()) { for (vector::iterator i = v.begin(); i != v.end(); ++i) { int size = reflection->FieldSize(*message, field); add(message, reflection, field, (TProto)boost::get(*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(message, reflection, field, (TProto)boost::get(value), index); if (field->is_repeated()) { bump_indexes(message, field, 1); } } } template 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 v = boost::get >(value); bts_assert(field->is_repeated()); for (vector::iterator i = v.begin(); i != v.end(); ++i) { int size = reflection->FieldSize(*message, field); TProto result = boost::lexical_cast(boost::get(*i)); add(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(boost::get(value)); set_field(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(MY_ARGS); break; case FieldDescriptor::TYPE_FLOAT: assign(MY_ARGS); break; case FieldDescriptor::TYPE_FIXED64: case FieldDescriptor::TYPE_INT64: assign(MY_ARGS); break; case FieldDescriptor::TYPE_UINT64: assign(MY_ARGS); break; case FieldDescriptor::TYPE_FIXED32: case FieldDescriptor::TYPE_INT32: if (value.which() == ValueType::string) { lexical_assign(MY_ARGS); } else { assign(MY_ARGS); } break; case FieldDescriptor::TYPE_UINT32: assign(MY_ARGS); break; case FieldDescriptor::TYPE_BOOL: assign(MY_ARGS); break; case FieldDescriptor::TYPE_STRING: if (value.which() == ValueType::integer || value.which() == ValueType::real) { lexical_assign(MY_ARGS); } else { assign(MY_ARGS); } break; case FieldDescriptor::TYPE_MESSAGE: assign(MY_ARGS); break; case FieldDescriptor::TYPE_ENUM: { if (value.which() == ValueType::list) { vector v = boost::get >(value); for (vector::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(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(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::iterator iter = _vals.begin(); for (;iter != _vals.end(); iter++) { DeferredValue& dv = *iter; if (dv.message == message && dv.field == field) { dv.index += count; } } } }}}//namespace