/* Copyright (C) 2003 by David White Copyright (C) 2005 - 2018 by Philippe Plantier Part of the Battle for Wesnoth Project https://www.wesnoth.org/ 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 2 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. See the COPYING file for more details. */ #include "variable_info.hpp" #include "variable_info_private.hpp" #include namespace variable_info_implementation { /** * Helper function to apply the result of a specified visitor to a variable_info object. * * @tparam V Visitor type. * @tparam T Visitor argument parameter pack. * * @param state Info state (the actual variable data). * @param args Arguments to forward to visitor constructor. * * @returns Visitor output in its specified type. * @throws std::range_error If @a state has an invalid type_ field. */ template typename V::result_t apply_visitor(typename V::param_t state, T&&... args) { static_assert(std::is_base_of< info_visitor_base< typename V::result_t, std::remove_reference_t>, V>::value, "Invalid visitor type."); // Create the visitor. V visitor(std::forward(args)...); switch(state.type_) { case state_start: return visitor.from_start(state); case state_named: return visitor.from_named(state); case state_indexed: return visitor.from_indexed(state); case state_temporary: return visitor.from_temporary(state); } throw std::range_error("Failed to convert the TVisitor::param_t type"); } } // end namespace variable_info_implementation using namespace variable_info_implementation; template variable_info::variable_info(const std::string& varname, maybe_const_t& vars) noexcept : name_(varname) , state_(vars) , valid_(true) { try { calculate_value(); } catch(const invalid_variablename_exception&) { valid_ = false; } } template void variable_info::calculate_value() { std::size_t previous_index = 0, name_size = name_.size(); for(std::size_t loop_index = 0; loop_index < name_size; loop_index++) { switch(name_[loop_index]) { case '.': case '[': /* '.' and '[' mark the end of a string key. * The result is obviously that '.' and '[' are * treated equally so 'aaa.9].bbbb[zzz.uu.7]' * is interpreted as 'aaa[9].bbbb.zzz.uu[7]' * Use is_valid_variable function for stricter variable name checking. */ apply_visitor>( state_, name_.substr(previous_index, loop_index - previous_index)); previous_index = loop_index + 1; break; case ']': // ']' marks the end of an integer key. apply_visitor>(state_, parse_index(&name_[previous_index])); // After ']' we always expect a '.' or the end of the string // Ignore the next char which is a '.' loop_index++; if(loop_index < name_.length() && name_[loop_index] != '.') { throw invalid_variablename_exception(); } previous_index = loop_index + 1; break; default: break; } } if(previous_index != name_.length() + 1) { // The string didn't end with ']' // In this case we still didn't add the key behind the last '.' apply_visitor>(state_, name_.substr(previous_index)); } } template bool variable_info::explicit_index() const { throw_on_invalid(); return state_.type_ == state_start || state_.type_ == state_indexed; } template maybe_const_t& variable_info::as_scalar() const { throw_on_invalid(); return apply_visitor>(state_); } template maybe_const_t& variable_info::as_container() const { throw_on_invalid(); return apply_visitor>(state_); } template maybe_const_t variable_info::as_array() const { throw_on_invalid(); return apply_visitor>(state_); } template void variable_info::throw_on_invalid() const { if(!valid_) { throw invalid_variablename_exception(); } } template std::string variable_info::get_error_message() const { return V::error_message(name_); } template bool variable_info::exists_as_attribute() const { throw_on_invalid(); return (state_.type_ == state_temporary) || ((state_.type_ == state_named) && state_.child_->has_attribute(state_.key_)); } template bool variable_info::exists_as_container() const { throw_on_invalid(); return apply_visitor>(state_); } template void variable_info_mutable::clear(bool only_tables) const { this->throw_on_invalid(); return apply_visitor>(this->state_, only_tables); } /** * In order to allow the appropriate 'children' argument to forward through to the appropriate * ctor of type T in as_range_visitor_base via apply_visitor, we need to specify the argument * type as an rvalue reference to lvalue reference in order to collapse to an lvalue reference. * * Using a convenient template alias for convenience. */ template using range_visitor_wrapper = as_range_visitor_base&>; template config::child_itors variable_info_mutable::append_array(std::vector children) const { this->throw_on_invalid(); return apply_visitor>(this->state_, children); } template config::child_itors variable_info_mutable::insert_array(std::vector children) const { this->throw_on_invalid(); return apply_visitor>(this->state_, children); } template config::child_itors variable_info_mutable::replace_array(std::vector children) const { this->throw_on_invalid(); return apply_visitor>(this->state_, children); } template void variable_info_mutable::merge_array(std::vector children) const { this->throw_on_invalid(); apply_visitor>(this->state_, children); } // Force compilation of the following template instantiations template class variable_info; template class variable_info; template class variable_info; template class variable_info_mutable; template class variable_info_mutable;