diff options
-rw-r--r-- | src/core/common/Rtti.cpp | 1 | ||||
-rw-r--r-- | src/core/common/Rtti.hpp | 5 | ||||
-rw-r--r-- | src/core/common/Variant.cpp | 56 | ||||
-rw-r--r-- | src/core/common/Variant.hpp | 99 | ||||
-rw-r--r-- | src/core/common/VariantConverter.cpp | 122 | ||||
-rw-r--r-- | src/core/common/VariantConverter.hpp | 51 | ||||
-rw-r--r-- | src/core/common/VariantReader.cpp | 131 | ||||
-rw-r--r-- | src/core/common/VariantReader.hpp | 56 |
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); }; } |