/*
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 .
*/
#include
#include
#include
#include
#include
#include "CharReader.hpp"
#include "Function.hpp"
#include "Logger.hpp"
#include "Number.hpp"
#include "Rtti.hpp"
#include "Variant.hpp"
#include "VariantConverter.hpp"
#include "VariantReader.hpp"
#include "VariantWriter.hpp"
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);
}
static std::string msgImplicitConversion(VariantType actualType,
VariantType requestedType)
{
return std::string("Implicit conversion from ") +
Variant::getTypeName(actualType) + std::string(" to ") +
Variant::getTypeName(requestedType);
}
bool VariantConverter::toBool(Variant &var, Logger &logger, Mode mode)
{
// Perform safe conversions
const VariantType type = var.getType();
switch (type) {
case VariantType::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 VariantType::NULLPTR:
var = false;
return true;
case VariantType::INT:
var = var.asInt() != 0;
return true;
case VariantType::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, VariantType::BOOL), var);
var = false;
return false;
}
bool VariantConverter::toInt(Variant &var, Logger &logger, Mode mode)
{
// Perform safe conversions
const VariantType type = var.getType();
switch (type) {
case VariantType::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 VariantType::NULLPTR:
var = 0;
return true;
case VariantType::BOOL:
var = var.asBool() ? 1 : 0;
return true;
case VariantType::DOUBLE:
var = (Variant::intType)var.asDouble();
return true;
case VariantType::STRING:
case VariantType::MAGIC: {
Number n;
if (n.parse(var.asString(), logger) && n.isInt()) {
var = (Variant::intType)n.intValue();
return true;
}
break;
}
case VariantType::ARRAY: {
try {
// JavaScript behaviour when converting arrays to doubles
const Variant::arrayType &a = var.asArray();
if (a.size() == 1) {
var = a[0].toInt();
return true;
}
}
catch (LoggableException ex) {
logger.log(ex, var);
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;
}
}
// No conversion possible, assign default value and log error
logger.error(msgUnexpectedType(var, VariantType::INT), var);
var = 0;
return false;
}
bool VariantConverter::toDouble(Variant &var, Logger &logger, Mode mode)
{
// Perform safe conversions
const VariantType type = var.getType();
switch (type) {
case VariantType::DOUBLE:
// No conversion needed if "var" already is a double
return true;
case VariantType::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 VariantType::NULLPTR:
var = 0.0;
return true;
case VariantType::BOOL:
var = var.asBool() ? 1.0 : 0.0;
return true;
case VariantType::STRING:
case VariantType::MAGIC: {
Number n;
if (n.parse(var.asString(), logger)) {
var = (Variant::doubleType)n.doubleValue();
return true;
}
break;
}
case VariantType::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, var);
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;
}
}
// No conversion possible, assign default value and log error
logger.error(msgUnexpectedType(var, VariantType::DOUBLE), var);
var = 0.0;
return false;
}
bool VariantConverter::toString(Variant &var, Logger &logger, Mode mode)
{
// Perform safe conversions (all these operations are considered "lossless")
const VariantType type = var.getType();
switch (type) {
case VariantType::NULLPTR:
logger.warning(msgImplicitConversion(type, VariantType::STRING), var);
var = "null";
return true;
case VariantType::BOOL:
logger.warning(msgImplicitConversion(type, VariantType::STRING), var);
var = var.asBool() ? "true" : "false";
return true;
case VariantType::INT: {
logger.warning(msgImplicitConversion(type, VariantType::STRING), var);
std::stringstream ss;
ss << var.asInt();
var = ss.str().c_str();
return true;
}
case VariantType::DOUBLE: {
logger.warning(msgImplicitConversion(type, VariantType::STRING), var);
std::stringstream ss;
ss << var.asDouble();
var = ss.str().c_str();
return true;
}
case VariantType::MAGIC:
case VariantType::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 VariantType::CARDINALITY: {
// Print cardinality syntax
Variant::cardinalityType card = var.asCardinality();
std::stringstream ss;
ss << "" << std::to_string(r.start - 1);
}
}
ss << "}>";
var = ss.str().c_str();
return true;
}
case VariantType::ARRAY:
case VariantType::MAP: {
std::stringstream ss;
VariantWriter::writeJson(var, ss, false);
var = ss.str().c_str();
return true;
}
case VariantType::OBJECT: {
// Print object address and type
Variant::objectType obj = var.asObject();
std::stringstream ss;
ss << "