From 26f1a0a4c88b8c87ddc63a43c30554992ab866b2 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 16 Jun 2015 16:19:34 -0700 Subject: [PATCH] support exception format strings --- object_schema.cpp | 5 +- object_store.cpp | 17 ++-- object_store_exceptions.cpp | 159 +++++++++++++++++++----------------- object_store_exceptions.hpp | 58 +++++++------ 4 files changed, 127 insertions(+), 112 deletions(-) diff --git a/object_schema.cpp b/object_schema.cpp index 4f0eef52..d31c15a6 100644 --- a/object_schema.cpp +++ b/object_schema.cpp @@ -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; } diff --git a/object_store.cpp b/object_store.cpp index 233923d7..3622c31b 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -201,11 +201,14 @@ std::vector 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; } diff --git a/object_store_exceptions.cpp b/object_store_exceptions.cpp index 0178f4dc..1482060a 100644 --- a/object_store_exceptions.cpp +++ b/object_store_exceptions.cpp @@ -20,99 +20,108 @@ #include "property.hpp" #include +#include 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 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}"} +}; + diff --git a/object_store_exceptions.hpp b/object_store_exceptions.hpp index af41e31f..098fea30 100644 --- a/object_store_exceptions.hpp +++ b/object_store_exceptions.hpp @@ -23,13 +23,14 @@ #include #include +#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 }; - enum class InfoKey { - OldVersion, - NewVersion, - ObjectType, - PropertyName, - PropertyType, - OldPropertyType, - PropertyObjectType, - OldPropertyObjectType, - PrimaryKey, - OldPrimaryKey, - }; + typedef const std::string InfoKey; typedef std::map 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 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 &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 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 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); }; }