summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/common/Rtti.cpp1
-rw-r--r--src/core/common/Rtti.hpp5
-rw-r--r--src/core/common/Variant.cpp56
-rw-r--r--src/core/common/Variant.hpp99
-rw-r--r--src/core/common/VariantConverter.cpp122
-rw-r--r--src/core/common/VariantConverter.hpp51
-rw-r--r--src/core/common/VariantReader.cpp131
-rw-r--r--src/core/common/VariantReader.hpp56
8 files changed, 459 insertions, 62 deletions
diff --git a/src/core/common/Rtti.cpp b/src/core/common/Rtti.cpp
index 1213669..17b8880 100644
--- a/src/core/common/Rtti.cpp
+++ b/src/core/common/Rtti.cpp
@@ -182,6 +182,7 @@ const Rtti Double{"double"};
const Rtti String{"string"};
const Rtti Array{"array"};
const Rtti Map{"map"};
+const Rtti Cardinality{"cardinality"};
const Rtti Function{"function"};
}
}
diff --git a/src/core/common/Rtti.hpp b/src/core/common/Rtti.hpp
index fa2692f..8350cb5 100644
--- a/src/core/common/Rtti.hpp
+++ b/src/core/common/Rtti.hpp
@@ -520,6 +520,11 @@ extern const Rtti Array;
extern const Rtti Map;
/**
+ * Cardinality type for use by the Variant::getRtti method.
+ */
+extern const Rtti Cardinality;
+
+/**
* Function type for use by the Variant::getRtti method.
*/
extern const Rtti Function;
diff --git a/src/core/common/Variant.cpp b/src/core/common/Variant.cpp
index c5db4e5..9b22b71 100644
--- a/src/core/common/Variant.cpp
+++ b/src/core/common/Variant.cpp
@@ -31,7 +31,8 @@ namespace ousia {
/* Class Variant::TypeException */
-Variant::TypeException::TypeException(VariantType actualType, VariantType requestedType)
+Variant::TypeException::TypeException(VariantType actualType,
+ VariantType requestedType)
: OusiaException(std::string("Variant: Requested \"") +
Variant::getTypeName(requestedType) +
std::string("\" but is \"") +
@@ -64,6 +65,8 @@ const char *Variant::getTypeName(VariantType type)
return "map";
case VariantType::OBJECT:
return "object";
+ case VariantType::CARDINALITY:
+ return "cardinality";
case VariantType::FUNCTION:
return "function";
}
@@ -108,7 +111,8 @@ Variant::arrayType Variant::toArray() const
{
ExceptionLogger logger;
Variant res{*this};
- VariantConverter::toArray(res, RttiTypes::None, logger, VariantConverter::Mode::ALL);
+ VariantConverter::toArray(res, RttiTypes::None, logger,
+ VariantConverter::Mode::ALL);
return res.asArray();
}
@@ -116,7 +120,8 @@ Variant::arrayType Variant::toArray(const Rtti &innerType) const
{
ExceptionLogger logger;
Variant res{*this};
- VariantConverter::toArray(res, innerType, logger, VariantConverter::Mode::ALL);
+ VariantConverter::toArray(res, innerType, logger,
+ VariantConverter::Mode::ALL);
return res.asArray();
}
@@ -124,7 +129,8 @@ Variant::mapType Variant::toMap() const
{
ExceptionLogger logger;
Variant res{*this};
- VariantConverter::toMap(res, RttiTypes::None, logger, VariantConverter::Mode::ALL);
+ VariantConverter::toMap(res, RttiTypes::None, logger,
+ VariantConverter::Mode::ALL);
return res.asMap();
}
@@ -132,13 +138,22 @@ Variant::mapType Variant::toMap(const Rtti &innerType) const
{
ExceptionLogger logger;
Variant res{*this};
- VariantConverter::toMap(res, innerType, logger, VariantConverter::Mode::ALL);
+ VariantConverter::toMap(res, innerType, logger,
+ VariantConverter::Mode::ALL);
return res.asMap();
}
+Variant::cardinalityType Variant::toCardinality() const
+{
+ ExceptionLogger logger;
+ Variant res{*this};
+ VariantConverter::toCardinality(res, logger, VariantConverter::Mode::ALL);
+ return res.asCardinality();
+}
+
/* Type management */
-const Rtti& Variant::getRtti() const
+const Rtti &Variant::getRtti() const
{
switch (type) {
case VariantType::NULLPTR:
@@ -156,6 +171,8 @@ const Rtti& Variant::getRtti() const
return RttiTypes::Array;
case VariantType::MAP:
return RttiTypes::Map;
+ case VariantType::CARDINALITY:
+ return RttiTypes::Cardinality;
case VariantType::FUNCTION:
return RttiTypes::Function;
case VariantType::OBJECT: {
@@ -198,28 +215,24 @@ bool operator<(const Variant &lhs, const Variant &rhs)
return lhs.asArray() < rhs.asArray();
case VariantType::MAP:
return lhs.asMap() < rhs.asMap();
+ case VariantType::CARDINALITY:
+ throw OusiaException(
+ "No sensible comparison on cardinalities is possible!");
case VariantType::OBJECT:
- return lhs.asObject().get() < rhs.asObject().get();
+ throw OusiaException(
+ "No sensible comparison on objects is possible!");
case VariantType::FUNCTION:
- return lhs.asFunction() < rhs.asFunction();
+ throw OusiaException(
+ "No sensible comparison on functions is possible!");
}
throw OusiaException("Internal Error! Unknown type!");
}
-bool operator>(const Variant &lhs, const Variant &rhs)
-{
- return rhs < lhs;
-}
+bool operator>(const Variant &lhs, const Variant &rhs) { return rhs < lhs; }
-bool operator<=(const Variant &lhs, const Variant &rhs)
-{
- return !(lhs > rhs);
-}
+bool operator<=(const Variant &lhs, const Variant &rhs) { return !(lhs > rhs); }
-bool operator>=(const Variant &lhs, const Variant &rhs)
-{
- return !(lhs < rhs);
-}
+bool operator>=(const Variant &lhs, const Variant &rhs) { return !(lhs < rhs); }
bool operator==(const Variant &lhs, const Variant &rhs)
{
@@ -244,6 +257,8 @@ bool operator==(const Variant &lhs, const Variant &rhs)
return lhs.asMap() == rhs.asMap();
case VariantType::OBJECT:
return lhs.asObject() == rhs.asObject();
+ case VariantType::CARDINALITY:
+ return lhs.asCardinality() == rhs.asCardinality();
case VariantType::FUNCTION:
return lhs.asFunction() == rhs.asFunction();
}
@@ -254,6 +269,5 @@ bool operator!=(const Variant &lhs, const Variant &rhs)
{
return !(lhs == rhs);
}
-
}
diff --git a/src/core/common/Variant.hpp b/src/core/common/Variant.hpp
index 381a13e..f623b6b 100644
--- a/src/core/common/Variant.hpp
+++ b/src/core/common/Variant.hpp
@@ -40,6 +40,7 @@
// http://nikic.github.io/2012/02/02/Pointer-magic-for-efficient-dynamic-value-representations.html
// later (will allow to use 8 bytes for a variant)
+#include <core/RangeSet.hpp>
#include <core/managed/Managed.hpp>
#include "Exceptions.hpp"
@@ -63,6 +64,7 @@ enum class VariantType : int16_t {
ARRAY,
MAP,
OBJECT,
+ CARDINALITY,
FUNCTION
};
@@ -111,6 +113,8 @@ public:
using arrayType = std::vector<Variant>;
using mapType = std::map<std::string, Variant>;
using objectType = Rooted<Managed>;
+ using cardinalityType = RangeSet<size_t>;
+ using rangeType = Range<size_t>;
using functionType = std::shared_ptr<Function>;
private:
@@ -192,6 +196,9 @@ private:
case VariantType::OBJECT:
ptrVal = new objectType(v.asObject());
break;
+ case VariantType::CARDINALITY:
+ ptrVal = new cardinalityType(v.asCardinality());
+ break;
case VariantType::FUNCTION:
ptrVal = new functionType(v.asFunction());
break;
@@ -225,6 +232,7 @@ private:
case VariantType::ARRAY:
case VariantType::MAP:
case VariantType::OBJECT:
+ case VariantType::CARDINALITY:
case VariantType::FUNCTION:
ptrVal = v.ptrVal;
v.ptrVal = nullptr;
@@ -253,6 +261,9 @@ private:
case VariantType::OBJECT:
delete static_cast<objectType *>(ptrVal);
break;
+ case VariantType::CARDINALITY:
+ delete static_cast<cardinalityType *>(ptrVal);
+ break;
case VariantType::FUNCTION:
delete static_cast<functionType *>(ptrVal);
break;
@@ -346,7 +357,7 @@ public:
*
* @param o is an object that can be converted to a Rooted handle.
*/
- template<class T>
+ template <class T>
static Variant fromObject(T o)
{
Variant res;
@@ -355,6 +366,18 @@ public:
}
/**
+ * Constructor for cardinality values. The given cardinality is copied and
+ *managed by the
+ * new Variant instance.
+ *
+ * @param c is a reference to the cardinality.
+ */
+ Variant(cardinalityType c) : ptrVal(nullptr)
+ {
+ setCardinality(std::move(c));
+ }
+
+ /**
* Named constructor for function values.
*
* @param f is a shared pointer pointing at the Function instance.
@@ -522,6 +545,13 @@ public:
bool isObject() const { return type == VariantType::OBJECT; }
/**
+ * Checks whether this Variant instance is a cardinality.
+ *
+ * @return true if the Variant instance is an cardinality, false otherwise.
+ */
+ bool isCardinality() const { return type == VariantType::CARDINALITY; }
+
+ /**
* Checks whether this Variant instance is a function.
*
* @return true if the Variant instance is a function, false otherwise.
@@ -666,13 +696,12 @@ public:
const mapType &asMap() const { return asObj<mapType>(VariantType::MAP); }
/**
- * Returns a pointer pointing at the stored managed object. Performs no type
- * conversion. Throws an exception if the underlying type is not a managed
- * object.
+ * Returns a reference to the map value. Performs no type conversion.
+ * Throws an exception if the underlying type is not a map.
*
- * @return pointer at the stored managed object.
+ * @return the map value as reference.
*/
- objectType asObject() { return asObj<objectType>(VariantType::OBJECT); }
+ mapType &asMap() { return asObj<mapType>(VariantType::MAP); }
/**
* Returns a pointer pointing at the stored managed object. Performs no type
@@ -687,12 +716,37 @@ public:
}
/**
- * Returns a reference to the map value. Performs no type conversion.
- * Throws an exception if the underlying type is not a map.
+ * Returns a pointer pointing at the stored managed object. Performs no type
+ * conversion. Throws an exception if the underlying type is not a managed
+ * object.
*
- * @return the map value as reference.
+ * @return pointer at the stored managed object.
*/
- mapType &asMap() { return asObj<mapType>(VariantType::MAP); }
+ objectType asObject() { return asObj<objectType>(VariantType::OBJECT); }
+
+ /**
+ * Returns a reference to the cardinality value. Performs no type
+ *conversion.
+ * Throws an exception if the underlying type is not a cardinality.
+ *
+ * @return the cardinality value as reference.
+ */
+ const cardinalityType &asCardinality() const
+ {
+ return asObj<cardinalityType>(VariantType::CARDINALITY);
+ }
+
+ /**
+ * Returns a reference to the cardinality value. Performs no type
+ * conversion.
+ * Throws an exception if the underlying type is not a cardinality.
+ *
+ * @return the cardinality value as reference.
+ */
+ cardinalityType &asCardinality()
+ {
+ return asObj<cardinalityType>(VariantType::CARDINALITY);
+ }
/**
* Returns a shared pointer pointing at the stored function object. Performs
@@ -784,6 +838,13 @@ public:
mapType toMap(const Rtti &innerType) const;
/**
+ * Returns the value of the Variant as cardinality.
+ *
+ * @return the value of the variant as cardinality.
+ */
+ cardinalityType toCardinality() const;
+
+ /**
* Sets the variant to null.
*/
void setNull()
@@ -832,7 +893,7 @@ public:
/**
* Sets the variant to the given string value.
*
- * @param d is the new string value.
+ * @param s is the new string value.
*/
void setString(const char *s)
{
@@ -849,7 +910,7 @@ public:
/**
* Sets the variant to the given magic string value.
*
- * @param d is the new magic string value.
+ * @param s is the new magic string value.
*/
void setMagic(const char *s)
{
@@ -882,7 +943,7 @@ public:
/**
* Sets the variant to the given map value.
*
- * @param a is the new map value.
+ * @param m is the new map value.
*/
void setMap(mapType m)
{
@@ -908,6 +969,18 @@ public:
}
/**
+ * Sets the variant to the given cardinality value.
+ *
+ * @param c is the new cardinality value.
+ */
+ void setCardinality(cardinalityType c)
+ {
+ destroy();
+ type = VariantType::CARDINALITY;
+ ptrVal = new cardinalityType(std::move(c));
+ }
+
+ /**
* Sets the variant to the given function.
*
* @param f is a std::shared_ptr pointing at a instance of the Function
diff --git a/src/core/common/VariantConverter.cpp b/src/core/common/VariantConverter.cpp
index a14c003..65d2039 100644
--- a/src/core/common/VariantConverter.cpp
+++ b/src/core/common/VariantConverter.cpp
@@ -16,6 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <cmath>
#include <cstdint>
#include <memory>
#include <string>
@@ -34,9 +35,9 @@ namespace ousia {
static std::string msgUnexpectedType(const Variant &v,
VariantType requestedType)
{
- return std::string("Cannot convert ") + v.getTypeName() + std::string(" (") +
- VariantWriter::writeJsonToString(v, false) + std::string(") to ") +
- Variant::getTypeName(requestedType);
+ return std::string("Cannot convert ") + v.getTypeName() +
+ std::string(" (") + VariantWriter::writeJsonToString(v, false) +
+ std::string(") to ") + Variant::getTypeName(requestedType);
}
static std::string msgImplicitConversion(VariantType actualType,
@@ -329,6 +330,114 @@ bool VariantConverter::toMap(Variant &var, const Rtti &innerType,
return false;
}
+bool VariantConverter::toCardinality(Variant &var, Logger &logger, Mode mode)
+{
+ // Perform safe conversions (all these operations are considered "lossless")
+ const VariantType type = var.getType();
+ if (type == VariantType::INT) {
+ int value = var.asInt();
+ var.setCardinality(Variant::cardinalityType{});
+ Variant::cardinalityType &card = var.asCardinality();
+ if (value < 0) {
+ logger.error(
+ "A value smaller 0 can not be converted to a cardinality!");
+ return false;
+ }
+ card.merge({(unsigned int) value});
+ return true;
+ }
+
+ // Perform lossy conversions
+ if (mode == Mode::ALL) {
+ switch (type) {
+ case VariantType::NULLPTR:
+ var.setCardinality(Variant::cardinalityType{});
+ return true;
+ case VariantType::BOOL: {
+ bool value = var.asBool();
+ var.setCardinality(Variant::cardinalityType{});
+ Variant::cardinalityType &card = var.asCardinality();
+ if (value) {
+ // accept any value
+ card.merge(Range<size_t>::typeRangeFrom(0));
+ }
+ return true;
+ }
+ case VariantType::DOUBLE: {
+ int value = (int)std::round(var.asDouble());
+ var.setCardinality(Variant::cardinalityType{});
+ Variant::cardinalityType &card = var.asCardinality();
+ if (value < 0) {
+ logger.error(
+ "A value smaller 0 can not be converted to a "
+ "cardinality!");
+ return false;
+ }
+ card.merge({(unsigned int) value});
+ return true;
+ }
+ case VariantType::ARRAY: {
+ Variant::arrayType arr = var.asArray();
+ var.setCardinality(Variant::cardinalityType{});
+ Variant::cardinalityType &card = var.asCardinality();
+ auto it = arr.begin();
+ while (it != arr.end()) {
+ Variant startVar = *it;
+ if (!startVar.isInt()) {
+ logger.error(
+ "A non-integer can not be interpreted as the start "
+ "of a range");
+ return false;
+ }
+ int start = startVar.asInt();
+ if (start < 0) {
+ logger.error(
+ "A value smaller 0 can not be converted to a "
+ "cardinality!");
+ return false;
+ }
+ it++;
+ if (it == arr.end()) {
+ return true;
+ }
+ Variant endVar = *it;
+ if (!endVar.isInt()) {
+ logger.error(
+ "A non-integer can not be interpreted as the end "
+ "of a range");
+ return false;
+ }
+ int end = endVar.asInt();
+ if (end <= start) {
+ logger.error(
+ std::string("The supposed start value ") +
+ std::to_string(start) +
+ " was bigger or equal to the supposed end value " +
+ std::to_string(end) + " of the Range.");
+ return false;
+ }
+ card.merge({(unsigned int) start, (unsigned int) end});
+ it++;
+ }
+ return true;
+ }
+ case VariantType::STRING: {
+ var.setCardinality(Variant::cardinalityType{});
+// Variant::cardinalityType &card = var.asCardinality();
+ // TODO: Implement!
+ return false;
+ }
+ default:
+ break;
+ }
+ }
+
+ // No conversion possible, assign the default value and log an error
+ logger.error(msgUnexpectedType(var, VariantType::CARDINALITY));
+ var.setCardinality(Variant::cardinalityType{});
+ return false;
+}
+
bool VariantConverter::toFunction(Variant &var, Logger &logger)
{
if (var.isFunction()) {
@@ -342,8 +451,7 @@ bool VariantConverter::toFunction(Variant &var, Logger &logger)
}
bool VariantConverter::convert(Variant &var, const Rtti &type,
- const Rtti &innerType, Logger &logger,
- Mode mode)
+ const Rtti &innerType, Logger &logger, Mode mode)
{
// Check for simple Variant types
if (&type == &RttiTypes::None) {
@@ -391,8 +499,8 @@ bool VariantConverter::convert(Variant &var, const Rtti &type,
return true;
}
-bool VariantConverter::convert(Variant &var, const Rtti &type,
- Logger &logger, Mode mode)
+bool VariantConverter::convert(Variant &var, const Rtti &type, Logger &logger,
+ Mode mode)
{
return convert(var, type, RttiTypes::None, logger, mode);
}
diff --git a/src/core/common/VariantConverter.hpp b/src/core/common/VariantConverter.hpp
index 0e66e39..7e317a2 100644
--- a/src/core/common/VariantConverter.hpp
+++ b/src/core/common/VariantConverter.hpp
@@ -50,13 +50,13 @@ public:
*/
enum class Mode {
/**
- * Performs only lossless and sane conversions.
- */
+ * Performs only lossless and sane conversions.
+ */
SAFE,
/**
- * Performs possibly lossy and probably unintuitive conversions.
- */
+ * Performs possibly lossy and probably unintuitive conversions.
+ */
ALL
};
@@ -122,7 +122,7 @@ public:
static bool toDouble(Variant &var, Logger &logger, Mode mode = Mode::SAFE);
/**
- * Converts the given variant to a double. If the "mode" parameter is set
+ * Converts the given variant to a string. If the "mode" parameter is set
* to Mode::SAFE, all primitive types can be converted to strings. For
* all other types the conversion fails. If "mode" is set to Mode::ALL,
* maps and arrays are converted to a JSON representation, objects and
@@ -163,7 +163,6 @@ public:
static bool toArray(Variant &var, const Rtti &innerType, Logger &logger,
Mode mode = Mode::SAFE);
-
/**
* Converts the given variant to an map with the given inner type. The given
* variant must be a map. If "innerType" points at a primitive Rtti type,
@@ -181,7 +180,36 @@ public:
* case the input/output parameter "var" will have the requested type.
*/
static bool toMap(Variant &var, const Rtti &innerType, Logger &logger,
- Mode mode = Mode::SAFE);
+ Mode mode = Mode::SAFE);
+
+ /**
+ * Converts the given variant to a cardinality. If the "mode" parameter is
+ * set to Mode::SAFE, only integers are converted to a cardinality with
+ * exactly that elment. If mode is set to Mode::ALL, the conversion gets
+ * more creative:
+ *
+ * * NULLPTR is converted to an empty cardinality that accepts nothing
+ * * BOOL is converted to either that empty cardinality (false) or to a
+ * cardinality accepting everything (true).
+ * * DOUBLE gets rounded to the next integer and then converted.
+ * * STRING gets parsed as a cardinality using the VariantReader.
+ * * ARRAY (of ints) is interpreted as ranges with interleaving start and
+ * end notation.
+ *
+ * Other converstions are not made. If the conversion was not possible, an
+ * empty cardinality is returned.
+ *
+ * @param var is instance of the Variant class that should be converted to
+ * the requested type.
+ * @param logger is a reference to the logger instance into which messages
+ * should be logged.
+ * @param mode is the conversion mode. See method description for the exact
+ * effect.
+ * @return true if the operation was successful, false otherwise. In any
+ * case the input/output parameter "var" will have the requested type.
+ */
+ static bool toCardinality(Variant &var, Logger &logger,
+ Mode mode = Mode::SAFE);
/**
* Makes sure the given variant is a function. If it is not, it is replaced
@@ -213,9 +241,8 @@ public:
* @return true if the operation was successful, false otherwise. In any
* case the input/output parameter "var" will have the requested type.
*/
- static bool convert(Variant &var, const Rtti &type,
- const Rtti &innerType, Logger &logger,
- Mode mode = Mode::SAFE);
+ static bool convert(Variant &var, const Rtti &type, const Rtti &innerType,
+ Logger &logger, Mode mode = Mode::SAFE);
/**
* Tries conversion to the given Rtti without any enforcement regarding
@@ -233,8 +260,8 @@ public:
* @return true if the operation was successful, false otherwise. In any
* case the input/output parameter "var" will have the requested type.
*/
- static bool convert(Variant &var, const Rtti &type,
- Logger &logger, Mode mode = Mode::SAFE);
+ static bool convert(Variant &var, const Rtti &type, Logger &logger,
+ Mode mode = Mode::SAFE);
};
}
diff --git a/src/core/common/VariantReader.cpp b/src/core/common/VariantReader.cpp
index db82954..6bc6df8 100644
--- a/src/core/common/VariantReader.cpp
+++ b/src/core/common/VariantReader.cpp
@@ -82,6 +82,7 @@ static const int STATE_WHITESPACE = 5;
static const int STATE_RESYNC = 6;
static const int STATE_EXPECT_COMMA = 7;
static const int STATE_HAS_KEY = 8;
+static const int STATE_HAS_START = 9;
/* Helper function for parsing arrays or objects */
@@ -517,6 +518,136 @@ std::pair<bool, Variant::mapType> VariantReader::parseObject(CharReader &reader,
return std::make_pair(res.first, res.second.asMap());
}
+static const std::unordered_set<char> cardDelims{' ', ',', '}'};
+
+std::pair<bool, Variant::cardinalityType> VariantReader::parseCardinality(
+ CharReader &reader, Logger &logger)
+{
+ // first we consume all whitespaces.
+ reader.consumeWhitespace();
+ // then we expect curly braces.
+ char c;
+ if (!reader.read(c) || c != '{') {
+ return unexpected(reader, logger, "{", c, Variant::cardinalityType{});
+ }
+
+ Variant::cardinalityType card{};
+
+ reader.consumeWhitespace();
+
+ // which should in turn be followed by ranges.
+ while (reader.peek(c)) {
+ if (Utils::isNumeric(c)) {
+ // in case of a numeric character we want to read an integer.
+ reader.resetPeek();
+ Number n;
+ n.parse(reader, logger, cardDelims);
+ if (!n.isInt() || n.intValue() < 0) {
+ return error(reader, logger, "Invalid number for cardinality!",
+ Variant::cardinalityType{});
+ }
+ unsigned int start = (unsigned int)n.intValue();
+ // if we have that we might either find a } or , making this a
+ // range or a - leading us to expect another integer.
+ reader.consumeWhitespace();
+ if (!reader.peek(c)) {
+ error(reader, logger, ERR_UNEXPECTED_END,
+ Variant::cardinalityType{});
+ }
+ switch (c) {
+ case '}':
+ case ',':
+ reader.resetPeek();
+ break;
+ case '-': {
+ reader.consumePeek();
+ // get another integer.
+ reader.consumeWhitespace();
+ if (!reader.peek(c)) {
+ error(reader, logger, ERR_UNEXPECTED_END,
+ Variant::cardinalityType{});
+ }
+ Number n2;
+ n2.parse(reader, logger, cardDelims);
+ if (!n2.isInt() || n2.intValue() < 0) {
+ return error(reader, logger,
+ "Invalid number for cardinality!",
+ Variant::cardinalityType{});
+ }
+
+ unsigned int end = (unsigned int)n2.intValue();
+ card.merge({start, end});
+ break;
+ }
+ default:
+ return unexpected(reader, logger, "}, , or -", c,
+ Variant::cardinalityType{});
+ }
+ if (c == '{' || c == ',') {
+ reader.resetPeek();
+ }
+ } else {
+ switch (c) {
+ case '*':
+ // in case of a Kleene star we can construct the
+ // cardinality right away.
+ card.merge(Variant::rangeType::typeRangeFrom(0));
+ break;
+ case '<':
+ case '>': {
+ // in case of an open range we expect a number.
+ reader.consumeWhitespace();
+ Number n;
+ if (!n.parse(reader, logger, cardDelims)) {
+ return error(reader, logger,
+ "Expected number in an open range "
+ "specifier!",
+ Variant::cardinalityType{});
+ }
+ if (!n.isInt() || n.intValue() < 0) {
+ return error(reader, logger,
+ "Invalid number for cardinality!",
+ Variant::cardinalityType{});
+ }
+ if (c == '<') {
+ card.merge(
+ Variant::rangeType{0, (unsigned int)n.intValue()});
+ } else {
+ card.merge(Variant::rangeType::typeRangeFrom(
+ (unsigned int)n.intValue()));
+ }
+ break;
+ }
+ default:
+ return unexpected(reader, logger,
+ "Unsigned integer, *, < or >", c,
+ Variant::cardinalityType{});
+ }
+ }
+ // after we have parsed a range, read all whitespaces.
+ reader.consumeWhitespace();
+ // ... and check if we are at the end.
+ if (!reader.read(c)) {
+ error(reader, logger, ERR_UNEXPECTED_END,
+ Variant::cardinalityType{});
+ }
+ switch (c) {
+ case '}':
+ return std::make_pair(true, card);
+ case ',':
+ reader.consumeWhitespace();
+ break;
+
+ default:
+ return unexpected(reader, logger, "} or ,", c,
+ Variant::cardinalityType{});
+ }
+ }
+
+ return error(reader, logger, ERR_UNEXPECTED_END,
+ Variant::cardinalityType{});
+}
+
std::pair<bool, Variant> VariantReader::parseGeneric(
CharReader &reader, Logger &logger, const std::unordered_set<char> &delims)
{
diff --git a/src/core/common/VariantReader.hpp b/src/core/common/VariantReader.hpp
index 8aaffd8..d939415 100644
--- a/src/core/common/VariantReader.hpp
+++ b/src/core/common/VariantReader.hpp
@@ -176,8 +176,9 @@ public:
* assumes that it is already inside the array and will not wait for a '['
* character.
*/
- static std::pair<bool, Variant::arrayType> parseArray(
- CharReader &reader, Logger &logger, char delim = 0);
+ static std::pair<bool, Variant::arrayType> parseArray(CharReader &reader,
+ Logger &logger,
+ char delim = 0);
/**
* Parses an object definition.
@@ -191,13 +192,50 @@ public:
* assumes that it is already inside the array and will not wait for a '['
* character.
*/
- static std::pair<bool, Variant::mapType> parseObject(
- CharReader &reader, Logger &logger, char delim = 0);
+ static std::pair<bool, Variant::mapType> parseObject(CharReader &reader,
+ Logger &logger,
+ char delim = 0);
+ /**
+ * Parses a Cardinality. A Cardinality is specified as a list of Ranges,
+ * separated by commas and enclosed in curly braces. The ranges can be
+ * either
+ *
+ * * simple unsigned integers
+ * a pair of unsigned integers with a hyphen in between (specifying the
+ range from the left to the right integer)
+ * * an unsigned integer preceded by a <, specifying the range from 0 to the
+ * integer.
+ * * an unsigned integer preceded by a >, specifying the range from the
+ * integer to infinity.
+ * * A Kleene-Star (*), specifying the range from 0 to infinity.
+ *
+ * Consider the following examples:
+ *
+ * * {3}
+ * * {0-1}, which is equivalent to {<1}
+ * * {>0}
+ * * {*}
+ *
+ * Note that the given Ranges will be merged internally to be non-redundant,
+ * as in the following examples:
+ *
+ * * {3-9, 7-10} will be optimized to {3-10}
+ * * {> 1, 8} will be optimized to {> 1}
+ * * {*, > 0} will be optimized to {*}
+ *
+ * @param reader is a reference to the CharReader instance which is
+ * the source for the character data. The reader will be positioned after
+ * the number or at the terminating delimiting character.
+ * @param logger is the logger instance that should be used to log error
+ * messages and warnings.
+ */
+ static std::pair<bool, Variant::cardinalityType> parseCardinality(
+ CharReader &reader, Logger &logger);
/**
* Tries to parse the most specific item from the given stream until one of
- * the given delimiters is reached or a meaningful literal (possibly an
- * array of literals) has been read. The resulting variant represents the
+ * the given delimiters is reached or a meaningful literal (possibly an
+ * array of literals) has been read. The resulting variant represents the
* value that has been read.
*
* @param reader is a reference to the CharReader instance which is
@@ -221,7 +259,7 @@ public:
* @param delims is a set of characters which will terminate the string.
* These characters are not included in the result. May not be nullptr.
* @param extractUnescapedStrings if set to true, interprets non-primitive
- * literals as unescaped strings, which may also contain whitespace
+ * literals as unescaped strings, which may also contain whitespace
* characters. Otherwise string literals are only generated until the next
* whitespace character.
*/
@@ -247,8 +285,8 @@ public:
* variant.) Information on why the operation has failed is passed to the
* logger.
*/
- static std::pair<bool, Variant> parseGenericString(
- const std::string &str, Logger &logger);
+ static std::pair<bool, Variant> parseGenericString(const std::string &str,
+ Logger &logger);
};
}