diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/RangeSet.hpp | 32 | ||||
| -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 | 132 | ||||
| -rw-r--r-- | src/core/common/VariantReader.hpp | 56 | 
9 files changed, 492 insertions, 62 deletions
diff --git a/src/core/RangeSet.hpp b/src/core/RangeSet.hpp index 2c138dc..b196dec 100644 --- a/src/core/RangeSet.hpp +++ b/src/core/RangeSet.hpp @@ -169,6 +169,16 @@ struct Range {  	{  		return Range(from, std::numeric_limits<T>::max());  	} + +	friend bool operator==(const Range<T> &lhs, const Range<T> &rhs) +	{ +		return lhs.start == rhs.start && lhs.end == rhs.end; +	} + +	friend bool operator!=(const Range<T> &lhs, const Range<T> &rhs) +	{ +		return !(lhs == rhs); +	}  };  /** @@ -341,6 +351,28 @@ public:  	{  		return this->ranges;  	} + +	friend bool operator==(const RangeSet<T> &lhs, const RangeSet<T> &rhs) +	{ +		if (lhs.ranges.size() != rhs.ranges.size()) { +			return false; +		} +		auto leftIt = lhs.ranges.begin(); +		auto rightIt = rhs.ranges.begin(); +		while (leftIt != lhs.ranges.end()) { +			if (*leftIt != *rightIt) { +				return false; +			} +			leftIt++; +			rightIt++; +		} +		return true; +	} + +	friend bool operator!=(const RangeSet<T> &lhs, const RangeSet<T> &rhs) +	{ +		return !(lhs == rhs); +	}  };  } diff --git a/src/core/common/Rtti.cpp b/src/core/common/Rtti.cpp index a913d76..6849a0e 100644 --- a/src/core/common/Rtti.cpp +++ b/src/core/common/Rtti.cpp @@ -207,6 +207,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 e2f1fa2..05cc728 100644 --- a/src/core/common/Rtti.hpp +++ b/src/core/common/Rtti.hpp @@ -541,6 +541,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 600fd9b..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)  { @@ -563,6 +694,7 @@ std::pair<bool, Variant> VariantReader::parseGenericToken(  	// Parse a string if a quote is reached  	if (c == '"' || c == '\'') { +		reader.resetPeek();  		auto res = parseString(reader, logger);  		return std::make_pair(res.first, res.second.c_str());  	} 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);  };  }  | 
