diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/core/RangeSet.hpp | 40 | ||||
-rw-r--r-- | src/core/common/Number.hpp | 2 | ||||
-rw-r--r-- | src/core/common/Variant.hpp | 2 | ||||
-rw-r--r-- | src/core/common/VariantConverter.cpp | 80 | ||||
-rw-r--r-- | src/core/common/VariantReader.cpp | 6 | ||||
-rw-r--r-- | src/core/common/VariantWriter.cpp | 1 | ||||
-rw-r--r-- | test/core/common/VariantConverterTest.cpp | 130 | ||||
-rw-r--r-- | test/core/common/VariantTest.cpp | 14 |
9 files changed, 259 insertions, 17 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f3e89ac..58037c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -224,6 +224,7 @@ IF(TEST) test/core/common/PropertyTest test/core/common/RttiTest test/core/common/SourceContextReaderTest + test/core/common/VariantConverterTest test/core/common/VariantReaderTest test/core/common/VariantWriterTest test/core/common/VariantTest diff --git a/src/core/RangeSet.hpp b/src/core/RangeSet.hpp index b196dec..fc0070f 100644 --- a/src/core/RangeSet.hpp +++ b/src/core/RangeSet.hpp @@ -128,7 +128,7 @@ struct Range { /** * Calculates the union of the two ranges -- note that this operation is - * only valid if the ranges overlapp. Use the RangeSet class if you cannot + * only valid if the ranges overlap. Use the RangeSet class if you cannot * guarantee that. */ Range<T> merge(const Range<T> &r) const @@ -137,6 +137,44 @@ struct Range { } /** + * Returns true if and only if this Range only accepts a single element. + * + * @return true if and only if this Range only accepts a single element. + */ + bool isPrimitive() const { return start == end; } + /** + * Returns true if and only if this Range [a,b] meets the criteria: + * * a > lower limit of the type range (a > negative infinity) + * * a < b + * * b < upper limit of the type range (b < infinity) + * + * @return true if and only if this Range is compact as defined above. + */ + bool isCompact() const + { + return start > std::numeric_limits<T>::min() && start < end && + end < std::numeric_limits<T>::max(); + } + + /** + * Returns true if and only if the lower limit of this Range is equal to the + * type minimum (negative infinity). + * + * @return true if and only if this Range is open at the lower end in the + * sense defined above. + */ + bool isOpenLow() const { return start == std::numeric_limits<T>::min(); } + + /** + * Returns true if and only if the upper limit of this Range is equal to the + * type maximum (positive infinity). + * + * @return true if and only if this Range is open at the upper end in the + * sense defined above. + */ + bool isOpenHigh() const { return end == std::numeric_limits<T>::max(); } + + /** * Returns a range that represents the spans the complete set defined by the * given type T. */ diff --git a/src/core/common/Number.hpp b/src/core/common/Number.hpp index 23db42e..5417c28 100644 --- a/src/core/common/Number.hpp +++ b/src/core/common/Number.hpp @@ -45,7 +45,7 @@ class Logger; class Number { private: /** - * Reprsents the part of the number: Base value a, nominator n, exponent e. + * Represents the part of the number: Base value a, nominator n, exponent e. */ enum class Part { A, N, E }; diff --git a/src/core/common/Variant.hpp b/src/core/common/Variant.hpp index f623b6b..fc4faf5 100644 --- a/src/core/common/Variant.hpp +++ b/src/core/common/Variant.hpp @@ -726,7 +726,7 @@ public: /** * Returns a reference to the cardinality value. Performs no type - *conversion. + * conversion. * Throws an exception if the underlying type is not a cardinality. * * @return the cardinality value as reference. diff --git a/src/core/common/VariantConverter.cpp b/src/core/common/VariantConverter.cpp index 65d2039..03eb2e8 100644 --- a/src/core/common/VariantConverter.cpp +++ b/src/core/common/VariantConverter.cpp @@ -111,8 +111,7 @@ bool VariantConverter::toInt(Variant &var, Logger &logger, Mode mode) case VariantType::STRING: case VariantType::MAGIC: { Number n; - n.parse(var.asString(), logger); - if (n.isInt()) { + if (n.parse(var.asString(), logger) && n.isInt()) { var = (Variant::intType)n.intValue(); return true; } @@ -122,13 +121,25 @@ bool VariantConverter::toInt(Variant &var, Logger &logger, Mode mode) try { // JavaScript behaviour when converting arrays to doubles const Variant::arrayType &a = var.asArray(); - var = (a.size() == 1) ? a[0].toInt() : 0.0; - return true; + if (a.size() == 1) { + var = a[0].toInt(); + return true; + } } catch (LoggableException ex) { logger.log(ex); + break; } } + case VariantType::CARDINALITY: { + const Variant::cardinalityType &card = var.asCardinality(); + if (card.getRanges().size() == 1 && + card.getRanges().begin()->isPrimitive()) { + var = (Variant::intType)card.getRanges().begin()->start; + return true; + } + break; + } default: break; } @@ -168,9 +179,11 @@ bool VariantConverter::toDouble(Variant &var, Logger &logger, Mode mode) case VariantType::STRING: case VariantType::MAGIC: { Number n; - n.parse(var.asString(), logger); - var = (Variant::doubleType)n.doubleValue(); - return true; + if (n.parse(var.asString(), logger)) { + var = (Variant::doubleType)n.doubleValue(); + return true; + } + break; } case VariantType::ARRAY: { try { @@ -181,8 +194,18 @@ bool VariantConverter::toDouble(Variant &var, Logger &logger, Mode mode) } catch (LoggableException ex) { logger.log(ex); + break; } } + case VariantType::CARDINALITY: { + const Variant::cardinalityType &card = var.asCardinality(); + if (card.getRanges().size() == 1 && + card.getRanges().begin()->isPrimitive()) { + var = (Variant::doubleType)card.getRanges().begin()->start; + return true; + } + break; + } default: break; } @@ -233,6 +256,40 @@ bool VariantConverter::toString(Variant &var, Logger &logger, Mode mode) // Perform lossy conversions if (mode == Mode::ALL) { switch (type) { + case VariantType::CARDINALITY: { + // Print cardinality syntax + Variant::cardinalityType card = var.asCardinality(); + std::stringstream ss; + ss << "<cardinality {"; + bool first = true; + for (Variant::rangeType r : card.getRanges()) { + if (first) { + first = false; + } else { + ss << ", "; + } + if (r.isPrimitive()) { + ss << std::to_string(r.start); + } else if (r.isCompact()) { + ss << std::to_string(r.start) << "-" + << std::to_string(r.end); + } else if (r.isOpenLow()) { + if (r.isOpenHigh()) { + // if it is open at both ends, we set a Kleene Star + ss << "*"; + } else { + ss << "<" << std::to_string(r.end + 1); + } + } else { + // because r is not compact and not open low, it must be + // open high. + ss << ">" << std::to_string(r.start - 1); + } + } + ss << "}>"; + var = ss.str().c_str(); + return true; + } case VariantType::ARRAY: case VariantType::MAP: { std::stringstream ss; @@ -343,7 +400,7 @@ bool VariantConverter::toCardinality(Variant &var, Logger &logger, Mode mode) "A value smaller 0 can not be converted to a cardinality!"); return false; } - card.merge({(unsigned int) value}); + card.merge({(unsigned int)value}); return true; } @@ -373,7 +430,7 @@ bool VariantConverter::toCardinality(Variant &var, Logger &logger, Mode mode) "cardinality!"); return false; } - card.merge({(unsigned int) value}); + card.merge({(unsigned int)value}); return true; } case VariantType::ARRAY: { @@ -416,14 +473,15 @@ bool VariantConverter::toCardinality(Variant &var, Logger &logger, Mode mode) std::to_string(end) + " of the Range."); return false; } - card.merge({(unsigned int) start, (unsigned int) end}); + card.merge({(unsigned int)start, (unsigned int)end}); it++; } return true; } case VariantType::STRING: { var.setCardinality(Variant::cardinalityType{}); -// Variant::cardinalityType &card = var.asCardinality(); + // Variant::cardinalityType &card = + // var.asCardinality(); // TODO: Implement! return false; } diff --git a/src/core/common/VariantReader.cpp b/src/core/common/VariantReader.cpp index 6bc6df8..bbceac5 100644 --- a/src/core/common/VariantReader.cpp +++ b/src/core/common/VariantReader.cpp @@ -610,11 +610,11 @@ std::pair<bool, Variant::cardinalityType> VariantReader::parseCardinality( Variant::cardinalityType{}); } if (c == '<') { - card.merge( - Variant::rangeType{0, (unsigned int)n.intValue()}); + card.merge(Variant::rangeType{ + 0, (unsigned int)n.intValue() - 1}); } else { card.merge(Variant::rangeType::typeRangeFrom( - (unsigned int)n.intValue())); + (unsigned int)n.intValue() + 1)); } break; } diff --git a/src/core/common/VariantWriter.cpp b/src/core/common/VariantWriter.cpp index fc23359..427ac5d 100644 --- a/src/core/common/VariantWriter.cpp +++ b/src/core/common/VariantWriter.cpp @@ -114,6 +114,7 @@ static void writeJsonInternal(const Variant &var, std::ostream &stream, case VariantType::DOUBLE: case VariantType::FUNCTION: case VariantType::OBJECT: + case VariantType::CARDINALITY: stream << var.toString(); return; case VariantType::STRING: diff --git a/test/core/common/VariantConverterTest.cpp b/test/core/common/VariantConverterTest.cpp new file mode 100644 index 0000000..a655aa0 --- /dev/null +++ b/test/core/common/VariantConverterTest.cpp @@ -0,0 +1,130 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <iostream> + +#include <gtest/gtest.h> + +#include <core/common/Logger.hpp> +#include <core/common/Rtti.hpp> +#include <core/common/Variant.hpp> +#include <core/common/VariantConverter.hpp> + +namespace ousia { + +static void assertBoolConversion(Variant conv, Variant expected, + bool expectedSuccess, + VariantConverter::Mode mode, Logger &logger) +{ + EXPECT_EQ(expectedSuccess, VariantConverter::toBool(conv, logger, mode)); + if (expectedSuccess) { + EXPECT_EQ(expected, conv); + } +} + +TEST(VariantConverter, toBool) +{ + // setup + Logger logger; + Variant b = true; + Variant i = 0; + Variant d = 2.3; + Variant s = "test"; + Variant A = Variant::arrayType{b, i, d, s}; + Variant M = Variant::mapType{{"b", b}, {"i", i}, {"d", d}, {"s", s}}; + Variant::cardinalityType card; + card.merge({2, 5}); + Variant C = card; + + // in safe mode only bool to bool conversion should be possible. + assertBoolConversion(b, b, true, VariantConverter::Mode::SAFE, logger); + assertBoolConversion(i, nullptr, false, VariantConverter::Mode::SAFE, + logger); + assertBoolConversion(d, nullptr, false, VariantConverter::Mode::SAFE, + logger); + assertBoolConversion(s, nullptr, false, VariantConverter::Mode::SAFE, + logger); + assertBoolConversion(A, nullptr, false, VariantConverter::Mode::SAFE, + logger); + assertBoolConversion(M, nullptr, false, VariantConverter::Mode::SAFE, + logger); + assertBoolConversion(C, nullptr, false, VariantConverter::Mode::SAFE, + logger); + + // in all mode more should be possible. + assertBoolConversion(b, b, true, VariantConverter::Mode::ALL, logger); + assertBoolConversion(i, false, true, VariantConverter::Mode::ALL, logger); + assertBoolConversion(d, true, true, VariantConverter::Mode::ALL, logger); + // it may be counter-intuitive at first, but everything else gets just + // converted to true. + assertBoolConversion(s, true, true, VariantConverter::Mode::ALL, logger); + assertBoolConversion(A, true, true, VariantConverter::Mode::ALL, logger); + assertBoolConversion(M, true, true, VariantConverter::Mode::ALL, logger); + assertBoolConversion(C, true, true, VariantConverter::Mode::ALL, logger); +} + +static void assertIntConversion(Variant conv, Variant expected, + bool expectedSuccess, + VariantConverter::Mode mode, Logger &logger) +{ + EXPECT_EQ(expectedSuccess, VariantConverter::toInt(conv, logger, mode)); + if (expectedSuccess) { + EXPECT_EQ(expected, conv); + } +} + +TEST(VariantConverter, toInt) +{ + // setup + Logger logger; + Variant b = true; + Variant i = 6; + Variant d = 2.7; + Variant s = "test"; + Variant A = Variant::arrayType{i}; + Variant M = Variant::mapType{{"b", b}, {"i", i}, {"d", d}, {"s", s}}; + Variant::cardinalityType card; + card.merge({4}); + Variant C = card; + + // in safe mode only int to int conversion should be possible. + assertIntConversion(b, nullptr, false, VariantConverter::Mode::SAFE, + logger); + assertIntConversion(i, i, true, VariantConverter::Mode::SAFE, logger); + assertIntConversion(d, nullptr, false, VariantConverter::Mode::SAFE, + logger); + assertIntConversion(s, nullptr, false, VariantConverter::Mode::SAFE, + logger); + assertIntConversion(A, nullptr, false, VariantConverter::Mode::SAFE, + logger); + assertIntConversion(M, nullptr, false, VariantConverter::Mode::SAFE, + logger); + assertIntConversion(C, nullptr, false, VariantConverter::Mode::SAFE, + logger); + + // in all mode more should be possible. + assertIntConversion(b, 1, true, VariantConverter::Mode::ALL, logger); + assertIntConversion(i, i, true, VariantConverter::Mode::ALL, logger); + assertIntConversion(d, 2, true, VariantConverter::Mode::ALL, logger); + assertIntConversion(s, nullptr, false, VariantConverter::Mode::ALL, logger); + assertIntConversion(A, i, true, VariantConverter::Mode::ALL, logger); + assertIntConversion(M, nullptr, false, VariantConverter::Mode::ALL, logger); + assertIntConversion(C, 4, true, VariantConverter::Mode::ALL, logger); +} +} + diff --git a/test/core/common/VariantTest.cpp b/test/core/common/VariantTest.cpp index 00109d9..6381607 100644 --- a/test/core/common/VariantTest.cpp +++ b/test/core/common/VariantTest.cpp @@ -124,6 +124,20 @@ TEST(Variant, stringValueConversion) ASSERT_EQ(42.5, v.toDouble()); } +TEST(Variant, cardinalityValue) +{ + Variant::cardinalityType card; + card.merge({1,4}); + card.merge({7,12}); + const Variant v{card}; + ASSERT_EQ(card, v.asCardinality()); + + ASSERT_TRUE(v.isCardinality()); + ASSERT_EQ(VariantType::CARDINALITY, v.getType()); + ASSERT_EQ(&RttiTypes::Cardinality, &v.getRtti()); +} + + TEST(Variant, arrayValue) { const Variant v{{"test1", 42}}; |