From a244defdbcdb22b878663ec9f56f3019ddb4e274 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 18 Jul 2016 20:12:24 +0200 Subject: [PATCH] Add additional Lua functions that make use of regular expressions to match against fully qualified property identifiers - Making the GUI use the new function setPropertyValueSingle to not incur the overhead of compiling the regex - The default setPropertyValue Lua function uses a wildcard, which replaces a * with the correct regex (.*) for ease of use - The setPropertyValueRegex Lua function allows a full regular expression to be used Closing #108 --- include/openspace/query/query.h | 3 + include/openspace/scene/scene.h | 2 +- modules/onscreengui/CMakeLists.txt | 2 +- modules/onscreengui/src/renderproperties.cpp | 2 +- src/properties/propertyowner.cpp | 3 +- src/query/query.cpp | 27 +++++ src/scene/scene.cpp | 20 +++- src/scene/scene_lua.inl | 117 ++++++++++++++++++- 8 files changed, 166 insertions(+), 10 deletions(-) diff --git a/include/openspace/query/query.h b/include/openspace/query/query.h index ccb05b58ca..a48c55aed3 100644 --- a/include/openspace/query/query.h +++ b/include/openspace/query/query.h @@ -26,12 +26,14 @@ #define __QUERY_H__ #include +#include namespace openspace { namespace properties { class Property; } + class Renderable; class Scene; class SceneGraphNode; @@ -42,6 +44,7 @@ Scene* sceneGraph(); SceneGraphNode* sceneGraphNode(const std::string& name); Renderable* renderable(const std::string& name); properties::Property* property(const std::string& uri); +std::vector allProperties(); } // namespace diff --git a/include/openspace/scene/scene.h b/include/openspace/scene/scene.h index 8950866867..4844d3ac7b 100644 --- a/include/openspace/scene/scene.h +++ b/include/openspace/scene/scene.h @@ -100,7 +100,7 @@ public: */ SceneGraphNode* sceneGraphNode(const std::string& name) const; - std::vector allSceneGraphNodes(); + std::vector allSceneGraphNodes() const; SceneGraph& sceneGraph(); diff --git a/modules/onscreengui/CMakeLists.txt b/modules/onscreengui/CMakeLists.txt index 2487474370..e0064f6c70 100644 --- a/modules/onscreengui/CMakeLists.txt +++ b/modules/onscreengui/CMakeLists.txt @@ -33,7 +33,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/guipropertycomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guitimecomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guiiswacomponent.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/renderproperties.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/renderproperties.h ) source_group("Header Files" FILES ${HEADER_FILES}) diff --git a/modules/onscreengui/src/renderproperties.cpp b/modules/onscreengui/src/renderproperties.cpp index 7ae57786cf..f027633784 100644 --- a/modules/onscreengui/src/renderproperties.cpp +++ b/modules/onscreengui/src/renderproperties.cpp @@ -46,7 +46,7 @@ void renderTooltip(Property* prop) { void executeScript(const std::string& id, const std::string& value) { std::string script = - "openspace.setPropertyValue('" + id + "', " + value + ");"; + "openspace.setPropertyValueSingle('" + id + "', " + value + ");"; OsEng.scriptEngine().queueScript(script); } diff --git a/src/properties/propertyowner.cpp b/src/properties/propertyowner.cpp index 5f54329aae..24221357a5 100644 --- a/src/properties/propertyowner.cpp +++ b/src/properties/propertyowner.cpp @@ -104,8 +104,9 @@ Property* PropertyOwner::property(const std::string& id) const { } } } - else + else { return *it; + } } bool PropertyOwner::hasProperty(const std::string& id) const { diff --git a/src/query/query.cpp b/src/query/query.cpp index e80cfcc36b..7d87a9ccbf 100644 --- a/src/query/query.cpp +++ b/src/query/query.cpp @@ -96,4 +96,31 @@ properties::Property* property(const std::string& uri) { } } +std::vector allProperties() { + std::vector properties; + + auto p = OsEng.globalPropertyOwner().propertiesRecursive(); + + properties.insert( + properties.end(), + p.begin(), + p.end() + ); + + const Scene* graph = sceneGraph(); + std::vector nodes = graph->allSceneGraphNodes(); + + for (SceneGraphNode* n : nodes) { + auto p = n->propertiesRecursive(); + properties.insert( + properties.end(), + p.begin(), + p.end() + ); + } + + return properties; +} + + } // namespace diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 0a4ad41624..ff6591ac9b 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -520,7 +520,7 @@ SceneGraphNode* Scene::sceneGraphNode(const std::string& name) const { return _graph.sceneGraphNode(name); } -std::vector Scene::allSceneGraphNodes() { +std::vector Scene::allSceneGraphNodes() const { return _graph.nodes(); } @@ -563,9 +563,25 @@ scripting::LuaLibrary Scene::luaLibrary() { "setPropertyValue", &luascriptfunctions::property_setValue, "string, *", + "Sets all properties identified by the URI (with potential wildcards) in " + "the first argument. The second argument can be any type, but it has to " + "match the type that the property (or properties) expect." + }, + { + "setPropertyValueRegex", + &luascriptfunctions::property_setValueRegex, + "Sets all properties that pass the regular expression in the first " + "argument. The second argument can be any type, but it has to match the " + "type of the properties that matched the regular expression. The regular " + "expression has to be of the ECMAScript grammar." + }, + { + "setPropertyValueSingle", + &luascriptfunctions::property_setValueSingle, + "string, *", "Sets a property identified by the URI in " "the first argument. The second argument can be any type, but it has to " - " agree with the type that the property expects", + "match the type that the property expects.", true }, { diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index c4472091ab..e1adcc30fb 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -22,18 +22,58 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ +#include + namespace openspace { +namespace { + +void applyRegularExpression(lua_State* L, std::regex regex, std::vector properties, int type) { + static const std::string _loggerCat = "property_setValue"; + + using ghoul::lua::errorLocation; + using ghoul::lua::luaTypeToString; + + for (properties::Property* prop : properties) { + // Check the regular expression for all properties + std::string id = prop->fullyQualifiedIdentifier(); + if (std::regex_match(id, regex)) { + // If the fully qualified id matches the regular expression, we queue the + // value change if the types agree + + if (type != prop->typeLua()) { + LERROR(errorLocation(L) << "Property '" << + prop->fullyQualifiedIdentifier() << + "' does not accept input of type '" << luaTypeToString(type) << + "'. Requested type: '" << luaTypeToString(prop->typeLua()) << "'"); + } + else { + prop->setLuaValue(L); + //ensure properties are synced over parallel connection + std::string value; + prop->getStringValue(value); + OsEng.parallelConnection().scriptMessage( + prop->fullyQualifiedIdentifier(), + value + ); + } + + } + } +} +} + + namespace luascriptfunctions { /** * \ingroup LuaScripts - * setPropertyValue(string, *): + * setPropertyValueSingle(string, *): * Sets the property identified by the URI in the first argument to the value passed to * the second argument. The type of the second argument is arbitrary, but it must agree * with the type the denoted Property expects */ -int property_setValue(lua_State* L) { +int property_setValueSingle(lua_State* L) { static const std::string _loggerCat = "property_setValue"; using ghoul::lua::errorLocation; using ghoul::lua::luaTypeToString; @@ -44,7 +84,7 @@ int property_setValue(lua_State* L) { std::string uri = luaL_checkstring(L, -2); const int type = lua_type(L, -1); - openspace::properties::Property* prop = property(uri); + properties::Property* prop = property(uri); if (!prop) { LERROR(errorLocation(L) << "Property with URI '" << uri << "' was not found"); return 0; @@ -57,7 +97,7 @@ int property_setValue(lua_State* L) { "'. Requested type: '" << luaTypeToString(prop->typeLua()) << "'"); return 0; } - else{ + else { prop->setLuaValue(L); //ensure properties are synced over parallel connection std::string value; @@ -68,6 +108,75 @@ int property_setValue(lua_State* L) { return 0; } +/** + * \ingroup LuaScripts + * setPropertyValueRegex(string, *): + * Sets all properties that pass the regular expression in the first argument. The second + * argument can be any type, but it has to match the type of the properties that matched + * the regular expression. The regular expression has to be of the ECMAScript grammar. +*/ +int property_setValueRegex(lua_State* L) { + static const std::string _loggerCat = "property_setValueRegex"; + using ghoul::lua::errorLocation; + using ghoul::lua::luaTypeToString; + + int nArguments = lua_gettop(L); + SCRIPT_CHECK_ARGUMENTS(L, 2, nArguments); + + std::string regex = luaL_checkstring(L, -2); + try { + applyRegularExpression( + L, + std::regex(regex, std::regex_constants::optimize), + allProperties(), + lua_type(L, -1) + ); + } + catch (const std::regex_error& e) { + LERROR("Malformed regular expression: '" << regex << "'"); + } + + return 0; +} + +/** +* \ingroup LuaScripts +* setPropertyValue(string, *): +* Sets all properties identified by the URI (with potential wildcards) in the first +* argument. The second argument can be any type, but it has to match the type that the +* property (or properties) expect. +*/ + +int property_setValue(lua_State* L) { + static const std::string _loggerCat = "property_setValueRegex"; + using ghoul::lua::errorLocation; + using ghoul::lua::luaTypeToString; + + int nArguments = lua_gettop(L); + SCRIPT_CHECK_ARGUMENTS(L, 2, nArguments); + + std::string regex = luaL_checkstring(L, -2); + + // Replace all wildcards * with the correct regex (.*) + size_t startPos = regex.find("*"); + while (startPos != std::string::npos) { + regex.replace(startPos, 1, "(.*)"); + startPos += 4; + + startPos = regex.find("*", startPos); + } + + + applyRegularExpression( + L, + std::regex(regex/*, std::regex_constants::optimize*/), + allProperties(), + lua_type(L, -1) + ); + + return 0; +} + /** * \ingroup LuaScripts * getPropertyValue(string):