/* 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 "Argument.hpp" #include "Exceptions.hpp" #include "Logger.hpp" #include "Rtti.hpp" #include "Utils.hpp" #include "VariantConverter.hpp" namespace ousia { /* Class Argument */ Argument::Argument(std::string name, const Rtti *type, const Rtti *innerType, Variant defaultValue, bool hasDefaultValue) : name(std::move(name)), type(type), innerType(innerType), defaultValue(std::move(defaultValue)), hasDefaultValue(hasDefaultValue) { } Argument::Argument(std::string name, const Rtti *type, Variant defaultValue) : Argument(std::move(name), type, &RttiTypes::None, defaultValue, true) { } Argument::Argument(std::string name, const Rtti *type) : Argument(std::move(name), type, &RttiTypes::None, nullptr, false) { } Argument Argument::Any(std::string name) { return Argument{name, &RttiTypes::None, &RttiTypes::None, nullptr, false}; } Argument Argument::Any(std::string name, Variant defaultValue) { return Argument{name, &RttiTypes::None, &RttiTypes::None, defaultValue, true}; } Argument Argument::Bool(std::string name) { return Argument{name, &RttiTypes::Bool}; } Argument Argument::Bool(std::string name, Variant::boolType defaultValue) { return Argument{name, &RttiTypes::Bool, defaultValue}; } Argument Argument::Int(std::string name) { return Argument{name, &RttiTypes::Int}; } Argument Argument::Int(std::string name, Variant::intType defaultValue) { return Argument{name, &RttiTypes::Int, defaultValue}; } Argument Argument::Double(std::string name) { return Argument{name, &RttiTypes::Double}; } Argument Argument::Double(std::string name, Variant::doubleType defaultValue) { return Argument{name, &RttiTypes::Double, defaultValue}; } Argument Argument::String(std::string name) { return Argument{name, &RttiTypes::String}; } Argument Argument::String(std::string name, const Variant::stringType &defaultValue) { return Argument{name, &RttiTypes::String, Variant::fromString(defaultValue)}; } Argument Argument::Object(std::string name, const Rtti *type) { return Argument(std::move(name), type, &RttiTypes::None, Variant::fromObject(nullptr), false); } Argument Argument::Object(std::string name, const Rtti *type, std::nullptr_t) { return Argument(std::move(name), type, &RttiTypes::None, Variant::fromObject(nullptr), true); } Argument Argument::Function(std::string name) { return Argument(std::move(name), &RttiTypes::Function); } Argument Argument::Function(std::string name, Variant::functionType defaultValue) { return Argument(std::move(name), &RttiTypes::Function, Variant::fromFunction(defaultValue)); } Argument Argument::Array(std::string name) { return Argument(std::move(name), &RttiTypes::Array); } Argument Argument::Array(std::string name, const Variant::arrayType &defaultValue) { return Argument(std::move(name), &RttiTypes::Array, defaultValue); } Argument Argument::Array(std::string name, const Rtti *innerType) { return Argument(std::move(name), &RttiTypes::Array, innerType, nullptr, false); } Argument Argument::Array(std::string name, const Rtti *innerType, const Variant::arrayType &defaultValue) { return Argument(std::move(name), &RttiTypes::Array, innerType, defaultValue, true); } Argument Argument::Map(std::string name) { return Argument(std::move(name), &RttiTypes::Map); } Argument Argument::Map(std::string name, const Variant::mapType &defaultValue) { return Argument(std::move(name), &RttiTypes::Map, defaultValue); } Argument Argument::Map(std::string name, const Rtti *innerType) { return Argument(std::move(name), &RttiTypes::Map, innerType, nullptr, false); } Argument Argument::Map(std::string name, const Rtti *innerType, const Variant::mapType &defaultValue) { return Argument(std::move(name), &RttiTypes::Map, innerType, defaultValue, true); } Argument Argument::Cardinality(std::string name) { return Argument{name, &RttiTypes::Cardinality}; } Argument Argument::Cardinality(std::string name, Variant::cardinalityType defaultValue) { return Argument{name, &RttiTypes::Cardinality, defaultValue}; } bool Argument::validate(Variant &var, Logger &logger) const { if (!VariantConverter::convert(var, type, innerType, logger, VariantConverter::Mode::SAFE)) { if (hasDefaultValue) { var = defaultValue; } return false; } return true; } const std::string &Argument::getName() const { return name; } const Variant &Argument::getDefaultValue() const { return defaultValue; } bool Argument::hasDefault() const { return hasDefaultValue; } /* Class Arguments */ // Instantiations of the "None" arguments const Arguments Arguments::None; static std::unordered_map buildArgumentNames( std::initializer_list arguments) { // Make sure the name is unique std::unordered_map res; size_t i = 0; for (const Argument &arg : arguments) { const std::string &name = arg.getName(); if (!Utils::isIdentifier(name)) { throw OusiaException{std::string("Argument name ") + name + std::string(" is not a valid identifier")}; } if (!res.emplace(name, i++).second) { throw OusiaException{std::string("Argument name \"") + name + std::string("\" is not unique")}; } } return res; } Arguments::Arguments(std::initializer_list arguments) : arguments(arguments), names(buildArgumentNames(arguments)), valid(true) { } bool Arguments::validateArray(Variant::arrayType &arr, Logger &logger) const { // Abort if no arguments were explicitly given -- everything is valid if (!valid) { return true; } Logger nullLogger; // Fetch the number of arguments N and the initial array size n const size_t n = arr.size(); const size_t N = arguments.size(); bool ok = true; // Make sure the argument list is not too long if (n > N) { ok = false; logger.error(std::string("Too many arguments: expected ") + std::to_string(N) + std::string(" arguments, but got ") + std::to_string(n)); } // Resize the array to the total number of elements arr.resize(N); // Make sure the given attributes have to correct type, insert default // values for (size_t a = 0; a < N; a++) { if (a < n) { ok = ok && arguments[a].validate(arr[a], logger); } else { if (arguments[a].hasDefault()) { arr[a] = arguments[a].getDefaultValue(); } else { // Call "validate" to inject a standard value arr[a] = Variant::fromObject(nullptr); arguments[a].validate(arr[a], nullLogger); logger.error(std::string("Missing argument ") + std::to_string(a + 1) + std::string(" \"") + arguments[a].getName() + std::string("\"")); ok = false; } } } return ok; } bool Arguments::validateMap(Variant::mapType &map, Logger &logger, bool ignoreUnknown) const { // Abort if no arguments were explicitly given -- everything is valid if (!valid) { return true; } Logger nullLogger; // Fetch the number of arguments N const size_t N = arguments.size(); std::vector set(N); bool ok = true; // Iterate over the map entries and search for the corresponding argument for (auto &e : map) { // Check whether an argument with the name of the current entry exists auto it = names.find(e.first); if (it != names.end()) { // Fetch the corresponding index in the "arguments" array size_t idx = it->second; set[idx] = arguments[idx].validate(e.second, logger); ok = ok && set[idx]; } else { if (ignoreUnknown) { logger.note(std::string("Ignoring argument \"") + e.first + std::string("\""), e.second); } else { logger.error(std::string("Unknown argument \"") + e.first + std::string("\""), e.second); ok = false; } } } // Insert all unset arguments for (size_t a = 0; a < N; a++) { if (!set[a]) { const std::string &name = arguments[a].getName(); if (arguments[a].hasDefault()) { map[name] = arguments[a].getDefaultValue(); } else { // Call "validate" to inject a standard value map[name] = Variant::fromObject(nullptr); arguments[a].validate(map[name], nullLogger); logger.error(std::string("Missing argument \"") + name + std::string("\"")); ok = false; } } } return ok; } }