diff options
Diffstat (limited to 'indra/llxuixml/llinitparam.h')
| -rw-r--r-- | indra/llxuixml/llinitparam.h | 1843 | 
1 files changed, 1843 insertions, 0 deletions
| diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h new file mode 100644 index 0000000000..869b0c2cd6 --- /dev/null +++ b/indra/llxuixml/llinitparam.h @@ -0,0 +1,1843 @@ +/**  +f * @file llinitparam.h + * @brief parameter block abstraction for creating complex objects and  + * parsing construction parameters from xml and LLSD + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * 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 <stddef.h> +#include <boost/function.hpp> +#include <boost/bind.hpp> +#include <boost/type_traits/is_convertible.hpp> +#include "llregistry.h" +#include "llmemory.h" + + +namespace LLInitParam +{ + +	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> +	struct ParamCompare<T, true> +	{ +		static bool equals(const T&a, const T &b) +		{ +			return false; +		} +	}; + +	// default constructor adaptor for InitParam Values +	// constructs default instances of the given type, returned by const reference +	template <typename T> +	struct DefaultInitializer +	{ +		typedef const T&			T_const_ref; +		// return reference to a single default instance of T +		// built-in types will be initialized to zero, default constructor otherwise +		static T_const_ref get() { static T t = T(); return t; }  +	}; + +	// helper functions and classes +	typedef ptrdiff_t param_handle_t; + +	template <typename T> +	class TypeValues +	{ +	public: +		// empty default implemenation of key cache +		class KeyCache +		{ +		public: +			void setKey(const std::string& key) {} +			std::string getKey() const { return ""; } +			void clearKey(){} +		}; + +		static bool get(const std::string& name, T& value) +		{ +			return false; +		} + +		static bool empty() +		{ +			return true; +		} + +		static std::vector<std::string>* getPossibleValues() { return NULL; } +	}; + +	template <typename T, typename DERIVED_TYPE = TypeValues<T> > +	class TypeValuesHelper +	:	public LLRegistrySingleton<std::string, T, DERIVED_TYPE > +	{ +		typedef LLRegistrySingleton<std::string, T, DERIVED_TYPE>	super_t; +		typedef LLSingleton<DERIVED_TYPE>							singleton_t; +	public: + +		//TODO: cache key by index to save on param block size +		class KeyCache +		{ +		public: +			void setKey(const std::string& key)  +			{ +				mKey = key;  +			} + +			void clearKey() +			{ +				mKey = ""; +			} + +			std::string getKey() const +			{  +				return mKey;  +			} + +		private: +			std::string mKey; +		}; + +		static bool get(const std::string& name, T& value) +		{ +			if (!singleton_t::instance().exists(name)) return false; + +			value = *singleton_t::instance().getValue(name); +			return true; +		} + +		static bool empty() +		{ +			return singleton_t::instance().LLRegistry<std::string, T>::empty(); +		} +	 +		//override this to add name value pairs +		static void declareValues() {} +	 +		void initSingleton() +		{ +			DERIVED_TYPE::declareValues(); +		} + +		static const std::vector<std::string>* getPossibleValues()  +		{  +			// in order to return a pointer to a member, we lazily +			// evaluate the result and store it in mValues here +			if (singleton_t::instance().mValues.empty()) +			{ +				typename super_t::Registrar::registry_map_t::const_iterator it; +				for (it = super_t::defaultRegistrar().beginItems(); it != super_t::defaultRegistrar().endItems(); ++it) +				{ +					singleton_t::instance().mValues.push_back(it->first); +				} +			} +			return &singleton_t::instance().mValues;  +		} + + +	protected: +		static void declare(const std::string& name, const T& value) +		{ +			super_t::defaultRegistrar().add(name, value); +		} + +	private: +		std::vector<std::string> mValues; +	}; + +	class Parser +	{ +		LOG_CLASS(Parser); + +	public: +		 +		struct CompareTypeID +		{ +			bool operator()(const std::type_info* lhs, const std::type_info* rhs) const +			{ +				return lhs->before(*rhs); +			} +		}; + +		typedef std::vector<std::pair<std::string, S32> >			name_stack_t; +		typedef std::pair<name_stack_t::const_iterator, name_stack_t::const_iterator>	name_stack_range_t; +		typedef std::vector<std::string>							possible_values_t; + +		typedef boost::function<bool (void*)>															parser_read_func_t; +		typedef boost::function<bool (const void*, const name_stack_t&)>								parser_write_func_t; +		typedef boost::function<void (const name_stack_t&, S32, S32, const possible_values_t*)>	parser_inspect_func_t; + +		typedef std::map<const std::type_info*, parser_read_func_t, CompareTypeID>		parser_read_func_map_t; +		typedef std::map<const std::type_info*, parser_write_func_t, CompareTypeID>		parser_write_func_map_t; +		typedef std::map<const std::type_info*, parser_inspect_func_t, CompareTypeID>	parser_inspect_func_map_t; + +		Parser() +		:	mParseSilently(false), +			mParseGeneration(0) +		{} +		virtual ~Parser(); + +		template <typename T> bool readValue(T& param) +	    { +		    parser_read_func_map_t::iterator found_it = mParserReadFuncs.find(&typeid(T)); +		    if (found_it != mParserReadFuncs.end()) +		    { +			    return found_it->second((void*)¶m); +		    } +		    return false; +	    } + +		template <typename T> bool writeValue(const T& param, const 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((const void*)¶m, name_stack); +		    } +		    return false; +		} + +		// dispatch inspection to registered inspection functions, for each parameter in a param block +		template <typename T> bool inspectValue(const 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 void parserWarning(const std::string& message); +		virtual void parserError(const std::string& message); +		void setParseSilently(bool silent) { mParseSilently = silent; } +		bool getParseSilently() { return mParseSilently; } + +		S32 getParseGeneration() { return mParseGeneration; } +		S32 newParseGeneration() { return ++mParseGeneration; } + + +	protected: +		template <typename T> +		void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func) +		{ +			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)); +		} + +		bool				mParseSilently; + +	private: +		parser_read_func_map_t		mParserReadFuncs; +		parser_write_func_map_t		mParserWriteFuncs; +		parser_inspect_func_map_t	mParserInspectFuncs; +		S32	mParseGeneration; +	}; + +	class BaseBlock; + +	class Param +	{ +	public: +		// public to allow choice blocks to clear provided flag on stale choices +		void setProvided(bool is_provided) { mIsProvided = is_provided; } + +	protected: +		bool getProvided() const { return mIsProvided; } + +		Param(class 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)(S32)mEnclosingBlockOffset)); +		} + +	private: +		friend class BaseBlock; + +		bool		mIsProvided; +		U16			mEnclosingBlockOffset; +	}; + +	// various callbacks and constraints associated with an individual param +	struct ParamDescriptor +	{ +	public: +		typedef bool(*merge_func_t)(Param&, const Param&, bool); +		typedef bool(*deserialize_func_t)(Param&, Parser&, const Parser::name_stack_range_t&, S32); +		typedef void(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, 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) +		:	mParamHandle(p), +			mMergeFunc(merge_func), +			mDeserializeFunc(deserialize_func), +			mSerializeFunc(serialize_func), +			mValidationFunc(validation_func), +			mInspectFunc(inspect_func), +			mMinCount(min_count), +			mMaxCount(max_count), +			mGeneration(0), +			mNumRefs(0) +		{} + +		ParamDescriptor() +		:	mParamHandle(0), +			mMergeFunc(NULL), +			mDeserializeFunc(NULL), +			mSerializeFunc(NULL), +			mValidationFunc(NULL), +			mInspectFunc(NULL), +			mMinCount(0), +			mMaxCount(0), +			mGeneration(0), +			mNumRefs(0) +		{} + +		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					mGeneration; +		S32					mNumRefs; +	}; + +	// each derived Block class keeps a static data structure maintaining offsets to various params +	class BlockDescriptor +	{ +	public: +		BlockDescriptor() +		:	mMaxParamOffset(0), +			mInitializationState(UNINITIALIZED), +			mCurrentBlockPtr(NULL) +		{} + +		typedef enum e_initialization_state +		{ +			UNINITIALIZED, +			INITIALIZING, +			INITIALIZED +		} EInitializationState; + +		void aggregateBlockData(BlockDescriptor& src_block_data); + +	public: +		typedef std::map<const std::string, ParamDescriptor*> param_map_t; // references param descriptors stored in mAllParams +		typedef std::vector<ParamDescriptor*> param_list_t;  + +		typedef std::list<ParamDescriptor> all_params_list_t;// references param descriptors stored in mAllParams +		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_map_t						mSynonyms;				// parameters with alternate 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 +	}; + +	class BaseBlock +	{ +	public: +		// "Multiple" constraint types, put here in root class to avoid ambiguity during use +		struct AnyAmount +		{ +			static U32 minCount() { return 0; } +			static U32 maxCount() { return U32_MAX; } +		}; + +		template<U32 MIN_AMOUNT> +		struct AtLeast +		{ +			static U32 minCount() { return MIN_AMOUNT; } +			static U32 maxCount() { return U32_MAX; } +		}; + +		template<U32 MAX_AMOUNT> +		struct AtMost +		{ +			static U32 minCount() { return 0; } +			static U32 maxCount() { return MAX_AMOUNT; } +		}; + +		template<U32 MIN_AMOUNT, U32 MAX_AMOUNT> +		struct Between +		{ +			static U32 minCount() { return MIN_AMOUNT; } +			static U32 maxCount() { return MAX_AMOUNT; } +		}; + +		template<U32 EXACT_COUNT> +		struct Exactly +		{ +			static U32 minCount() { return EXACT_COUNT; } +			static U32 maxCount() { return EXACT_COUNT; } +		}; + +		// this typedef identifies derived classes as being blocks +		typedef void baseblock_base_class_t; +		LOG_CLASS(BaseBlock); +		friend class Param; + +		BaseBlock(); +		virtual ~BaseBlock(); +		bool submitValue(const 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; + +		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 setLastChangedParam(const Param& last_param, bool user_provided); + +		S32 getLastChangeVersion() const { return mChangeVersion; } +		bool isDefault() const { return mChangeVersion == 0; } + +		bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack); +		bool serializeBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), const BaseBlock* diff_block = NULL) const; +		bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t()) const; + +		const BlockDescriptor& mostDerivedBlockDescriptor() const { return *mBlockDescriptor; } +		BlockDescriptor& mostDerivedBlockDescriptor() { return *mBlockDescriptor; } + +		// 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; +		} + +		static void addParam(BlockDescriptor& block_data, const ParamDescriptor& param, const char* name); +	protected: +		void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size); + + +		// take all provided params from other and apply to self +		bool merge(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite); + +		// can be updated in getters +		mutable S32				mChangeVersion; + +		BlockDescriptor*		mBlockDescriptor;	// most derived block descriptor + +		static BlockDescriptor& selfBlockDescriptor() +		{ +			static BlockDescriptor sBlockDescriptor; +			return sBlockDescriptor; +		} + +	private: +		const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const; +		ParamDescriptor* findParamDescriptor(param_handle_t handle); +	}; + + +	template<typename T> +	struct ParamIterator +	{ +		typedef typename std::vector<T>::const_iterator		const_iterator; +		typedef typename std::vector<T>::iterator			iterator; +	}; + +	// these templates allow us to distinguish between template parameters +	// that derive from BaseBlock and those that don't +	// this is supposedly faster than boost::is_convertible and its ilk +	template<typename T, typename Void = void> +	struct IsBaseBlock +	{ +		static const bool value = false; +	}; + +	template<typename T> +	struct IsBaseBlock<T, typename T::baseblock_base_class_t> +	{ +		static const bool value = true; +	}; + +	// specialize for custom parsing/decomposition of specific classes +	// e.g. TypedParam<LLRect> has left, top, right, bottom, etc... +	template<typename	T, +			typename	NAME_VALUE_LOOKUP = TypeValues<T>, +			bool		HAS_MULTIPLE_VALUES = false, +			bool		VALUE_IS_BLOCK = IsBaseBlock<T>::value> +	class TypedParam  +	:	public Param +	{ +	public: +		typedef const T&																	value_const_ref_t; +		typedef value_const_ref_t															value_assignment_t; +		typedef typename NAME_VALUE_LOOKUP::KeyCache										key_cache_t; +		typedef	TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK>		self_t; + +		TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)  +		:	Param(block_descriptor.mCurrentBlockPtr) +		{ +			if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) +			{ +				ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), +												&mergeWith, +												&deserializeParam, +												&serializeParam, +												validate_func, +												&inspectParam, +												min_count, max_count); +				BaseBlock::addParam(block_descriptor, param_descriptor, name); +			} + +			mData.mValue = value; +		}  + +		bool isProvided() const { return Param::getProvided(); } + +		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)  +		{  +			self_t& typed_param = static_cast<self_t&>(param); +			// no further names in stack, attempt to parse value now +			if (name_stack.first == name_stack.second) +			{ +				if (parser.readValue<T>(typed_param.mData.mValue)) +				{ +					typed_param.mData.clearKey(); +					typed_param.setProvided(true); +					typed_param.enclosingBlock().setLastChangedParam(param, true); +					return true; +				} +				 +				// try to parse a known named value +				if(!NAME_VALUE_LOOKUP::empty()) +				{ +					// try to parse a known named value +					std::string name; +					if (parser.readValue<std::string>(name)) +					{ +						// try to parse a per type named value +						if (NAME_VALUE_LOOKUP::get(name, typed_param.mData.mValue)) +						{ +							typed_param.mData.setKey(name); +							typed_param.setProvided(true); +							typed_param.enclosingBlock().setLastChangedParam(param, true); +							return true; +						} + +					} +				} +			} +			return false; +		} + +		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) +		{ +			const self_t& typed_param = static_cast<const self_t&>(param); +			if (!typed_param.isProvided()) return; + +			if (!name_stack.empty()) +			{ +				name_stack.back().second = parser.newParseGeneration(); +			} + +			std::string key = typed_param.mData.getKey(); + +			// first try to write out name of name/value pair + +			if (!key.empty()) +			{ +				if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->mData.getKey(), key)) +				{ +					if (!parser.writeValue<std::string>(key, name_stack)) +					{ +						return; +					} +				} +			} +			// then try to serialize value directly +			else if (!diff_param || !ParamCompare<T>::equals(typed_param.get(), static_cast<const self_t*>(diff_param)->get()))					{ +				if (!parser.writeValue<T>(typed_param.mData.mValue, name_stack))  +				{ +					return; +				} +			} +		} + +		static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) +		{ +			// tell parser about our actual type +			parser.inspectValue<T>(name_stack, min_count, max_count, NULL); +			// then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) +			if (NAME_VALUE_LOOKUP::getPossibleValues()) +			{ +				parser.inspectValue<std::string>(name_stack, min_count, max_count, NAME_VALUE_LOOKUP::getPossibleValues()); +			} +		} + +		void set(value_assignment_t val, bool flag_as_provided = true) +		{ +			mData.mValue = val; +			mData.clearKey(); +			setProvided(flag_as_provided); +			Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided); +		} + +		void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true) +		{ +			if (!isProvided()) +			{ +				set(val, flag_as_provided); +			} +		} + +		// implicit conversion +		operator value_assignment_t() const { return get(); }  +		// explicit conversion +		value_assignment_t operator()() const { return get(); }  + +	protected: +		value_assignment_t get() const +		{ +			return mData.mValue; +		} + +		static bool mergeWith(Param& dst, const Param& src, bool overwrite) +		{ +			const self_t& src_typed_param = static_cast<const self_t&>(src); +			self_t& dst_typed_param = static_cast<self_t&>(dst); +			if (src_typed_param.isProvided() +				&& (overwrite || !dst_typed_param.isProvided())) +			{ +				dst_typed_param.mData.clearKey(); +				dst_typed_param.set(src_typed_param.get()); +				return true; +			} +			return false; +		} + +		struct Data : public key_cache_t +		{ +			T mValue; +		}; + +		Data		mData; +	}; + +	// parameter that is a block +	template <typename T, typename NAME_VALUE_LOOKUP> +	class TypedParam<T, NAME_VALUE_LOOKUP, false, true>  +	:	public T, +		public Param +	{ +	public: +		typedef const T											value_const_t; +		typedef T												value_t; +		typedef value_const_t&									value_const_ref_t; +		typedef value_const_ref_t								value_assignment_t; +		typedef typename NAME_VALUE_LOOKUP::KeyCache			key_cache_t; +		typedef TypedParam<T, NAME_VALUE_LOOKUP, false, true>	self_t; + +		TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) +		:	Param(block_descriptor.mCurrentBlockPtr), +			T(value) +		{ +			if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) +			{ +				ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), +												&mergeWith, +												&deserializeParam, +												&serializeParam, +												validate_func,  +												&inspectParam, +												min_count, max_count); +				BaseBlock::addParam(block_descriptor, param_descriptor, name); +			} +		} + +		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)  +		{  +			self_t& typed_param = static_cast<self_t&>(param); +			// attempt to parse block... +			if(typed_param.deserializeBlock(parser, name_stack)) +			{ +				typed_param.mData.clearKey(); +				typed_param.enclosingBlock().setLastChangedParam(param, true); +				return true; +			} + +			if(!NAME_VALUE_LOOKUP::empty()) +			{ +				// try to parse a known named value +				std::string name; +				if (parser.readValue<std::string>(name)) +				{ +					// try to parse a per type named value +					if (NAME_VALUE_LOOKUP::get(name, typed_param)) +					{ +						typed_param.enclosingBlock().setLastChangedParam(param, true); +						typed_param.mData.setKey(name); +						typed_param.mData.mKeyVersion = typed_param.getLastChangeVersion(); +						return true; +					} + +				} +			} +			return false; +		} + +		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) +		{ +			const self_t& typed_param = static_cast<const self_t&>(param); +			if (!name_stack.empty()) +			{ +				name_stack.back().second = parser.newParseGeneration(); +			} + +			std::string key = typed_param.mData.getKey(); +			if (!key.empty() && typed_param.mData.mKeyVersion == typed_param.getLastChangeVersion()) +			{ +				if (!parser.writeValue<std::string>(key, name_stack)) +				{ +					return; +				} +			} +			else +			{ +				typed_param.serializeBlock(parser, name_stack, static_cast<const self_t*>(diff_param)); +			} +		} + +		static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) +		{ +			// I am a param that is also a block, so just recurse into my contents +			const self_t& typed_param = static_cast<const self_t&>(param); +			typed_param.inspectBlock(parser, name_stack); +		} + +		// a param-that-is-a-block is provided when the user has set one of its child params +		// *and* the block as a whole validates +		bool isProvided() const  +		{  +			// only validate block when it hasn't already passed validation and user has supplied *some* value +			if (Param::getProvided() && mData.mValidatedVersion < T::getLastChangeVersion()) +			{ +				// a sub-block is "provided" when it has been filled in enough to be valid +				mData.mValidated = T::validateBlock(false); +				mData.mValidatedVersion = T::getLastChangeVersion(); +			} +			return Param::getProvided() && mData.mValidated; +		} + +		// assign block contents to this param-that-is-a-block +		void set(value_assignment_t val, bool flag_as_provided = true) +		{ +			value_t::operator=(val); +			mData.clearKey(); +			// force revalidation of block by clearing known provided version +			// next call to isProvided() will update provision status based on validity +			mData.mValidatedVersion = 0; +			setProvided(flag_as_provided); +			Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided); +		} + +		void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true) +		{ +			if (!isProvided()) +			{ +				set(val, flag_as_provided); +			} +		} + +		// propagate changed status up to enclosing block +		/*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided) +		{  +			T::setLastChangedParam(last_param, user_provided); +			Param::enclosingBlock().setLastChangedParam(*this, user_provided); +			if (user_provided) +			{ +				// a child param has been explicitly changed +				// so *some* aspect of this block is now provided +				setProvided(true); +			} +		} + +		// implicit conversion +		operator value_assignment_t() const { return get(); }  +		// explicit conversion +		value_assignment_t operator()() const { return get(); }  + +	protected: +		value_assignment_t get() const +		{ +			return *this; +		} + +		static bool mergeWith(Param& dst, const Param& src, bool overwrite) +		{ +			const self_t& src_typed_param = static_cast<const self_t&>(src); +			self_t& dst_typed_param = static_cast<self_t&>(dst); +			if (dst_typed_param.T::merge(T::selfBlockDescriptor(), src_typed_param, overwrite || !dst_typed_param.isProvided())) +			{ +				dst_typed_param.mData.clearKey(); +				return true; +			} +			return false; +		} + +		struct Data : public key_cache_t +		{ +			S32 			mKeyVersion; +			mutable S32 	mValidatedVersion; +			mutable bool 	mValidated; // lazy validation flag + +			Data()  +			:	mKeyVersion(0), +				mValidatedVersion(0), +				mValidated(false) +			{} +		}; +		Data	mData; +	}; + +	// container of non-block parameters +	template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP> +	class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, false>  +	:	public Param +	{ +	public: +		typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, false>		self_t; +		typedef typename std::vector<VALUE_TYPE>							container_t; +		typedef const container_t&											value_assignment_t; + +		typedef VALUE_TYPE													value_t; +		typedef value_t&													value_ref_t; +		typedef const value_t&												value_const_ref_t; +		 +		typedef typename NAME_VALUE_LOOKUP::KeyCache						key_cache_t; + +		TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)  +		:	Param(block_descriptor.mCurrentBlockPtr), +			mValues(value) +		{ +			mCachedKeys.resize(mValues.size()); +			if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) +			{ +				ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), +												&mergeWith, +												&deserializeParam, +												&serializeParam, +												validate_func, +												&inspectParam, +												min_count, max_count); +				BaseBlock::addParam(block_descriptor, param_descriptor, name); +			} +		}  + +		bool isProvided() const { return Param::getProvided(); } + +		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)  +		{  +			self_t& typed_param = static_cast<self_t&>(param); +			value_t value; +			// no further names in stack, attempt to parse value now +			if (name_stack.first == name_stack.second) +			{ +				// attempt to read value directly +				if (parser.readValue<value_t>(value)) +				{ +					typed_param.mValues.push_back(value); +					// save an empty name/value key as a placeholder +					typed_param.mCachedKeys.push_back(key_cache_t()); +					typed_param.enclosingBlock().setLastChangedParam(param, true); +					typed_param.setProvided(true); +					return true; +				} +				 +				// try to parse a known named value +				if(!NAME_VALUE_LOOKUP::empty()) +				{ +					// try to parse a known named value +					std::string name; +					if (parser.readValue<std::string>(name)) +					{ +						// try to parse a per type named value +						if (NAME_VALUE_LOOKUP::get(name, typed_param.mValues)) +						{ +							typed_param.mValues.push_back(value); +							typed_param.mCachedKeys.push_back(key_cache_t()); +							typed_param.mCachedKeys.back().setKey(name); +							typed_param.enclosingBlock().setLastChangedParam(param, true); +							typed_param.setProvided(true); +							return true; +						} + +					} +				} +			} +			return false; +		} + +		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) +		{ +			const self_t& typed_param = static_cast<const self_t&>(param); +			if (!typed_param.isProvided() || name_stack.empty()) return; + +			typename container_t::const_iterator it = typed_param.mValues.begin(); +			for (typename std::vector<key_cache_t>::const_iterator key_it = typed_param.mCachedKeys.begin(); +				it != typed_param.mValues.end(); +				++key_it, ++it) +			{ +				std::string key = key_it->get(); +				name_stack.back().second = parser.newParseGeneration(); + +				if(!key.empty()) +				{ +					if(!parser.writeValue<std::string>(key, name_stack)) +					{ +						return; +					} +				} +				// not parse via name values, write out value directly +				else if (!parser.writeValue<VALUE_TYPE>(*it, name_stack)) +				{ +					return; +				} +			} +		} + +		static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) +		{ +			parser.inspectValue<VALUE_TYPE>(name_stack, min_count, max_count, NULL); +			if (NAME_VALUE_LOOKUP::getPossibleValues()) +			{ +				parser.inspectValue<std::string>(name_stack, min_count, max_count, NAME_VALUE_LOOKUP::getPossibleValues()); +			} +		} + +		void set(value_assignment_t val, bool flag_as_provided = true) +		{ +			mValues = val; +			mCachedKeys.clear(); +			mCachedKeys.resize(mValues.size()); +			setProvided(flag_as_provided); +			Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided); +		} + + +		void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true) +		{ +			if (!isProvided()) +			{ +				set(val, flag_as_provided); +			} +		} + +		value_ref_t add() +		{ +			mValues.push_back(value_t()); +			mCachedKeys.push_back(key_cache_t()); +			setProvided(true); +			return mValues.back(); +		} + +		void add(value_const_ref_t item) +		{ +			mValues.push_back(item); +			mCachedKeys.push_back(key_cache_t()); +			setProvided(true); +		} + +		// implicit conversion +		operator value_assignment_t() const { return self_t::get(); }  +		// explicit conversion +		value_assignment_t operator()() const { return get(); }  + +		U32 numValidElements() const +		{ +			return mValues.size(); +		} + +	protected: +		value_assignment_t get() const +		{ +			return mValues; +		} + +		static bool mergeWith(Param& dst, const Param& src, bool overwrite) +		{ +			const self_t& src_typed_param = static_cast<const self_t&>(src); +			self_t& dst_typed_param = static_cast<self_t&>(dst); + +			if (src_typed_param.isProvided() +				&& (overwrite || !dst_typed_param.isProvided())) +			{ +				dst_typed_param.set(src_typed_param.get()); +				return true; +			} +			return false; +		} + +		container_t		mValues; +		std::vector<key_cache_t>	mCachedKeys; +	}; + +	// container of block parameters +	template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP> +	class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, true>  +	:	public Param +	{ +	public: +		typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, true>	self_t; +		typedef typename std::vector<VALUE_TYPE>						container_t; +		typedef const container_t&										value_assignment_t; + +		typedef VALUE_TYPE												value_t; +		typedef value_t&												value_ref_t; +		typedef const value_t&											value_const_ref_t; + +		typedef typename NAME_VALUE_LOOKUP::KeyCache					key_cache_t; + +		TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)  +		:	Param(block_descriptor.mCurrentBlockPtr), +			mValues(value), +			mLastParamGeneration(0) +		{ +			mCachedKeys.resize(mValues.size()); +			if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) +			{ +				ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), +												&mergeWith, +												&deserializeParam, +												&serializeParam, +												validate_func, +												&inspectParam, +												min_count, max_count); +				BaseBlock::addParam(block_descriptor, param_descriptor, name); +			} +		}  + +		bool isProvided() const { return Param::getProvided(); } + +		value_ref_t operator[](S32 index) { return mValues[index]; } +		value_const_ref_t operator[](S32 index) const { return mValues[index]; } + +		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)  +		{  +			self_t& typed_param = static_cast<self_t&>(param); +			if (generation != typed_param.mLastParamGeneration || typed_param.mValues.empty()) +			{ +				typed_param.mValues.push_back(value_t()); +				typed_param.mCachedKeys.push_back(Data()); +				typed_param.enclosingBlock().setLastChangedParam(param, true); +				typed_param.mLastParamGeneration = generation; +			} + +			value_t& value = typed_param.mValues.back(); + +			// attempt to parse block... +			if(value.deserializeBlock(parser, name_stack)) +			{ +				typed_param.setProvided(true); +				return true; +			} + +			if(!NAME_VALUE_LOOKUP::empty()) +			{ +				// try to parse a known named value +				std::string name; +				if (parser.readValue<std::string>(name)) +				{ +					// try to parse a per type named value +					if (NAME_VALUE_LOOKUP::get(name, value)) +					{ +						typed_param.mCachedKeys.back().setKey(name); +						typed_param.mCachedKeys.back().mKeyVersion = value.getLastChangeVersion(); +						typed_param.enclosingBlock().setLastChangedParam(param, true); +						typed_param.setProvided(true); +						return true; +					} + +				} +			} + +			return false; +		} + +		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) +		{ +			const self_t& typed_param = static_cast<const self_t&>(param); +			if (!typed_param.isProvided() || name_stack.empty()) return; + +			typename container_t::const_iterator it = typed_param.mValues.begin(); +			for (typename std::vector<Data>::const_iterator key_it = typed_param.mCachedKeys.begin(); +				it != typed_param.mValues.end(); +				++key_it, ++it) +			{ +				name_stack.back().second = parser.newParseGeneration(); + +				std::string key = key_it->getKey(); +				if (!key.empty() && key_it->mKeyVersion == it->getLastChangeVersion()) +				{ +					if(!parser.writeValue<std::string>(key, name_stack)) +					{ +						return; +					} +				} +				// Not parsed via named values, write out value directly +				// NOTE: currently we don't worry about removing default values in Multiple +				else if (!it->serializeBlock(parser, name_stack, NULL)) +				{ +					return; +				} +			} +		} + +		static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) +		{ +			// I am a vector of blocks, so describe my contents recursively +			value_t().inspectBlock(parser, name_stack); +		} + +		void set(value_assignment_t val, bool flag_as_provided = true) +		{ +			mValues = val; +			mCachedKeys.clear(); +			mCachedKeys.resize(mValues.size()); +			setProvided(flag_as_provided); +			Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided); +		} + +		void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true) +		{ +			if (!isProvided()) +			{ +				set(val, flag_as_provided); +			} +		} + +		value_ref_t add() +		{ +			mValues.push_back(value_t()); +			mCachedKeys.push_back(Data()); +			setProvided(true); +			return mValues.back(); +		} + +		void add(value_const_ref_t item) +		{ +			mValues.push_back(item); +			mCachedKeys.push_back(Data()); +			setProvided(true); +		} + +		// implicit conversion +		operator value_assignment_t() const { return self_t::get(); }  +		// explicit conversion +		value_assignment_t operator()() const { return get(); }  + +		U32 numValidElements() const +		{ +			U32 count = 0; +			for (typename container_t::const_iterator it = mValues.begin(); +				it != mValues.end(); +				++it) +			{ +				if(it->validateBlock(false)) count++; +			} +			return count; +		} + +	protected: +		value_assignment_t get() const +		{ +			return mValues; +		} + +		static bool mergeWith(Param& dst, const Param& src, bool overwrite) +		{ +			const self_t& src_typed_param = static_cast<const self_t&>(src); +			self_t& dst_typed_param = static_cast<self_t&>(dst); + +			if (src_typed_param.isProvided() +				&& (overwrite || !dst_typed_param.isProvided())) +			{ +				dst_typed_param.set(src_typed_param.get()); +				return true; +			} +			return false; +		} + +		struct Data : public key_cache_t +		{ +			S32 mKeyVersion;	// version of block for which key was last valid + +			Data() : mKeyVersion(0) {} +		}; + +		container_t			mValues; +		std::vector<Data>	mCachedKeys; + +		S32			mLastParamGeneration; +	}; + +	template <typename DERIVED_BLOCK> +	class Choice : public BaseBlock +	{ +		typedef Choice<DERIVED_BLOCK> self_t; +		typedef Choice<DERIVED_BLOCK> enclosing_block_t; +		 +		LOG_CLASS(self_t); +	public: +		// take all provided params from other and apply to self +		bool overwriteFrom(const self_t& other) +		{ +			return merge(selfBlockDescriptor(), other, true); +		} + +		// take all provided params that are not already provided, and apply to self +		bool fillFrom(const self_t& other) +		{ +			return merge(selfBlockDescriptor(), other, false); +		} + +		// merge with other block +		bool merge(BlockDescriptor& block_data, const self_t& other, bool overwrite) +		{ +			// only merge a choice if we are overwriting with other's contents +			if (overwrite) +			{ +				mCurChoice = other.mCurChoice; +				return BaseBlock::merge(selfBlockDescriptor(), other, overwrite); +			} +			return false; +		} + +		// clear out old choice when param has changed +		/*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided) +		{  +			param_handle_t changed_param_handle = BaseBlock::getHandleFromParam(&last_param); +			// if we have a new choice... +			if (changed_param_handle != mCurChoice) +			{ +				// clear provided flag on previous choice +				Param* previous_choice = BaseBlock::getParamFromHandle(mCurChoice); +				if (previous_choice)  +				{ +					previous_choice->setProvided(false); +				} +				mCurChoice = changed_param_handle; +			} +			BaseBlock::setLastChangedParam(last_param, user_provided); +		} + +	protected: +		Choice() +		:	mCurChoice(0) +		{ +			BaseBlock::init(selfBlockDescriptor(), BaseBlock::selfBlockDescriptor(), sizeof(DERIVED_BLOCK)); +		} + +		// Alternatives are mutually exclusive wrt other Alternatives in the same block.   +		// One alternative in a block will always have isChosen() == true. +		// At most one alternative in a block will have isProvided() == true. +		template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> > +		class Alternative : public TypedParam<T, NAME_VALUE_LOOKUP, false> +		{ +		public: +			friend class Choice<DERIVED_BLOCK>; + +			typedef Alternative<T, NAME_VALUE_LOOKUP>									self_t; +			typedef TypedParam<T, NAME_VALUE_LOOKUP, false, IsBaseBlock<T>::value>		super_t; +			typedef typename super_t::value_assignment_t								value_assignment_t; + +			explicit Alternative(const char* name, value_assignment_t val = DefaultInitializer<T>::get()) +			:	super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, NULL, 0, 1), +				mOriginalValue(val) +			{ +				// assign initial choice to first declared option +				DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::selfBlockDescriptor().mCurrentBlockPtr); +				if (LL_UNLIKELY( +						DERIVED_BLOCK::selfBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING +							&& blockp->mCurChoice == 0)) +				{ +					blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this); +				} +			} + +			Alternative& operator=(value_assignment_t val) +			{ +				super_t::set(val); +				return *this; +			} + +			void operator()(typename super_t::value_assignment_t val)  +			{  +				super_t::set(val); +			} + +			operator value_assignment_t() const  +			{  +				if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this) +				{ +					return super_t::get();  +				} +				return mOriginalValue; +			}  + +			value_assignment_t operator()() const  +			{  +				if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this) +				{ +					return super_t::get();  +				} +				return mOriginalValue; +			}  + +			bool isChosen() const +			{ +				return static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this; +			} +		 +		private: +			T			mOriginalValue; +		}; + +	protected: +		static BlockDescriptor& selfBlockDescriptor() +		{ +			static BlockDescriptor sBlockDescriptor; +			return sBlockDescriptor; +		} + +	private: +		param_handle_t	mCurChoice; + +		const Param* getCurrentChoice() const +		{ +			return BaseBlock::getParamFromHandle(mCurChoice); +		} +	}; + +	template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock> +	class Block  +	:	public BASE_BLOCK +	{ +		typedef Block<DERIVED_BLOCK, BASE_BLOCK> self_t; +		typedef Block<DERIVED_BLOCK, BASE_BLOCK> block_t; + +	public: +		typedef BASE_BLOCK base_block_t; + +		// take all provided params from other and apply to self +		bool overwriteFrom(const self_t& other) +		{ +			return BaseBlock::merge(selfBlockDescriptor(), other, true); +		} + +		// take all provided params that are not already provided, and apply to self +		bool fillFrom(const self_t& other) +		{ +			return BaseBlock::merge(selfBlockDescriptor(), other, false); +		} +	protected: +		Block() +		{ +			//#pragma message("Parsing LLInitParam::Block") +			BaseBlock::init(selfBlockDescriptor(), BASE_BLOCK::selfBlockDescriptor(), sizeof(DERIVED_BLOCK)); +		} + +		// +		// Nested classes for declaring parameters +		// +		template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> > +		class Optional : public TypedParam<T, NAME_VALUE_LOOKUP, false> +		{ +		public: +			typedef TypedParam<T, NAME_VALUE_LOOKUP, false, IsBaseBlock<T>::value>		super_t; +			typedef typename super_t::value_assignment_t								value_assignment_t; + +			explicit Optional(const char* name = "", value_assignment_t val = DefaultInitializer<T>::get()) +			:	super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, NULL, 0, 1) +			{ +				//#pragma message("Parsing LLInitParam::Block::Optional") +			} + +			Optional& operator=(value_assignment_t val) +			{ +				set(val); +				return *this; +			} + +			DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val) +			{ +				super_t::set(val); +				return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); +			} +			using super_t::operator(); +		}; + +		template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> > +		class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false> +		{ +		public: +			typedef TypedParam<T, NAME_VALUE_LOOKUP, false, IsBaseBlock<T>::value>		super_t; +			typedef Mandatory<T, NAME_VALUE_LOOKUP>										self_t; +			typedef typename super_t::value_assignment_t								value_assignment_t; + +			// mandatory parameters require a name to be parseable +			explicit Mandatory(const char* name = "", value_assignment_t val = DefaultInitializer<T>::get()) +			:	super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, &validate, 1, 1) +			{} + +			Mandatory& operator=(value_assignment_t val) +			{ +				set(val); +				return *this; +			} + +			DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val) +			{ +				super_t::set(val); +				return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); +			} +			using super_t::operator(); + +			static bool validate(const Param* p) +			{ +				// valid only if provided +				return static_cast<const self_t*>(p)->isProvided(); +			} + +		}; + +		template <typename T, typename RANGE = BaseBlock::AnyAmount, typename NAME_VALUE_LOOKUP = TypeValues<T> > +		class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true> +		{ +		public: +			typedef TypedParam<T, NAME_VALUE_LOOKUP, true, IsBaseBlock<T>::value>	super_t; +			typedef Multiple<T, RANGE, NAME_VALUE_LOOKUP>							self_t; +			typedef typename super_t::container_t									container_t; +			typedef typename super_t::value_assignment_t							value_assignment_t; +			typedef typename container_t::iterator									iterator; +			typedef typename container_t::const_iterator							const_iterator; + +			explicit Multiple(const char* name = "", value_assignment_t val = DefaultInitializer<container_t>::get()) +			:	super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, &validate, RANGE::minCount(), RANGE::maxCount()) +			{} + +			using super_t::operator(); + +			Multiple& operator=(value_assignment_t val) +			{ +				set(val); +				return *this; +			} +			 +			DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val) +			{ +				super_t::set(val); +				return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); +			} + +			static bool validate(const Param* paramp)  +			{ +				U32 num_valid = ((super_t*)paramp)->numValidElements(); +				return RANGE::minCount() <= num_valid && num_valid <= RANGE::maxCount(); +			} +		}; + +		class Deprecated : public Param +		{ +		public: +			explicit Deprecated(const char* name) +			:	Param(DERIVED_BLOCK::selfBlockDescriptor().mCurrentBlockPtr) +			{ +				BlockDescriptor& block_descriptor = DERIVED_BLOCK::selfBlockDescriptor(); +				if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) +				{ +					ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), +													NULL, +													&deserializeParam, +													NULL, +													NULL, +													NULL,  +													0, S32_MAX); +					BaseBlock::addParam(block_descriptor, param_descriptor, name); +				} +			} +			 +			static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation) +			{ +				if (name_stack.first == name_stack.second) +				{ +					//std::string message = llformat("Deprecated value %s ignored", getName().c_str()); +					//parser.parserWarning(message); +					return true; +				} + +				return false; +			} +		}; + +		typedef Deprecated Ignored; + +	protected: +		static BlockDescriptor& selfBlockDescriptor() +		{ +			static BlockDescriptor sBlockDescriptor; +			return sBlockDescriptor; +		} +	}; +	 +	template<typename T, typename DERIVED = TypedParam<T> > +	class BlockValue +	:	public Block<TypedParam<T, TypeValues<T>, false> >, +		public Param +	{ +	public: +		typedef enum e_value_age +		{	 +			OLDER_THAN_BLOCK,	// mData.mValue needs to be refreshed from the block parameters +			NEWER_THAN_BLOCK,	// mData.mValue holds the authoritative value (which has been replicated to the block parameters via setBlockFromValue) +			SAME_AS_BLOCK		// mData.mValue is derived from the block parameters, which are authoritative +		} EValueAge; + +		typedef BlockValue<T>										self_t; +		typedef Block<TypedParam<T, TypeValues<T>, false> >			block_t; +		typedef const T&											value_const_ref_t; +		typedef value_const_ref_t									value_assignment_t; +		typedef typename TypeValues<T>::KeyCache					key_cache_t; + +		BlockValue(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) +		:	Param(block_descriptor.mCurrentBlockPtr), +			mData(value, NEWER_THAN_BLOCK) +		{ +			if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) +			{ +				ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), +												&mergeWith, +												&deserializeParam, +												&serializeParam, +												validate_func, +												&inspectParam, +												min_count, max_count); +				BaseBlock::addParam(block_descriptor, param_descriptor, name); +			} +		} + +		// implicit conversion +		operator value_assignment_t() const { return get(); }  +		// explicit conversion +		value_assignment_t operator()() const { return get(); }  + +		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation) +		{ +			DERIVED& typed_param = static_cast<DERIVED&>(param); +			// type to apply parse direct value T +			if (name_stack.first == name_stack.second) +			{ +				if(parser.readValue<T>(typed_param.mData.mValue)) +				{ +					typed_param.enclosingBlock().setLastChangedParam(param, true); +					typed_param.setProvided(true); +					typed_param.mData.clearKey(); +					typed_param.mData.mValueAge = NEWER_THAN_BLOCK; +					typed_param.setBlockFromValue(); + +					return true; +				} + +				if(!TypeValues<T>::empty()) +				{ +					// try to parse a known named value +					std::string name; +					if (parser.readValue<std::string>(name)) +					{ +						// try to parse a per type named value +						if (TypeValues<T>::get(name, typed_param.mData.mValue)) +						{ +							typed_param.mData.setKey(name); +							typed_param.enclosingBlock().setLastChangedParam(param, true); +							typed_param.setProvided(true); +							typed_param.mData.mValueAge = NEWER_THAN_BLOCK; +							typed_param.setBlockFromValue(); + +							return true; +						} +					} +				} +			} + +			// fall back on parsing block components for T +			// if we deserialized at least one component... +			if (typed_param.BaseBlock::deserializeBlock(parser, name_stack)) +			{ +				// ...our block is provided, and considered changed +				typed_param.enclosingBlock().setLastChangedParam(param, true); +				typed_param.setProvided(true); +				return true; +			} +			return false; +		} + +		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) +		{ +			const self_t& typed_param = static_cast<const self_t&>(param); +			 +			if (!typed_param.isProvided()) return; +			 +			std::string key = typed_param.mData.getKey(); + +			// first try to write out name of name/value pair +			if (!key.empty()) +			{ +				if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->mData.getKey(), key)) +				{ +					if (!parser.writeValue<std::string>(key, name_stack)) +					{ +						return; +					} +				} +			} +			// then try to serialize value directly +			else if (!diff_param || !ParamCompare<T>::equals(typed_param.get(), (static_cast<const self_t*>(diff_param))->get()))	 +            { +				 +				if (parser.writeValue<T>(typed_param.mData.mValue, name_stack))  +				{ +					return; +				} + +				//RN: *always* serialize provided components of BlockValue (don't pass diff_param on), +				// since these tend to be viewed as the constructor arguments for the value T.  It seems +				// cleaner to treat the uniqueness of a BlockValue according to the generated value, and +				// not the individual components.  This way <color red="0" green="1" blue="0"/> will not +				// be exported as <color green="1"/>, since it was probably the intent of the user to  +				// be specific about the RGB color values.  This also fixes an issue where we distinguish +				// between rect.left not being provided and rect.left being explicitly set to 0 (same as default) +				typed_param.BaseBlock::serializeBlock(parser, name_stack, NULL); +			} +		} + +		static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) +		{ +			// first, inspect with actual type... +			parser.inspectValue<T>(name_stack, min_count, max_count, NULL); +			if (TypeValues<T>::getPossibleValues()) +			{ +				//...then inspect with possible string values... +				parser.inspectValue<std::string>(name_stack, min_count, max_count, TypeValues<T>::getPossibleValues()); +			} +			// then recursively inspect contents... +			const self_t& typed_param = static_cast<const self_t&>(param); +			typed_param.inspectBlock(parser, name_stack); +		} + + +		bool isProvided() const  +		{ +			if (!Param::getProvided()) return false; + +			// block has an updated parameter +			// if cached value is stale, regenerate from params +			if (mData.mValueAge == OLDER_THAN_BLOCK) +			{ +				if (block_t::validateBlock(false)) +				{ +					static_cast<const DERIVED*>(this)->setValueFromBlock(); +					// clear stale keyword associated with old value +					mData.clearKey(); +					mData.mValueAge = SAME_AS_BLOCK; +					return true; +				} +				else +				{ +					//block value incomplete, so not considered provided +					// will attempt to revalidate on next call to isProvided() +					return false;   +				} +			} +			else +			{ +				// we have a valid value in hand +				return true; +			} +		} + +		void set(value_assignment_t val, bool flag_as_provided = true) +		{ +			Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided); +			 +			// set param version number to be up to date, so we ignore block contents +			mData.mValueAge = NEWER_THAN_BLOCK; + +			mData.mValue = val; +			mData.clearKey(); +			setProvided(flag_as_provided); +			static_cast<DERIVED*>(this)->setBlockFromValue(); +		} + +		void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true) +		{ +			// don't override any user provided value +			if (!isProvided()) +			{ +				set(val, flag_as_provided); +			} +		} + + 		// propagate change status up to enclosing block +		/*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided) +		{  +			BaseBlock::setLastChangedParam(last_param, user_provided); +			Param::enclosingBlock().setLastChangedParam(*this, user_provided); +			if (user_provided) +			{ +				setProvided(true);  // some component provided +				// a parameter changed, so our value is out of date +				mData.mValueAge = OLDER_THAN_BLOCK; +			} +		} + +	protected: +		value_assignment_t get() const +		{ +			// if some parameters were provided, issue warnings on invalid blocks +			if (Param::getProvided() && (mData.mValueAge == OLDER_THAN_BLOCK)) +			{ +				// go ahead and issue warnings at this point if any param is invalid +				if(block_t::validateBlock(true)) +				{ +					static_cast<const DERIVED*>(this)->setValueFromBlock(); +					mData.clearKey(); +					mData.mValueAge = SAME_AS_BLOCK; +				} +			} + +			return mData.mValue; +		} + + +		struct Data : public key_cache_t +		{ +			Data(const T& value, EValueAge age)  +			:	mValue(value), +				mValueAge(age) +			{} + +			T			mValue; +			EValueAge	mValueAge; +		}; + +		// mutable to allow lazy updates on get +		mutable Data		mData; + +	private: +		static bool mergeWith(Param& dst, const Param& src, bool overwrite) +		{ +			const DERIVED& src_typed_param = static_cast<const DERIVED&>(src); +			DERIVED& dst_typed_param = static_cast<DERIVED&>(dst); + +			if (src_typed_param.isProvided() +				&& (overwrite || !dst_typed_param.isProvided())) +			{ +				if (src_typed_param.mData.mValueAge == NEWER_THAN_BLOCK) +				{ +					// copy value over +					dst_typed_param.set(src_typed_param.get()); +				} +				else +				{ +					// merge individual parameters into destination +					dst_typed_param.merge(block_t::selfBlockDescriptor(), src_typed_param, overwrite); +				} +				return true; +			} +			return false; +		} +	}; + +	template<>  +	struct ParamCompare<LLSD, false> +	{ +		static bool equals(const LLSD &a, const LLSD &b); +	}; +} + +#endif // LL_LLPARAM_H | 
