diff options
Diffstat (limited to 'indra/llxuixml/llinitparam.h')
-rw-r--r-- | indra/llxuixml/llinitparam.h | 1823 |
1 files changed, 1823 insertions, 0 deletions
diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h new file mode 100644 index 0000000000..a1d0831939 --- /dev/null +++ b/indra/llxuixml/llinitparam.h @@ -0,0 +1,1823 @@ +/** + * @file llinitparam.h + * @brief parameter block abstraction for creating complex objects and + * parsing construction parameters from xml and LLSD + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLPARAM_H +#define LL_LLPARAM_H + +#include <vector> + +#include <stddef.h> +#include <boost/function.hpp> +#include <boost/bind.hpp> +#include <boost/range/iterator_range.hpp> +#include "llregistry.h" +#include "llmemory.h" + + +namespace LLInitParam +{ + template <typename T> + class ParamCompare { + public: + static bool equals(const T &a, const T &b); + }; + + template<class T> + bool ParamCompare<T>::equals(const T &a, const T&b) + { + return a == b; + } + + + // 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 boost::iterator_range<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)mEnclosingBlockOffset)); + } + + private: + friend class BaseBlock; + + bool mIsProvided; + S16 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), + 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) + {} + + 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: + // 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 silent = false) 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); + + const Param* getLastChangedParam() const { return mLastChangedParam ? getParamFromHandle(mLastChangedParam) : NULL; } + 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; + virtual bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t()) const; + + const BlockDescriptor& getBlockDescriptor() const { return *mBlockDescriptor; } + BlockDescriptor& getBlockDescriptor() { 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 overwriteFromImpl(BlockDescriptor& block_data, const BaseBlock& other); + + // take all provided params that are not already provided, and apply to self + bool fillFromImpl(BlockDescriptor& block_data, const BaseBlock& other); + + // can be updated in getters + mutable param_handle_t mLastChangedParam; + mutable S32 mChangeVersion; + + BlockDescriptor* mBlockDescriptor; // most derived block descriptor + + static BlockDescriptor 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 the ilk + template<typename T, typename Void = void> + struct is_BaseBlock + : boost::false_type + {}; + + template<typename T> + struct is_BaseBlock<T, typename T::baseblock_base_class_t> + : boost::true_type + {}; + + // 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, + typename VALUE_IS_BLOCK = typename is_BaseBlock<T>::type> + 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 (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.empty()) + { + if (parser.readValue<T>(typed_param.mData.mValue)) + { + 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 = src_typed_param; + 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, boost::true_type> + : 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, boost::true_type> 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 (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.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(true); + 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 (overwrite) + { + if (dst_typed_param.T::overwriteFrom(src_typed_param)) + { + dst_typed_param.mData.clearKey(); + return true; + } + } + else + { + if (dst_typed_param.T::fillFrom(src_typed_param)) + { + 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, boost::false_type> + : public Param + { + public: + typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::false_type> 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 (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.empty()) + { + // 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(); } + + bool hasNValidElements(S32 n) const + { + return mValues.size() >= n; + } + + 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 || !isProvided())) + { + dst_typed_param = src_typed_param; + 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, boost::true_type> + : public Param + { + public: + typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::true_type> 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 (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(true)) 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 = src_typed_param; + 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) + { + mCurChoice = other.mCurChoice; + return BaseBlock::overwriteFromImpl(sBlockDescriptor, other); + } + + // take all provided params that are not already provided, and apply to self + bool fillFrom(const self_t& other) + { + 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(sBlockDescriptor, BaseBlock::sBlockDescriptor, 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> 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::sBlockDescriptor, name, val, NULL, 0, 1), + mOriginalValue(val) + { + // assign initial choice to first declared option + DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::sBlockDescriptor.mCurrentBlockPtr); + if (DERIVED_BLOCK::sBlockDescriptor.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 sBlockDescriptor; + + private: + param_handle_t mCurChoice; + + const Param* getCurrentChoice() const + { + return BaseBlock::getParamFromHandle(mCurChoice); + } + }; + + template<typename DERIVED_BLOCK> + BlockDescriptor + Choice<DERIVED_BLOCK>::sBlockDescriptor; + + //struct CardinalityConstraint + //{ + // virtual std::pair<S32, S32> getRange() = 0; + //}; + + 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; } + }; + + 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::overwriteFromImpl(sBlockDescriptor, other); + } + + // take all provided params that are not already provided, and apply to self + bool fillFrom(const self_t& other) + { + return BaseBlock::fillFromImpl(sBlockDescriptor, other); + } + protected: + Block() + { + //#pragma message("Parsing LLInitParam::Block") + BaseBlock::init(sBlockDescriptor, BASE_BLOCK::sBlockDescriptor, 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> 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::sBlockDescriptor, 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> 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::sBlockDescriptor, 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 = AnyAmount, typename NAME_VALUE_LOOKUP = TypeValues<T> > + class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true> + { + public: + typedef TypedParam<T, NAME_VALUE_LOOKUP, true> 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::sBlockDescriptor, 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::sBlockDescriptor.mCurrentBlockPtr) + { + BlockDescriptor& block_descriptor = DERIVED_BLOCK::sBlockDescriptor; + if (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.empty()) + { + //std::string message = llformat("Deprecated value %s ignored", getName().c_str()); + //parser.parserWarning(message); + return true; + } + + return false; + } + }; + + typedef Deprecated Ignored; + + protected: + static BlockDescriptor sBlockDescriptor; + }; + + template<typename DERIVED_BLOCK, typename BASE_BLOCK> + BlockDescriptor + Block<DERIVED_BLOCK, BASE_BLOCK>::sBlockDescriptor; + + template<typename T, typename DERIVED = TypedParam<T> > + class BlockValue + : public Block<TypedParam<T, TypeValues<T>, false> >, + public Param + { + public: + 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) + { + if (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) + { + self_t& typed_param = static_cast<self_t&>(param); + // type to apply parse direct value T + if (name_stack.empty()) + { + if(parser.readValue<T>(typed_param.mData.mValue)) + { + typed_param.enclosingBlock().setLastChangedParam(param, true); + typed_param.setProvided(true); + typed_param.mData.mLastParamVersion = typed_param.BaseBlock::getLastChangeVersion(); + 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.mLastParamVersion = typed_param.BaseBlock::getLastChangeVersion(); + 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 + { + // either param value provided directly or block is sufficiently filled in + // if cached value is stale, regenerate from params + if (Param::getProvided() && mData.mLastParamVersion < BaseBlock::getLastChangeVersion()) + { + if (block_t::validateBlock(true)) + { + mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock(); + // clear stale keyword associated with old value + mData.clearKey(); + mData.mLastParamVersion = BaseBlock::getLastChangeVersion(); + return true; + } + else + { + //block value incomplete, so not considered provided + // will attempt to revalidate on next call to isProvided() + return false; + } + } + // either no data provided, or we have a valid value in hand + return Param::getProvided(); + } + + 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.mLastParamVersion = BaseBlock::getLastChangeVersion(); + + mData.mValue = val; + mData.clearKey(); + setProvided(flag_as_provided); + } + + 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 + } + } + + protected: + value_assignment_t get() const + { + if (mData.mLastParamVersion < BaseBlock::getLastChangeVersion() && block_t::validateBlock(true)) + { + mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock(); + mData.clearKey(); + mData.mLastParamVersion = BaseBlock::getLastChangeVersion(); + } + + return mData.mValue; + } + + // mutable to allow lazy updates on get + struct Data : public key_cache_t + { + Data(const T& value) + : mValue(value), + mLastParamVersion(0) + {} + + T mValue; + S32 mLastParamVersion; + }; + + mutable Data mData; + + private: + static bool mergeWith(Param& dst, const Param& src, bool overwrite) + { + const self_t& src_param = static_cast<const self_t&>(src); + self_t& dst_typed_param = static_cast<self_t&>(dst); + + if (src_param.isProvided() + && (overwrite || !dst_typed_param.isProvided())) + { + // assign individual parameters + if (overwrite) + { + dst_typed_param.BaseBlock::overwriteFromImpl(block_t::sBlockDescriptor, src_param); + } + else + { + dst_typed_param.BaseBlock::fillFromImpl(block_t::sBlockDescriptor, src_param); + } + // then copy actual value + dst_typed_param.mData.mValue = src_param.get(); + dst_typed_param.mData.clearKey(); + dst_typed_param.setProvided(true); + return true; + } + return false; + } + }; + + template<> + bool ParamCompare<boost::function<void (const std::string &,void *)> >::equals( + const boost::function<void (const std::string &,void *)> &a, + const boost::function<void (const std::string &,void *)> &b); + + template<> + bool ParamCompare<boost::function<void (const LLSD &,const LLSD &)> >::equals( + const boost::function<void (const LLSD &,const LLSD &)> &a, + const boost::function<void (const LLSD &,const LLSD &)> &b); + + template<> + bool ParamCompare<LLSD>::equals(const LLSD &a, const LLSD &b); +} + +#endif // LL_LLPARAM_H |