/** 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