support exception format strings

This commit is contained in:
Ari Lazier 2015-06-16 16:19:34 -07:00
parent 3874860d50
commit 26f1a0a4c8
4 changed files with 127 additions and 112 deletions

View File

@ -55,10 +55,7 @@ ObjectSchema::ObjectSchema(Group *group, const std::string &name) : name(name) {
if (primary_key.length()) { if (primary_key.length()) {
auto primary_key_prop = primary_key_property(); auto primary_key_prop = primary_key_property();
if (!primary_key_prop) { if (!primary_key_prop) {
throw ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaMismatchedPrimaryKey, { throw ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaChangedPrimaryKey, name, primary_key);
{ObjectStoreException::InfoKey::ObjectType, name},
{ObjectStoreException::InfoKey::PrimaryKey, ""},
{ObjectStoreException::InfoKey::OldPrimaryKey, primary_key}});
} }
primary_key_prop->is_primary = true; primary_key_prop->is_primary = true;
} }

View File

@ -201,11 +201,14 @@ std::vector<ObjectStoreException> ObjectStore::validate_schema(Group *group, Obj
// check for change to primary key // check for change to primary key
if (table_schema.primary_key != target_schema.primary_key) { if (table_schema.primary_key != target_schema.primary_key) {
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaMismatchedPrimaryKey, { if (table_schema.primary_key.length()) {
{ObjectStoreException::InfoKey::ObjectType, target_schema.name}, exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaChangedPrimaryKey,
{ObjectStoreException::InfoKey::PrimaryKey, target_schema.primary_key}, table_schema.name, table_schema.primary_key));
{ObjectStoreException::InfoKey::OldPrimaryKey, table_schema.primary_key}, }
})); else {
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaNewPrimaryKey,
target_schema.name, target_schema.primary_key));
}
} }
// check for new missing properties // check for new missing properties
@ -346,9 +349,7 @@ bool ObjectStore::create_tables(Group *group, ObjectStore::Schema &target_schema
bool ObjectStore::is_schema_at_version(Group *group, uint64_t version) { bool ObjectStore::is_schema_at_version(Group *group, uint64_t version) {
uint64_t old_version = get_schema_version(group); uint64_t old_version = get_schema_version(group);
if (old_version > version && old_version != NotVersioned) { if (old_version > version && old_version != NotVersioned) {
throw ObjectStoreException(ObjectStoreException::Kind::RealmVersionGreaterThanSchemaVersion, { throw ObjectStoreException(old_version, version);
{ObjectStoreException::InfoKey::OldVersion, to_string(old_version)},
{ObjectStoreException::InfoKey::NewVersion, to_string(version)}});
} }
return old_version != version; return old_version != version;
} }

View File

@ -20,99 +20,108 @@
#include "property.hpp" #include "property.hpp"
#include <realm/util/assert.hpp> #include <realm/util/assert.hpp>
#include <regex>
using namespace realm; using namespace realm;
using namespace std; using namespace std;
ObjectStoreException::CustomWhat ObjectStoreException::s_custom_what = nullptr; ObjectStoreException::ObjectStoreException(Kind kind, Info info) : m_kind(kind), m_info(info), m_what(generate_what()) {}
string ObjectStoreException::s_property_string = "property";
string ObjectStoreException::s_property_string_upper = "Property";
ObjectStoreException::ObjectStoreException(Kind kind, Info info) : m_kind(kind), m_info(info) {
set_what();
}
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop) : m_kind(kind) { ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop) : m_kind(kind) {
m_info[InfoKey::ObjectType] = object_type; m_info[InfoKeyObjectType] = object_type;
m_info[InfoKey::PropertyName] = prop.name; m_info[InfoKeyPropertyName] = prop.name;
m_info[InfoKey::PropertyType] = string_for_property_type(prop.type); m_info[InfoKeyPropertyType] = string_for_property_type(prop.type);
m_info[InfoKey::PropertyObjectType] = prop.object_type; m_info[InfoKeyPropertyObjectType] = prop.object_type;
set_what(); m_what = generate_what();
} }
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp) : ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp) :
m_kind(kind) { m_kind(kind) {
m_info[InfoKey::ObjectType] = object_type; m_info[InfoKeyObjectType] = object_type;
m_info[InfoKey::PropertyName] = prop.name; m_info[InfoKeyPropertyName] = prop.name;
m_info[InfoKey::PropertyType] = string_for_property_type(prop.type); m_info[InfoKeyPropertyType] = string_for_property_type(prop.type);
m_info[InfoKey::OldPropertyType] = string_for_property_type(oldProp.type); m_info[InfoKeyOldPropertyType] = string_for_property_type(oldProp.type);
m_info[InfoKey::PropertyObjectType] = prop.object_type; m_info[InfoKeyPropertyObjectType] = prop.object_type;
m_info[InfoKey::OldPropertyObjectType] = oldProp.object_type; m_info[InfoKeyOldPropertyObjectType] = oldProp.object_type;
set_what(); m_what = generate_what();
} }
void ObjectStoreException::set_what() { ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const std::string primary_key) : m_kind(kind) {
if (s_custom_what) { m_info[InfoKeyObjectType] = object_type;
string custom = s_custom_what(*this); m_info[InfoKeyPrimaryKey] = primary_key;
if (custom.length()) { m_what = generate_what();
m_what = custom; }
return;
}
}
switch (m_kind) { ObjectStoreException::ObjectStoreException(uint64_t old_version, uint64_t new_version) : m_kind(Kind::RealmVersionGreaterThanSchemaVersion) {
case Kind::RealmVersionGreaterThanSchemaVersion: m_info[InfoKeyOldVersion] = to_string(old_version);
m_what = "Provided schema version " + m_info[InfoKey::OldVersion] + m_info[InfoKeyNewVersion] = to_string(new_version);
" is less than last set version " + m_info[InfoKey::NewVersion] + "."; m_what = generate_what();
break;
case Kind::RealmPropertyTypeNotIndexable:
m_what = "Can't index " + s_property_string + " '" + m_info[InfoKey::ObjectType] + "." + m_info[InfoKey::PropertyName] + "': " +
"indexing a " + s_property_string + " of type '" + m_info[InfoKey::PropertyType] + "' is currently not supported";
break;
case Kind::RealmDuplicatePrimaryKeyValue:
m_what = "Primary key " + s_property_string + " '" + m_info[InfoKey::PropertyType] + "' has duplicate values after migration.";
break;
case Kind::ObjectSchemaMissingProperty:
m_what = s_property_string_upper + " '" + m_info[InfoKey::PropertyName] + "' is missing from latest object model.";
break;
case Kind::ObjectSchemaNewProperty:
m_what = s_property_string_upper + " '" + m_info[InfoKey::PropertyName] + "' has been added to latest object model.";
break;
case Kind::ObjectSchemaMismatchedTypes:
m_what = s_property_string_upper + " types for '" + m_info[InfoKey::PropertyName] + "' " + s_property_string + " do not match. " +
"Old type '" + m_info[InfoKey::OldPropertyType] + "', new type '" + m_info[InfoKey::PropertyType] + "'";
break;
case Kind::ObjectSchemaMismatchedObjectTypes:
m_what = "Target object type for " + s_property_string + " '" + m_info[InfoKey::PropertyName] + "' does not match. " +
"Old type '" + m_info[InfoKey::OldPropertyObjectType] + "', new type '" + m_info[InfoKey::PropertyObjectType] + "'.";
break;
case Kind::ObjectSchemaMismatchedPrimaryKey:
if (!m_info[InfoKey::PrimaryKey].length()) {
m_what = s_property_string_upper + " '" + m_info[InfoKey::OldPrimaryKey] + "' is no longer a primary key.";
}
else {
m_what = s_property_string_upper + " '" + m_info[InfoKey::PrimaryKey] + "' has been made a primary key.";
}
break;
case Kind::ObjectStoreValidationFailure:
m_what = "Migration is required for object type '" + info().at(InfoKey::ObjectType) + "' due to the following errors:";
for (auto error : m_validation_errors) {
m_what += string("\n- ") + error.what();
}
break;
}
} }
ObjectStoreException::ObjectStoreException(vector<ObjectStoreException> validation_errors, const string &object_type) : ObjectStoreException::ObjectStoreException(vector<ObjectStoreException> validation_errors, const string &object_type) :
m_validation_errors(validation_errors), m_kind(Kind::ObjectStoreValidationFailure), m_info({{InfoKey::ObjectType, object_type}}) { m_validation_errors(validation_errors),
set_what(); m_kind(Kind::ObjectStoreValidationFailure),
m_info({{InfoKeyObjectType, object_type}}),
m_what(generate_what()) {
} }
void ObjectStoreException::set_property_string(std::string property_string) { string ObjectStoreException::generate_what() const {
s_property_string = s_property_string_upper = property_string; auto format_string = s_custom_format_strings.find(m_kind);
s_property_string[0] = tolower(s_property_string[0]); if (format_string != s_custom_format_strings.end()) {
s_property_string_upper[0] = toupper(s_property_string_upper[0]); return populate_format_string(format_string->second);
}
return populate_format_string(s_default_format_strings.at(m_kind));
} }
string ObjectStoreException::validation_errors_string() const {
string errors_string;
for (auto error : m_validation_errors) {
errors_string += string("\n- ") + error.what();
}
return errors_string;
}
std::string ObjectStoreException::populate_format_string(const std::string & format_string) const {
string out_string, current(format_string);
smatch sm;
regex re("\\{(\\w+)\\}");
while(regex_search(current, sm, re)) {
out_string += sm.prefix();
const string &key = sm[1];
if (key == "ValidationString") {
out_string += validation_errors_string();
}
else {
out_string += m_info.at(key);
}
current = sm.suffix();
}
out_string += current;
return out_string;
}
ObjectStoreException::FormatStrings ObjectStoreException::s_custom_format_strings;
const ObjectStoreException::FormatStrings ObjectStoreException::s_default_format_strings = {
{Kind::RealmVersionGreaterThanSchemaVersion,
"Provided schema version {InfoKeyOldVersion} is less than last set version {InfoKeyNewVersion}."},
{Kind::RealmPropertyTypeNotIndexable,
"Can't index property {InfoKeyObjectType}.{InfoKeyPropertyName}: indexing a property of type '{InfoKeyPropertyType}' is currently not supported"},
{Kind::RealmDuplicatePrimaryKeyValue,
"Primary key property '{InfoKeyPropertyType}' has duplicate values after migration."},
{Kind::ObjectSchemaMissingProperty,
"Property '{InfoKeyPropertyName}' is missing from latest object model."},
{Kind::ObjectSchemaNewProperty,
"Property '{InfoKeyPropertyName}' has been added to latest object model."},
{Kind::ObjectSchemaMismatchedTypes,
"Property types for '{InfoKeyPropertyName}' property do not match. Old type '{InfoKeyOldPropertyType}', new type '{InfoKeyPropertyType}'"},
{Kind::ObjectSchemaMismatchedObjectTypes,
"Target object type for property '{InfoKeyPropertyName}' does not match. Old type '{InfoKeyOldPropertyObjectType}', new type '{InfoKeyPropertyObjectType}'."},
{Kind::ObjectSchemaChangedPrimaryKey,
"Property '{InfoKeyPrimaryKey}' is no longer a primary key."},
{Kind::ObjectSchemaNewPrimaryKey,
"Property '{InfoKeyPrimaryKey}' has been made a primary key."},
{Kind::ObjectStoreValidationFailure,
"Migration is required for object type '{InfoKeyObjectType}' due to the following errors: {ValidationErrors}"}
};

View File

@ -23,13 +23,14 @@
#include <map> #include <map>
#include <string> #include <string>
#define INFO_KEY(key) InfoKey InfoKey##key = "InfoKey" #key;
namespace realm { namespace realm {
class Property; class Property;
class ObjectStoreException : public std::exception { class ObjectStoreException : public std::exception {
public: public:
enum class Kind { enum class Kind {
// thrown when calling update_realm_to_schema and the realm version is greater than the given version
RealmVersionGreaterThanSchemaVersion, // OldVersion, NewVersion RealmVersionGreaterThanSchemaVersion, // OldVersion, NewVersion
RealmPropertyTypeNotIndexable, // ObjectType, PropertyName, PropertyType RealmPropertyTypeNotIndexable, // ObjectType, PropertyName, PropertyType
RealmDuplicatePrimaryKeyValue, // ObjectType, PropertyName, PropertyType RealmDuplicatePrimaryKeyValue, // ObjectType, PropertyName, PropertyType
@ -37,55 +38,62 @@ namespace realm {
ObjectSchemaNewProperty, // ObjectType, PropertyName, PropertyType ObjectSchemaNewProperty, // ObjectType, PropertyName, PropertyType
ObjectSchemaMismatchedTypes, // ObjectType, PropertyName, PropertyType, OldPropertyType ObjectSchemaMismatchedTypes, // ObjectType, PropertyName, PropertyType, OldPropertyType
ObjectSchemaMismatchedObjectTypes, // ObjectType, PropertyName, PropertyType, ObjectType, OldObjectType ObjectSchemaMismatchedObjectTypes, // ObjectType, PropertyName, PropertyType, ObjectType, OldObjectType
ObjectSchemaMismatchedPrimaryKey, // ObjectType, PrimaryKey, OldPrimaryKey ObjectSchemaChangedPrimaryKey, // ObjectType, PrimaryKey
ObjectSchemaNewPrimaryKey, // ObjectType, PrimaryKey
ObjectStoreValidationFailure, // ObjectType, vector<ObjectStoreException> ObjectStoreValidationFailure, // ObjectType, vector<ObjectStoreException>
}; };
enum class InfoKey { typedef const std::string InfoKey;
OldVersion,
NewVersion,
ObjectType,
PropertyName,
PropertyType,
OldPropertyType,
PropertyObjectType,
OldPropertyObjectType,
PrimaryKey,
OldPrimaryKey,
};
typedef std::map<InfoKey, std::string> Info; typedef std::map<InfoKey, std::string> Info;
ObjectStoreException(Kind kind, Info info = Info());
ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop); ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop);
// ObjectSchemaMismatchedTypes, ObjectSchemaMismatchedObjectTypes
ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp); ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp);
// ObjectSchemaChangedPrimaryKey, ObjectSchemaNewPrimaryKey
ObjectStoreException(Kind kind, const std::string &object_type, const std::string primary_key);
// RealmVersionGreaterThanSchemaVersion
ObjectStoreException(uint64_t old_version, uint64_t new_version);
// ObjectStoreValidationFailure // ObjectStoreValidationFailure
ObjectStoreException(std::vector<ObjectStoreException> validation_errors, const std::string &object_type); ObjectStoreException(std::vector<ObjectStoreException> validation_errors, const std::string &object_type);
ObjectStoreException::Kind kind() const { return m_kind; } ObjectStoreException::Kind kind() const { return m_kind; }
const ObjectStoreException::Info &info() const { return m_info; } const ObjectStoreException::Info &info() const { return m_info; }
const std::vector<ObjectStoreException> &validation_errors() { return m_validation_errors; }
const char *what() const noexcept override { return m_what.c_str(); } const char *what() const noexcept override { return m_what.c_str(); }
// implement CustomWhat to customize exception messages per platform/language // implement CustomWhat to customize exception messages per platform/language
typedef std::string (*CustomWhat)(ObjectStoreException &); typedef std::map<Kind, std::string> FormatStrings;
static void set_custom_what(CustomWhat message_generator) { s_custom_what = message_generator; } static void set_custom_format_strings(FormatStrings custom_format_strings) { s_custom_format_strings = custom_format_strings; }
// set the string used in defualt messages to represent the property object - defaults to 'property'
static void set_property_string(std::string property_string);
private: private:
ObjectStoreException(Kind kind, Info info = Info());
Kind m_kind; Kind m_kind;
Info m_info; Info m_info;
std::vector<ObjectStoreException> m_validation_errors; std::vector<ObjectStoreException> m_validation_errors;
std::string m_what; std::string m_what;
void set_what(); std::string generate_what() const;
std::string validation_errors_string() const;
std::string populate_format_string(const std::string &format_string) const;
static CustomWhat s_custom_what; static const FormatStrings s_default_format_strings;
static std::string s_property_string; static FormatStrings s_custom_format_strings;
static std::string s_property_string_upper;
public:
INFO_KEY(OldVersion);
INFO_KEY(NewVersion);
INFO_KEY(ObjectType);
INFO_KEY(PropertyName);
INFO_KEY(PropertyType);
INFO_KEY(OldPropertyType);
INFO_KEY(PropertyObjectType);
INFO_KEY(OldPropertyObjectType);
INFO_KEY(PrimaryKey);
}; };
} }