Simply log errors and continue when type errors occur while loading the user config file, rather than only loading the portion of the file before the error

Originally committed to SVN as r6017.
This commit is contained in:
Thomas Goyne 2011-12-22 21:12:15 +00:00
parent d7ae51a5af
commit daff67b150
4 changed files with 95 additions and 57 deletions

View File

@ -71,10 +71,10 @@ void Options::ConfigUser() {
}
/// @todo Handle other errors such as parsing and notifying the user.
LoadConfig(*stream);
LoadConfig(*stream, true);
}
void Options::LoadConfig(std::istream& stream) {
void Options::LoadConfig(std::istream& stream, bool ignore_errors) {
/// @todo Store all previously loaded configs in an array for bug report purposes,
/// this is just a temp stub.
json::UnknownElement config_root;
@ -88,7 +88,7 @@ void Options::LoadConfig(std::istream& stream) {
LOG_E("option/load") << "json::Exception: " << e.what();
}
ConfigVisitor config_visitor(values, "");
ConfigVisitor config_visitor(values, "", ignore_errors);
config_root.Accept(config_visitor);
}

View File

@ -19,23 +19,34 @@
/// @see option_visit.h
/// @ingroup libaegisub
#include "option_visit.h"
#ifndef LAGI_PRE
#include <cmath>
#include <memory>
#endif
#include <libaegisub/colour.h>
#include <libaegisub/log.h>
#include <libaegisub/option_value.h>
#include "option_visit.h"
namespace agi {
ConfigVisitor::ConfigVisitor(OptionValueMap &val, const std::string &member_name)
ConfigVisitor::ConfigVisitor(OptionValueMap &val, const std::string &member_name, bool ignore_errors)
: values(val)
, name(member_name)
, ignore_errors(ignore_errors)
{
}
template<class ErrorType>
void ConfigVisitor::Error(const char *message) {
if (ignore_errors)
LOG_E("option/load/config_visitor") << "Error loading option from user configuration: " << message;
else
throw ErrorType(message);
}
void ConfigVisitor::Visit(const json::Object& object) {
json::Object::const_iterator index(object.begin()), index_end(object.end());
@ -43,7 +54,7 @@ void ConfigVisitor::Visit(const json::Object& object) {
name += "/";
for (; index != index_end; ++index) {
ConfigVisitor config_visitor(values, name + index->first);
ConfigVisitor config_visitor(values, name + index->first, ignore_errors);
index->second.Accept(config_visitor);
}
}
@ -59,17 +70,21 @@ inline int64_t convert_unknown(json::UnknownElement const& ue) {
}
template<class OptionValueType, class ValueType>
static OptionValue *read_array(json::Array const& src, std::string const& array_type, std::string const& name, void (OptionValueType::*set_list)(const std::vector<ValueType>&)) {
OptionValue *ConfigVisitor::ReadArray(json::Array const& src, std::string const& array_type, void (OptionValueType::*set_list)(const std::vector<ValueType>&)) {
std::vector<ValueType> arr;
arr.reserve(src.size());
for (json::Array::const_iterator it = src.begin(); it != src.end(); ++it) {
json::Object const& obj = *it;
if (obj.size() != 1)
throw OptionJsonValueArray("Invalid array member");
if (obj.begin()->first != array_type)
throw OptionJsonValueArray("Attempt to insert value into array of wrong type");
if (obj.size() != 1) {
Error<OptionJsonValueArray>("Invalid array member");
return 0;
}
if (obj.begin()->first != array_type) {
Error<OptionJsonValueArray>("Attempt to insert value into array of wrong type");
return 0;
}
arr.push_back(convert_unknown<ValueType>(obj.begin()->second));
}
@ -80,27 +95,31 @@ static OptionValue *read_array(json::Array const& src, std::string const& array_
}
void ConfigVisitor::Visit(const json::Array& array) {
if (array.empty())
throw OptionJsonValueArray("Cannot infer the type of an empty array");
if (array.empty()) {
Error<OptionJsonValueArray>("Cannot infer the type of an empty array");
return;
}
json::Object const& front = array.front();
if (front.size() != 1)
throw OptionJsonValueArray("Invalid array member");
if (front.size() != 1) {
Error<OptionJsonValueArray>("Invalid array member");
return;
}
const std::string& array_type = front.begin()->first;
if (array_type == "string")
AddOptionValue(read_array(array, array_type, name, &OptionValueListString::SetListString));
AddOptionValue(ReadArray(array, array_type, &OptionValueListString::SetListString));
else if (array_type == "int")
AddOptionValue(read_array(array, array_type, name, &OptionValueListInt::SetListInt));
AddOptionValue(ReadArray(array, array_type, &OptionValueListInt::SetListInt));
else if (array_type == "double")
AddOptionValue(read_array(array, array_type, name, &OptionValueListDouble::SetListDouble));
AddOptionValue(ReadArray(array, array_type, &OptionValueListDouble::SetListDouble));
else if (array_type == "bool")
AddOptionValue(read_array(array, array_type, name, &OptionValueListBool::SetListBool));
AddOptionValue(ReadArray(array, array_type, &OptionValueListBool::SetListBool));
else if (array_type == "colour")
AddOptionValue(read_array(array, array_type, name, &OptionValueListColour::SetListColour));
AddOptionValue(ReadArray(array, array_type, &OptionValueListColour::SetListColour));
else
throw OptionJsonValueArray("Array type not handled");
Error<OptionJsonValueArray>("Array type not handled");
}
@ -125,10 +144,15 @@ void ConfigVisitor::Visit(const json::Boolean& boolean) {
}
void ConfigVisitor::Visit(const json::Null& null) {
throw OptionJsonValueNull("Attempt to read null value");
Error<OptionJsonValueNull>("Attempt to read null value");
}
void ConfigVisitor::AddOptionValue(OptionValue* opt) {
if (!opt) {
assert(ignore_errors);
return;
}
if (!values.count(name)) {
values[name] = opt;
return;
@ -138,46 +162,54 @@ void ConfigVisitor::AddOptionValue(OptionValue* opt) {
// method throws
std::auto_ptr<OptionValue> auto_opt(opt);
switch (opt->GetType()) {
case OptionValue::Type_String:
values[name]->SetString(opt->GetString());
break;
try {
switch (opt->GetType()) {
case OptionValue::Type_String:
values[name]->SetString(opt->GetString());
break;
case OptionValue::Type_Int:
values[name]->SetInt(opt->GetInt());
break;
case OptionValue::Type_Int:
values[name]->SetInt(opt->GetInt());
break;
case OptionValue::Type_Double:
values[name]->SetDouble(opt->GetDouble());
break;
case OptionValue::Type_Double:
values[name]->SetDouble(opt->GetDouble());
break;
case OptionValue::Type_Colour:
values[name]->SetColour(opt->GetColour());
break;
case OptionValue::Type_Colour:
values[name]->SetColour(opt->GetColour());
break;
case OptionValue::Type_Bool:
values[name]->SetBool(opt->GetBool());
break;
case OptionValue::Type_Bool:
values[name]->SetBool(opt->GetBool());
break;
case OptionValue::Type_List_String:
values[name]->SetListString(opt->GetListString());
break;
case OptionValue::Type_List_String:
values[name]->SetListString(opt->GetListString());
break;
case OptionValue::Type_List_Int:
values[name]->SetListInt(opt->GetListInt());
break;
case OptionValue::Type_List_Int:
values[name]->SetListInt(opt->GetListInt());
break;
case OptionValue::Type_List_Double:
values[name]->SetListDouble(opt->GetListDouble());
break;
case OptionValue::Type_List_Double:
values[name]->SetListDouble(opt->GetListDouble());
break;
case OptionValue::Type_List_Colour:
values[name]->SetListColour(opt->GetListColour());
break;
case OptionValue::Type_List_Colour:
values[name]->SetListColour(opt->GetListColour());
break;
case OptionValue::Type_List_Bool:
values[name]->SetListBool(opt->GetListBool());
break;
case OptionValue::Type_List_Bool:
values[name]->SetListBool(opt->GetListBool());
break;
}
}
catch (agi::OptionValueError const& e) {
if (ignore_errors)
LOG_E("option/load/config_visitor") << "Error loading option from user configuration: " << e.GetChainedMessage();
else
throw;
}
}
} // namespace agi

View File

@ -33,12 +33,17 @@ DEFINE_SIMPLE_EXCEPTION_NOINNER(OptionJsonValueNull, OptionJsonValueError, "opti
class ConfigVisitor : public json::ConstVisitor {
OptionValueMap &values;
std::string name;
typedef std::pair<std::string, OptionValue*> OptionValuePair;
bool ignore_errors;
template<class ErrorType>
void Error(const char *message);
template<class OptionValueType, class ValueType>
OptionValue *ReadArray(json::Array const& src, std::string const& array_type, void (OptionValueType::*set_list)(const std::vector<ValueType>&));
void AddOptionValue(OptionValue* opt);
public:
ConfigVisitor(OptionValueMap &val, const std::string &member_name);
ConfigVisitor(OptionValueMap &val, const std::string &member_name, bool ignore_errors = false);
void Visit(const json::Array& array);
void Visit(const json::Object& object);

View File

@ -75,7 +75,8 @@ private:
/// @brief Load a config file into the Options object.
/// @param config Config to load.
void LoadConfig(std::istream& stream);
/// @param ignore_errors Log invalid entires in the option file and continue rather than throwing an exception
void LoadConfig(std::istream& stream, bool ignore_errors = false);
/// @brief Write an option to file.
/// @param[out] obj Parent object