diff options
-rw-r--r-- | src/BlockTypeRegistry.cpp | 85 | ||||
-rw-r--r-- | src/BlockTypeRegistry.h | 62 | ||||
-rw-r--r-- | tests/BlockTypeRegistry/BlockTypeRegistryTest.cpp | 37 |
3 files changed, 181 insertions, 3 deletions
diff --git a/src/BlockTypeRegistry.cpp b/src/BlockTypeRegistry.cpp index 45ad20082..cc6945e27 100644 --- a/src/BlockTypeRegistry.cpp +++ b/src/BlockTypeRegistry.cpp @@ -56,6 +56,33 @@ AString BlockInfo::hintValue( +void BlockInfo::setHint(const AString & aHintKey, const AString & aHintValue) +{ + mHints[aHintKey] = aHintValue; + + // Warn if the hint is already provided by a callback (aHintValue will be ignored when evaluating the hint): + auto itrC = mHintCallbacks.find(aHintKey); + if (itrC != mHintCallbacks.end()) + { + LOGINFO("Setting a static hint %s for block type %s, but there's already a callback for that hint. The static hint will be ignored.", + aHintKey.c_str(), mBlockTypeName.c_str() + ); + } +} + + + + + +void BlockInfo::removeHint(const AString & aHintKey) +{ + mHints.erase(aHintKey); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // BlockTypeRegistry: @@ -123,6 +150,43 @@ void BlockTypeRegistry::removeAllByPlugin(const AString & aPluginName) +void BlockTypeRegistry::setBlockTypeHint( + const AString & aBlockTypeName, + const AString & aHintKey, + const AString & aHintValue +) +{ + cCSLock lock(mCSRegistry); + auto blockInfo = mRegistry.find(aBlockTypeName); + if (blockInfo == mRegistry.end()) + { + throw NotRegisteredException(aBlockTypeName, aHintKey, aHintValue); + } + blockInfo->second->setHint(aHintKey, aHintValue); +} + + + + + +void BlockTypeRegistry::removeBlockTypeHint( + const AString & aBlockTypeName, + const AString & aHintKey +) +{ + cCSLock lock(mCSRegistry); + auto blockInfo = mRegistry.find(aBlockTypeName); + if (blockInfo == mRegistry.end()) + { + return; + } + blockInfo->second->removeHint(aHintKey); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // BlockTypeRegistry::AlreadyRegisteredException: @@ -151,3 +215,24 @@ AString BlockTypeRegistry::AlreadyRegisteredException::message( aPreviousRegistration->pluginName().c_str() ); } + + + + + +//////////////////////////////////////////////////////////////////////////////// +// BlockTypeRegistry::NotRegisteredException: + +BlockTypeRegistry::NotRegisteredException::NotRegisteredException( + const AString & aBlockTypeName, + const AString & aHintKey, + const AString & aHintValue +): + Super(Printf( + "Attempting to set a hint of nonexistent BlockTypeName.\n\tBlockTypeName = %s\n\tHintKey = %s\n\tHintValue = %s", + aBlockTypeName.c_str(), + aHintKey.c_str(), + aHintValue.c_str() + )) +{ +} diff --git a/src/BlockTypeRegistry.h b/src/BlockTypeRegistry.h index 6a24445c5..3fbcc44c2 100644 --- a/src/BlockTypeRegistry.h +++ b/src/BlockTypeRegistry.h @@ -40,7 +40,7 @@ public: /** Retrieves the value associated with the specified hint for this specific BlockTypeName and BlockState. - Queries callbacks first, then hints if a callback doesn't exist. + Queries hint callbacks first, then static hints if a callback doesn't exist. Returns an empty string if hint not found at all. */ AString hintValue( const AString & aHintName, @@ -52,6 +52,15 @@ public: const AString & blockTypeName() const { return mBlockTypeName; } std::shared_ptr<cBlockHandler> handler() const { return mHandler; } + /** Sets (creates or updates) a static hint. + Hints provided by callbacks are unaffected by this - callbacks are "higher priority", they overwrite anything set here. + Logs an info message if the hint is already provided by a hint callback. */ + void setHint(const AString & aHintKey, const AString & aHintValue); + + /** Removes a hint. + Silently ignored if the hint hasn't been previously set. */ + void removeHint(const AString & aHintKey); + private: @@ -64,10 +73,12 @@ private: /** The callbacks to call for various interaction. */ std::shared_ptr<cBlockHandler> mHandler; - /** Optional hints for any subsystem to use, such as "IsSnowable" -> "1". */ + /** Optional static hints for any subsystem to use, such as "IsSnowable" -> "1". + Hint callbacks are of higher priority than mHints - if a hint is provided by a mHintCallback, its value in mHints is ignored. */ std::map<AString, AString> mHints; - /** The callbacks for dynamic evaluation of hints, such as "LightValue" -> function(BlockTypeName, BlockState). */ + /** The callbacks for dynamic evaluation of hints, such as "LightValue" -> function(BlockTypeName, BlockState). + Hint callbacks are of higher priority than mHints - if a hint is provided by a mHintCallback, its value in mHints is ignored. */ std::map<AString, HintCallback> mHintCallbacks; }; @@ -86,6 +97,7 @@ class BlockTypeRegistry public: // fwd: class AlreadyRegisteredException; + class NotRegisteredException; /** Creates an empty new instance of the block type registry */ @@ -109,6 +121,22 @@ public: /** Removes all registrations done by the specified plugin. */ void removeAllByPlugin(const AString & aPluginName); + /** Sets (adds or overwrites) a single Hint value for a BlockType. + Throws NotRegisteredException if the BlockTypeName is not registered. */ + void setBlockTypeHint( + const AString & aBlockTypeName, + const AString & aHintKey, + const AString & aHintValue + ); + + /** Removes a previously registered single Hint value for a BlockType. + Throws NotRegisteredException if the BlockTypeName is not registered. + Silently ignored if the Hint hasn't been previously set. */ + void removeBlockTypeHint( + const AString & aBlockTypeName, + const AString & aHintKey + ); + private: @@ -156,3 +184,31 @@ private: std::shared_ptr<BlockInfo> aNewRegistration ); }; + + + + + +/** The exception thrown from BlockTypeRegistry::setBlockTypeHint() if the block type has not been registered before. */ +class BlockTypeRegistry::NotRegisteredException: public std::runtime_error +{ + using Super = std::runtime_error; + +public: + + /** Creates a new instance of the exception that provides info on both the original registration and the newly attempted + registration that caused the failure. */ + NotRegisteredException( + const AString & aBlockTypeName, + const AString & aHintKey, + const AString & aHintValue + ); + + // Simple getters: + const AString & blockTypeName() const { return mBlockTypeName; } + + +private: + + const AString mBlockTypeName; +}; diff --git a/tests/BlockTypeRegistry/BlockTypeRegistryTest.cpp b/tests/BlockTypeRegistry/BlockTypeRegistryTest.cpp index c2d8717e5..3479e55d7 100644 --- a/tests/BlockTypeRegistry/BlockTypeRegistryTest.cpp +++ b/tests/BlockTypeRegistry/BlockTypeRegistryTest.cpp @@ -64,6 +64,42 @@ static void testSimpleReg() +/** Tests setting and removing a BlockType hint. */ +static void testHintSetRemove() +{ + LOGD("Testing hint addition and removal..."); + + // Register the block type: + BlockTypeRegistry reg; + AString blockTypeName("test:block1"); + AString pluginName("testPlugin"); + AString hint1("testHint1"); + AString hint1Value("value1"); + AString hint2("testHint2"); + AString hint2Value("value2"); + std::shared_ptr<cBlockHandler> handler(new cBlockHandler(0x12345678)); + std::map<AString, AString> hints = {{hint1, hint1Value}}; + std::map<AString, BlockInfo::HintCallback> hintCallbacks; + reg.registerBlockType(pluginName, blockTypeName, handler, hints, hintCallbacks); + + // Modify the hints: + auto blockInfo = reg.blockInfo(blockTypeName); + reg.setBlockTypeHint(blockTypeName, hint2, hint2Value); + TEST_EQUAL(blockInfo->hintValue(hint2, BlockState()), hint2Value); // Was created successfully + reg.setBlockTypeHint(blockTypeName, hint1, "testValue2"); + TEST_EQUAL(blockInfo->hintValue(hint1, BlockState()), "testValue2"); // Was updated successfully + reg.removeBlockTypeHint(blockTypeName, hint2); + TEST_EQUAL(blockInfo->hintValue(hint2, BlockState()), ""); // Was removed successfully + + // Test the error reporting: + TEST_THROWS(reg.setBlockTypeHint("nonexistent", "hint", "value"), BlockTypeRegistry::NotRegisteredException); + reg.removeBlockTypeHint(blockTypeName, "nonexistent"); // Should fail silently +} + + + + + /** Tests that the plugin-based information is used correctly for registration. Registers two different block types with two different plugins, then tries to re-register them from a different plugin. Finally removes the registration through removeAllByPlugin() and checks its success. */ @@ -190,6 +226,7 @@ static void testThreadLocking() static void testBlockTypeRegistry() { testSimpleReg(); + testHintSetRemove(); testPlugins(); testHintCallbacks(); testThreadLocking(); |