support exception format strings
This commit is contained in:
parent
3874860d50
commit
26f1a0a4c8
|
@ -55,10 +55,7 @@ ObjectSchema::ObjectSchema(Group *group, const std::string &name) : name(name) {
|
|||
if (primary_key.length()) {
|
||||
auto primary_key_prop = primary_key_property();
|
||||
if (!primary_key_prop) {
|
||||
throw ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaMismatchedPrimaryKey, {
|
||||
{ObjectStoreException::InfoKey::ObjectType, name},
|
||||
{ObjectStoreException::InfoKey::PrimaryKey, ""},
|
||||
{ObjectStoreException::InfoKey::OldPrimaryKey, primary_key}});
|
||||
throw ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaChangedPrimaryKey, name, primary_key);
|
||||
}
|
||||
primary_key_prop->is_primary = true;
|
||||
}
|
||||
|
|
|
@ -201,11 +201,14 @@ std::vector<ObjectStoreException> ObjectStore::validate_schema(Group *group, Obj
|
|||
|
||||
// check for change to primary key
|
||||
if (table_schema.primary_key != target_schema.primary_key) {
|
||||
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaMismatchedPrimaryKey, {
|
||||
{ObjectStoreException::InfoKey::ObjectType, target_schema.name},
|
||||
{ObjectStoreException::InfoKey::PrimaryKey, target_schema.primary_key},
|
||||
{ObjectStoreException::InfoKey::OldPrimaryKey, table_schema.primary_key},
|
||||
}));
|
||||
if (table_schema.primary_key.length()) {
|
||||
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaChangedPrimaryKey,
|
||||
table_schema.name, table_schema.primary_key));
|
||||
}
|
||||
else {
|
||||
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaNewPrimaryKey,
|
||||
target_schema.name, target_schema.primary_key));
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
uint64_t old_version = get_schema_version(group);
|
||||
if (old_version > version && old_version != NotVersioned) {
|
||||
throw ObjectStoreException(ObjectStoreException::Kind::RealmVersionGreaterThanSchemaVersion, {
|
||||
{ObjectStoreException::InfoKey::OldVersion, to_string(old_version)},
|
||||
{ObjectStoreException::InfoKey::NewVersion, to_string(version)}});
|
||||
throw ObjectStoreException(old_version, version);
|
||||
}
|
||||
return old_version != version;
|
||||
}
|
||||
|
|
|
@ -20,99 +20,108 @@
|
|||
#include "property.hpp"
|
||||
|
||||
#include <realm/util/assert.hpp>
|
||||
#include <regex>
|
||||
|
||||
using namespace realm;
|
||||
using namespace std;
|
||||
|
||||
ObjectStoreException::CustomWhat ObjectStoreException::s_custom_what = nullptr;
|
||||
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, Info info) : m_kind(kind), m_info(info), m_what(generate_what()) {}
|
||||
|
||||
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop) : m_kind(kind) {
|
||||
m_info[InfoKey::ObjectType] = object_type;
|
||||
m_info[InfoKey::PropertyName] = prop.name;
|
||||
m_info[InfoKey::PropertyType] = string_for_property_type(prop.type);
|
||||
m_info[InfoKey::PropertyObjectType] = prop.object_type;
|
||||
set_what();
|
||||
m_info[InfoKeyObjectType] = object_type;
|
||||
m_info[InfoKeyPropertyName] = prop.name;
|
||||
m_info[InfoKeyPropertyType] = string_for_property_type(prop.type);
|
||||
m_info[InfoKeyPropertyObjectType] = prop.object_type;
|
||||
m_what = generate_what();
|
||||
}
|
||||
|
||||
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp) :
|
||||
m_kind(kind) {
|
||||
m_info[InfoKey::ObjectType] = object_type;
|
||||
m_info[InfoKey::PropertyName] = prop.name;
|
||||
m_info[InfoKey::PropertyType] = string_for_property_type(prop.type);
|
||||
m_info[InfoKey::OldPropertyType] = string_for_property_type(oldProp.type);
|
||||
m_info[InfoKey::PropertyObjectType] = prop.object_type;
|
||||
m_info[InfoKey::OldPropertyObjectType] = oldProp.object_type;
|
||||
set_what();
|
||||
m_info[InfoKeyObjectType] = object_type;
|
||||
m_info[InfoKeyPropertyName] = prop.name;
|
||||
m_info[InfoKeyPropertyType] = string_for_property_type(prop.type);
|
||||
m_info[InfoKeyOldPropertyType] = string_for_property_type(oldProp.type);
|
||||
m_info[InfoKeyPropertyObjectType] = prop.object_type;
|
||||
m_info[InfoKeyOldPropertyObjectType] = oldProp.object_type;
|
||||
m_what = generate_what();
|
||||
}
|
||||
|
||||
void ObjectStoreException::set_what() {
|
||||
if (s_custom_what) {
|
||||
string custom = s_custom_what(*this);
|
||||
if (custom.length()) {
|
||||
m_what = custom;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const std::string primary_key) : m_kind(kind) {
|
||||
m_info[InfoKeyObjectType] = object_type;
|
||||
m_info[InfoKeyPrimaryKey] = primary_key;
|
||||
m_what = generate_what();
|
||||
}
|
||||
|
||||
switch (m_kind) {
|
||||
case Kind::RealmVersionGreaterThanSchemaVersion:
|
||||
m_what = "Provided schema version " + m_info[InfoKey::OldVersion] +
|
||||
" is less than last set version " + m_info[InfoKey::NewVersion] + ".";
|
||||
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(uint64_t old_version, uint64_t new_version) : m_kind(Kind::RealmVersionGreaterThanSchemaVersion) {
|
||||
m_info[InfoKeyOldVersion] = to_string(old_version);
|
||||
m_info[InfoKeyNewVersion] = to_string(new_version);
|
||||
m_what = generate_what();
|
||||
}
|
||||
|
||||
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}}) {
|
||||
set_what();
|
||||
m_validation_errors(validation_errors),
|
||||
m_kind(Kind::ObjectStoreValidationFailure),
|
||||
m_info({{InfoKeyObjectType, object_type}}),
|
||||
m_what(generate_what()) {
|
||||
}
|
||||
|
||||
void ObjectStoreException::set_property_string(std::string property_string) {
|
||||
s_property_string = s_property_string_upper = property_string;
|
||||
s_property_string[0] = tolower(s_property_string[0]);
|
||||
s_property_string_upper[0] = toupper(s_property_string_upper[0]);
|
||||
|
||||
string ObjectStoreException::generate_what() const {
|
||||
auto format_string = s_custom_format_strings.find(m_kind);
|
||||
if (format_string != s_custom_format_strings.end()) {
|
||||
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}"}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -23,13 +23,14 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#define INFO_KEY(key) InfoKey InfoKey##key = "InfoKey" #key;
|
||||
|
||||
namespace realm {
|
||||
class Property;
|
||||
|
||||
class ObjectStoreException : public std::exception {
|
||||
public:
|
||||
enum class Kind {
|
||||
// thrown when calling update_realm_to_schema and the realm version is greater than the given version
|
||||
RealmVersionGreaterThanSchemaVersion, // OldVersion, NewVersion
|
||||
RealmPropertyTypeNotIndexable, // ObjectType, PropertyName, PropertyType
|
||||
RealmDuplicatePrimaryKeyValue, // ObjectType, PropertyName, PropertyType
|
||||
|
@ -37,55 +38,62 @@ namespace realm {
|
|||
ObjectSchemaNewProperty, // ObjectType, PropertyName, PropertyType
|
||||
ObjectSchemaMismatchedTypes, // ObjectType, PropertyName, PropertyType, OldPropertyType
|
||||
ObjectSchemaMismatchedObjectTypes, // ObjectType, PropertyName, PropertyType, ObjectType, OldObjectType
|
||||
ObjectSchemaMismatchedPrimaryKey, // ObjectType, PrimaryKey, OldPrimaryKey
|
||||
ObjectSchemaChangedPrimaryKey, // ObjectType, PrimaryKey
|
||||
ObjectSchemaNewPrimaryKey, // ObjectType, PrimaryKey
|
||||
ObjectStoreValidationFailure, // ObjectType, vector<ObjectStoreException>
|
||||
};
|
||||
|
||||
enum class InfoKey {
|
||||
OldVersion,
|
||||
NewVersion,
|
||||
ObjectType,
|
||||
PropertyName,
|
||||
PropertyType,
|
||||
OldPropertyType,
|
||||
PropertyObjectType,
|
||||
OldPropertyObjectType,
|
||||
PrimaryKey,
|
||||
OldPrimaryKey,
|
||||
};
|
||||
typedef const std::string InfoKey;
|
||||
typedef std::map<InfoKey, std::string> Info;
|
||||
|
||||
ObjectStoreException(Kind kind, Info info = Info());
|
||||
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);
|
||||
|
||||
// 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
|
||||
ObjectStoreException(std::vector<ObjectStoreException> validation_errors, const std::string &object_type);
|
||||
|
||||
ObjectStoreException::Kind kind() const { return m_kind; }
|
||||
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(); }
|
||||
|
||||
// implement CustomWhat to customize exception messages per platform/language
|
||||
typedef std::string (*CustomWhat)(ObjectStoreException &);
|
||||
static void set_custom_what(CustomWhat message_generator) { s_custom_what = message_generator; }
|
||||
|
||||
// set the string used in defualt messages to represent the property object - defaults to 'property'
|
||||
static void set_property_string(std::string property_string);
|
||||
typedef std::map<Kind, std::string> FormatStrings;
|
||||
static void set_custom_format_strings(FormatStrings custom_format_strings) { s_custom_format_strings = custom_format_strings; }
|
||||
|
||||
private:
|
||||
ObjectStoreException(Kind kind, Info info = Info());
|
||||
|
||||
Kind m_kind;
|
||||
Info m_info;
|
||||
std::vector<ObjectStoreException> m_validation_errors;
|
||||
|
||||
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 std::string s_property_string;
|
||||
static std::string s_property_string_upper;
|
||||
static const FormatStrings s_default_format_strings;
|
||||
static FormatStrings s_custom_format_strings;
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue