From 5d0e02b8aab17ec4aee1037338d2d3c9e5e48e8a Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Mon, 10 Feb 2025 18:57:41 +0100 Subject: [PATCH] ResourceMap: Add string enum API --- lib/src/Base/Common/ResourceMap.cxx | 53 +++++++++++++++++- lib/src/Base/Common/openturns/ResourceMap.hxx | 14 +++++ python/src/ResourceMap_doc.i.in | 54 +++++++++++++++++++ python/test/t_ResourceMap_missing.py | 5 +- python/test/t_ResourceMap_std.py | 12 +++++ 5 files changed, 135 insertions(+), 3 deletions(-) diff --git a/lib/src/Base/Common/ResourceMap.cxx b/lib/src/Base/Common/ResourceMap.cxx index f83adfe08b..36f99db4ae 100644 --- a/lib/src/Base/Common/ResourceMap.cxx +++ b/lib/src/Base/Common/ResourceMap.cxx @@ -27,6 +27,7 @@ #include "openturns/Path.hxx" #include "openturns/Collection.hxx" #include "openturns/XMLToolbox.hxx" + #ifdef OPENTURNS_HAVE_LIBXML2 #include #endif @@ -107,6 +108,11 @@ std::vector ResourceMap::GetBoolKeys() return GetInstance().lock().getBoolKeys(); } +std::vector ResourceMap::GetStringEnum(const String & key) +{ + return GetInstance().lock().getStringEnum(key); +} + /* Get a value in the map */ String ResourceMap::GetType(const String & key) { @@ -195,6 +201,11 @@ void ResourceMap::AddAsString(const String & key, const String & value) GetInstance().lock().addAsString(key, value); } +void ResourceMap::AddAsStringEnum(const String & key, const String & value, const std::vector & enumValues) +{ + GetInstance().lock().addAsStringEnum(key, value, enumValues); +} + void ResourceMap::AddAsBool(const String & key, const Bool value) { GetInstance().lock().addAsBool(key, value); @@ -215,6 +226,11 @@ Bool ResourceMap::HasKey(const String & key) return GetInstance().lock().hasKey(key); } +Bool ResourceMap::HasStringEnum(const String & key) +{ + return GetInstance().lock().hasStringEnum(key); +} + void ResourceMap::Reload() { return GetInstance().lock().reload(); @@ -319,6 +335,11 @@ Bool ResourceMap::hasKey(const String & key) const || (mapBool_.find(key) != mapBool_.end())); } +Bool ResourceMap::hasStringEnum(const String & key) const +{ + return mapStringEnum_.find(key) != mapStringEnum_.end(); +} + void ResourceMap::removeKey(const String & key) { if (!hasKey(key)) @@ -326,7 +347,11 @@ void ResourceMap::removeKey(const String & key) const String keyType(getType(key)); if (keyType == "str") + { mapString_.erase(mapString_.find(key)); + if (mapStringEnum_.find(key) != mapStringEnum_.end()) + mapStringEnum_.erase(mapStringEnum_.find(key)); + } else if (keyType == "float") mapScalar_.erase(mapScalar_.find(key)); else if (keyType == "int") @@ -453,6 +478,14 @@ void ResourceMap::setAsString(const String & key, const String & value) const MapStringType::iterator it = mapString_.find(key); if (it == mapString_.end()) throw InternalException(HERE) << "Key '" << key << "' is missing in ResourceMap as a String."; + const MapStringEnumType::iterator it2 = mapStringEnum_.find(key); + if (it2 != mapStringEnum_.end() && std::find(it2->second.begin(), it2->second.end(), value) == it2->second.end()) + { + String possibleValues; + for (std::vector::iterator it3 = it2->second.begin(); it3 != it2->second.end(); ++ it3) + possibleValues += *it3 + ", "; + throw InvalidArgumentException(HERE) << "Value for key '" << key << "' must be one of: " << possibleValues << " got '" << value << "'"; + } it->second = value; } @@ -487,6 +520,16 @@ void ResourceMap::addAsString(const String & key, const String & value) mapString_[key] = value; } +void ResourceMap::addAsStringEnum(const String & key, const String & value, const std::vector & enumValues) +{ + if (mapString_.find(key) != mapString_.end()) + throw InternalException(HERE) << "Key '" << key << "' is already in ResourceMap as a String."; + mapString_[key] = value; + if (std::find(enumValues.begin(), enumValues.end(), value) == enumValues.end()) + throw InternalException(HERE) << "Enum values do not contain value '" << value << "'"; + mapStringEnum_[key] = enumValues; +} + void ResourceMap::addAsBool(const String & key, const Bool value) { if (mapBool_.find(key) != mapBool_.end()) @@ -718,7 +761,7 @@ void ResourceMap::loadDefaultConfiguration() addAsUnsignedInteger("Contour-DefaultLevelsNumber", 10); addAsBool("Contour-DefaultIsFilled", false); addAsBool("Contour-DefaultDrawLabels", true); - addAsString("Contour-DefaultColorMapNorm", "linear"); + addAsStringEnum("Contour-DefaultColorMapNorm", "linear", {"asinh", "linear", "log", "logit", "symlog", "rank"}); addAsString("Contour-DefaultColorMap", "viridis"); addAsString("Contour-DefaultColorBarPosition", "right"); addAsString("Contour-DefaultExtend", "both"); @@ -1817,6 +1860,14 @@ std::vector ResourceMap::getStringKeys() const return keys; } +/* return the enum list associated to a key */ +std::vector ResourceMap::getStringEnum(const String & key) +{ + const MapStringEnumType::const_iterator it = mapStringEnum_.find(key); + if (it != mapStringEnum_.end()) return it->second; + throw InternalException(HERE) << "Key '" << key << "' has no string enum."; +} + std::vector ResourceMap::getBoolKeys() const { std::vector keys; diff --git a/lib/src/Base/Common/openturns/ResourceMap.hxx b/lib/src/Base/Common/openturns/ResourceMap.hxx index 0549e4ccc3..e77bdf5b60 100644 --- a/lib/src/Base/Common/openturns/ResourceMap.hxx +++ b/lib/src/Base/Common/openturns/ResourceMap.hxx @@ -65,6 +65,7 @@ public: /** Add a value in the maps */ static void AddAsString(const String & key, const String & value); + static void AddAsStringEnum(const String & key, const String & value, const std::vector & enumValues); static void AddAsBool(const String & key, const Bool value); static void AddAsUnsignedInteger(const String & key, const UnsignedInteger value); static void AddAsScalar(const String & key, const Scalar value); @@ -82,10 +83,14 @@ public: static std::vector GetScalarKeys(); static std::vector GetUnsignedIntegerKeys(); static std::vector GetBoolKeys(); + static std::vector GetStringEnum(const String & key); /** Is the specific key present ? */ static Bool HasKey(const String & key); + /** Is the specific key associated to a string enum ? */ + static Bool HasStringEnum(const String & key); + /** Remove a key */ static void RemoveKey(const String & key); @@ -124,6 +129,9 @@ protected: */ std::vector getBoolKeys() const; + /** return the enum list associated to a key */ + std::vector getStringEnum(const String & key); + /** Method for retrieving information from the resource map * @param key The name under which the value is stored in the ResourceMap * @return the type of the key @@ -220,6 +228,7 @@ protected: * @param value The value written to a string */ void addAsString(const String & key, const String & value); + void addAsStringEnum(const String & key, const String & value, const std::vector & enumValues); /** Method for adding information into the resource map * @param key The name under which the value is stored in the ResourceMap @@ -242,6 +251,9 @@ protected: /** Is the specific key present ? */ Bool hasKey(const String & key) const; + /** Is the specific key associated to a string enum ? */ + Bool hasStringEnum(const String & key) const; + /** Remove a key from the resource map */ void removeKey(const String & key); @@ -280,11 +292,13 @@ private: typedef std::map< String, Scalar > MapScalarType; typedef std::map< String, UnsignedInteger > MapUnsignedIntegerType; typedef std::map< String, Bool > MapBoolType; + typedef std::map< String, std::vector< String > > MapStringEnumType; MapStringType mapString_; MapScalarType mapScalar_; MapUnsignedIntegerType mapUnsignedInteger_; MapBoolType mapBool_; + MapStringEnumType mapStringEnum_; friend struct ResourceMap_init; }; /* class ResourceMap */ diff --git a/python/src/ResourceMap_doc.i.in b/python/src/ResourceMap_doc.i.in index a97b3785f4..cf6ea8985d 100644 --- a/python/src/ResourceMap_doc.i.in +++ b/python/src/ResourceMap_doc.i.in @@ -234,6 +234,30 @@ Examples // --------------------------------------------------------------------- +%feature("docstring") OT::ResourceMap::AddAsStringEnum +"Add a new string enum parameter. + +Similar to :meth:`AddAsString` except :meth:`SetAsString` calls will make sure the +value associated to that key is part of the list of possible supplied values. + +Parameters +---------- +key : str + An identifier associated to the parameter. +value : str + The value associated to that key. The key is added to the string map even if + it already exists in another map (float, int or bool). +enumValues : sequence of str + Possible values + +Examples +-------- +>>> import openturns as ot +>>> ot.ResourceMap.AddAsStringEnum('View-ImageFormat3', 'png', ['png', 'jpg', 'bmp']) +>>> ot.ResourceMap.RemoveKey('View-ImageFormat3')" + +// --------------------------------------------------------------------- + %feature("docstring") OT::ResourceMap::GetAsString "Access a string parameter. @@ -413,6 +437,21 @@ keys : tuple of str // --------------------------------------------------------------------- +%feature("docstring") OT::ResourceMap::GetStringEnum +"Get the possible values of a string enum. + +Parameters +---------- +key : str + An identifier associated to the parameter. + +Returns +------- +enums : tuple of str + The list of possible values." + +// --------------------------------------------------------------------- + %feature("docstring") OT::ResourceMap::HasKey "Check if an entry exists. @@ -428,6 +467,21 @@ has_key : bool // --------------------------------------------------------------------- +%feature("docstring") OT::ResourceMap::HasStringEnum +"Check if an entry is associated to a string enum. + +Parameters +---------- +key : str + An identifier associated to the parameter. + +Returns +------- +hasStringEnum : bool + Whether an entry with that key exists." + +// --------------------------------------------------------------------- + %feature("docstring") OT::ResourceMap::Reload "Reload the configuration. diff --git a/python/test/t_ResourceMap_missing.py b/python/test/t_ResourceMap_missing.py index cd8ba3b006..72b956bbe7 100755 --- a/python/test/t_ResourceMap_missing.py +++ b/python/test/t_ResourceMap_missing.py @@ -13,9 +13,10 @@ resourcemap_lines += f.read().splitlines() resourcemap_content = {} for line in resourcemap_lines: - match = re.search(r'addAs(\w+)\("([\w\-]+)",[ ]*([\w\d\.\-\"]+)\);', line) + match = re.search(r'addAs(\w+)\("([\w\-]+)",[ ]*([\w\d\.\-\"]+)[\),]', line) if match is not None: key, vtype, value = match.group(2), match.group(1), match.group(3) + print(key, vtype, value) if key == "SymbolicParser-Backend": continue if vtype == "Scalar": @@ -24,7 +25,7 @@ resourcemap_content[key] = int(value) elif vtype == "Bool": resourcemap_content[key] = {"true": True, "false": False}[value] - elif vtype == "String": + elif vtype.startswith("String"): resourcemap_content[key] = value.strip('"') else: raise ValueError(f"got {key}") diff --git a/python/test/t_ResourceMap_std.py b/python/test/t_ResourceMap_std.py index 0a704e92c7..181c666205 100755 --- a/python/test/t_ResourceMap_std.py +++ b/python/test/t_ResourceMap_std.py @@ -12,3 +12,15 @@ "Extract from ResourceMap: Cache-MaxSize -> ", ot.ResourceMap.Get("Cache-MaxSize"), ) + +# check string enum api +ot.ResourceMap.AddAsStringEnum("foo", "a", ["a", "b"]) +assert ot.ResourceMap.HasStringEnum("foo") +assert ot.ResourceMap.GetStringEnum("foo") == ("a", "b") +ot.ResourceMap.SetAsString("foo", "b") +ok = False +try: + ot.ResourceMap.SetAsString("foo", "z") +except Exception: + ok = True +assert ok