<commit_before>// sol2

// The MIT License (MIT)

// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef SOL_USERTYPE_CORE_HPP
#define SOL_USERTYPE_CORE_HPP

#include "wrapper.hpp"
#include "call.hpp"
#include "stack.hpp"
#include "types.hpp"
#include "stack_reference.hpp"
#include "usertype_traits.hpp"
#include "inheritance.hpp"
#include "raii.hpp"
#include "deprecate.hpp"
#include "object.hpp"

#include <sstream>

namespace sol {
	namespace usertype_detail {
		struct no_comp {
			template <typename A, typename B>
			bool operator()(A&&, B&&) const {
				return false;
			}
		};

		template <typename T>
		int is_check(lua_State* L) {
			return stack::push(L, stack::check<T>(L, 1, &no_panic));
		}

		template <typename T>
		inline int member_default_to_string(std::true_type, lua_State* L) {
			decltype(auto) ts = stack::get<T>(L, 1).to_string();
			return stack::push(L, std::forward<decltype(ts)>(ts));
		}

		template <typename T>
		inline int member_default_to_string(std::false_type, lua_State* L) {
			return luaL_error(L, "cannot perform to_string on '%s': no 'to_string' overload in namespace, 'to_string' member function, or operator<<(ostream&, ...) present", detail::demangle<T>().data());
		}

		template <typename T>
		inline int adl_default_to_string(std::true_type, lua_State* L) {
			using namespace std;
			decltype(auto) ts = to_string(stack::get<T>(L, 1));
			return stack::push(L, std::forward<decltype(ts)>(ts));
		}

		template <typename T>
		inline int adl_default_to_string(std::false_type, lua_State* L) {
			return member_default_to_string<T>(meta::supports_to_string_member<T>(), L);
		}

		template <typename T>
		inline int oss_default_to_string(std::true_type, lua_State* L) {
			std::ostringstream oss;
			oss << stack::get<T>(L, 1);
			return stack::push(L, oss.str());
		}

		template <typename T>
		inline int oss_default_to_string(std::false_type, lua_State* L) {
			return adl_default_to_string<T>(meta::supports_adl_to_string<T>(), L);
		}

		template <typename T>
		inline int default_to_string(lua_State* L) {
			return oss_default_to_string<T>(meta::supports_ostream_op<T>(), L);
		}

		template <typename T, typename Op>
		int comparsion_operator_wrap(lua_State* L) {
			auto maybel = stack::check_get<T>(L, 1);
			if (maybel) {
				auto mayber = stack::check_get<T>(L, 2);
				if (mayber) {
					auto& l = *maybel;
					auto& r = *mayber;
					if (std::is_same<no_comp, Op>::value) {
						return stack::push(L, detail::ptr(l) == detail::ptr(r));
					}
					else {
						Op op;
						return stack::push(L, (detail::ptr(l) == detail::ptr(r)) || op(detail::deref(l), detail::deref(r)));
					}
				}
			}
			return stack::push(L, false);
		}

		template <typename T, typename Op, typename Supports, typename Regs, meta::enable<Supports> = meta::enabler>
		inline void make_reg_op(Regs& l, int& index, const char* name) {
			lua_CFunction f = &comparsion_operator_wrap<T, Op>;
			l[index] = luaL_Reg{ name, f };
			++index;
		}

		template <typename T, typename Op, typename Supports, typename Regs, meta::disable<Supports> = meta::enabler>
		inline void make_reg_op(Regs&, int&, const char*) {
			// Do nothing if there's no support
		}

		template <typename T, typename Supports, typename Regs, meta::enable<Supports> = meta::enabler>
		inline void make_to_string_op(Regs& l, int& index) {
			const char* name = to_string(meta_function::to_string).c_str();
			lua_CFunction f = &detail::static_trampoline<&default_to_string<T>>;
			l[index] = luaL_Reg{ name, f };
			++index;
		}

		template <typename T, typename Supports, typename Regs, meta::disable<Supports> = meta::enabler>
		inline void make_to_string_op(Regs&, int&) {
			// Do nothing if there's no support
		}

		template <typename T, typename Regs, meta::enable<meta::has_deducible_signature<T>> = meta::enabler>
		inline void make_call_op(Regs& l, int& index) {
			const char* name = to_string(meta_function::call).c_str();
			lua_CFunction f = &c_call<decltype(&T::operator()), &T::operator()>;
			l[index] = luaL_Reg{ name, f };
			++index;
		}

		template <typename T, typename Regs, meta::disable<meta::has_deducible_signature<T>> = meta::enabler>
		inline void make_call_op(Regs&, int&) {
			// Do nothing if there's no support
		}

		template <typename T, typename Regs, meta::enable<meta::has_size<T>> = meta::enabler>
		inline void make_length_op(Regs& l, int& index) {
			typedef decltype(std::declval<T>().size()) R;
			using sz_func = R(T::*)()const;
			const char* name = to_string(meta_function::length).c_str();
			l[index] = luaL_Reg{ name, &c_call<decltype(static_cast<sz_func>(&T::size)), static_cast<sz_func>(&T::size)> };
			++index;
		}

		template <typename T, typename Regs, meta::disable<meta::has_size<T>> = meta::enabler>
		inline void make_length_op(Regs&, int&) {
			// Do nothing if there's no support
		}

		template <typename T, typename Regs, meta::enable<meta::neg<std::is_pointer<T>>, std::is_destructible<T>>>
		void make_destructor(Regs& l, int& index) {
			const char* name = to_string(meta_function::garbage_collect).c_str();
			l[index] = luaL_Reg{ name, is_unique_usertype<T>::value ? &detail::unique_destruct<T> : &detail::usertype_alloc_destruct<T> };
			++index;
		}

		template <typename T, typename Regs, meta::disable<meta::neg<std::is_pointer<T>>, std::is_destructible<T>>>
		void make_destructor(Regs& l, int& index) {
			if (!std::is_destructible<T>::value) {
				// if the value is not destructible, plant an erroring __gc method
				// to warn the user of a problem when it comes around
				// this won't trigger if the user performs `new_usertype` / `new_simple_usertype` and
				// rigs the class up properly
				const char* name = to_string(meta_function::garbage_collect).c_str();
				l[index] = luaL_Reg{ name, &detail::cannot_destruct<T> };
				++index;
			}
		}

		template <typename T, typename Regs, typename Fx>
		void insert_default_registrations(std::false_type, Regs&, int&, Fx&&) {
			// no-op
		}

		template <typename T, typename Regs, typename Fx>
		void insert_default_registrations(std::true_type, Regs& l, int& index, Fx&& fx) {
			if (fx(meta_function::less_than)) {
				const char* name = to_string(meta_function::less_than).c_str();
				usertype_detail::make_reg_op<T, std::less<>, meta::supports_op_less<T>>(l, index, name);
			}
			if (fx(meta_function::less_than_or_equal_to)) {
				const char* name = to_string(meta_function::less_than_or_equal_to).c_str();
				usertype_detail::make_reg_op<T, std::less_equal<>, meta::supports_op_less_equal<T>>(l, index, name);
			}
			if (fx(meta_function::equal_to)) {
				const char* name = to_string(meta_function::equal_to).c_str();
				usertype_detail::make_reg_op<T, std::conditional_t<meta::supports_op_equal<T>::value, std::equal_to<>, usertype_detail::no_comp>, std::true_type>(l, index, name);
			}
			if (fx(meta_function::pairs)) {
				const char* name = to_string(meta_function::pairs).c_str();
				l[index] = luaL_Reg{ name, container_usertype_metatable<as_container_t<T>>::pairs_call };
				++index;
			}
			if (fx(meta_function::length)) {
				usertype_detail::make_length_op<T>(l, index);
			}
			if (fx(meta_function::to_string)) {
				usertype_detail::make_to_string_op<T, is_to_stringable<T>>(l, index);
			}
			if (fx(meta_function::call_function)) {
				usertype_detail::make_call_op<T>(l, index);
			}
		}

		template <typename T, typename Regs, typename Fx>
		void insert_default_registrations(Regs& l, int& index, Fx&& fx) {
			insert_default_registrations<T>(is_automagical<T>(), l, index, std::forward<Fx>(fx));
		}
	} // namespace usertype_detail

	namespace stack { namespace stack_detail {
		template <typename T>
		struct undefined_metatable {
			typedef meta::all<meta::neg<std::is_pointer<T>>, std::is_destructible<T>> is_destructible;
			typedef std::remove_pointer_t<T> P;
			lua_State* L;
			const char* key;

			undefined_metatable(lua_State* l, const char* k)
			: L(l), key(k) {
			}

			void operator()() const {
				if (luaL_newmetatable(L, key) == 1) {
					luaL_Reg l[32]{};
					int index = 0;
					auto fx = [](meta_function) { return true; };
					usertype_detail::insert_default_registrations<P>(l, index, fx);
					usertype_detail::make_destructor<T>(l, index);
					luaL_setfuncs(L, l, 0);

					// __type table
					lua_createtable(L, 0, 2);
					const std::string& name = detail::demangle<T>();
					lua_pushlstring(L, name.c_str(), name.size());
					lua_setfield(L, -2, "name");
					lua_CFunction is_func = &usertype_detail::is_check<T>;
					lua_pushcclosure(L, is_func, 0);
					lua_setfield(L, -2, "is");
					lua_setfield(L, -2, to_string(meta_function::type).c_str());
				}
				lua_setmetatable(L, -2);
			}
		};
	}
	} // namespace stack::stack_detail
} // namespace sol

#endif // SOL_USERTYPE_CORE_HPP
<commit_msg>clang chokes on statically casted constant function expressions, thanks clang<commit_after>// sol2

// The MIT License (MIT)

// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef SOL_USERTYPE_CORE_HPP
#define SOL_USERTYPE_CORE_HPP

#include "wrapper.hpp"
#include "call.hpp"
#include "stack.hpp"
#include "types.hpp"
#include "stack_reference.hpp"
#include "usertype_traits.hpp"
#include "inheritance.hpp"
#include "raii.hpp"
#include "deprecate.hpp"
#include "object.hpp"

#include <sstream>

namespace sol {
	namespace usertype_detail {
		struct no_comp {
			template <typename A, typename B>
			bool operator()(A&&, B&&) const {
				return false;
			}
		};

		template <typename T>
		int is_check(lua_State* L) {
			return stack::push(L, stack::check<T>(L, 1, &no_panic));
		}

		template <typename T>
		inline int member_default_to_string(std::true_type, lua_State* L) {
			decltype(auto) ts = stack::get<T>(L, 1).to_string();
			return stack::push(L, std::forward<decltype(ts)>(ts));
		}

		template <typename T>
		inline int member_default_to_string(std::false_type, lua_State* L) {
			return luaL_error(L, "cannot perform to_string on '%s': no 'to_string' overload in namespace, 'to_string' member function, or operator<<(ostream&, ...) present", detail::demangle<T>().data());
		}

		template <typename T>
		inline int adl_default_to_string(std::true_type, lua_State* L) {
			using namespace std;
			decltype(auto) ts = to_string(stack::get<T>(L, 1));
			return stack::push(L, std::forward<decltype(ts)>(ts));
		}

		template <typename T>
		inline int adl_default_to_string(std::false_type, lua_State* L) {
			return member_default_to_string<T>(meta::supports_to_string_member<T>(), L);
		}

		template <typename T>
		inline int oss_default_to_string(std::true_type, lua_State* L) {
			std::ostringstream oss;
			oss << stack::get<T>(L, 1);
			return stack::push(L, oss.str());
		}

		template <typename T>
		inline int oss_default_to_string(std::false_type, lua_State* L) {
			return adl_default_to_string<T>(meta::supports_adl_to_string<T>(), L);
		}

		template <typename T>
		inline int default_to_string(lua_State* L) {
			return oss_default_to_string<T>(meta::supports_ostream_op<T>(), L);
		}

		template <typename T, typename Op>
		int comparsion_operator_wrap(lua_State* L) {
			auto maybel = stack::check_get<T>(L, 1);
			if (maybel) {
				auto mayber = stack::check_get<T>(L, 2);
				if (mayber) {
					auto& l = *maybel;
					auto& r = *mayber;
					if (std::is_same<no_comp, Op>::value) {
						return stack::push(L, detail::ptr(l) == detail::ptr(r));
					}
					else {
						Op op;
						return stack::push(L, (detail::ptr(l) == detail::ptr(r)) || op(detail::deref(l), detail::deref(r)));
					}
				}
			}
			return stack::push(L, false);
		}

		template <typename T, typename Op, typename Supports, typename Regs, meta::enable<Supports> = meta::enabler>
		inline void make_reg_op(Regs& l, int& index, const char* name) {
			lua_CFunction f = &comparsion_operator_wrap<T, Op>;
			l[index] = luaL_Reg{ name, f };
			++index;
		}

		template <typename T, typename Op, typename Supports, typename Regs, meta::disable<Supports> = meta::enabler>
		inline void make_reg_op(Regs&, int&, const char*) {
			// Do nothing if there's no support
		}

		template <typename T, typename Supports, typename Regs, meta::enable<Supports> = meta::enabler>
		inline void make_to_string_op(Regs& l, int& index) {
			const char* name = to_string(meta_function::to_string).c_str();
			lua_CFunction f = &detail::static_trampoline<&default_to_string<T>>;
			l[index] = luaL_Reg{ name, f };
			++index;
		}

		template <typename T, typename Supports, typename Regs, meta::disable<Supports> = meta::enabler>
		inline void make_to_string_op(Regs&, int&) {
			// Do nothing if there's no support
		}

		template <typename T, typename Regs, meta::enable<meta::has_deducible_signature<T>> = meta::enabler>
		inline void make_call_op(Regs& l, int& index) {
			const char* name = to_string(meta_function::call).c_str();
			lua_CFunction f = &c_call<decltype(&T::operator()), &T::operator()>;
			l[index] = luaL_Reg{ name, f };
			++index;
		}

		template <typename T, typename Regs, meta::disable<meta::has_deducible_signature<T>> = meta::enabler>
		inline void make_call_op(Regs&, int&) {
			// Do nothing if there's no support
		}

		template <typename T, typename Regs, meta::enable<meta::has_size<T>> = meta::enabler>
		inline void make_length_op(Regs& l, int& index) {
			const char* name = to_string(meta_function::length).c_str();
#ifdef __clang__
			l[index] = luaL_Reg{ name, &c_call<decltype(&T::size), &T::size> };
#else
			typedef decltype(std::declval<T>().size()) R;
			using sz_func = R(T::*)()const;
			const char* name = to_string(meta_function::length).c_str();
			l[index] = luaL_Reg{ name, &c_call<decltype(static_cast<sz_func>(&T::size)), static_cast<sz_func>(&T::size)> };
#endif
			++index;
		}

		template <typename T, typename Regs, meta::disable<meta::has_size<T>> = meta::enabler>
		inline void make_length_op(Regs&, int&) {
			// Do nothing if there's no support
		}

		template <typename T, typename Regs, meta::enable<meta::neg<std::is_pointer<T>>, std::is_destructible<T>>>
		void make_destructor(Regs& l, int& index) {
			const char* name = to_string(meta_function::garbage_collect).c_str();
			l[index] = luaL_Reg{ name, is_unique_usertype<T>::value ? &detail::unique_destruct<T> : &detail::usertype_alloc_destruct<T> };
			++index;
		}

		template <typename T, typename Regs, meta::disable<meta::neg<std::is_pointer<T>>, std::is_destructible<T>>>
		void make_destructor(Regs& l, int& index) {
			if (!std::is_destructible<T>::value) {
				// if the value is not destructible, plant an erroring __gc method
				// to warn the user of a problem when it comes around
				// this won't trigger if the user performs `new_usertype` / `new_simple_usertype` and
				// rigs the class up properly
				const char* name = to_string(meta_function::garbage_collect).c_str();
				l[index] = luaL_Reg{ name, &detail::cannot_destruct<T> };
				++index;
			}
		}

		template <typename T, typename Regs, typename Fx>
		void insert_default_registrations(std::false_type, Regs&, int&, Fx&&) {
			// no-op
		}

		template <typename T, typename Regs, typename Fx>
		void insert_default_registrations(std::true_type, Regs& l, int& index, Fx&& fx) {
			if (fx(meta_function::less_than)) {
				const char* name = to_string(meta_function::less_than).c_str();
				usertype_detail::make_reg_op<T, std::less<>, meta::supports_op_less<T>>(l, index, name);
			}
			if (fx(meta_function::less_than_or_equal_to)) {
				const char* name = to_string(meta_function::less_than_or_equal_to).c_str();
				usertype_detail::make_reg_op<T, std::less_equal<>, meta::supports_op_less_equal<T>>(l, index, name);
			}
			if (fx(meta_function::equal_to)) {
				const char* name = to_string(meta_function::equal_to).c_str();
				usertype_detail::make_reg_op<T, std::conditional_t<meta::supports_op_equal<T>::value, std::equal_to<>, usertype_detail::no_comp>, std::true_type>(l, index, name);
			}
			if (fx(meta_function::pairs)) {
				const char* name = to_string(meta_function::pairs).c_str();
				l[index] = luaL_Reg{ name, container_usertype_metatable<as_container_t<T>>::pairs_call };
				++index;
			}
			if (fx(meta_function::length)) {
				usertype_detail::make_length_op<T>(l, index);
			}
			if (fx(meta_function::to_string)) {
				usertype_detail::make_to_string_op<T, is_to_stringable<T>>(l, index);
			}
			if (fx(meta_function::call_function)) {
				usertype_detail::make_call_op<T>(l, index);
			}
		}

		template <typename T, typename Regs, typename Fx>
		void insert_default_registrations(Regs& l, int& index, Fx&& fx) {
			insert_default_registrations<T>(is_automagical<T>(), l, index, std::forward<Fx>(fx));
		}
	} // namespace usertype_detail

	namespace stack { namespace stack_detail {
		template <typename T>
		struct undefined_metatable {
			typedef meta::all<meta::neg<std::is_pointer<T>>, std::is_destructible<T>> is_destructible;
			typedef std::remove_pointer_t<T> P;
			lua_State* L;
			const char* key;

			undefined_metatable(lua_State* l, const char* k)
			: L(l), key(k) {
			}

			void operator()() const {
				if (luaL_newmetatable(L, key) == 1) {
					luaL_Reg l[32]{};
					int index = 0;
					auto fx = [](meta_function) { return true; };
					usertype_detail::insert_default_registrations<P>(l, index, fx);
					usertype_detail::make_destructor<T>(l, index);
					luaL_setfuncs(L, l, 0);

					// __type table
					lua_createtable(L, 0, 2);
					const std::string& name = detail::demangle<T>();
					lua_pushlstring(L, name.c_str(), name.size());
					lua_setfield(L, -2, "name");
					lua_CFunction is_func = &usertype_detail::is_check<T>;
					lua_pushcclosure(L, is_func, 0);
					lua_setfield(L, -2, "is");
					lua_setfield(L, -2, to_string(meta_function::type).c_str());
				}
				lua_setmetatable(L, -2);
			}
		};
	}
	} // namespace stack::stack_detail
} // namespace sol

#endif // SOL_USERTYPE_CORE_HPP
<|endoftext|>


<commit_before>#ifndef SILICIUM_ITERATOR_RANGE_HPP
#define SILICIUM_ITERATOR_RANGE_HPP

#include <silicium/config.hpp>
#include <boost/static_assert.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/utility/addressof.hpp>
#include <cassert>

namespace Si
{
	namespace detail
	{
		// std::iterator_traits<T const *>::value_type seems to be T (GCC 4.8),
		// but that
		// is not useful at all because the const is missing.
		template <class Iterator>
		struct actual_value_type
		{
			typedef typename std::remove_reference<decltype(
			    *std::declval<Iterator>())>::type type;
		};
	}

	template <class Iterator>
	struct iterator_range
	{
		typedef typename std::iterator_traits<Iterator>::difference_type
		    difference_type;
		typedef typename detail::actual_value_type<Iterator>::type value_type;
		typedef Iterator iterator;
		typedef Iterator const_iterator;

		BOOST_CONSTEXPR iterator_range() BOOST_NOEXCEPT : m_begin(Iterator()),
		                                                  m_end(Iterator())
		{
		}

		template <class It1, class It2>
		BOOST_CONSTEXPR iterator_range(It1 &&begin, It2 &&end) BOOST_NOEXCEPT
		    : m_begin(std::forward<It1>(begin)),
		      m_end(std::forward<It2>(end))
		{
		}

		template <class OtherIterator>
		iterator_range(iterator_range<OtherIterator> const &other)
		    BOOST_NOEXCEPT : m_begin(other.begin()),
		                     m_end(other.end())
		{
		}

		template <class OtherIterator>
		iterator_range &
		operator=(iterator_range<OtherIterator> const &other) BOOST_NOEXCEPT
		{
			m_begin = other.begin();
			m_end = other.end();
			return *this;
		}

		BOOST_CONSTEXPR Iterator const &begin() const BOOST_NOEXCEPT
		{
			return m_begin;
		}

		BOOST_CONSTEXPR Iterator const &end() const BOOST_NOEXCEPT
		{
			return m_end;
		}

		BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT
		{
			return m_begin == m_end;
		}

		/// This method is only available with random access iterators. Use
		/// std::distance(begin(r), end(r))
		/// if you really want a potential O(n) operation.
		BOOST_CONSTEXPR difference_type size() const BOOST_NOEXCEPT
		{
			return m_end - m_begin;
		}

		/// This method is only available with random access iterators.
		/// If you need this for weaker iterators, use a loop that calls
		/// pop_front() for individual elements.
		SILICIUM_CXX14_CONSTEXPR void
		pop_front(difference_type n) BOOST_NOEXCEPT
		{
			assert(n <= size());
			m_begin += n;
		}

		SILICIUM_CXX14_CONSTEXPR void pop_front() BOOST_NOEXCEPT
		{
			assert(!empty());
			++m_begin;
		}

		value_type &front() const BOOST_NOEXCEPT
		{
			assert(!empty());
			return *begin();
		}

		/// This method is only available with random access iterators so that
		/// it takes O(1) in time.
		value_type &operator[](difference_type index) const BOOST_NOEXCEPT
		{
			(void)static_cast<std::random_access_iterator_tag>(
			    typename std::iterator_traits<Iterator>::iterator_category{});
			assert(index < size());
			return begin()[index];
		}

	private:
		Iterator m_begin, m_end;
	};

	template <class Iterator1, class Iterator2>
	BOOST_CONSTEXPR bool
	pointing_to_same_subrange(iterator_range<Iterator1> const &left,
	                          iterator_range<Iterator2> const &right)
	{
		return (left.begin() == right.begin()) && (left.end() == right.end());
	}

	template <class Iterator>
	BOOST_CONSTEXPR Iterator const &begin(iterator_range<Iterator> const &range)
	{
		return range.begin();
	}

	// for Boost.Range compatibility
	template <class Iterator>
	BOOST_CONSTEXPR Iterator const &
	range_begin(iterator_range<Iterator> const &range)
	{
		return range.begin();
	}

	template <class Iterator>
	BOOST_CONSTEXPR Iterator const &end(iterator_range<Iterator> const &range)
	{
		return range.end();
	}

	template <class Iterator1, class Iterator2>
	BOOST_CONSTEXPR auto make_iterator_range(Iterator1 &&begin, Iterator2 &&end)
#if !SILICIUM_COMPILER_HAS_AUTO_RETURN_TYPE
	    -> iterator_range<typename std::decay<Iterator1>::type>
#endif
	{
		typedef typename std::decay<Iterator1>::type iterator_type;
		BOOST_STATIC_ASSERT(
		    (std::is_same<iterator_type,
		                  typename std::decay<Iterator2>::type>::value));
		return iterator_range<iterator_type>(
		    std::forward<Iterator1>(begin), std::forward<Iterator2>(end));
	}

	template <class Range>
	auto make_iterator_range(Range &&range)
#if !SILICIUM_COMPILER_HAS_AUTO_RETURN_TYPE
	    -> decltype(make_iterator_range(std::begin(range), std::end(range)))
#endif
	{
		using std::begin;
		using std::end;
		return make_iterator_range(begin(range), end(range));
	}

	template <class ContiguousIterator>
	auto make_contiguous_range(ContiguousIterator begin, ContiguousIterator end)
#if !SILICIUM_COMPILER_HAS_AUTO_RETURN_TYPE
	    -> decltype(make_iterator_range(boost::addressof(begin),
	                                    boost::addressof(end)))
#endif
	{
		if (begin == end)
		{
			typename std::remove_reference<decltype(*begin)>::type *data =
			    nullptr;
			return make_iterator_range(data, data);
		}
		auto *const data_begin = boost::addressof(*begin);
		auto *const data_end = data_begin + std::distance(begin, end);
		return make_iterator_range(data_begin, data_end);
	}

	template <class ContiguousRange>
	auto make_contiguous_range(ContiguousRange &&range)
#if !SILICIUM_COMPILER_HAS_AUTO_RETURN_TYPE
	    -> decltype(make_iterator_range(boost::addressof(*std::begin(range)),
	                                    boost::addressof(*std::end(range))))
#endif
	{
		using std::begin;
		using std::end;
		auto begin_ = begin(range);
		auto end_ = end(range);
		return make_contiguous_range(begin_, end_);
	}
}

#endif
<commit_msg>corrections for compiler without auto return type<commit_after>#ifndef SILICIUM_ITERATOR_RANGE_HPP
#define SILICIUM_ITERATOR_RANGE_HPP

#include <silicium/config.hpp>
#include <boost/static_assert.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/utility/addressof.hpp>
#include <cassert>

namespace Si
{
	namespace detail
	{
		// std::iterator_traits<T const *>::value_type seems to be T (GCC 4.8),
		// but that
		// is not useful at all because the const is missing.
		template <class Iterator>
		struct actual_value_type
		{
			typedef typename std::remove_reference<decltype(
			    *std::declval<Iterator>())>::type type;
		};
	}

	template <class Iterator>
	struct iterator_range
	{
		typedef typename std::iterator_traits<Iterator>::difference_type
		    difference_type;
		typedef typename detail::actual_value_type<Iterator>::type value_type;
		typedef Iterator iterator;
		typedef Iterator const_iterator;

		BOOST_CONSTEXPR iterator_range() BOOST_NOEXCEPT : m_begin(Iterator()),
		                                                  m_end(Iterator())
		{
		}

		template <class It1, class It2>
		BOOST_CONSTEXPR iterator_range(It1 &&begin, It2 &&end) BOOST_NOEXCEPT
		    : m_begin(std::forward<It1>(begin)),
		      m_end(std::forward<It2>(end))
		{
		}

		template <class OtherIterator>
		iterator_range(iterator_range<OtherIterator> const &other)
		    BOOST_NOEXCEPT : m_begin(other.begin()),
		                     m_end(other.end())
		{
		}

		template <class OtherIterator>
		iterator_range &
		operator=(iterator_range<OtherIterator> const &other) BOOST_NOEXCEPT
		{
			m_begin = other.begin();
			m_end = other.end();
			return *this;
		}

		BOOST_CONSTEXPR Iterator const &begin() const BOOST_NOEXCEPT
		{
			return m_begin;
		}

		BOOST_CONSTEXPR Iterator const &end() const BOOST_NOEXCEPT
		{
			return m_end;
		}

		BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT
		{
			return m_begin == m_end;
		}

		/// This method is only available with random access iterators. Use
		/// std::distance(begin(r), end(r))
		/// if you really want a potential O(n) operation.
		BOOST_CONSTEXPR difference_type size() const BOOST_NOEXCEPT
		{
			return m_end - m_begin;
		}

		/// This method is only available with random access iterators.
		/// If you need this for weaker iterators, use a loop that calls
		/// pop_front() for individual elements.
		SILICIUM_CXX14_CONSTEXPR void
		pop_front(difference_type n) BOOST_NOEXCEPT
		{
			assert(n <= size());
			m_begin += n;
		}

		SILICIUM_CXX14_CONSTEXPR void pop_front() BOOST_NOEXCEPT
		{
			assert(!empty());
			++m_begin;
		}

		value_type &front() const BOOST_NOEXCEPT
		{
			assert(!empty());
			return *begin();
		}

		/// This method is only available with random access iterators so that
		/// it takes O(1) in time.
		value_type &operator[](difference_type index) const BOOST_NOEXCEPT
		{
			(void)static_cast<std::random_access_iterator_tag>(
			    typename std::iterator_traits<Iterator>::iterator_category{});
			assert(index < size());
			return begin()[index];
		}

	private:
		Iterator m_begin, m_end;
	};

	template <class Iterator1, class Iterator2>
	BOOST_CONSTEXPR bool
	pointing_to_same_subrange(iterator_range<Iterator1> const &left,
	                          iterator_range<Iterator2> const &right)
	{
		return (left.begin() == right.begin()) && (left.end() == right.end());
	}

	template <class Iterator>
	BOOST_CONSTEXPR Iterator const &begin(iterator_range<Iterator> const &range)
	{
		return range.begin();
	}

	// for Boost.Range compatibility
	template <class Iterator>
	BOOST_CONSTEXPR Iterator const &
	range_begin(iterator_range<Iterator> const &range)
	{
		return range.begin();
	}

	template <class Iterator>
	BOOST_CONSTEXPR Iterator const &end(iterator_range<Iterator> const &range)
	{
		return range.end();
	}

	template <class Iterator1, class Iterator2>
	BOOST_CONSTEXPR auto make_iterator_range(Iterator1 &&begin, Iterator2 &&end)
	    -> iterator_range<typename std::decay<Iterator1>::type>
	{
		typedef typename std::decay<Iterator1>::type iterator_type;
		BOOST_STATIC_ASSERT(
		    (std::is_same<iterator_type,
		                  typename std::decay<Iterator2>::type>::value));
		return iterator_range<iterator_type>(
		    std::forward<Iterator1>(begin), std::forward<Iterator2>(end));
	}

	template <class Range>
	auto make_iterator_range(Range &&range)
	    -> decltype(make_iterator_range(std::begin(range), std::end(range)))
	{
		using std::begin;
		using std::end;
		return make_iterator_range(begin(range), end(range));
	}

	template <class ContiguousIterator>
	auto make_contiguous_range(ContiguousIterator begin, ContiguousIterator end)
	    -> decltype(make_iterator_range(boost::addressof(*begin),
	                                    boost::addressof(*end)))
	{
		if (begin == end)
		{
			typename std::remove_reference<decltype(*begin)>::type *data =
			    nullptr;
			return make_iterator_range(data, data);
		}
		auto *const data_begin = boost::addressof(*begin);
		auto *const data_end = data_begin + std::distance(begin, end);
		return make_iterator_range(data_begin, data_end);
	}

	template <class ContiguousRange>
	auto make_contiguous_range(ContiguousRange &&range)
	    -> decltype(make_iterator_range(boost::addressof(*std::begin(range)),
	                                    boost::addressof(*std::end(range))))
	{
		using std::begin;
		using std::end;
		auto begin_ = begin(range);
		auto end_ = end(range);
		return make_contiguous_range(begin_, end_);
	}
}

#endif
<|endoftext|>


<commit_before>#pragma once
//=====================================================================//
/*! @file
    @brief  SEEDA03 client クラス @n
			Copyright 2017 Kunihito Hiramatsu
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "main.hpp"

#include "GR/core/ethernet_client.hpp"
#include "sample.hpp"

// #define CLIENT_DEBUG

namespace seeda {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief  client クラス
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	class client {

#ifdef CLIENT_DEBUG
		typedef utils::format debug_format;
#else
		typedef utils::null_format debug_format;
#endif

		static const uint16_t PORT = 3000;	///< クライアント・ポート

		net::ethernet_client	client_;

		typedef utils::basic_format<net::ether_string<net::ethernet::format_id::client0, 2048> > format;

		enum class task {
			startup,
			connect,
			time_sync,
			make_form,
			url_decode,
			send_data,
			disconnect,
		};

		task		task_;

		net::ip_address		ip_;

		uint16_t	port_;
		uint32_t	delay_;
		uint32_t	timeout_;

		time_t		time_;

		struct tm	tm_;

		char		form_[1024];
		char		data_[2048];

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief  コンストラクタ
		*/
		//-----------------------------------------------------------------//
		client(net::ethernet& eth) : client_(eth, PORT), task_(task::startup),
#ifdef SEEDA
			ip_(192, 168, 1, 3),
#else
			ip_(192, 168, 3, 7),
#endif
			port_(PORT),
			delay_(0), timeout_(0), time_(0) { }

		//-----------------------------------------------------------------//
		/*!
			@brief  IP アドレスの取得
			@return IP アドレス
		*/
		//-----------------------------------------------------------------//
		const net::ip_address& get_ip() const { return ip_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  IP アドレスの参照
			@return IP アドレス
		*/
		//-----------------------------------------------------------------//
		net::ip_address& at_ip() { return ip_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  ポートの取得
			@return ポート
		*/
		//-----------------------------------------------------------------//
		uint16_t get_port() const { return port_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  ポートの設定
			@param[in]	port	ポート
		*/
		//-----------------------------------------------------------------//
		void set_port(uint16_t port) { port_ = port; }

		//-----------------------------------------------------------------//
		/*!
			@brief  接続開始
		*/
		//-----------------------------------------------------------------//
		void start_connect()
		{
			task_ = task::connect;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  再接続
		*/
		//-----------------------------------------------------------------//
		void restart()
		{
			task_ = task::disconnect;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  サービス
		*/
		//-----------------------------------------------------------------//
		void service()
		{
			time_t t;
			switch(task_) {

			case task::startup:
				break;

			case task::connect:
				if(client_.connect(ip_, port_, TMO_NBLK)) {
					debug_format("Start SEEDA03 Client: %s port(%d), fd(%d)\n")
						% ip_.c_str() % port_ % client_.get_cepid();
					timeout_ = 5 * 100;  // 5 sec
					time_ = get_sample_data().time_;
					format::chaout().set_fd(client_.get_cepid());
					task_ = task::time_sync;
				}
				break;

			case task::time_sync:
				if(!client_.connected()) {
					task_ = task::disconnect;
					break;
				}
				{
					auto t = get_sample_data().time_;
					if(time_ == t) break;
					time_ = t;
					struct tm* m = localtime(&t);
					tm_ = *m;
					task_ = task::make_form;
				}
				break;

			case task::make_form:
				utils::sformat("%04d/%02d/%02d,%02d:%02d:%02d", form_, sizeof(form_))
					% static_cast<uint32_t>(tm_.tm_year + 1900)
					% static_cast<uint32_t>(tm_.tm_mon + 1)
					% static_cast<uint32_t>(tm_.tm_mday)
					% static_cast<uint32_t>(tm_.tm_hour)
					% static_cast<uint32_t>(tm_.tm_min)
					% static_cast<uint32_t>(tm_.tm_sec);
				{
					const sample_data& smd = get_sample_data();
					for(int ch = 0; ch < 8; ++ch) {
						utils::sformat(",", form_, sizeof(form_), true);
						smd.smp_[ch].make_csv2(form_, sizeof(form_), true);
					}
				}
				utils::sformat("\n", form_, sizeof(form_), true);
				task_ = task::url_decode;
				break;

			case task::url_decode:
				utils::str::url_decode_to_str(form_, data_, sizeof(data_));
				task_ = task::send_data;
				break;

			case task::send_data:
				format::chaout().clear();
				format("POST /api/?val=%s HTTP/1.1\n") % data_;
				format("Host: %d.%d.%d.%d\n")
					% static_cast<int>(ip_[0])
					% static_cast<int>(ip_[1])
					% static_cast<int>(ip_[2])
					% static_cast<int>(ip_[3]);
				format("Content-Type: application/x-www-form-urlencoded\n");
				format("User-Agent: SEEDA03 Post Client\n");
				format("Connection: close\n\n");
				format::chaout().flush();
				task_ = task::disconnect;
				break;

			case task::disconnect:
				client_.stop();
				debug_format("Client disconnected: %s\n") % ip_.c_str();
				task_ = task::connect;
				break;
			}
		}
	};
}
<commit_msg>update TCP client management<commit_after>#pragma once
//=====================================================================//
/*! @file
    @brief  SEEDA03 client クラス @n
			Copyright 2017 Kunihito Hiramatsu
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "main.hpp"

#include "GR/core/ethernet_client.hpp"
#include "sample.hpp"

// #define CLIENT_DEBUG

namespace seeda {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief  client クラス
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	class client {

#ifdef CLIENT_DEBUG
		typedef utils::format debug_format;
#else
		typedef utils::null_format debug_format;
#endif

		static const uint16_t PORT = 3000;	///< クライアント・ポート

		net::ethernet_client	client_;

		typedef utils::basic_format<net::ether_string<net::ethernet::format_id::client0, 2048> > format;

		enum class task {
			startup,
			req_connect,
			wait_connect,
			time_sync,
			make_form,
			url_decode,
			send_data,
			disconnect,
			sync_close,
		};

		task		task_;

		net::ip_address		ip_;

		uint16_t	port_;
		uint32_t	delay_;
		uint32_t	timeout_;

		time_t		time_;

		struct tm	tm_;

		char		form_[1024];
		char		data_[2048];

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief  コンストラクタ
		*/
		//-----------------------------------------------------------------//
		client(net::ethernet& eth) : client_(eth, PORT), task_(task::startup),
#ifdef SEEDA
			ip_(192, 168, 1, 3),
#else
			ip_(192, 168, 3, 7),
#endif
			port_(PORT),
			delay_(0), timeout_(0), time_(0) { }

		//-----------------------------------------------------------------//
		/*!
			@brief  IP アドレスの取得
			@return IP アドレス
		*/
		//-----------------------------------------------------------------//
		const net::ip_address& get_ip() const { return ip_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  IP アドレスの参照
			@return IP アドレス
		*/
		//-----------------------------------------------------------------//
		net::ip_address& at_ip() { return ip_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  ポートの取得
			@return ポート
		*/
		//-----------------------------------------------------------------//
		uint16_t get_port() const { return port_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  ポートの設定
			@param[in]	port	ポート
		*/
		//-----------------------------------------------------------------//
		void set_port(uint16_t port) { port_ = port; }

		//-----------------------------------------------------------------//
		/*!
			@brief  接続開始
		*/
		//-----------------------------------------------------------------//
		void start_connect()
		{
			task_ = task::req_connect;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  再接続
		*/
		//-----------------------------------------------------------------//
		void restart()
		{
			task_ = task::disconnect;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  サービス
		*/
		//-----------------------------------------------------------------//
		void service()
		{
			time_t t;
			switch(task_) {

			case task::startup:
				break;

			case task::req_connect:
				if(client_.connect(ip_, port_, TMO_NBLK)) {
				}
				timeout_ = 5 * 100;  // 再接続待ち時間
				task_ = task::wait_connect;
				break;

			case task::wait_connect:
				if(!client_.connected()) {
					if(timeout_ > 0) {
						--timeout_;
					} else {
						auto st = client_.get_ethernet().get_stat(client_.get_cepid());
						debug_format("TCP Client stat: %d\n") % static_cast<int>(st);
						// 接続しないので、「re_connect」要求を出してみる
						if(client_.re_connect()) {
							task_ = task::req_connect;
						} else {
							task_ = task::disconnect;
						}
						timeout_ = 5 * 100;  // 再接続待ち時間
					}
					break;
				} else {
					debug_format("Start SEEDA03 Client: %s port(%d), fd(%d)\n")
						% ip_.c_str() % port_ % client_.get_cepid();
					format::chaout().set_fd(client_.get_cepid());
					time_ = get_sample_data().time_;
					task_ = task::time_sync;
				}
				break;

			case task::time_sync:
				{
					auto t = get_sample_data().time_;
					if(time_ == t) break;
					time_ = t;
					struct tm* m = localtime(&t);
					tm_ = *m;
					task_ = task::make_form;
				}
				break;

			case task::make_form:
				utils::sformat("%04d/%02d/%02d,%02d:%02d:%02d", form_, sizeof(form_))
					% static_cast<uint32_t>(tm_.tm_year + 1900)
					% static_cast<uint32_t>(tm_.tm_mon + 1)
					% static_cast<uint32_t>(tm_.tm_mday)
					% static_cast<uint32_t>(tm_.tm_hour)
					% static_cast<uint32_t>(tm_.tm_min)
					% static_cast<uint32_t>(tm_.tm_sec);
				{
					const sample_data& smd = get_sample_data();
					for(int ch = 0; ch < 8; ++ch) {
						utils::sformat(",", form_, sizeof(form_), true);
						smd.smp_[ch].make_csv2(form_, sizeof(form_), true);
					}
				}
				utils::sformat("\n", form_, sizeof(form_), true);
				task_ = task::url_decode;
				break;

			case task::url_decode:
				utils::str::url_decode_to_str(form_, data_, sizeof(data_));
				task_ = task::send_data;
				break;

			case task::send_data:
				format::chaout().clear();
				format("POST /api/?val=%s HTTP/1.1\n") % data_;
				format("Host: %d.%d.%d.%d\n")
					% static_cast<int>(ip_[0])
					% static_cast<int>(ip_[1])
					% static_cast<int>(ip_[2])
					% static_cast<int>(ip_[3]);
				format("Content-Type: application/x-www-form-urlencoded\n");
				format("User-Agent: SEEDA03 Post Client\n");
				format("Connection: close\n\n");
				format::chaout().flush();
				task_ = task::disconnect;
				break;

			case task::disconnect:
				client_.stop();
				timeout_ = 5;
				task_ = task::sync_close;
				break;

			case task::sync_close:
				if(timeout_ > 0) {
					--timeout_;
				} else {
					debug_format("Client disconnected: %s\n") % ip_.c_str();
					task_ = task::req_connect;
				}
				break;
			}
		}
	};
}
<|endoftext|>


<commit_before>// Copyright (C) 2013  James Turner - zakalawe@mac.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//

#include <simgear/package/Package.hxx>

#include <cassert>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/case_conv.hpp>

#include <simgear/debug/logstream.hxx>
#include <simgear/structure/exception.hxx>

#include <simgear/package/Catalog.hxx>
#include <simgear/package/Install.hxx>
#include <simgear/package/Root.hxx>

namespace simgear {

namespace pkg {

Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) :
    m_catalog(aCatalog)
{
    initWithProps(aProps);
}

void Package::initWithProps(const SGPropertyNode* aProps)
{
    m_props = const_cast<SGPropertyNode*>(aProps);
// cache tag values
    BOOST_FOREACH(const SGPropertyNode* c, aProps->getChildren("tag")) {
      std::string t(c->getStringValue());
      m_tags.insert(boost::to_lower_copy(t));
    }
}

bool Package::matches(const SGPropertyNode* aFilter) const
{
    int nChildren = aFilter->nChildren();
    for (int i = 0; i < nChildren; i++) {
        const SGPropertyNode* c = aFilter->getChild(i);
        if (strutils::starts_with(c->getName(), "rating-")) {
            int minRating = c->getIntValue();
            std::string rname = c->getName() + 7;
            int ourRating = m_props->getChild("rating")->getIntValue(rname, 0);
            if (ourRating < minRating) {
                return false;
            }
        }

        if (strcmp(c->getName(), "tag") == 0) {
            std::string tag(c->getStringValue());
            boost::to_lower(tag);
            if (m_tags.find(tag) == m_tags.end()) {
                return false;
            }
        }

        // substring search of name, description
        if (strcmp(c->getName(), "name") == 0) {
          std::string n(c->getStringValue());
          boost::to_lower(n);
          size_t pos = boost::to_lower_copy(name()).find(n);
          if (pos == std::string::npos) {
            return false;
          }
        }

        if (strcmp(c->getName(), "description") == 0) {
          std::string n(c->getStringValue());
          boost::to_lower(n);
          size_t pos = boost::to_lower_copy(description()).find(n);
          if (pos == std::string::npos) {
            return false;
          }
        }

        SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << c->getName());
    } // of filter props iteration

    return true;
}

bool Package::isInstalled() const
{
    SGPath p(m_catalog->installRoot());
    p.append("Aircraft");
    p.append(id());

    // anything to check for? look for a valid revision file?
    return p.exists();
}

InstallRef Package::install()
{
    SGPath p(m_catalog->installRoot());
    p.append("Aircraft");
    p.append(id());
    if (p.exists()) {
        return Install::createFromPath(p, m_catalog);
    }

    InstallRef ins(new Install(this, p));
    m_catalog->root()->scheduleToUpdate(ins);
    return ins;
}

std::string Package::id() const
{
    return m_props->getStringValue("id");
}

std::string Package::md5() const
{
    return m_props->getStringValue("md5");
}

unsigned int Package::revision() const
{
    return m_props->getIntValue("revision");
}

std::string Package::name() const
{
    return m_props->getStringValue("name");
}

std::string Package::description() const
{
    return getLocalisedProp("decription");
}

SGPropertyNode* Package::properties() const
{
    return m_props.ptr();
}

string_list Package::thumbnailUrls() const
{
    string_list r;
    BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail")) {
        r.push_back(dl->getStringValue());
    }
    return r;
}

string_list Package::downloadUrls() const
{
    string_list r;
    BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("download")) {
        r.push_back(dl->getStringValue());
    }
    return r;
}

std::string Package::getLocalisedProp(const std::string& aName) const
{
    return getLocalisedString(m_props, aName.c_str());
}

std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
{
    std::string locale = m_catalog->root()->getLocale();
    if (aRoot->hasChild(locale)) {
        const SGPropertyNode* localeRoot = aRoot->getChild(locale.c_str());
        if (localeRoot->hasChild(aName)) {
            return localeRoot->getStringValue(aName);
        }
    }

    return aRoot->getStringValue(aName);
}

PackageList Package::dependencies() const
{
    PackageList result;

    BOOST_FOREACH(SGPropertyNode* dep, m_props->getChildren("depends")) {
        std::string depName = dep->getStringValue("package");
        unsigned int rev = dep->getIntValue("revision", 0);

    // prefer local hangar package if possible, in case someone does something
    // silly with naming. Of course flightgear's aircraft search doesn't know
    // about hanagrs, so names still need to be unique.
        PackageRef depPkg = m_catalog->getPackageById(depName);
        if (!depPkg) {
            Root* rt = m_catalog->root();
            depPkg = rt->getPackageById(depName);
            if (!depPkg) {
                throw sg_exception("Couldn't satisfy dependency of " + id() + " : " + depName);
            }
        }

        if (depPkg->revision() < rev) {
            throw sg_range_exception("Couldn't find suitable revision of " + depName);
        }

    // forbid recursive dependency graphs, we don't need that level
    // of complexity for aircraft resources
        assert(depPkg->dependencies() == PackageList());

        result.push_back(depPkg);
    }

    return result;
}

} // of namespace pkg

} // of namespace simgear
<commit_msg>Package: use correct tag for download url.<commit_after>// Copyright (C) 2013  James Turner - zakalawe@mac.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//

#include <simgear/package/Package.hxx>

#include <cassert>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/case_conv.hpp>

#include <simgear/debug/logstream.hxx>
#include <simgear/structure/exception.hxx>

#include <simgear/package/Catalog.hxx>
#include <simgear/package/Install.hxx>
#include <simgear/package/Root.hxx>

namespace simgear {

namespace pkg {

Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) :
    m_catalog(aCatalog)
{
    initWithProps(aProps);
}

void Package::initWithProps(const SGPropertyNode* aProps)
{
    m_props = const_cast<SGPropertyNode*>(aProps);
// cache tag values
    BOOST_FOREACH(const SGPropertyNode* c, aProps->getChildren("tag")) {
      std::string t(c->getStringValue());
      m_tags.insert(boost::to_lower_copy(t));
    }
}

bool Package::matches(const SGPropertyNode* aFilter) const
{
    int nChildren = aFilter->nChildren();
    for (int i = 0; i < nChildren; i++) {
        const SGPropertyNode* c = aFilter->getChild(i);
        if (strutils::starts_with(c->getName(), "rating-")) {
            int minRating = c->getIntValue();
            std::string rname = c->getName() + 7;
            int ourRating = m_props->getChild("rating")->getIntValue(rname, 0);
            if (ourRating < minRating) {
                return false;
            }
        }

        if (strcmp(c->getName(), "tag") == 0) {
            std::string tag(c->getStringValue());
            boost::to_lower(tag);
            if (m_tags.find(tag) == m_tags.end()) {
                return false;
            }
        }

        // substring search of name, description
        if (strcmp(c->getName(), "name") == 0) {
          std::string n(c->getStringValue());
          boost::to_lower(n);
          size_t pos = boost::to_lower_copy(name()).find(n);
          if (pos == std::string::npos) {
            return false;
          }
        }

        if (strcmp(c->getName(), "description") == 0) {
          std::string n(c->getStringValue());
          boost::to_lower(n);
          size_t pos = boost::to_lower_copy(description()).find(n);
          if (pos == std::string::npos) {
            return false;
          }
        }

        SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << c->getName());
    } // of filter props iteration

    return true;
}

bool Package::isInstalled() const
{
    SGPath p(m_catalog->installRoot());
    p.append("Aircraft");
    p.append(id());

    // anything to check for? look for a valid revision file?
    return p.exists();
}

InstallRef Package::install()
{
    SGPath p(m_catalog->installRoot());
    p.append("Aircraft");
    p.append(id());
    if (p.exists()) {
        return Install::createFromPath(p, m_catalog);
    }

    InstallRef ins(new Install(this, p));
    m_catalog->root()->scheduleToUpdate(ins);
    return ins;
}

std::string Package::id() const
{
    return m_props->getStringValue("id");
}

std::string Package::md5() const
{
    return m_props->getStringValue("md5");
}

unsigned int Package::revision() const
{
    return m_props->getIntValue("revision");
}

std::string Package::name() const
{
    return m_props->getStringValue("name");
}

std::string Package::description() const
{
    return getLocalisedProp("decription");
}

SGPropertyNode* Package::properties() const
{
    return m_props.ptr();
}

string_list Package::thumbnailUrls() const
{
    string_list r;
    BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail")) {
        r.push_back(dl->getStringValue());
    }
    return r;
}

string_list Package::downloadUrls() const
{
    string_list r;
    BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) {
        r.push_back(dl->getStringValue());
    }
    return r;
}

std::string Package::getLocalisedProp(const std::string& aName) const
{
    return getLocalisedString(m_props, aName.c_str());
}

std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
{
    std::string locale = m_catalog->root()->getLocale();
    if (aRoot->hasChild(locale)) {
        const SGPropertyNode* localeRoot = aRoot->getChild(locale.c_str());
        if (localeRoot->hasChild(aName)) {
            return localeRoot->getStringValue(aName);
        }
    }

    return aRoot->getStringValue(aName);
}

PackageList Package::dependencies() const
{
    PackageList result;

    BOOST_FOREACH(SGPropertyNode* dep, m_props->getChildren("depends")) {
        std::string depName = dep->getStringValue("package");
        unsigned int rev = dep->getIntValue("revision", 0);

    // prefer local hangar package if possible, in case someone does something
    // silly with naming. Of course flightgear's aircraft search doesn't know
    // about hanagrs, so names still need to be unique.
        PackageRef depPkg = m_catalog->getPackageById(depName);
        if (!depPkg) {
            Root* rt = m_catalog->root();
            depPkg = rt->getPackageById(depName);
            if (!depPkg) {
                throw sg_exception("Couldn't satisfy dependency of " + id() + " : " + depName);
            }
        }

        if (depPkg->revision() < rev) {
            throw sg_range_exception("Couldn't find suitable revision of " + depName);
        }

    // forbid recursive dependency graphs, we don't need that level
    // of complexity for aircraft resources
        assert(depPkg->dependencies() == PackageList());

        result.push_back(depPkg);
    }

    return result;
}

} // of namespace pkg

} // of namespace simgear
<|endoftext|>


<commit_before>#include <algorithm>
#include <chrono>

#include "control.h"
#include "bvs/archutils.h"

BVS::ModuleDataMap BVS::Control::modules;

BVS::Control::Control(Info& info)
	: info(info),
	logger("Control"),
	runningThreads(0),
	masterModules(),
	pools(),
	flag(SystemFlag::PAUSE),
	mutex(),
	masterLock(mutex),
	monitor(),
	controlThread(),
	round(0)
{}

void BVS::Control::registerModule(const std::string& id, Module* module)
{
	modules[id] = std::shared_ptr<ModuleData>(new ModuleData(
				id,
				std::string(),
				std::string(),
				module,
				nullptr,
				false,
				std::string(),
				ControlFlag::WAIT,
				Status::NONE,
				ConnectorMap()));
}

BVS::Control& BVS::Control::masterController(const bool forkMasterController)
{
	if (forkMasterController)
	{
		LOG(3, "master -> FORKED!");
		controlThread = std::thread(&Control::masterController, this, false);
		return *this;
	}
	else
	{
		nameThisThread("masterControl");

		// startup sync
		monitor.notify_all();
		monitor.wait(masterLock, [&](){ return runningThreads.load() == 0; });
		runningThreads.store(0);
	}

	std::chrono::time_point<std::chrono::high_resolution_clock> timer =
		std::chrono::high_resolution_clock::now();

	while (flag!=SystemFlag::QUIT)
	{
		// round sync
		monitor.wait(masterLock, [&](){ return runningThreads.load() == 0; });

		info.lastRoundDuration =
			std::chrono::duration_cast<std::chrono::milliseconds>
			(std::chrono::high_resolution_clock::now() - timer);
		timer = std::chrono::high_resolution_clock::now();

		switch (flag)
		{
			case SystemFlag::QUIT:
				break;
			case SystemFlag::PAUSE:
				if (!controlThread.joinable()) return *this;
				LOG(3, "PAUSE...");
				monitor.wait(masterLock, [&](){ return flag != SystemFlag::PAUSE; });
				timer = std::chrono::high_resolution_clock::now();
				break;
			case SystemFlag::RUN:
			case SystemFlag::STEP:
				LOG(3, "ROUND: " << round);
				info.round = round++;

				for (auto& module: modules)
				{
					module.second->flag = ControlFlag::RUN;
					if (module.second->asThread) runningThreads.fetch_add(1);
				}
				for (auto& pool: pools) pool.second->flag = ControlFlag::RUN;
				runningThreads.fetch_add(pools.size());

				monitor.notify_all();
				for (auto& it: masterModules) moduleController(*(it.get()));

				if (flag==SystemFlag::STEP) flag = SystemFlag::PAUSE;
				break;
			case SystemFlag::STEP_BACK:
				break;
		}
		LOG(3, "WAIT FOR THREADS AND POOLS!");

		if (!controlThread.joinable() && flag!=SystemFlag::RUN) return *this;
	}

	for (auto& pool: pools) pool.second->flag = ControlFlag::QUIT;

	masterLock.unlock();

	return *this;
}

BVS::Control& BVS::Control::sendCommand(const SystemFlag controlFlag)
{
	LOG(3, "FLAG: " << (int)controlFlag);
	flag = controlFlag;

	if (controlThread.joinable()) monitor.notify_all();
	else masterController(false);

	if (controlFlag==SystemFlag::QUIT)
	{
		if (controlThread.joinable())
		{
			LOG(3, "JOIN MASTER CONTROLLER!");
			controlThread.join();
		}
	}

	return *this;
}

BVS::Control& BVS::Control::startModule(std::string id)
{
	std::shared_ptr<ModuleData> data = modules[id];

	if (!data->poolName.empty())
	{
		LOG(3, id << " -> POOL(" << data->poolName << ")");
		if (pools.find(data->poolName)==pools.end())
		{
			pools[data->poolName] = std::make_shared<PoolData>(data->poolName, ControlFlag::WAIT);
			pools[data->poolName]->modules.push_back(modules[id]);
			pools[data->poolName]->thread = std::thread(&Control::poolController, this, pools[data->poolName]);
			runningThreads.fetch_add(1);
		}
		else
		{
			pools[data->poolName]->modules.push_back(modules[id]);
		}
	}
	else if (data->asThread)
	{
		LOG(3, id << " -> THREAD");
		runningThreads.fetch_add(1);
		data->thread = std::thread(&Control::threadController, this, data);
	}
	else
	{
		LOG(3, id << " -> MASTER");
		masterModules.push_back(modules[id]);
	}

	return *this;
}

BVS::Control& BVS::Control::notifyThreads()
{
	monitor.notify_all();
	return *this;
}

BVS::Control& BVS::Control::purgeData(std::string moduleID)
{
	if (!pools.empty())
	{
		std::string pool = modules[moduleID]->poolName;
		if (!pool.empty())
		{
			ModuleDataVector& poolModules = pools[pool]->modules;
			if (!poolModules.empty())
				poolModules.erase(std::remove_if
						(poolModules.begin(), poolModules.end(),
						 [&](std::shared_ptr<ModuleData> data)
						 { return data->id==moduleID; }));
		}
	}

	if (!masterModules.empty())
		masterModules.erase(std::remove_if
				(masterModules.begin(), masterModules.end(),
				 [&](std::shared_ptr<ModuleData> data)
				 { return data->id==moduleID; }));

	modules[moduleID]->connectors.clear();
	modules.erase(moduleID);

	return *this;
}

BVS::Control& BVS::Control::moduleController(ModuleData& data)
{
	std::chrono::time_point<std::chrono::high_resolution_clock> modTimer =
		std::chrono::high_resolution_clock::now();

	switch (data.flag)
	{
		case ControlFlag::QUIT:
			break;
		case ControlFlag::WAIT:
			break;
		case ControlFlag::RUN:
			data.status = data.module->execute();

			data.flag = ControlFlag::WAIT;
			break;
	}

	info.moduleDurations[data.id] =
		std::chrono::duration_cast<std::chrono::milliseconds>
		(std::chrono::high_resolution_clock::now() - modTimer);

	return *this;
}

BVS::Control& BVS::Control::threadController(std::shared_ptr<ModuleData> data)
{
	nameThisThread(("[M]"+data->id).c_str());
	std::unique_lock<std::mutex> threadLock(mutex);

	while (bool(data->flag))
	{
		runningThreads.fetch_sub(1);
		LOG(3, data->id << " -> WAIT!");
		monitor.notify_all();
		monitor.wait(threadLock, [&](){ return data->flag != ControlFlag::WAIT; });

		moduleController(*(data.get()));
	}

	return *this;
}

BVS::Control& BVS::Control::poolController(std::shared_ptr<PoolData> data)
{
	nameThisThread(("[P]"+data->poolName).c_str());
	LOG(3, "POOL(" << data->poolName << ") STARTED!");
	std::unique_lock<std::mutex> threadLock(mutex);

	while (bool(data->flag) && !data->modules.empty())
	{
		for (auto& module: data->modules) moduleController(*(module.get()));

		data->flag = ControlFlag::WAIT;
		runningThreads.fetch_sub(1);
		LOG(3, "POOL(" << data->poolName << ") WAIT!");
		monitor.notify_all();
		monitor.wait(threadLock, [&](){ return data->flag != ControlFlag::WAIT; });
	}

	pools.erase(pools.find(data->poolName));

	LOG(3, "POOL(" << data->poolName << ") QUITTING!");
	return *this;
}

<commit_msg>control: reposition 'WAIT FOR THREADS...' message<commit_after>#include <algorithm>
#include <chrono>

#include "control.h"
#include "bvs/archutils.h"

BVS::ModuleDataMap BVS::Control::modules;

BVS::Control::Control(Info& info)
	: info(info),
	logger("Control"),
	runningThreads(0),
	masterModules(),
	pools(),
	flag(SystemFlag::PAUSE),
	mutex(),
	masterLock(mutex),
	monitor(),
	controlThread(),
	round(0)
{}

void BVS::Control::registerModule(const std::string& id, Module* module)
{
	modules[id] = std::shared_ptr<ModuleData>(new ModuleData(
				id,
				std::string(),
				std::string(),
				module,
				nullptr,
				false,
				std::string(),
				ControlFlag::WAIT,
				Status::NONE,
				ConnectorMap()));
}

BVS::Control& BVS::Control::masterController(const bool forkMasterController)
{
	if (forkMasterController)
	{
		LOG(3, "master -> FORKED!");
		controlThread = std::thread(&Control::masterController, this, false);
		return *this;
	}
	else
	{
		nameThisThread("masterControl");

		// startup sync
		monitor.notify_all();
		monitor.wait(masterLock, [&](){ return runningThreads.load() == 0; });
		runningThreads.store(0);
	}

	std::chrono::time_point<std::chrono::high_resolution_clock> timer =
		std::chrono::high_resolution_clock::now();

	while (flag!=SystemFlag::QUIT)
	{
		// round sync
		monitor.wait(masterLock, [&](){ return runningThreads.load() == 0; });

		info.lastRoundDuration =
			std::chrono::duration_cast<std::chrono::milliseconds>
			(std::chrono::high_resolution_clock::now() - timer);
		timer = std::chrono::high_resolution_clock::now();

		switch (flag)
		{
			case SystemFlag::QUIT:
				break;
			case SystemFlag::PAUSE:
				if (!controlThread.joinable()) return *this;
				LOG(3, "PAUSE...");
				monitor.wait(masterLock, [&](){ return flag != SystemFlag::PAUSE; });
				timer = std::chrono::high_resolution_clock::now();
				break;
			case SystemFlag::RUN:
			case SystemFlag::STEP:
				LOG(3, "ROUND: " << round);
				info.round = round++;

				for (auto& module: modules)
				{
					module.second->flag = ControlFlag::RUN;
					if (module.second->asThread) runningThreads.fetch_add(1);
				}
				for (auto& pool: pools) pool.second->flag = ControlFlag::RUN;
				runningThreads.fetch_add(pools.size());

				monitor.notify_all();
				for (auto& it: masterModules) moduleController(*(it.get()));

				if (flag==SystemFlag::STEP) flag = SystemFlag::PAUSE;
				LOG(3, "WAIT FOR THREADS AND POOLS!");
				break;
			case SystemFlag::STEP_BACK:
				break;
		}

		if (!controlThread.joinable() && flag!=SystemFlag::RUN) return *this;
	}

	for (auto& pool: pools) pool.second->flag = ControlFlag::QUIT;

	masterLock.unlock();

	return *this;
}

BVS::Control& BVS::Control::sendCommand(const SystemFlag controlFlag)
{
	LOG(3, "FLAG: " << (int)controlFlag);
	flag = controlFlag;

	if (controlThread.joinable()) monitor.notify_all();
	else masterController(false);

	if (controlFlag==SystemFlag::QUIT)
	{
		if (controlThread.joinable())
		{
			LOG(3, "JOIN MASTER CONTROLLER!");
			controlThread.join();
		}
	}

	return *this;
}

BVS::Control& BVS::Control::startModule(std::string id)
{
	std::shared_ptr<ModuleData> data = modules[id];

	if (!data->poolName.empty())
	{
		LOG(3, id << " -> POOL(" << data->poolName << ")");
		if (pools.find(data->poolName)==pools.end())
		{
			pools[data->poolName] = std::make_shared<PoolData>(data->poolName, ControlFlag::WAIT);
			pools[data->poolName]->modules.push_back(modules[id]);
			pools[data->poolName]->thread = std::thread(&Control::poolController, this, pools[data->poolName]);
			runningThreads.fetch_add(1);
		}
		else
		{
			pools[data->poolName]->modules.push_back(modules[id]);
		}
	}
	else if (data->asThread)
	{
		LOG(3, id << " -> THREAD");
		runningThreads.fetch_add(1);
		data->thread = std::thread(&Control::threadController, this, data);
	}
	else
	{
		LOG(3, id << " -> MASTER");
		masterModules.push_back(modules[id]);
	}

	return *this;
}

BVS::Control& BVS::Control::notifyThreads()
{
	monitor.notify_all();
	return *this;
}

BVS::Control& BVS::Control::purgeData(std::string moduleID)
{
	if (!pools.empty())
	{
		std::string pool = modules[moduleID]->poolName;
		if (!pool.empty())
		{
			ModuleDataVector& poolModules = pools[pool]->modules;
			if (!poolModules.empty())
				poolModules.erase(std::remove_if
						(poolModules.begin(), poolModules.end(),
						 [&](std::shared_ptr<ModuleData> data)
						 { return data->id==moduleID; }));
		}
	}

	if (!masterModules.empty())
		masterModules.erase(std::remove_if
				(masterModules.begin(), masterModules.end(),
				 [&](std::shared_ptr<ModuleData> data)
				 { return data->id==moduleID; }));

	modules[moduleID]->connectors.clear();
	modules.erase(moduleID);

	return *this;
}

BVS::Control& BVS::Control::moduleController(ModuleData& data)
{
	std::chrono::time_point<std::chrono::high_resolution_clock> modTimer =
		std::chrono::high_resolution_clock::now();

	switch (data.flag)
	{
		case ControlFlag::QUIT:
			break;
		case ControlFlag::WAIT:
			break;
		case ControlFlag::RUN:
			data.status = data.module->execute();

			data.flag = ControlFlag::WAIT;
			break;
	}

	info.moduleDurations[data.id] =
		std::chrono::duration_cast<std::chrono::milliseconds>
		(std::chrono::high_resolution_clock::now() - modTimer);

	return *this;
}

BVS::Control& BVS::Control::threadController(std::shared_ptr<ModuleData> data)
{
	nameThisThread(("[M]"+data->id).c_str());
	std::unique_lock<std::mutex> threadLock(mutex);

	while (bool(data->flag))
	{
		runningThreads.fetch_sub(1);
		LOG(3, data->id << " -> WAIT!");
		monitor.notify_all();
		monitor.wait(threadLock, [&](){ return data->flag != ControlFlag::WAIT; });

		moduleController(*(data.get()));
	}

	return *this;
}

BVS::Control& BVS::Control::poolController(std::shared_ptr<PoolData> data)
{
	nameThisThread(("[P]"+data->poolName).c_str());
	LOG(3, "POOL(" << data->poolName << ") STARTED!");
	std::unique_lock<std::mutex> threadLock(mutex);

	while (bool(data->flag) && !data->modules.empty())
	{
		for (auto& module: data->modules) moduleController(*(module.get()));

		data->flag = ControlFlag::WAIT;
		runningThreads.fetch_sub(1);
		LOG(3, "POOL(" << data->poolName << ") WAIT!");
		monitor.notify_all();
		monitor.wait(threadLock, [&](){ return data->flag != ControlFlag::WAIT; });
	}

	pools.erase(pools.find(data->poolName));

	LOG(3, "POOL(" << data->poolName << ") QUITTING!");
	return *this;
}

<|endoftext|>


<commit_before>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2014 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
**      disclaimer.
**    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
**      following disclaimer in the documentation and/or other materials provided with the distribution.
**    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***********************************************************************************************************************/

#include "views/MainView.h"
#include "Scene.h"
#include "Logger/src/Timer.h"
#include "cursor/Cursor.h"
#include "items/Item.h"

#include <items/RootItem.h>

namespace Visualization {

MainView::MainView(Scene *scene) :
	View(scene, nullptr), miniMap(new MiniMap(scene, this)), scaleLevel(SCALING_FACTOR)
{
	setRenderHint(QPainter::Antialiasing);
	setRenderHint(QPainter::TextAntialiasing);

	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);

	setMiniMapSize(MINIMAP_DEFAULT_WIDTH, MINIMAP_DEFAULT_HEIGHT);
}

MainView::~MainView()
{
	SAFE_DELETE(miniMap);
}

void MainView::setMiniMapSize(int width, int height)
{
	if ( miniMap )
	{
		miniMap->resize(width, height);
		miniMap->sceneRectChanged(scene()->sceneRect());
	}
}

bool MainView::event( QEvent *event )
{
	bool result = false;

	switch (event->type())
	{
		case QEvent::KeyPress :
		{
			QKeyEvent *k = (QKeyEvent *)event;
			keyPressEvent(k);
			if (k->key() == Qt::Key_Backtab || k->key() == Qt::Key_Tab ) event->accept();
			result = true;
		}
		break;
		case QEvent::KeyRelease :
		{
			QKeyEvent *k = (QKeyEvent *)event;
			keyReleaseEvent(k);
			if (k->key() == Qt::Key_Backtab || k->key() == Qt::Key_Tab ) event->accept();
			result = true;
		}
		break;
		default: result = View::event( event );
		break;
	}

	// The checks below are necessary to avoid infinite recursion.
	if ( event->type() != QEvent::ChildAdded
			&& event->type() != QEvent::ChildRemoved
			&& event->type() != QEvent::ChildPolished) updateInfoLabels();
	return result;
}

void MainView::resizeEvent(QResizeEvent *event)
{
	View::resizeEvent(event);
	if ( miniMap )
	{
		miniMap->updatePosition();
		miniMap->visibleRectChanged();
	}
}

void MainView::wheelEvent(QWheelEvent *event)
{
	// Zoom
	if (event->modifiers() == Qt::ControlModifier)
	{
		if ( event->delta() > 0 ) scaleLevel--;
		else scaleLevel++;

		if ( scaleLevel <= 0 ) scaleLevel = 1;
		qreal factor = scaleFactor();
		setTransform(QTransform::fromScale(factor, factor));

		if ( miniMap ) miniMap->visibleRectChanged();

		scene()->setMainViewScalingFactor(factor);
	}
	// Scroll
	else if (event->modifiers() == Qt::NoModifier || event->modifiers() == Qt::ShiftModifier)
	{
		auto bar = event->orientation() == Qt::Vertical && event->modifiers() == Qt::NoModifier
				? verticalScrollBar() : horizontalScrollBar();

		bar->setValue( bar->value() - event->delta() );
	}
}

qreal MainView::scaleFactor() const
{
	if (scaleLevel < SCALING_FACTOR)
		return SCALING_FACTOR / (qreal) scaleLevel;
	else return std::pow(2, SCALING_FACTOR - scaleLevel);
}

void MainView::scrollContentsBy(int dx, int dy)
{
	View::scrollContentsBy(dx, dy);
	if ( miniMap ) miniMap->visibleRectChanged();
}

void MainView::keyPressEvent(QKeyEvent *event)
{
	if ( (event->modifiers() & Qt::ControlModifier || event->modifiers() & Qt::ShiftModifier)
			&& event->key() == Qt::Key_Print)
	{
		event->accept();
		scene()->setHiddenItemCategories(0);

		QPrinter printer;
		printer.setOutputFormat(QPrinter::PdfFormat);
		printer.setFullPage(true);
		printer.setResolution(300);

		QSvgGenerator svggen;
		svggen.setResolution(90);

		bool ownerIgnoresScale = setCursorAndOwnerIgnoreScaleForScreenShot(false, false);

		if (event->modifiers() & Qt::ShiftModifier)
		{
			// Print scene
			printer.setOutputFileName("screenshot-scene.pdf");
			printer.setPaperSize(scene()->sceneRect().size().toSize(), QPrinter::Point);
			QPainter painter(&printer);
			painter.setRenderHint(QPainter::Antialiasing);
			scene()->render( &painter );

			svggen.setFileName("screenshot-scene.svg");
			svggen.setSize(scene()->sceneRect().size().toSize());
			QPainter svgPainter(&svggen);
			svgPainter.setRenderHint(QPainter::Antialiasing);
			scene()->render(&svgPainter);

			QImage image(PNG_SCREENSHOT_SCALE * scene()->sceneRect().size().toSize(), QImage::Format_ARGB32);
			image.fill(Qt::transparent);
			QPainter pmapPainter(&image);
			pmapPainter.setRenderHint(QPainter::Antialiasing);
			//pmapPainter.scale(2,2);
			scene()->render(&pmapPainter);
			image.save("screenshot-scene.png");
		}
		else
		{
			// Print view
			printer.setOutputFileName("screenshot-view.pdf");
			printer.setPaperSize(viewport()->rect().size(), QPrinter::Point);
			QPainter painter(&printer);
			painter.setRenderHint(QPainter::Antialiasing);
			render(&painter);

			svggen.setFileName("screenshot-view.svg");
			svggen.setSize(viewport()->rect().size());
			QPainter svgPainter(&svggen);
			svgPainter.setRenderHint(QPainter::Antialiasing);
			render(&svgPainter);

			QImage image(PNG_SCREENSHOT_SCALE * viewport()->rect().size(), QImage::Format_ARGB32);
			image.fill(Qt::transparent);
			QPainter pmapPainter(&image);
			pmapPainter.setRenderHint(QPainter::Antialiasing);
			render(&pmapPainter);
			image.save("screenshot-view.png");
		}

		setCursorAndOwnerIgnoreScaleForScreenShot(true, ownerIgnoresScale);
	}
	else if (event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_F11)
	{
		event->accept();
		QWidget* window = this;
		while (window->parentWidget()) window = window->parentWidget();

		if (window->isFullScreen()) window->showNormal();
		else window->showFullScreen();
	}
	else if (event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_F10)
	{
		event->accept();
		if (miniMap->width() == MINIMAP_DEFAULT_WIDTH && miniMap->height() ==  MINIMAP_DEFAULT_HEIGHT)
			setMiniMapSize(width(), height());
		else setMiniMapSize(MINIMAP_DEFAULT_WIDTH, MINIMAP_DEFAULT_HEIGHT);
	}
	else if (event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_F9)
	{
		event->accept();
		if (miniMap->isVisible()) miniMap->hide();
		else miniMap->show();
	}
	else if (event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_F7)
	{
		event->accept();
		showTimers_ = ~showTimers_;
		updateInfoLabels();
	}
	else View::keyPressEvent(event);
}

void MainView::updateInfoLabels()
{
	if (!showTimers_)
	{
		if (!infoLabels_.isEmpty())
		{
			for (auto l : infoLabels_) SAFE_DELETE(l);
			infoLabels_.clear();
		}
		return;
	}

	int labelIndex = 0;
	int yPos = 10;

	// Add timers
	for (auto name : Logger::Timer::timerNames())
	{
		QLabel* label{};

		if (infoLabels_.size() <= labelIndex)
		{
			label = new QLabel(this);
			label->setAttribute(Qt::WA_TransparentForMouseEvents);
			infoLabels_.append(label);
			label->show();
			label->setStyleSheet("QLabel { color : red; }");
		}
		else label = infoLabels_.at(labelIndex);

		// Set the position of the label
		label->move(10, yPos);

		// Construct label text
		auto info = name + ":  ";
		for (auto val : Logger::Timer::timer(name)->values())
			info += QString::number(val) + "  ";

		label->setText(info);
		label->adjustSize();

		++labelIndex;
		yPos+=label->height();
	}

	// Remove unnecessary labels
	while (labelIndex < infoLabels_.size() )
	{
		SAFE_DELETE(infoLabels_.last());
		infoLabels_.removeLast();
	}
}

void MainView::paintEvent(QPaintEvent* event)
{
	auto t = Logger::Timer::start("Main view paint");
	View::paintEvent(event);
	t->tick();
}

void MainView::mouseMoveEvent(QMouseEvent *event)
{
	if (!isPanning_) return View::mouseMoveEvent(event);

	event->accept();
	horizontalScrollBar()->setValue(horizontalScrollBar()->value() - (event->x() - panStartPos_.x()));
	verticalScrollBar()->setValue(verticalScrollBar()->value() - (event->y() - panStartPos_.y()));
	panStartPos_ = event->pos();
}

void MainView::mousePressEvent(QMouseEvent *event)
{
	if (event->modifiers() != Qt::ControlModifier) return View::mousePressEvent(event);

	event->accept();
	isPanning_ = true;
	panStartPos_ = event->pos();
	setCursor(Qt::ClosedHandCursor);
}

void MainView::mouseReleaseEvent(QMouseEvent *event)
{
	if (!isPanning_) return View::mouseReleaseEvent(event);

	event->accept();
	setCursor(Qt::ArrowCursor);
	isPanning_ = false;
}

bool MainView::setCursorAndOwnerIgnoreScaleForScreenShot(bool ignore, bool modifyOwner)
{
	bool ownerIgnoresScale = false;

	auto cursor = scene()->mainCursor();
	if (cursor)
	{
		if ( auto vis = cursor->visualization() ) vis->setFlag(Item::ItemIgnoresTransformations, ignore);
		auto owner = cursor->owner();
		if (owner)
		{
			while (owner->parent()) owner = owner->parent();
			if (!ignore)
			{
				ownerIgnoresScale = owner->flags() & Item::ItemIgnoresTransformations;
				if (ownerIgnoresScale) owner->setFlag(Item::ItemIgnoresTransformations, false);
			}
			else if (modifyOwner) owner->setFlag(Item::ItemIgnoresTransformations, true);
		}
	}

	return ownerIgnoresScale;
}

}
<commit_msg>Fix propagation of mouse wheel event<commit_after>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2014 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
**      disclaimer.
**    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
**      following disclaimer in the documentation and/or other materials provided with the distribution.
**    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***********************************************************************************************************************/

#include "views/MainView.h"
#include "Scene.h"
#include "Logger/src/Timer.h"
#include "cursor/Cursor.h"
#include "items/Item.h"

#include <items/RootItem.h>

namespace Visualization {

MainView::MainView(Scene *scene) :
	View(scene, nullptr), miniMap(new MiniMap(scene, this)), scaleLevel(SCALING_FACTOR)
{
	setRenderHint(QPainter::Antialiasing);
	setRenderHint(QPainter::TextAntialiasing);

	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);

	setMiniMapSize(MINIMAP_DEFAULT_WIDTH, MINIMAP_DEFAULT_HEIGHT);
}

MainView::~MainView()
{
	SAFE_DELETE(miniMap);
}

void MainView::setMiniMapSize(int width, int height)
{
	if ( miniMap )
	{
		miniMap->resize(width, height);
		miniMap->sceneRectChanged(scene()->sceneRect());
	}
}

bool MainView::event( QEvent *event )
{
	bool result = false;

	switch (event->type())
	{
		case QEvent::KeyPress :
		{
			QKeyEvent *k = (QKeyEvent *)event;
			keyPressEvent(k);
			if (k->key() == Qt::Key_Backtab || k->key() == Qt::Key_Tab ) event->accept();
			result = true;
		}
		break;
		case QEvent::KeyRelease :
		{
			QKeyEvent *k = (QKeyEvent *)event;
			keyReleaseEvent(k);
			if (k->key() == Qt::Key_Backtab || k->key() == Qt::Key_Tab ) event->accept();
			result = true;
		}
		break;
		default: result = View::event( event );
		break;
	}

	// The checks below are necessary to avoid infinite recursion.
	if ( event->type() != QEvent::ChildAdded
			&& event->type() != QEvent::ChildRemoved
			&& event->type() != QEvent::ChildPolished) updateInfoLabels();
	return result;
}

void MainView::resizeEvent(QResizeEvent *event)
{
	View::resizeEvent(event);
	if ( miniMap )
	{
		miniMap->updatePosition();
		miniMap->visibleRectChanged();
	}
}

void MainView::wheelEvent(QWheelEvent *event)
{
	// Zoom
	if (event->modifiers() == Qt::ControlModifier)
	{
		if ( event->delta() > 0 ) scaleLevel--;
		else scaleLevel++;

		if ( scaleLevel <= 0 ) scaleLevel = 1;
		qreal factor = scaleFactor();
		setTransform(QTransform::fromScale(factor, factor));

		if ( miniMap ) miniMap->visibleRectChanged();

		scene()->setMainViewScalingFactor(factor);
	}
	// Scroll
	else if (event->modifiers() == Qt::NoModifier || event->modifiers() == Qt::ShiftModifier)
	{
		auto bar = event->orientation() == Qt::Vertical && event->modifiers() == Qt::NoModifier
				? verticalScrollBar() : horizontalScrollBar();

		bar->setValue( bar->value() - event->delta() );
	}

	View::wheelEvent(event);
}

qreal MainView::scaleFactor() const
{
	if (scaleLevel < SCALING_FACTOR)
		return SCALING_FACTOR / (qreal) scaleLevel;
	else return std::pow(2, SCALING_FACTOR - scaleLevel);
}

void MainView::scrollContentsBy(int dx, int dy)
{
	View::scrollContentsBy(dx, dy);
	if ( miniMap ) miniMap->visibleRectChanged();
}

void MainView::keyPressEvent(QKeyEvent *event)
{
	if ( (event->modifiers() & Qt::ControlModifier || event->modifiers() & Qt::ShiftModifier)
			&& event->key() == Qt::Key_Print)
	{
		event->accept();
		scene()->setHiddenItemCategories(0);

		QPrinter printer;
		printer.setOutputFormat(QPrinter::PdfFormat);
		printer.setFullPage(true);
		printer.setResolution(300);

		QSvgGenerator svggen;
		svggen.setResolution(90);

		bool ownerIgnoresScale = setCursorAndOwnerIgnoreScaleForScreenShot(false, false);

		if (event->modifiers() & Qt::ShiftModifier)
		{
			// Print scene
			printer.setOutputFileName("screenshot-scene.pdf");
			printer.setPaperSize(scene()->sceneRect().size().toSize(), QPrinter::Point);
			QPainter painter(&printer);
			painter.setRenderHint(QPainter::Antialiasing);
			scene()->render( &painter );

			svggen.setFileName("screenshot-scene.svg");
			svggen.setSize(scene()->sceneRect().size().toSize());
			QPainter svgPainter(&svggen);
			svgPainter.setRenderHint(QPainter::Antialiasing);
			scene()->render(&svgPainter);

			QImage image(PNG_SCREENSHOT_SCALE * scene()->sceneRect().size().toSize(), QImage::Format_ARGB32);
			image.fill(Qt::transparent);
			QPainter pmapPainter(&image);
			pmapPainter.setRenderHint(QPainter::Antialiasing);
			//pmapPainter.scale(2,2);
			scene()->render(&pmapPainter);
			image.save("screenshot-scene.png");
		}
		else
		{
			// Print view
			printer.setOutputFileName("screenshot-view.pdf");
			printer.setPaperSize(viewport()->rect().size(), QPrinter::Point);
			QPainter painter(&printer);
			painter.setRenderHint(QPainter::Antialiasing);
			render(&painter);

			svggen.setFileName("screenshot-view.svg");
			svggen.setSize(viewport()->rect().size());
			QPainter svgPainter(&svggen);
			svgPainter.setRenderHint(QPainter::Antialiasing);
			render(&svgPainter);

			QImage image(PNG_SCREENSHOT_SCALE * viewport()->rect().size(), QImage::Format_ARGB32);
			image.fill(Qt::transparent);
			QPainter pmapPainter(&image);
			pmapPainter.setRenderHint(QPainter::Antialiasing);
			render(&pmapPainter);
			image.save("screenshot-view.png");
		}

		setCursorAndOwnerIgnoreScaleForScreenShot(true, ownerIgnoresScale);
	}
	else if (event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_F11)
	{
		event->accept();
		QWidget* window = this;
		while (window->parentWidget()) window = window->parentWidget();

		if (window->isFullScreen()) window->showNormal();
		else window->showFullScreen();
	}
	else if (event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_F10)
	{
		event->accept();
		if (miniMap->width() == MINIMAP_DEFAULT_WIDTH && miniMap->height() ==  MINIMAP_DEFAULT_HEIGHT)
			setMiniMapSize(width(), height());
		else setMiniMapSize(MINIMAP_DEFAULT_WIDTH, MINIMAP_DEFAULT_HEIGHT);
	}
	else if (event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_F9)
	{
		event->accept();
		if (miniMap->isVisible()) miniMap->hide();
		else miniMap->show();
	}
	else if (event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_F7)
	{
		event->accept();
		showTimers_ = ~showTimers_;
		updateInfoLabels();
	}
	else View::keyPressEvent(event);
}

void MainView::updateInfoLabels()
{
	if (!showTimers_)
	{
		if (!infoLabels_.isEmpty())
		{
			for (auto l : infoLabels_) SAFE_DELETE(l);
			infoLabels_.clear();
		}
		return;
	}

	int labelIndex = 0;
	int yPos = 10;

	// Add timers
	for (auto name : Logger::Timer::timerNames())
	{
		QLabel* label{};

		if (infoLabels_.size() <= labelIndex)
		{
			label = new QLabel(this);
			label->setAttribute(Qt::WA_TransparentForMouseEvents);
			infoLabels_.append(label);
			label->show();
			label->setStyleSheet("QLabel { color : red; }");
		}
		else label = infoLabels_.at(labelIndex);

		// Set the position of the label
		label->move(10, yPos);

		// Construct label text
		auto info = name + ":  ";
		for (auto val : Logger::Timer::timer(name)->values())
			info += QString::number(val) + "  ";

		label->setText(info);
		label->adjustSize();

		++labelIndex;
		yPos+=label->height();
	}

	// Remove unnecessary labels
	while (labelIndex < infoLabels_.size() )
	{
		SAFE_DELETE(infoLabels_.last());
		infoLabels_.removeLast();
	}
}

void MainView::paintEvent(QPaintEvent* event)
{
	auto t = Logger::Timer::start("Main view paint");
	View::paintEvent(event);
	t->tick();
}

void MainView::mouseMoveEvent(QMouseEvent *event)
{
	if (!isPanning_) return View::mouseMoveEvent(event);

	event->accept();
	horizontalScrollBar()->setValue(horizontalScrollBar()->value() - (event->x() - panStartPos_.x()));
	verticalScrollBar()->setValue(verticalScrollBar()->value() - (event->y() - panStartPos_.y()));
	panStartPos_ = event->pos();
}

void MainView::mousePressEvent(QMouseEvent *event)
{
	if (event->modifiers() != Qt::ControlModifier) return View::mousePressEvent(event);

	event->accept();
	isPanning_ = true;
	panStartPos_ = event->pos();
	setCursor(Qt::ClosedHandCursor);
}

void MainView::mouseReleaseEvent(QMouseEvent *event)
{
	if (!isPanning_) return View::mouseReleaseEvent(event);

	event->accept();
	setCursor(Qt::ArrowCursor);
	isPanning_ = false;
}

bool MainView::setCursorAndOwnerIgnoreScaleForScreenShot(bool ignore, bool modifyOwner)
{
	bool ownerIgnoresScale = false;

	auto cursor = scene()->mainCursor();
	if (cursor)
	{
		if ( auto vis = cursor->visualization() ) vis->setFlag(Item::ItemIgnoresTransformations, ignore);
		auto owner = cursor->owner();
		if (owner)
		{
			while (owner->parent()) owner = owner->parent();
			if (!ignore)
			{
				ownerIgnoresScale = owner->flags() & Item::ItemIgnoresTransformations;
				if (ownerIgnoresScale) owner->setFlag(Item::ItemIgnoresTransformations, false);
			}
			else if (modifyOwner) owner->setFlag(Item::ItemIgnoresTransformations, true);
		}
	}

	return ownerIgnoresScale;
}

}
<|endoftext|>


<commit_before>#include <gtest/gtest.h>

#include <memory>

#include <noise/module/codecs/NoiseSourcesCodec.h>
#include <noise/EncodedModuleGroup.h>

class TestEncodedModuleGroup : public ::testing::Test {
protected:
};

TEST_F(TestEncodedModuleGroup, TestFullGroupDecode)
{
    std::string s =
R"({
    "perlin_example":{
        "type":"Perlin",
        "Frequency" : 1.7,
        "Lacunarity" : 0.999,
        "NoiseQuality" : "Fast",
        "OctaveCount" : 8,
        "Persistence" : 0.001,
        "Seed" : 101
    },
    "voronoi_example":{
        "type":"Voronoi",
        "Frequency" : 2.7,
        "Displacement" : 3.1,
        "DistanceEnabled" : true,
        "Seed" : 47
    },
    "add_example":{
        "type":"Add",
        "SourceModule":[
            "voronoi_example",
            "perlin_example"
        ]
    }
})";
    noise::EncodedModuleGroup emg;
    emg.encodedModules = spotify::json::decode<noise::EncodedModuleGroup::EncodedModuleMap>(s);
    auto decoded_modules = emg.decode();

    const noise::module::ModulePtr perlin_p = decoded_modules.at("perlin_example");
    const noise::module::Perlin& perlin_cast = static_cast<noise::module::Perlin&>(perlin_p->getModule());
    EXPECT_EQ(1.7, perlin_cast.GetFrequency());
    EXPECT_EQ(0.999, perlin_cast.GetLacunarity());
    EXPECT_EQ(noise::QUALITY_FAST, perlin_cast.GetNoiseQuality());
    EXPECT_EQ(8, perlin_cast.GetOctaveCount());
    EXPECT_EQ(0.001, perlin_cast.GetPersistence());
    EXPECT_EQ(101, perlin_cast.GetSeed());

    const noise::module::ModulePtr voronoi_p = decoded_modules.at("voronoi_example");
    const noise::module::Voronoi& voronoi_cast = static_cast<noise::module::Voronoi&>(voronoi_p->getModule());
    EXPECT_EQ(2.7, voronoi_cast.GetFrequency());
    EXPECT_EQ(3.1, voronoi_cast.GetDisplacement());
    EXPECT_TRUE(voronoi_cast.IsDistanceEnabled());
    EXPECT_EQ(47, voronoi_cast.GetSeed());

    const noise::module::ModulePtr add_p = decoded_modules.at("add_example");

    const noise::module::Module* add_source_0 = &(add_p->getModule().GetSourceModule(0));
    const noise::module::Module* add_source_1 = &(add_p->getModule().GetSourceModule(1));
    const noise::module::Module* perlin_module = &(perlin_p->getModule());
    const noise::module::Module* voronoi_module = &(voronoi_p->getModule());

    EXPECT_TRUE(add_source_0 == perlin_module ||
                add_source_0 == voronoi_module);
    EXPECT_TRUE(add_source_1 == perlin_module ||
                add_source_1 == voronoi_module);

    double pv = perlin_p->getValue(1.1, 2.2, 3.3);
    double vv = voronoi_p->getValue(1.1, 2.2, 3.3);
    double av = add_p->getValue(1.1, 2.2, 3.3);
    EXPECT_EQ(pv + vv, av);
}

TEST_F(TestEncodedModuleGroup, TestNoiseSourcesDecodeEmpty)
{
    noise::module::NoiseSources ns;
    ns = spotify::json::decode<noise::module::NoiseSources>(R"({})");
    EXPECT_FALSE(ns.sourceModules);
    EXPECT_FALSE(ns.controlModule);
    EXPECT_FALSE(ns.displaceModules);
}

TEST_F(TestEncodedModuleGroup, TestNoiseSourcesDecodeFull)
{
    noise::module::NoiseSources ns;
    ns = spotify::json::decode<noise::module::NoiseSources>(
        R"({
            "SourceModule":["a", "b", "c"],
            "ControlModule":"z",
            "DisplaceModule":["s", "t", "u"]
        })");
    EXPECT_TRUE(ns.sourceModules);
    EXPECT_TRUE(ns.controlModule);
    EXPECT_TRUE(ns.displaceModules);
    EXPECT_EQ("c", ns.sourceModules.get()[2]);
    EXPECT_EQ("z", ns.controlModule.get());
    EXPECT_EQ("t", ns.displaceModules.get()[1]);
}

TEST_F(TestEncodedModuleGroup, TestNoiseSourcesEncodeEmpty)
{
    noise::module::NoiseSources ns;
    std::string s = spotify::json::encode<noise::module::NoiseSources>(ns);
    EXPECT_EQ(R"({})", s);
}

TEST_F(TestEncodedModuleGroup, TestNoiseSourcesEncodeFull)
{
    noise::module::NoiseSources ns;
    ns.sourceModules.emplace(std::initializer_list<std::string>({"a", "b", "c"}));
    ns.controlModule.emplace("z");
    ns.displaceModules.emplace(std::initializer_list<std::string>({"s", "t", "u"}));
    std::string s = spotify::json::encode<noise::module::NoiseSources>(ns);
    EXPECT_EQ(R"({"SourceModule":["a","b","c"],"ControlModule":"z","DisplaceModule":["s","t","u"]})", s);
}
<commit_msg>Correct DisplaceModules name in EMG test<commit_after>#include <gtest/gtest.h>

#include <memory>

#include <noise/module/codecs/NoiseSourcesCodec.h>
#include <noise/EncodedModuleGroup.h>

class TestEncodedModuleGroup : public ::testing::Test
{
protected:
    void remove_whitespace(std::string& s)
    {
        s.erase(std::remove_if(s.begin(),
                               s.end(),
                               [](char c) { return std::isspace(c, std::locale()); }),
                s.end());
    }
};

TEST_F(TestEncodedModuleGroup, TestFullGroupDecode)
{
    std::string s =
R"({
    "perlin_example":{
        "type":"Perlin",
        "Frequency" : 1.7,
        "Lacunarity" : 0.999,
        "NoiseQuality" : "Fast",
        "OctaveCount" : 8,
        "Persistence" : 0.001,
        "Seed" : 101
    },
    "voronoi_example":{
        "type":"Voronoi",
        "Frequency" : 2.7,
        "Displacement" : 3.1,
        "DistanceEnabled" : true,
        "Seed" : 47
    },
    "add_example":{
        "type":"Add",
        "SourceModule":[
            "voronoi_example",
            "perlin_example"
        ]
    }
})";
    noise::EncodedModuleGroup emg;
    emg.encodedModules = spotify::json::decode<noise::EncodedModuleGroup::EncodedModuleMap>(s);
    auto decoded_modules = emg.decode();

    const noise::module::ModulePtr perlin_p = decoded_modules.at("perlin_example");
    const noise::module::Perlin& perlin_cast = static_cast<noise::module::Perlin&>(perlin_p->getModule());
    EXPECT_EQ(1.7, perlin_cast.GetFrequency());
    EXPECT_EQ(0.999, perlin_cast.GetLacunarity());
    EXPECT_EQ(noise::QUALITY_FAST, perlin_cast.GetNoiseQuality());
    EXPECT_EQ(8, perlin_cast.GetOctaveCount());
    EXPECT_EQ(0.001, perlin_cast.GetPersistence());
    EXPECT_EQ(101, perlin_cast.GetSeed());

    const noise::module::ModulePtr voronoi_p = decoded_modules.at("voronoi_example");
    const noise::module::Voronoi& voronoi_cast = static_cast<noise::module::Voronoi&>(voronoi_p->getModule());
    EXPECT_EQ(2.7, voronoi_cast.GetFrequency());
    EXPECT_EQ(3.1, voronoi_cast.GetDisplacement());
    EXPECT_TRUE(voronoi_cast.IsDistanceEnabled());
    EXPECT_EQ(47, voronoi_cast.GetSeed());

    const noise::module::ModulePtr add_p = decoded_modules.at("add_example");

    const noise::module::Module* add_source_0 = &(add_p->getModule().GetSourceModule(0));
    const noise::module::Module* add_source_1 = &(add_p->getModule().GetSourceModule(1));
    const noise::module::Module* perlin_module = &(perlin_p->getModule());
    const noise::module::Module* voronoi_module = &(voronoi_p->getModule());

    EXPECT_TRUE(add_source_0 == perlin_module ||
                add_source_0 == voronoi_module);
    EXPECT_TRUE(add_source_1 == perlin_module ||
                add_source_1 == voronoi_module);

    double pv = perlin_p->getValue(1.1, 2.2, 3.3);
    double vv = voronoi_p->getValue(1.1, 2.2, 3.3);
    double av = add_p->getValue(1.1, 2.2, 3.3);
    EXPECT_EQ(pv + vv, av);
}

TEST_F(TestEncodedModuleGroup, TestNoiseSourcesDecodeEmpty)
{
    noise::module::NoiseSources ns;
    ns = spotify::json::decode<noise::module::NoiseSources>(R"({})");
    EXPECT_FALSE(ns.sourceModules);
    EXPECT_FALSE(ns.controlModule);
    EXPECT_FALSE(ns.displaceModules);
}

TEST_F(TestEncodedModuleGroup, TestNoiseSourcesDecodeFull)
{
    noise::module::NoiseSources ns;
    ns = spotify::json::decode<noise::module::NoiseSources>(
        R"({
            "SourceModule":["a", "b", "c"],
            "ControlModule":"z",
            "DisplaceModules":["s", "t", "u"]
        })");
    EXPECT_TRUE(ns.sourceModules);
    EXPECT_TRUE(ns.controlModule);
    EXPECT_TRUE(ns.displaceModules);
    if (ns.sourceModules)
        EXPECT_EQ("c", ns.sourceModules.get()[2]);
    if (ns.controlModule)
        EXPECT_EQ("z", ns.controlModule.get());
    if (ns.displaceModules)
        EXPECT_EQ("t", ns.displaceModules.get()[1]);
}

TEST_F(TestEncodedModuleGroup, TestNoiseSourcesEncodeEmpty)
{
    noise::module::NoiseSources ns;
    std::string s = spotify::json::encode<noise::module::NoiseSources>(ns);
    EXPECT_EQ(R"({})", s);
}

TEST_F(TestEncodedModuleGroup, TestNoiseSourcesEncodeFull)
{
    noise::module::NoiseSources ns;
    ns.sourceModules.emplace(std::initializer_list<std::string>({"a", "b", "c"}));
    ns.controlModule.emplace("z");
    ns.displaceModules.emplace(std::initializer_list<std::string>({"s", "t", "u"}));
    std::string s = spotify::json::encode<noise::module::NoiseSources>(ns);
    std::string expected(R"(
{
    "SourceModule" : ["a","b","c"] ,
    "ControlModule" : "z",
    "DisplaceModules" : ["s","t","u"]
}
)");
    remove_whitespace(expected);
    EXPECT_EQ(expected, s);
}
<|endoftext|>