summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-01-11 01:03:07 +0100
committerAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-01-11 01:03:07 +0100
commit7950e05b381308a3beb3c6d1538de6af047e5c0c (patch)
tree70177ad76b5cdae74d551ed03e4acec939edd501 /src
parentc204e8dc7e0d4f1317747d5f7f2e17aab449de1a (diff)
Refactored conversion routines used in the Typesystem class and the Variant class into an own class, implemented missing conversion from string to integer/double, implemented proper JSON serialization of variants
Diffstat (limited to 'src')
-rw-r--r--src/core/common/Variant.cpp204
-rw-r--r--src/core/common/Variant.hpp99
-rw-r--r--src/core/common/VariantConverter.cpp255
-rw-r--r--src/core/common/VariantConverter.hpp72
-rw-r--r--src/core/common/VariantWriter.cpp165
-rw-r--r--src/core/common/VariantWriter.hpp57
-rw-r--r--src/core/model/Typesystem.cpp42
7 files changed, 679 insertions, 215 deletions
diff --git a/src/core/common/Variant.cpp b/src/core/common/Variant.cpp
index e216aa1..b1a65ea 100644
--- a/src/core/common/Variant.cpp
+++ b/src/core/common/Variant.cpp
@@ -20,8 +20,11 @@
#include <core/managed/Managed.hpp>
+#include "Logger.hpp"
#include "Utils.hpp"
#include "Variant.hpp"
+#include "VariantConverter.hpp"
+#include "VariantWriter.hpp"
namespace ousia {
@@ -68,115 +71,124 @@ const char *Variant::getTypeName(Type type)
Variant::boolType Variant::toBool() const
{
- switch (getType()) {
- case Type::NULLPTR:
- return false;
- case Type::BOOL:
- return asBool();
- case Type::INT:
- return asInt() != 0;
- case Type::DOUBLE:
- return asDouble() != 0.0;
- default:
- return true;
- }
- return false;
+ ExceptionLogger logger;
+ Variant res{*this};
+ VariantConverter::toBool(res, logger, VariantConverter::Mode::ALL);
+ return res.asBool();
}
Variant::intType Variant::toInt() const
{
- switch (getType()) {
- case Type::NULLPTR:
- return 0;
- case Type::BOOL:
- return asBool() ? 1 : 0;
- case Type::INT:
- return asInt();
- case Type::DOUBLE:
- return asDouble();
- case Type::STRING:
- case Type::MAGIC:
- return 0; // TODO: Parse string as int
- case Type::ARRAY: {
- // JavaScript behaviour when converting arrays to ints
- const arrayType &a = asArray();
- return (a.size() == 1) ? a[0].toInt() : 0;
- }
- default:
- return 0;
- }
- return false;
+ ExceptionLogger logger;
+ Variant res{*this};
+ VariantConverter::toInt(res, logger, VariantConverter::Mode::ALL);
+ return res.asInt();
}
Variant::doubleType Variant::toDouble() const
{
- switch (getType()) {
- case Type::NULLPTR:
- return 0.0;
- case Type::BOOL:
- return asBool() ? 1.0 : 0.0;
- case Type::INT:
- return asInt();
- case Type::DOUBLE:
- return asDouble();
- case Type::STRING:
- case Type::MAGIC:
- return 0.0; // TODO: Parse string as double
- case Type::ARRAY: {
- // JavaScript behaviour when converting array to doubles
- const arrayType &a = asArray();
- return (a.size() == 1) ? a[0].toDouble() : 0;
- }
- default:
- return 0.0;
- }
- return false;
+ ExceptionLogger logger;
+ Variant res{*this};
+ VariantConverter::toDouble(res, logger, VariantConverter::Mode::ALL);
+ return res.asDouble();
}
Variant::stringType Variant::toString(bool escape) const
{
- switch (getType()) {
- case Type::NULLPTR:
- return "null";
- case Type::BOOL:
- return asBool() ? "true" : "false";
- case Type::INT: {
- std::stringstream ss;
- ss << asInt();
- return ss.str();
- }
- case Type::DOUBLE: {
- std::stringstream ss;
- ss << asDouble();
- return ss.str();
- }
- case Type::STRING: {
- case Type::MAGIC:
- // TODO: Use proper serialization function
- if (escape) {
- std::stringstream ss;
- ss << "\"" << asString() << "\"";
- return ss.str();
- } else {
- return asString();
- }
- }
- case Type::ARRAY:
- return Utils::join(asArray(), ", ", "[", "]");
- case Type::MAP:
- return Utils::join(asMap(), ", ", "{", "}");
- case Type::OBJECT: {
- std::stringstream ss;
- ss << "<object " << ptrVal << ">";
- return ss.str();
- }
- case Type::FUNCTION: {
- std::stringstream ss;
- ss << "<function " << static_cast<functionType*>(ptrVal)->get() << ">";
- return ss.str();
- }
+ ExceptionLogger logger;
+ Variant res{*this};
+ VariantConverter::toString(res, logger, VariantConverter::Mode::ALL);
+ return res.asString();
+}
+
+/* Output stream operators */
+
+std::ostream &operator<<(std::ostream &os, const Variant &v)
+{
+ VariantWriter::writeJson(v, os, true);
+ return os;
+}
+
+/* Comparison operators */
+
+bool operator<(const Variant &lhs, const Variant &rhs)
+{
+ // If the types do not match, we can not do a meaningful comparison.
+ if (lhs.getType() != rhs.getType()) {
+ throw Variant::TypeException(lhs.getType(), rhs.getType());
+ }
+ switch (lhs.getType()) {
+ case Variant::Type::NULLPTR:
+ return false;
+ case Variant::Type::BOOL:
+ return lhs.boolVal < rhs.boolVal;
+ case Variant::Type::INT:
+ return lhs.intVal < rhs.intVal;
+ case Variant::Type::DOUBLE:
+ return lhs.doubleVal < rhs.doubleVal;
+ case Variant::Type::MAGIC:
+ case Variant::Type::STRING:
+ return lhs.asString() < rhs.asString();
+ case Variant::Type::ARRAY:
+ return lhs.asArray() < rhs.asArray();
+ case Variant::Type::MAP:
+ return lhs.asMap() < rhs.asMap();
+ case Variant::Type::OBJECT:
+ return lhs.asObject().get() < rhs.asObject().get();
+ case Variant::Type::FUNCTION:
+ return lhs.asFunction() < rhs.asFunction();
+ }
+ 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 !(lhs > rhs);
+}
+
+bool operator>=(const Variant &lhs, const Variant &rhs)
+{
+ return !(lhs < rhs);
+}
+
+bool operator==(const Variant &lhs, const Variant &rhs)
+{
+ if (lhs.getType() != rhs.getType()) {
+ return false;
+ }
+ switch (lhs.getType()) {
+ case Variant::Type::NULLPTR:
+ return true;
+ case Variant::Type::BOOL:
+ return lhs.boolVal == rhs.boolVal;
+ case Variant::Type::INT:
+ return lhs.intVal == rhs.intVal;
+ case Variant::Type::DOUBLE:
+ return lhs.doubleVal == rhs.doubleVal;
+ case Variant::Type::STRING:
+ case Variant::Type::MAGIC:
+ return lhs.asString() == rhs.asString();
+ case Variant::Type::ARRAY:
+ return lhs.asArray() == rhs.asArray();
+ case Variant::Type::MAP:
+ return lhs.asMap() == rhs.asMap();
+ case Variant::Type::OBJECT:
+ return lhs.asObject() == rhs.asObject();
+ case Variant::Type::FUNCTION:
+ return lhs.asFunction() == rhs.asFunction();
}
- return "";
+ throw OusiaException("Internal Error! Unknown type!");
}
+
+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 9c061c1..254e748 100644
--- a/src/core/common/Variant.hpp
+++ b/src/core/common/Variant.hpp
@@ -864,23 +864,17 @@ public:
*/
const char *getTypeName() { return Variant::getTypeName(getType()); }
- /**
- * Prints the Variant to the output stream.
+ /*
+ * Output stream operator.
*/
- friend std::ostream &operator<<(std::ostream &os, const Variant &v)
- {
- return os << v.toString(true);
- }
/**
- * Prints a key value pair to the output stream.
+ * Prints the Variant to the output stream as JSON data.
+ *
+ * @param os is the output stream the variant should be written to.
+ * @param v is the variant that should be written to the output stream.
*/
- friend std::ostream &operator<<(std::ostream &os,
- const mapType::value_type &v)
- {
- // TODO: Use proper serialization function
- return os << "\"" << v.first << "\": " << v.second.toString(true);
- }
+ friend std::ostream &operator<<(std::ostream &os, const Variant &v);
/*
* Comprison operators.
@@ -895,35 +889,7 @@ public:
* @param rhs is the right hand side of the comparison.
* @return true if lhs is smaller than rhs.
*/
- friend bool operator<(const Variant &lhs, const Variant &rhs)
- {
- // If the types do not match, we can not do a meaningful comparison.
- if (lhs.getType() != rhs.getType()) {
- throw TypeException(lhs.getType(), rhs.getType());
- }
- switch (lhs.getType()) {
- case Type::NULLPTR:
- return false;
- case Type::BOOL:
- return lhs.boolVal < rhs.boolVal;
- case Type::INT:
- return lhs.intVal < rhs.intVal;
- case Type::DOUBLE:
- return lhs.doubleVal < rhs.doubleVal;
- case Type::MAGIC:
- case Type::STRING:
- return lhs.asString() < rhs.asString();
- case Type::ARRAY:
- return lhs.asArray() < rhs.asArray();
- case Type::MAP:
- return lhs.asMap() < rhs.asMap();
- case Type::OBJECT:
- return lhs.asObject().get() < rhs.asObject().get();
- case Type::FUNCTION:
- return lhs.asFunction() < rhs.asFunction();
- }
- throw OusiaException("Internal Error! Unknown type!");
- }
+ friend bool operator<(const Variant &lhs, const Variant &rhs);
/**
* Returns true if the given left hand side is larger than the right hand
@@ -934,10 +900,7 @@ public:
* @param rhs is the right hand side of the comparison.
* @return true if lhs is larger than rhs.
*/
- friend bool operator>(const Variant &lhs, const Variant &rhs)
- {
- return rhs < lhs;
- }
+ friend bool operator>(const Variant &lhs, const Variant &rhs);
/**
* Returns true if the given left hand side is smaller or equal to the
@@ -948,10 +911,7 @@ public:
* @param rhs is the right hand side of the comparison.
* @return true if lhs is smaller than or equal to rhs.
*/
- friend bool operator<=(const Variant &lhs, const Variant &rhs)
- {
- return !(lhs > rhs);
- }
+ friend bool operator<=(const Variant &lhs, const Variant &rhs);
/**
* Returns true if the given left hand side is larger or equal to the
@@ -962,10 +922,7 @@ public:
* @param rhs is the right hand side of the comparison.
* @return true if lhs is larger than or equal to rhs.
*/
- friend bool operator>=(const Variant &lhs, const Variant &rhs)
- {
- return !(lhs < rhs);
- }
+ friend bool operator>=(const Variant &lhs, const Variant &rhs);
/**
* Returns true if the given left hand side and right hand side are equal.
@@ -976,34 +933,7 @@ public:
* @param rhs is the right hand side of the comparison.
* @return true if lhs equals rhs.
*/
- friend bool operator==(const Variant &lhs, const Variant &rhs)
- {
- if (lhs.getType() != rhs.getType()) {
- return false;
- }
- switch (lhs.getType()) {
- case Type::NULLPTR:
- return true;
- case Type::BOOL:
- return lhs.boolVal == rhs.boolVal;
- case Type::INT:
- return lhs.intVal == rhs.intVal;
- case Type::DOUBLE:
- return lhs.doubleVal == rhs.doubleVal;
- case Type::STRING:
- case Type::MAGIC:
- return lhs.asString() == rhs.asString();
- case Type::ARRAY:
- return lhs.asArray() == rhs.asArray();
- case Type::MAP:
- return lhs.asMap() == rhs.asMap();
- case Type::OBJECT:
- return lhs.asObject() == rhs.asObject();
- case Type::FUNCTION:
- return lhs.asFunction() == rhs.asFunction();
- }
- throw OusiaException("Internal Error! Unknown type!");
- }
+ friend bool operator==(const Variant &lhs, const Variant &rhs);
/**
* Returns true if the given left hand side are equal. Uses the comparison
@@ -1014,10 +944,7 @@ public:
* @param rhs is the right hand side of the comparison.
* @return true if lhs is not equal to rhs.
*/
- friend bool operator!=(const Variant &lhs, const Variant &rhs)
- {
- return !(lhs == rhs);
- }
+ friend bool operator!=(const Variant &lhs, const Variant &rhs);
};
}
diff --git a/src/core/common/VariantConverter.cpp b/src/core/common/VariantConverter.cpp
new file mode 100644
index 0000000..f9d465e
--- /dev/null
+++ b/src/core/common/VariantConverter.cpp
@@ -0,0 +1,255 @@
+/*
+ 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 <string>
+#include <sstream>
+
+#include "Logger.hpp"
+#include "Number.hpp"
+#include "Rtti.hpp"
+#include "Variant.hpp"
+#include "VariantConverter.hpp"
+#include "VariantWriter.hpp"
+
+namespace ousia {
+
+static std::string msgUnexpectedType(Variant::Type actualType,
+ Variant::Type requestedType)
+{
+ return std::string("Cannot convert ") + Variant::getTypeName(actualType) +
+ std::string(" to ") + Variant::getTypeName(requestedType);
+}
+
+bool VariantConverter::toBool(Variant &var, Logger &logger, Mode mode)
+{
+ // Perform safe conversions
+ const Variant::Type type = var.getType();
+ switch (type) {
+ case Variant::Type::BOOL:
+ // No conversion needed if "var" already is a boolean
+ return true;
+ default:
+ break;
+ }
+
+ // Perform potentially dangerous conversions in the "ALL" mode
+ if (mode == Mode::ALL) {
+ switch (var.getType()) {
+ case Variant::Type::NULLPTR:
+ var = false;
+ return true;
+ case Variant::Type::INT:
+ var = var.asInt() != 0;
+ return true;
+ case Variant::Type::DOUBLE:
+ var = var.asDouble() != 0.0;
+ return true;
+ default:
+ var = true;
+ return true;
+ }
+ }
+
+ // No conversion possible, assign default value and log error
+ logger.error(msgUnexpectedType(var.getType(), Variant::Type::BOOL));
+ var = false;
+ return false;
+}
+
+bool VariantConverter::toInt(Variant &var, Logger &logger, Mode mode)
+{
+ // Perform safe conversions
+ const Variant::Type type = var.getType();
+ switch (type) {
+ case Variant::Type::INT:
+ // No conversion needed if "var" already is an integer
+ return true;
+ default:
+ break;
+ }
+
+ // Perform all potentially dangerous conversions in the "ALL" mode
+ if (mode == Mode::ALL) {
+ switch (type) {
+ case Variant::Type::NULLPTR:
+ var = 0;
+ return true;
+ case Variant::Type::BOOL:
+ var = var.asBool() ? 1 : 0;
+ return true;
+ case Variant::Type::DOUBLE:
+ var = (Variant::intType)var.asDouble();
+ return true;
+ case Variant::Type::STRING:
+ case Variant::Type::MAGIC: {
+ Number n;
+ n.parse(var.asString(), logger);
+ if (n.isInt()) {
+ var = (Variant::intType)n.intValue();
+ } else {
+ var = (Variant::doubleType)n.doubleValue();
+ }
+ return true;
+ }
+ case Variant::Type::ARRAY: {
+ 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;
+ }
+ catch (LoggableException ex) {
+ logger.log(ex);
+ }
+ }
+ default:
+ break;
+ }
+ }
+
+ // No conversion possible, assign default value and log error
+ logger.error(msgUnexpectedType(var.getType(), Variant::Type::INT));
+ var = 0;
+ return false;
+}
+
+bool VariantConverter::toDouble(Variant &var, Logger &logger, Mode mode)
+{
+ // Perform safe conversions
+ const Variant::Type type = var.getType();
+ switch (type) {
+ case Variant::Type::DOUBLE:
+ // No conversion needed if "var" already is a double
+ return true;
+ case Variant::Type::INT:
+ // Converting integers to doubles is safe
+ var = (Variant::doubleType)var.asInt();
+ return true;
+ default:
+ break;
+ }
+
+ // Perform all potentially dangerous conversions in the "ALL" mode
+ if (mode == Mode::ALL) {
+ switch (type) {
+ case Variant::Type::NULLPTR:
+ var = 0.0;
+ return true;
+ case Variant::Type::BOOL:
+ var = var.asBool() ? 1.0 : 0.0;
+ return true;
+ case Variant::Type::STRING:
+ case Variant::Type::MAGIC: {
+ Number n;
+ n.parse(var.asString(), logger);
+ var = (Variant::doubleType)n.doubleValue();
+ return true;
+ }
+ case Variant::Type::ARRAY: {
+ try {
+ // JavaScript behaviour when converting arrays to doubles
+ const Variant::arrayType &a = var.asArray();
+ var = (a.size() == 1) ? a[0].toDouble() : 0.0;
+ return true;
+ }
+ catch (LoggableException ex) {
+ logger.log(ex);
+ }
+ }
+ default:
+ break;
+ }
+ }
+
+ // No conversion possible, assign default value and log error
+ logger.error(msgUnexpectedType(var.getType(), Variant::Type::DOUBLE));
+ var = 0.0;
+ return false;
+}
+
+bool VariantConverter::toString(Variant &var, Logger &logger, Mode mode)
+{
+ // Perform safe conversions (all these operations are considered "lossless")
+ const Variant::Type type = var.getType();
+ switch (type) {
+ case Variant::Type::NULLPTR:
+ var = "null";
+ return true;
+ case Variant::Type::BOOL:
+ var = var.asBool() ? "true" : "false";
+ return true;
+ case Variant::Type::INT: {
+ std::stringstream ss;
+ ss << var.asInt();
+ var = ss.str().c_str();
+ return true;
+ }
+ case Variant::Type::DOUBLE: {
+ std::stringstream ss;
+ ss << var.asDouble();
+ var = ss.str().c_str();
+ return true;
+ }
+ case Variant::Type::MAGIC:
+ case Variant::Type::STRING:
+ // No conversion needed if "var" already is a string (or a magic
+ // string value)
+ return true;
+ default:
+ break;
+ }
+
+ // Perform lossy conversions
+ if (mode == Mode::ALL) {
+ switch (type) {
+ case Variant::Type::ARRAY:
+ case Variant::Type::MAP: {
+ std::stringstream ss;
+ VariantWriter::writeJson(var, ss, false);
+ var = ss.str().c_str();
+ return true;
+ }
+ case Variant::Type::OBJECT: {
+ // Print object address and type
+ Variant::objectType obj = var.asObject();
+ std::stringstream ss;
+ ss << "<object " << obj.get() << " (" << obj->type().name << ")"
+ << ">";
+ var = ss.str().c_str();
+ return true;
+ }
+ case Variant::Type::FUNCTION: {
+ // Print function pointer address
+ Variant::functionType obj = var.asFunction();
+ std::stringstream ss;
+ ss << "<function " << obj.get() << ">";
+ var = ss.str().c_str();
+ return true;
+ }
+ default:
+ break;
+ }
+ }
+
+ // No conversion possible, assign default value and log error
+ logger.error(msgUnexpectedType(var.getType(), Variant::Type::STRING));
+ var = "";
+ return false;
+}
+}
+
diff --git a/src/core/common/VariantConverter.hpp b/src/core/common/VariantConverter.hpp
new file mode 100644
index 0000000..b683083
--- /dev/null
+++ b/src/core/common/VariantConverter.hpp
@@ -0,0 +1,72 @@
+/*
+ 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/>.
+*/
+
+#ifndef _OUSIA_VARIANT_CONVERTER_HPP_
+#define _OUSIA_VARIANT_CONVERTER_HPP_
+
+namespace ousia {
+
+// Forward declaration
+class Variant;
+class Logger;
+
+/**
+ * The VariantConverter class is used to convert a variant to a certain
+ * prespecified type. The functions ensure that the variant has the requested
+ * type, even if the conversion fails.
+ */
+class VariantConverter {
+
+public:
+ /**
+ * Enumeration used to define the mode of conversion -- either only safe
+ * conversions (without any data loss) are performed, or all possible
+ * conversions are tried (with possible data loss).
+ */
+ enum class Mode {
+ SAFE, ALL
+ };
+
+ /**
+ * Makes sure the given variant is a boolean. If the "mode" parameter is
+ * set to Mode::SAFE, only booleans can be converted to booleans. For all
+ * other types the conversion fails. If "mode" is set to Mode::ALL, nullptr
+ * values and zero numeric values are treated as "false", all other values
+ * are treated as "true".
+ *
+ * @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.
+ */
+ static bool toBool(Variant &var, Logger &logger, Mode mode = Mode::SAFE);
+
+ static bool toInt(Variant &var, Logger &logger, Mode mode = Mode::SAFE);
+
+ static bool toDouble(Variant &var, Logger &logger, Mode mode = Mode::SAFE);
+
+ static bool toString(Variant &var, Logger &logger, Mode mode = Mode::SAFE);
+
+};
+
+}
+
+#endif /* _OUSIA_VARIANT_CONVERTER_HPP_ */
+
diff --git a/src/core/common/VariantWriter.cpp b/src/core/common/VariantWriter.cpp
new file mode 100644
index 0000000..39dfc3b
--- /dev/null
+++ b/src/core/common/VariantWriter.cpp
@@ -0,0 +1,165 @@
+/*
+ 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 "Variant.hpp"
+#include "VariantWriter.hpp"
+
+namespace ousia {
+
+/**
+ * Helper function used to write a JSON string, including quotation marks.
+ *
+ * @param str is the string that should be serialized.
+ * @param stream is the stream to which the JSON string should be written.
+ */
+static void writeJsonString(const std::string str, std::ostream &stream)
+{
+ stream << "\"";
+ for (char c : str) {
+ switch (c) {
+ case '\b':
+ stream << "\\b";
+ break;
+ case '\f':
+ stream << "\\f";
+ break;
+ case '\n':
+ stream << "\\n";
+ break;
+ case '\r':
+ stream << "\\r";
+ break;
+ case '\t':
+ stream << "\\t";
+ break;
+ case '\v':
+ stream << "\\v";
+ break;
+ case '\\':
+ stream << "\\";
+ break;
+ case '"':
+ stream << "\\\"";
+ break;
+ default:
+ stream << c;
+ break;
+ }
+ }
+ stream << "\"";
+}
+
+/**
+ * Helper function used to write the indentation, but only if the pretty mode
+ * is enabled.
+ *
+ * @param stream is the stream the result should be written to.
+ * @param pretty if false, no indentation is written.
+ */
+static void writeIndentation(std::ostream &stream, bool pretty, int level)
+{
+ if (pretty) {
+ for (int i = 0; i < level; i++) {
+ stream << "\t";
+ }
+ }
+}
+
+/**
+ * Helper function used to write a linebreak, but only if the pretty mode is
+ * enabled.
+ *
+ * @param stream is the stream the result should be written to.
+ * @param pretty if false, no linebreak is written.
+ */
+static void writeLinebreak(std::ostream &stream, bool pretty)
+{
+ if (pretty) {
+ stream << "\n";
+ }
+}
+
+/**
+ * Helper function used to serialize JSON with indentation.
+ *
+ * @param var is the variant that should be serialized.
+ * @param stream is the stream the result should be written to.
+ * @param pretty if true, the resulting value is properly indented.
+ * @param level is the current indentation level.
+ */
+static void writeJsonInternal(const Variant &var, std::ostream &stream,
+ bool pretty, int level)
+{
+ switch (var.getType()) {
+ case Variant::Type::NULLPTR:
+ case Variant::Type::BOOL:
+ case Variant::Type::INT:
+ case Variant::Type::DOUBLE:
+ case Variant::Type::FUNCTION:
+ case Variant::Type::OBJECT:
+ stream << var.toString();
+ return;
+ case Variant::Type::STRING:
+ case Variant::Type::MAGIC:
+ writeJsonString(var.toString(), stream);
+ return;
+ case Variant::Type::ARRAY: {
+ stream << "[";
+ writeLinebreak(stream, pretty);
+ const Variant::arrayType &arr = var.asArray();
+ for (size_t i = 0; i < arr.size(); i++) {
+ writeIndentation(stream, pretty, level + 1);
+ writeJsonInternal(arr[i], stream, pretty, level + 1);
+ if (i + 1 != arr.size()) {
+ stream << ",";
+ }
+ writeLinebreak(stream, pretty);
+ }
+ writeIndentation(stream, pretty, level);
+ stream << "]";
+ return;
+ }
+ case Variant::Type::MAP: {
+ writeIndentation(stream, pretty, level);
+ stream << "{";
+ writeLinebreak(stream, pretty);
+ const Variant::mapType &map = var.asMap();
+ for (auto it = map.cbegin(); it != map.cend();) {
+ writeIndentation(stream, pretty, level + 1);
+ writeJsonString(it->first, stream);
+ stream << (pretty ? ": " : ":");
+ writeJsonInternal(it->second, stream, pretty, level + 1);
+ if ((++it) != map.cend()) {
+ stream << ",";
+ }
+ writeLinebreak(stream, pretty);
+ }
+ writeIndentation(stream, pretty, level);
+ stream << "}";
+ return;
+ }
+ }
+}
+
+void VariantWriter::writeJson(const Variant &var, std::ostream &stream,
+ bool pretty)
+{
+ writeJsonInternal(var, stream, pretty, 0);
+}
+}
+
diff --git a/src/core/common/VariantWriter.hpp b/src/core/common/VariantWriter.hpp
new file mode 100644
index 0000000..211da34
--- /dev/null
+++ b/src/core/common/VariantWriter.hpp
@@ -0,0 +1,57 @@
+/*
+ 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/>.
+*/
+
+/**
+ * @file VariantWriter.hpp
+ *
+ * Contains the VariantWriter class which provides serialization functions for
+ * Variant types.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OUSIA_VARIANT_WRITER_HPP_
+#define _OUSIA_VARIANT_WRITER_HPP_
+
+#include <ostream>
+
+namespace ousia {
+
+// Forward declaration
+class Variant;
+
+/**
+ * Class which provides serialization functions for writing variants to an
+ * output stream in various formats.
+ */
+class VariantWriter {
+public:
+ /**
+ * Dumps the Variant as JSON data.
+ *
+ * @param var is the variant that should be serialized.
+ * @param stream is the stream the result should be written to.
+ * @param pretty if true, the resulting value is properly indented.
+ */
+ static void writeJson(const Variant &var, std::ostream &stream,
+ bool pretty = true);
+};
+}
+
+#endif /* _OUSIA_VARIANT_WRITER_HPP_ */
+
diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp
index f082f39..117b50a 100644
--- a/src/core/model/Typesystem.cpp
+++ b/src/core/model/Typesystem.cpp
@@ -20,6 +20,7 @@
#include <core/common/Rtti.hpp>
#include <core/common/Utils.hpp>
+#include <core/common/VariantConverter.hpp>
namespace ousia {
namespace model {
@@ -38,57 +39,32 @@ bool Type::build(Variant &data, Logger &logger) const
}
}
-/* Class StringType */
+/* Class BoolType */
-bool StringType::doBuild(Variant &data, Logger &logger) const
+bool BoolType::doBuild(Variant &data, Logger &logger) const
{
- // Cannot convert non-primitive values to strings
- if (!data.isPrimitive()) {
- throw LoggableException{"Expected string or primitive input."};
- }
-
- // Perform an implicit type conversion
- if (!data.isString()) {
- // Convert the variant value to a string and set it
- const char *oldName = data.getTypeName();
- data = data.toString().c_str();
-
- // Log conversions as these may be potentially unwanted
- logger.note(std::string("Implicit conversion from ") + oldName +
- " to string.");
- }
- return true;
+ return VariantConverter::toBool(data, logger);
}
/* Class IntType */
bool IntType::doBuild(Variant &data, Logger &logger) const
{
- if (!data.isInt()) {
- throw LoggableException{"Expected integer value."};
- }
- return true;
+ return VariantConverter::toInt(data, logger);
}
/* Class DoubleType */
bool DoubleType::doBuild(Variant &data, Logger &logger) const
{
- if (!data.isInt() && !data.isDouble()) {
- throw LoggableException{"Expected double value."};
- }
- data = Variant{data.toDouble()};
- return true;
+ return VariantConverter::toDouble(data, logger);
}
-/* Class BoolType */
+/* Class StringType */
-bool BoolType::doBuild(Variant &data, Logger &logger) const
+bool StringType::doBuild(Variant &data, Logger &logger) const
{
- if (!data.isBool()) {
- throw LoggableException("Expected boolean value!");
- }
- return true;
+ return VariantConverter::toString(data, logger);
}
/* Class EnumType */