phoenix-firestorm/indra/llxuixml/llinitparam.h

1830 lines
54 KiB
C++

/**
* @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*)&param);
}
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*)&param, 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<boost::function<void (void)> >::equals(
const boost::function<void (void)> &a,
const boost::function<void (void)> &b);
template<>
bool ParamCompare<LLSD>::equals(const LLSD &a, const LLSD &b);
}
#endif // LL_LLPARAM_H