summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/model/Typesystem.cpp47
-rw-r--r--src/core/model/Typesystem.hpp43
-rw-r--r--test/core/model/TypesystemTest.cpp140
3 files changed, 215 insertions, 15 deletions
diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp
index 2899e03..c2ab363 100644
--- a/src/core/model/Typesystem.cpp
+++ b/src/core/model/Typesystem.cpp
@@ -94,34 +94,48 @@ bool BoolType::doBuild(Variant &var, Logger &logger) const
bool EnumType::doBuild(Variant &var, Logger &logger) const
{
+ // If the variant is an int, check whether the value is in range
if (var.isInt()) {
int i = var.asInt();
if (i < 0 || i >= (int)values.size()) {
throw LoggableException("Value is out of range.");
}
- } else if (var.isMagic()) {
+ return true;
+ }
+
+ // If the given variant is a magic value it may be an enumeration constant.
+ // Set the variant to the numeric value
+ if (var.isMagic()) {
// Fetch the given constant name and look it up in the value map
const std::string &name = var.asMagic();
auto it = values.find(name);
- // Throw an execption
+ // Throw an execption if the given string value is not found
if (it == values.end()) {
throw LoggableException(std::string("Unknown enum constant: \"") +
name + std::string("\""));
}
var = it->second;
+ return true;
}
- return true;
+ throw LoggableException{"Expected integer or identifier"};
}
-EnumType EnumType::createValidated(Manager &mgr, std::string name,
+Rooted<EnumType> EnumType::createValidated(Manager &mgr, std::string name,
Handle<Typesystem> system,
const std::vector<std::string> &values,
Logger &logger)
{
// Map used to store the unique values of the enum
- std::map<std::string, Variant::intType> unique_values;
+ std::map<std::string, Ordinal> unique_values;
+ // The given vector may not be empty
+ if (values.empty()) {
+ logger.error("Enumeration constants may not be empty.");
+ }
+
+ // Iterate over the input vector, check the constant names for validity and
+ // uniqueness and insert them into the internal values map
for (size_t i = 0; i < values.size(); i++) {
if (!Utils::isIdentifier(values[i])) {
logger.error(values[i] + " is no valid identifier.");
@@ -132,7 +146,28 @@ EnumType EnumType::createValidated(Manager &mgr, std::string name,
" was duplicated.");
}
}
- return std::move(EnumType(mgr, name, system, unique_values));
+ return new EnumType{mgr, name, system, unique_values};
+}
+
+std::string EnumType::nameOf(Ordinal i) const
+{
+ if (i >= 0 && i < (int)values.size()) {
+ for (const auto &v : values) {
+ if (v.second == i) {
+ return v.first;
+ }
+ }
+ }
+ throw LoggableException("Ordinal value out of range.");
+}
+
+EnumType::Ordinal EnumType::valueOf(const std::string &name) const
+{
+ auto it = values.find(name);
+ if (it != values.end()) {
+ return it->second;
+ }
+ throw LoggableException(std::string("Unknown enum constant: ") + name);
}
/* Class ArrayType */
diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp
index 3511354..8562546 100644
--- a/src/core/model/Typesystem.hpp
+++ b/src/core/model/Typesystem.hpp
@@ -279,12 +279,15 @@ public:
* The EnumType class represents a user defined enumeration type.
*/
class EnumType : public Type {
+public:
+ using Ordinal = Variant::intType;
+
private:
/**
* Map containing the enumeration type values and the associated integer
* representation.
*/
- const std::map<std::string, Variant::intType> values;
+ const std::map<std::string, Ordinal> values;
protected:
/**
@@ -303,24 +306,47 @@ protected:
* instance from a previously created name to value map.
*/
EnumType(Manager &mgr, std::string name, Handle<Typesystem> system,
- std::map<std::string, Variant::intType> values)
+ std::map<std::string, Ordinal> values)
: Type(mgr, std::move(name), system, false), values(std::move(values))
{
}
public:
/**
- * TODO: DOC
+ * Creates a new enum instance and validates the incomming value vector.
+ *
+ * @param mgr is the underlying Manager instance.
+ * @param name is the name of the EnumType instance. Should be a valid
+ * identifier.
+ * @param values is a vector containing the enumeration type constants.
+ * The constants are checked for validity (must be a valid identifier) and
+ * uniqueness (each value must exist exactly once).
+ * @param logger is the Logger instance into which errors should be written.
*/
- static EnumType createValidated(Manager &mgr, std::string name,
- Handle<Typesystem> system,
- const std::vector<std::string> &values,
- Logger &logger);
+ static Rooted<EnumType> createValidated(
+ Manager &mgr, std::string name, Handle<Typesystem> system,
+ const std::vector<std::string> &values, Logger &logger);
/**
- * TODO: DOC
+ * Creates a Variant containing a valid representation of a variable of this
+ * EnumType instance. The variant will point at the first enumeration
+ * constant.
+ *
+ * @return a variant pointing at the first enumeration constant.
*/
Variant create() const override { return Variant{0}; }
+
+ /**
+ * Returns the name of the given ordinal number. Throws a LoggableException
+ * if the ordinal number is out of range.
+ */
+ std::string nameOf(Ordinal i) const;
+
+ /**
+ * Returns the ordinal numer associated with the given enumeration constant
+ * name. Throws a LoggableException if the string does not exist.
+ */
+ Ordinal valueOf(const std::string &name) const;
};
/**
@@ -350,6 +376,7 @@ public:
* @param mgr is the Manager instance to be used for the Node.
* @param type holds a reference to the type descriptor holding the type
* of the attribute.
+ * @param name is the name of the Attribute. Should be a valid identifier.
* @param defaultValue is the default value of the attribute
* @param optional should be set to true if the if the default value should
* be used.
diff --git a/test/core/model/TypesystemTest.cpp b/test/core/model/TypesystemTest.cpp
index 3447b1c..f94e91b 100644
--- a/test/core/model/TypesystemTest.cpp
+++ b/test/core/model/TypesystemTest.cpp
@@ -240,6 +240,145 @@ TEST(BoolType, conversion)
}
}
+/* Class EnumType */
+
+TEST(EnumType, rtti)
+{
+ Logger logger;
+ Manager mgr;
+ Rooted<EnumType> enumType{EnumType::createValidated(
+ mgr, "enum", nullptr, {"a", "b", "c"}, logger)};
+ ASSERT_TRUE(enumType->isa(RttiTypes::EnumType));
+ ASSERT_TRUE(enumType->isa(typeOf<Type>()));
+ ASSERT_TRUE(enumType->isa(typeOf<Node>()));
+}
+
+TEST(EnumType, creation)
+{
+ Logger logger;
+ Manager mgr;
+ Rooted<EnumType> enumType{EnumType::createValidated(
+ mgr, "enum", nullptr, {"a", "b", "c"}, logger)};
+ Variant val = enumType->create();
+ ASSERT_TRUE(val.isInt());
+ ASSERT_EQ(0, val.asInt());
+}
+
+TEST(EnumType, conversion)
+{
+ Logger logger;
+ Manager mgr;
+ Rooted<EnumType> enumType{EnumType::createValidated(
+ mgr, "enum", nullptr, {"a", "b", "c"}, logger)};
+
+ {
+ Variant val{1};
+ ASSERT_TRUE(enumType->build(val, logger));
+ ASSERT_TRUE(val.isInt());
+ ASSERT_EQ(1, val.asInt());
+ }
+
+ {
+ Variant val;
+ val.setMagic("a");
+ ASSERT_TRUE(enumType->build(val, logger));
+ ASSERT_TRUE(val.isInt());
+ ASSERT_EQ(0, val.asInt());
+ }
+
+ {
+ Variant val;
+ val.setMagic("c");
+ ASSERT_TRUE(enumType->build(val, logger));
+ ASSERT_TRUE(val.isInt());
+ ASSERT_EQ(2, val.asInt());
+ }
+
+ {
+ Variant val{"c"};
+ ASSERT_FALSE(enumType->build(val, logger));
+ ASSERT_TRUE(val.isInt());
+ ASSERT_EQ(0, val.asInt());
+ }
+
+ {
+ Variant val;
+ val.setMagic("d");
+ ASSERT_FALSE(enumType->build(val, logger));
+ ASSERT_TRUE(val.isInt());
+ ASSERT_EQ(0, val.asInt());
+ }
+
+ {
+ Variant val{5};
+ ASSERT_FALSE(enumType->build(val, logger));
+ ASSERT_TRUE(val.isInt());
+ ASSERT_EQ(0, val.asInt());
+ }
+}
+
+TEST(EnumType, createValidated)
+{
+ Manager mgr;
+
+ {
+ Logger logger;
+ Rooted<EnumType> enumType{EnumType::createValidated(
+ mgr, "enum", nullptr, {"a", "b", "c"}, logger)};
+ ASSERT_EQ(Severity::DEBUG, logger.getMaxEncounteredSeverity());
+ }
+
+ {
+ Logger logger;
+ Rooted<EnumType> enumType{EnumType::createValidated(
+ mgr, "enum", nullptr, {"a", "a", "c"}, logger)};
+ ASSERT_EQ(Severity::ERROR, logger.getMaxEncounteredSeverity());
+ }
+
+ {
+ Logger logger;
+ Rooted<EnumType> enumType{EnumType::createValidated(
+ mgr, "enum", nullptr, {}, logger)};
+ ASSERT_EQ(Severity::ERROR, logger.getMaxEncounteredSeverity());
+ }
+
+ {
+ Logger logger;
+ Rooted<EnumType> enumType{EnumType::createValidated(
+ mgr, "enum", nullptr, {"a a"}, logger)};
+ ASSERT_EQ(Severity::ERROR, logger.getMaxEncounteredSeverity());
+ }
+}
+
+TEST(EnumType, nameOf)
+{
+ Logger logger;
+ Manager mgr;
+
+ Rooted<EnumType> enumType{EnumType::createValidated(
+ mgr, "enum", nullptr, {"a", "b", "c"}, logger)};
+
+ ASSERT_EQ("a", enumType->nameOf(0));
+ ASSERT_EQ("b", enumType->nameOf(1));
+ ASSERT_EQ("c", enumType->nameOf(2));
+ ASSERT_THROW(enumType->nameOf(-1), LoggableException);
+ ASSERT_THROW(enumType->nameOf(3), LoggableException);
+}
+
+TEST(EnumType, valueOf)
+{
+ Logger logger;
+ Manager mgr;
+
+ Rooted<EnumType> enumType{EnumType::createValidated(
+ mgr, "enum", nullptr, {"a", "b", "c"}, logger)};
+
+ ASSERT_EQ(0, enumType->valueOf("a"));
+ ASSERT_EQ(1, enumType->valueOf("b"));
+ ASSERT_EQ(2, enumType->valueOf("c"));
+ ASSERT_THROW(enumType->valueOf("d"), LoggableException);
+ ASSERT_THROW(enumType->valueOf("e"), LoggableException);
+}
/* Class ArrayType */
@@ -328,6 +467,5 @@ TEST(UnknownType, conversion)
ASSERT_EQ(val1, val2);
}
}
-
}
}