Skip to content
Snippets Groups Projects
llinitparam.h 76.7 KiB
Newer Older
 * @brief parameter block abstraction for creating complex objects and 
 * parsing construction parameters from xml and LLSD
 *
 * $LicenseInfo:firstyear=2008&license=viewerlgpl$
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 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
 * Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_LLPARAM_H
#define LL_LLPARAM_H

#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/is_enum.hpp>
#include <absl/container/node_hash_map.h>
#include "llpredicate.h"
#include "llsd.h"
namespace LLTypeTags
{
	template <typename INNER_TYPE, int _SORT_ORDER>
	struct TypeTagBase
	{
		typedef void		is_tag_t;
		typedef INNER_TYPE	inner_t;
		static const int	SORT_ORDER=_SORT_ORDER;
	};

	template <int VAL1, int VAL2>
	struct GreaterThan
	{
		static const bool value = VAL1 > VAL2;
	};

	template<typename ITEM, typename REST, bool NEEDS_SWAP = GreaterThan<ITEM::SORT_ORDER, REST::SORT_ORDER>::value >
	struct Swap
	{
Richard Linden's avatar
Richard Linden committed
		typedef typename ITEM::template Cons<REST>::value_t value_t;
	};

	template<typename ITEM, typename REST>
	struct Swap<ITEM, REST, true>
	{
Richard Linden's avatar
Richard Linden committed
		typedef typename REST::template Cons<Swap<ITEM, typename REST::inner_t>::value_t>::value_t value_t;
	};

	template<typename T, typename SORTABLE = void>
	struct IsSortable
	{
		static const bool value = false;
	};

	template<typename T>
	struct IsSortable<T, typename T::is_tag_t>
	{
		static const bool value = true;
	};

	template<typename ITEM, typename REST, bool IS_REST_SORTABLE = IsSortable<REST>::value>
	struct InsertInto
	{
Richard Linden's avatar
Richard Linden committed
		typedef typename ITEM::template Cons<REST>::value_t value_t;
	};

	template<typename ITEM, typename REST>
	struct InsertInto <ITEM, REST, true>
	{
		typedef typename Swap<ITEM, REST>::value_t value_t;
	};

	template<typename T, bool SORTABLE = IsSortable<T>::value>
	struct Sorted
	{
		typedef T value_t;
	};

	template<typename T>
	struct Sorted <T, true>
	{
		typedef typename InsertInto<T, typename Sorted<typename T::inner_t>::value_t>::value_t value_t;
	};
}

Richard Nelson's avatar
Richard Nelson committed
	// used to indicate no matching value to a given name when parsing
	struct Flag{};

	template<typename T> const T& defaultValue() { static T value; return value; }
Richard Linden's avatar
Richard Linden committed
	// wraps comparison operator between any 2 values of the same type
	// specialize to handle cases where equality isn't defined well, or at all
Richard Nelson's avatar
Richard Nelson committed
	template <typename T, bool IS_BOOST_FUNCTION = boost::is_convertible<T, boost::function_base>::value >
    struct ParamCompare 
	{
    	static bool equals(const T &a, const T &b)
		{
			return a == b;
		}
	// boost function types are not comparable
	template<typename T>
Richard Nelson's avatar
Richard Nelson committed
	struct ParamCompare<T, true>
	{
		static bool equals(const T&a, const T &b)
		{
			return false;
		}
	};
		static bool equals(const LLSD &a, const LLSD &b) { return false; }
Richard Nelson's avatar
Richard Nelson committed
	template<>
	struct ParamCompare<Flag, false>
	{
		static bool equals(const Flag& a, const Flag& b) { return false; }
	};

	// helper functions and classes
	typedef ptrdiff_t param_handle_t;
Richard Linden's avatar
Richard Linden committed
	struct IS_A_BLOCK {};
	struct NOT_BLOCK {};

	// these templates allow us to distinguish between template parameters
	// that derive from BaseBlock and those that don't
	template<typename T, typename BLOCK_IDENTIFIER = void>
	struct IsBlock
	{
		typedef NOT_BLOCK value_t;
	};

	template<typename T>
	struct IsBlock<T, typename T::baseblock_base_class_t>
	{
		typedef IS_A_BLOCK value_t;
	};

Richard Linden's avatar
Richard Linden committed
	// ParamValue class directly manages the wrapped value
	// by holding on to a copy (scalar params)
	// or deriving from it (blocks)
	// has specializations for custom value behavior
	// and "tag" values like Lazy and Atomic
Richard Linden's avatar
Richard Linden committed
	template<typename T, typename VALUE_IS_BLOCK = typename IsBlock<T>::value_t>
	class ParamValue
	{
		typedef ParamValue<T, VALUE_IS_BLOCK>	self_t;

	public:
		typedef T	default_value_t;
		typedef T	value_t;

		ParamValue(): mValue() {}
		ParamValue(const default_value_t& other) : mValue(other) {}

		void setValue(const value_t& val)
		{
			mValue = val;
		}

		const value_t& getValue() const
		{
			return mValue;
		}

		T& getValue()
		{
			return mValue;
		}

		bool isValid() const { return true; }

Richard Linden's avatar
Richard Linden committed
	protected:
		T mValue;
	};

	template<typename T>
	class ParamValue<T, IS_A_BLOCK> 
Richard Linden's avatar
Richard Linden committed
	{
		typedef ParamValue<T, IS_A_BLOCK>	self_t;
	public:
		typedef T	default_value_t;
		typedef T	value_t;

		ParamValue() 
		:	T()
Richard Linden's avatar
Richard Linden committed
		{}

		ParamValue(const default_value_t& other)
		:	T(other)
Richard Linden's avatar
Richard Linden committed
		{}

		void setValue(const value_t& val)
		{
			*this = val;
		}

		const value_t& getValue() const
		{
			return *this;
		}

		T& getValue()
		{
			return *this;
		}
	};

	// empty default implementation of key cache
	// leverages empty base class optimization
	:	public ParamValue<typename LLTypeTags::Sorted<T>::value_t>
	private:
		struct Inaccessable{};
		typedef std::map<std::string, T> value_name_map_t;
		typedef Inaccessable name_t;
Richard Linden's avatar
Richard Linden committed
		typedef TypeValues<T> type_value_t;
Richard Linden's avatar
Richard Linden committed
		typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t>	param_value_t;
		typedef typename param_value_t::value_t	value_t;
Richard Linden's avatar
Richard Linden committed

		TypeValues(const typename param_value_t::value_t& val)
		:	param_value_t(val)
Richard Linden's avatar
Richard Linden committed
		{}

		void setValueName(const std::string& key) {}
		std::string getValueName() const { return ""; }
Richard Linden's avatar
Richard Linden committed
		std::string calcValueName(const value_t& value) const { return ""; }
Richard Linden's avatar
Richard Linden committed
		static bool getValueFromName(const std::string& name, value_t& value)
		static std::vector<std::string>* getPossibleValues()
Richard Linden's avatar
Richard Linden committed
		void assignNamedValue(const Inaccessable& name)
		{}

		operator const value_t&() const
		{
			return param_value_t::getValue();
Richard Linden's avatar
Richard Linden committed
		}

		const value_t& operator()() const
		{
			return param_value_t::getValue();
Richard Linden's avatar
Richard Linden committed
		}

		static value_name_map_t* getValueNames() {return NULL;}
Richard Linden's avatar
Richard Linden committed
	// helper class to implement name value lookups
	// and caching of last used name
Richard Linden's avatar
Richard Linden committed
	template <typename T, typename DERIVED_TYPE = TypeValues<T>, bool IS_SPECIALIZED = true >
	:	public ParamValue<typename LLTypeTags::Sorted<T>::value_t>
Richard Linden's avatar
Richard Linden committed
		typedef TypeValuesHelper<T, DERIVED_TYPE, IS_SPECIALIZED> self_t;
Leslie Linden's avatar
Leslie Linden committed
		typedef typename std::map<std::string, T> value_name_map_t;
		typedef std::string name_t;
Richard Linden's avatar
Richard Linden committed
		typedef self_t type_value_t;
		typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t;
		typedef typename param_value_t::value_t	value_t;
Richard Linden's avatar
Richard Linden committed

		TypeValuesHelper(const typename param_value_t::value_t& val)
		:	param_value_t(val)
Richard Linden's avatar
Richard Linden committed
		{}

		//TODO: cache key by index to save on param block size
		void setValueName(const std::string& value_name) 
		std::string getValueName() const
		{ 
			return mValueName; 
		}
Richard Linden's avatar
Richard Linden committed
		std::string calcValueName(const value_t& value) const
		{
			value_name_map_t* map = getValueNames();
			for (typename value_name_map_t::iterator it = map->begin(), end_it = map->end();
				it != end_it;
				++it)
			{
				if (ParamCompare<T>::equals(it->second, value))
				{
					return it->first;
				}
			}

			return "";
		}

Richard Linden's avatar
Richard Linden committed
		static bool getValueFromName(const std::string& name, value_t& value)
Leslie Linden's avatar
Leslie Linden committed
			typename value_name_map_t::iterator found_it = map->find(name);
			if (found_it == map->end()) return false;
			static value_name_map_t sMap;
			static bool sInitialized = false;
				sInitialized = true;
				DERIVED_TYPE::declareValues();
		static std::vector<std::string>* getPossibleValues()
		{
			static std::vector<std::string> sValues;

			value_name_map_t* map = getValueNames();
Leslie Linden's avatar
Leslie Linden committed
			for (typename value_name_map_t::iterator it = map->begin(), end_it = map->end();
				 it != end_it;
				 ++it)
			{
				sValues.push_back(it->first);
			}
			return &sValues;
		}
Richard Linden's avatar
Richard Linden committed
		static void declare(const std::string& name, const value_t& value)
Richard Linden's avatar
Richard Linden committed
		void operator ()(const std::string& name)
		{
			*this = name;
		}

		void assignNamedValue(const std::string& name)
		{
			if (getValueFromName(name, param_value_t::getValue()))
Richard Linden's avatar
Richard Linden committed
			{
				setValueName(name);
			}
		}

		operator const value_t&() const
		{
			return param_value_t::getValue();
Richard Linden's avatar
Richard Linden committed
		}

		const value_t& operator()() const
		{
			return param_value_t::getValue();
Richard Linden's avatar
Richard Linden committed
		}

Richard Linden's avatar
Richard Linden committed
		static void getName(const std::string& name, const value_t& value)
Richard Linden's avatar
Richard Linden committed
	// string types can support custom named values, but need
	// to disambiguate in code between a string that is a named value
	// and a string that is a name
Richard Linden's avatar
Richard Linden committed
	template <typename DERIVED_TYPE>
	class TypeValuesHelper<std::string, DERIVED_TYPE, true>
	:	public TypeValuesHelper<std::string, DERIVED_TYPE, false>
	{
	public:
Richard Linden's avatar
Richard Linden committed
		typedef TypeValuesHelper<std::string, DERIVED_TYPE, true> self_t;
		typedef TypeValuesHelper<std::string, DERIVED_TYPE, false> base_t;
Richard Linden's avatar
Richard Linden committed
		typedef std::string value_t;
		typedef std::string name_t;
		typedef self_t type_value_t;

Richard Linden's avatar
Richard Linden committed
		TypeValuesHelper(const std::string& val)
Richard Linden's avatar
Richard Linden committed
		{}

		void operator ()(const std::string& name)
Richard Linden's avatar
Richard Linden committed
			*this = name;
		}

		self_t& operator =(const std::string& name)
		{
			if (base_t::getValueFromName(name, ParamValue<std::string>::getValue()))
Richard Linden's avatar
Richard Linden committed
			{
				base_t::setValueName(name);
Richard Linden's avatar
Richard Linden committed
			}
			else
			{
				ParamValue<std::string>::setValue(name);
Richard Linden's avatar
Richard Linden committed
			}
			return *this;
		}
		
		operator const value_t&() const
		{
			return ParamValue<std::string>::getValue();
Richard Linden's avatar
Richard Linden committed
		}

		const value_t& operator()() const
		{
			return ParamValue<std::string>::getValue();
Richard Linden's avatar
Richard Linden committed
	// parser base class with mechanisms for registering readers/writers/inspectors of different types
Richard Linden's avatar
Richard Linden committed
	class LL_COMMON_API Parser
		typedef std::vector<std::pair<std::string, bool> >					name_stack_t;
		typedef std::pair<name_stack_t::iterator, name_stack_t::iterator>	name_stack_range_t;
		typedef std::vector<std::string>									possible_values_t;
		typedef bool (*parser_read_func_t)(Parser& parser, void* output);
		typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&);
		typedef boost::function<void (name_stack_t&, S32, S32, const possible_values_t*)>	parser_inspect_func_t;
		typedef std::map<const std::type_info*, parser_read_func_t>		parser_read_func_map_t;
		typedef std::map<const std::type_info*, parser_write_func_t>	parser_write_func_map_t;
		typedef std::map<const std::type_info*, parser_inspect_func_t>	parser_inspect_func_map_t;
		Parser(parser_read_func_map_t& read_map, parser_write_func_map_t& write_map, parser_inspect_func_map_t& inspect_map)
			mParserReadFuncs(&read_map),
			mParserWriteFuncs(&write_map),
			mParserInspectFuncs(&inspect_map)
		template <typename T> bool readValue(T& param, typename boost::disable_if<boost::is_enum<T> >::type* dummy = 0)
			parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T));
			if (found_it != mParserReadFuncs->end())
				return found_it->second(*this, (void*)&param);
		template <typename T> bool readValue(T& param, typename boost::enable_if<boost::is_enum<T> >::type* dummy = 0)
			parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T));
			if (found_it != mParserReadFuncs->end())
				return found_it->second(*this, (void*)&param);
				found_it = mParserReadFuncs->find(&typeid(S32));
				if (found_it != mParserReadFuncs->end())
					S32 int_value;
					bool parsed = found_it->second(*this, (void*)&int_value);
					param = (T)int_value;
					return parsed;
		template <typename T> bool writeValue(const T& param, name_stack_t& name_stack)
			parser_write_func_map_t::iterator found_it = mParserWriteFuncs->find(&typeid(T));
			if (found_it != mParserWriteFuncs->end())
				return found_it->second(*this, (const void*)&param, name_stack);

		// dispatch inspection to registered inspection functions, for each parameter in a param block
		template <typename T> bool inspectValue(name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values)
		    parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs->find(&typeid(T));
		    if (found_it != mParserInspectFuncs->end())
		    {
			    found_it->second(name_stack, min_count, max_count, possible_values);
				return true;
		    }
			return false;
		}

		virtual std::string getCurrentElementName() = 0;
		virtual std::string getCurrentFileName() = 0;
		virtual void parserWarning(const std::string& message);
		virtual void parserError(const std::string& message);
		void setParseSilently(bool silent) { mParseSilently = silent; }

	protected:
		template <typename T>
		void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func = NULL)
			mParserReadFuncs->insert(std::make_pair(&typeid(T), read_func));
			mParserWriteFuncs->insert(std::make_pair(&typeid(T), write_func));
		}

		template <typename T>
		void registerInspectFunc(parser_inspect_func_t inspect_func)
		{
			mParserInspectFuncs->insert(std::make_pair(&typeid(T), inspect_func));
		parser_read_func_map_t*		mParserReadFuncs;
		parser_write_func_map_t*	mParserWriteFuncs;
		parser_inspect_func_map_t*	mParserInspectFuncs;
	enum ESerializePredicates
	{
		PROVIDED,
		REQUIRED,
		VALID,
		HAS_DEFAULT_VALUE,
		EMPTY
	};

	typedef LLPredicate::Rule<ESerializePredicates> predicate_rule_t;

	predicate_rule_t default_parse_rules();
	// various callbacks and constraints associated with an individual param
Richard Linden's avatar
Richard Linden committed
	struct LL_COMMON_API ParamDescriptor
			virtual ~UserData() = default;
		typedef bool(*merge_func_t)(Param&, const Param&, bool);
		typedef bool(*deserialize_func_t)(Param&, Parser&, Parser::name_stack_range_t&, bool);
		typedef bool(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const predicate_rule_t rules, const Param* diff_param);
		typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count);
		typedef bool(*validation_func_t)(const Param*);

		ParamDescriptor(param_handle_t p, 
						merge_func_t merge_func, 
						deserialize_func_t deserialize_func, 
						serialize_func_t serialize_func,
						validation_func_t validation_func,
						inspect_func_t inspect_func,
						S32 min_count,
						S32 max_count);

		param_handle_t		mParamHandle;
		merge_func_t		mMergeFunc;
		deserialize_func_t	mDeserializeFunc;
		serialize_func_t	mSerializeFunc;
		inspect_func_t		mInspectFunc;
		validation_func_t	mValidationFunc;
		S32					mMinCount;
		S32					mMaxCount;
		S32					mNumRefs;
	typedef boost::shared_ptr<ParamDescriptor> ParamDescriptorPtr;
	// each derived Block class keeps a static data structure maintaining offsets to various params
Richard Linden's avatar
Richard Linden committed
	class LL_COMMON_API BlockDescriptor

		typedef enum e_initialization_state
		{
			UNINITIALIZED,
			INITIALIZING,
			INITIALIZED
		} EInitializationState;

		void aggregateBlockData(BlockDescriptor& src_block_data);
		void addParam(ParamDescriptorPtr param, const char* name);
		typedef absl::node_hash_map<std::string, ParamDescriptorPtr>							param_map_t; 
		typedef std::vector<ParamDescriptorPtr>													param_list_t; 
		typedef std::list<ParamDescriptorPtr>													all_params_list_t;
		typedef std::vector<std::pair<param_handle_t, ParamDescriptor::validation_func_t> >		param_validation_list_t;

		param_map_t						mNamedParams;			// parameters with associated names
		param_list_t					mUnnamedParams;			// parameters with_out_ associated names
		param_validation_list_t			mValidationList;		// parameters that must be validated
		all_params_list_t				mAllParams;				// all parameters, owns descriptors
		size_t							mMaxParamOffset;
		EInitializationState			mInitializationState;	// whether or not static block data has been initialized
		class BaseBlock*				mCurrentBlockPtr;		// pointer to block currently being constructed
		//TODO: implement in terms of owned_ptr
		template<typename T>
Richard Linden's avatar
Richard Linden committed
	class LazyValue
Richard Linden's avatar
Richard Linden committed
		LazyValue()
Richard Linden's avatar
Richard Linden committed
		~LazyValue()
Richard Linden's avatar
Richard Linden committed
		LazyValue(const T& value)
Richard Linden's avatar
Richard Linden committed
			mPtr = new T(value);
		}
Richard Linden's avatar
Richard Linden committed
		LazyValue(const LazyValue& other)
Richard Linden's avatar
Richard Linden committed
		LazyValue& operator = (const LazyValue& other)
Richard Linden's avatar
Richard Linden committed
			else
Richard Linden's avatar
Richard Linden committed
		bool operator==(const LazyValue& other) const
		{
			if (empty() || other.empty()) return false;
			return *mPtr == *other.mPtr;
		}
			bool empty() const
			{
				return mPtr == NULL;
			}

			void set(const T& other)
			{
Richard Linden's avatar
Richard Linden committed
			return *ensureInstance();
Richard Linden's avatar
Richard Linden committed
			return *ensureInstance();
		}
Richard Linden's avatar
Richard Linden committed
		operator const T&() const
		{ 
			return get(); 
			}

		private:
			// lazily allocate an instance of T
			T* ensureInstance() const
			{
				if (mPtr == NULL)
				{
					mPtr = new T();
				}
				return mPtr;
			}

		private:

			mutable T* mPtr;
		};

Richard Linden's avatar
Richard Linden committed
	// root class of all parameter blocks

Richard Linden's avatar
Richard Linden committed
	{
	public:
		// lift block tags into baseblock namespace so derived classes do not need to qualify them
Richard Linden's avatar
Richard Linden committed
		typedef LLInitParam::IS_A_BLOCK IS_A_BLOCK;
		typedef LLInitParam::NOT_BLOCK NOT_A_BLOCK;
		template<typename T>
		struct Sequential : public LLTypeTags::TypeTagBase<T, 2>
		{
			template <typename S> struct Cons { typedef Sequential<ParamValue<S> > value_t; };
			template <typename S> struct Cons<Sequential<S> > { typedef Sequential<S> value_t; };
		};

Richard Linden's avatar
Richard Linden committed
		template<typename T>
		struct Atomic : public LLTypeTags::TypeTagBase<T, 1>
		{
			template <typename S> struct Cons { typedef Atomic<ParamValue<S> > value_t; };
Richard Linden's avatar
Richard Linden committed
			template <typename S> struct Cons<Atomic<S> > { typedef Atomic<S> value_t; };
Richard Linden's avatar
Richard Linden committed

		template<typename T, typename BLOCK_T = typename IsBlock<T>::value_t >
		struct Lazy : public LLTypeTags::TypeTagBase<T, 0>
		{
			template <typename S> struct Cons
			{
				typedef Lazy<ParamValue<S, BLOCK_T>, BLOCK_T> value_t;
			};
Richard Linden's avatar
Richard Linden committed
			template <typename S> struct Cons<Lazy<S, IS_A_BLOCK> >
Richard Linden's avatar
Richard Linden committed
				typedef Lazy<S, IS_A_BLOCK> value_t;
Richard Linden's avatar
Richard Linden committed
			template <typename S> struct Cons<Lazy<S, NOT_A_BLOCK> >
Richard Linden's avatar
Richard Linden committed
				typedef Lazy<S, BLOCK_T> value_t;
		// "Multiple" constraint types, put here in root class to avoid ambiguity during use
		struct AnyAmount
		{
Richard Nelson's avatar
Richard Nelson committed
			enum { minCount = 0 };
			enum { maxCount = U32_MAX };
		};

		template<U32 MIN_AMOUNT>
		struct AtLeast
		{
Richard Nelson's avatar
Richard Nelson committed
			enum { minCount = MIN_AMOUNT };
			enum { maxCount = U32_MAX };
		};

		template<U32 MAX_AMOUNT>
		struct AtMost
		{
Richard Nelson's avatar
Richard Nelson committed
			enum { minCount = 0 };
			enum { maxCount = MAX_AMOUNT };
		};

		template<U32 MIN_AMOUNT, U32 MAX_AMOUNT>
		struct Between
		{
Richard Nelson's avatar
Richard Nelson committed
			enum { minCount = MIN_AMOUNT };
			enum { maxCount = MAX_AMOUNT };
		};

		template<U32 EXACT_COUNT>
		struct Exactly
		{
Richard Nelson's avatar
Richard Nelson committed
			enum { minCount = EXACT_COUNT };
			enum { maxCount = EXACT_COUNT };
		// this typedef identifies derived classes as being blocks
		typedef void baseblock_base_class_t;
		LOG_CLASS(BaseBlock);
		friend class Param;

		BaseBlock()
		:	mValidated(false),
			mParamProvided(false)
		{}

		virtual ~BaseBlock() = default;
		bool submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent=false);

		param_handle_t getHandleFromParam(const Param* param) const;
		bool validateBlock(bool emit_errors = true) const;
		bool isProvided() const
		{
			return mParamProvided;
		}

		bool isValid() const
		{
			return validateBlock(false);
		}


		Param* getParamFromHandle(const param_handle_t param_handle)
		{
			if (param_handle == 0) return NULL;
			U8* baseblock_address = reinterpret_cast<U8*>(this);
			return reinterpret_cast<Param*>(baseblock_address + param_handle);
		}

		const Param* getParamFromHandle(const param_handle_t param_handle) const
		{
			const U8* baseblock_address = reinterpret_cast<const U8*>(this);
			return reinterpret_cast<const Param*>(baseblock_address + param_handle);
		}

		void addSynonym(Param& param, const std::string& synonym);

		// Blocks can override this to do custom tracking of changes
		virtual void paramChanged(const Param& changed_param, bool user_provided) 
		{
			if (user_provided)
			{
				// a child param has been explicitly changed
				// so *some* aspect of this block is now provided
				mValidated = false;
				mParamProvided = true;
			}
		}
		bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
		bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t rule, const BaseBlock* diff_block = NULL) const;
		bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const;
		virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); }
		virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); }

		// take all provided params from other and apply to self
		bool overwriteFrom(const BaseBlock& other)
		{
			return false;
		}

		// take all provided params that are not already provided, and apply to self
		bool fillFrom(const BaseBlock& other)
		{
			return false;
		}

		ParamDescriptorPtr findParamDescriptor(const Param& param);

		// take all provided params from other and apply to self
		bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite);

		static BlockDescriptor& getBlockDescriptor()
		{
			static BlockDescriptor sBlockDescriptor;
			return sBlockDescriptor;
		}

	protected:
		void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size);


Richard Nelson's avatar
Richard Nelson committed
		bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite)
Richard Nelson's avatar
Richard Nelson committed
			return mergeBlock(block_data, source, overwrite);
		mutable bool 	mValidated; // lazy validation flag
		bool			mParamProvided;

	private:
		const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const;
	};

Richard Linden's avatar
Richard Linden committed
	class LL_COMMON_API Param
	{
	public:
		void setProvided(bool is_provided = true)
		{
			mIsProvided = is_provided;
			enclosingBlock().paramChanged(*this, is_provided);
		}
			mIsProvided = other.mIsProvided;
			// don't change mEnclosingblockoffset
	protected:

		bool anyProvided() const { return mIsProvided; }

		Param(BaseBlock* enclosing_block);

		// store pointer to enclosing block as offset to reduce space and allow for quick copying
		BaseBlock& enclosingBlock() const
		{ 
			const U8* my_addr = reinterpret_cast<const U8*>(this);
			// get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class
			return *const_cast<BaseBlock*>
				(reinterpret_cast<const BaseBlock*>
					(my_addr - (ptrdiff_t)getEnclosingBlockOffset()));
		}

		U32 getEnclosingBlockOffset() const
			return ((U32)mEnclosingBlockOffsetHigh << 16) | (U32)mEnclosingBlockOffsetLow;
Richard Linden's avatar
Richard Linden committed
		friend class BaseBlock;
		//24 bits for member offset field and 1 bit for provided flag
		U16		mEnclosingBlockOffsetLow;
		U8		mEnclosingBlockOffsetHigh:7;
		U8		mIsProvided:1;
	template<typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
	struct ParamIterator
	{
Richard Linden's avatar
Richard Linden committed
		typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::const_iterator	const_iterator;
		typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::iterator			iterator;
Richard Linden's avatar
Richard Linden committed
	// wrapper for parameter with a known type
	// specialized to handle 4 cases:
	// simple "scalar" value
	// parameter that is itself a block
	// multiple scalar values, stored in a vector
	// multiple blocks, stored in a vector