1830 lines
54 KiB
C++
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*)¶m);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T> bool writeValue(const T& param, const name_stack_t& name_stack)
|
|
{
|
|
parser_write_func_map_t::iterator found_it = mParserWriteFuncs.find(&typeid(T));
|
|
if (found_it != mParserWriteFuncs.end())
|
|
{
|
|
return found_it->second((const void*)¶m, name_stack);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// dispatch inspection to registered inspection functions, for each parameter in a param block
|
|
template <typename T> bool inspectValue(const name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values)
|
|
{
|
|
parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs.find(&typeid(T));
|
|
if (found_it != mParserInspectFuncs.end())
|
|
{
|
|
found_it->second(name_stack, min_count, max_count, possible_values);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual std::string getCurrentElementName() = 0;
|
|
virtual void parserWarning(const std::string& message);
|
|
virtual void parserError(const std::string& message);
|
|
void setParseSilently(bool silent) { mParseSilently = silent; }
|
|
bool getParseSilently() { return mParseSilently; }
|
|
|
|
S32 getParseGeneration() { return mParseGeneration; }
|
|
S32 newParseGeneration() { return ++mParseGeneration; }
|
|
|
|
|
|
protected:
|
|
template <typename T>
|
|
void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func)
|
|
{
|
|
mParserReadFuncs.insert(std::make_pair(&typeid(T), read_func));
|
|
mParserWriteFuncs.insert(std::make_pair(&typeid(T), write_func));
|
|
}
|
|
|
|
template <typename T>
|
|
void registerInspectFunc(parser_inspect_func_t inspect_func)
|
|
{
|
|
mParserInspectFuncs.insert(std::make_pair(&typeid(T), inspect_func));
|
|
}
|
|
|
|
bool mParseSilently;
|
|
|
|
private:
|
|
parser_read_func_map_t mParserReadFuncs;
|
|
parser_write_func_map_t mParserWriteFuncs;
|
|
parser_inspect_func_map_t mParserInspectFuncs;
|
|
S32 mParseGeneration;
|
|
};
|
|
|
|
class BaseBlock;
|
|
|
|
class Param
|
|
{
|
|
public:
|
|
// public to allow choice blocks to clear provided flag on stale choices
|
|
void setProvided(bool is_provided) { mIsProvided = is_provided; }
|
|
|
|
protected:
|
|
bool getProvided() const { return mIsProvided; }
|
|
|
|
Param(class BaseBlock* enclosing_block);
|
|
|
|
// store pointer to enclosing block as offset to reduce space and allow for quick copying
|
|
BaseBlock& enclosingBlock() const
|
|
{
|
|
const U8* my_addr = reinterpret_cast<const U8*>(this);
|
|
// get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class
|
|
return *const_cast<BaseBlock*>(
|
|
reinterpret_cast<const BaseBlock*>(my_addr + (ptrdiff_t)mEnclosingBlockOffset));
|
|
}
|
|
|
|
private:
|
|
friend class BaseBlock;
|
|
|
|
bool mIsProvided;
|
|
S16 mEnclosingBlockOffset;
|
|
};
|
|
|
|
// various callbacks and constraints associated with an individual param
|
|
struct ParamDescriptor
|
|
{
|
|
public:
|
|
typedef bool(*merge_func_t)(Param&, const Param&, bool);
|
|
typedef bool(*deserialize_func_t)(Param&, Parser&, const Parser::name_stack_range_t&, S32);
|
|
typedef void(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const Param* diff_param);
|
|
typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count);
|
|
typedef bool(*validation_func_t)(const Param*);
|
|
|
|
ParamDescriptor(param_handle_t p,
|
|
merge_func_t merge_func,
|
|
deserialize_func_t deserialize_func,
|
|
serialize_func_t serialize_func,
|
|
validation_func_t validation_func,
|
|
inspect_func_t inspect_func,
|
|
S32 min_count,
|
|
S32 max_count)
|
|
: mParamHandle(p),
|
|
mMergeFunc(merge_func),
|
|
mDeserializeFunc(deserialize_func),
|
|
mSerializeFunc(serialize_func),
|
|
mValidationFunc(validation_func),
|
|
mInspectFunc(inspect_func),
|
|
mMinCount(min_count),
|
|
mMaxCount(max_count),
|
|
mNumRefs(0)
|
|
{}
|
|
|
|
ParamDescriptor()
|
|
: mParamHandle(0),
|
|
mMergeFunc(NULL),
|
|
mDeserializeFunc(NULL),
|
|
mSerializeFunc(NULL),
|
|
mValidationFunc(NULL),
|
|
mInspectFunc(NULL),
|
|
mMinCount(0),
|
|
mMaxCount(0),
|
|
mGeneration(0),
|
|
mNumRefs(0)
|
|
{}
|
|
|
|
param_handle_t mParamHandle;
|
|
|
|
merge_func_t mMergeFunc;
|
|
deserialize_func_t mDeserializeFunc;
|
|
serialize_func_t mSerializeFunc;
|
|
inspect_func_t mInspectFunc;
|
|
validation_func_t mValidationFunc;
|
|
S32 mMinCount;
|
|
S32 mMaxCount;
|
|
S32 mGeneration;
|
|
S32 mNumRefs;
|
|
};
|
|
|
|
// each derived Block class keeps a static data structure maintaining offsets to various params
|
|
class BlockDescriptor
|
|
{
|
|
public:
|
|
BlockDescriptor()
|
|
: mMaxParamOffset(0),
|
|
mInitializationState(UNINITIALIZED)
|
|
{}
|
|
|
|
typedef enum e_initialization_state
|
|
{
|
|
UNINITIALIZED,
|
|
INITIALIZING,
|
|
INITIALIZED
|
|
} EInitializationState;
|
|
|
|
void aggregateBlockData(BlockDescriptor& src_block_data);
|
|
|
|
public:
|
|
typedef std::map<const std::string, ParamDescriptor*> param_map_t; // references param descriptors stored in mAllParams
|
|
typedef std::vector<ParamDescriptor*> param_list_t;
|
|
|
|
typedef std::list<ParamDescriptor> all_params_list_t;// references param descriptors stored in mAllParams
|
|
typedef std::vector<std::pair<param_handle_t, ParamDescriptor::validation_func_t> > param_validation_list_t;
|
|
|
|
param_map_t mNamedParams; // parameters with associated names
|
|
param_map_t mSynonyms; // parameters with alternate names
|
|
param_list_t mUnnamedParams; // parameters with_out_ associated names
|
|
param_validation_list_t mValidationList; // parameters that must be validated
|
|
all_params_list_t mAllParams; // all parameters, owns descriptors
|
|
|
|
size_t mMaxParamOffset;
|
|
|
|
EInitializationState mInitializationState; // whether or not static block data has been initialized
|
|
class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed
|
|
};
|
|
|
|
class BaseBlock
|
|
{
|
|
public:
|
|
// this typedef identifies derived classes as being blocks
|
|
typedef void baseblock_base_class_t;
|
|
LOG_CLASS(BaseBlock);
|
|
friend class Param;
|
|
|
|
BaseBlock();
|
|
virtual ~BaseBlock();
|
|
bool submitValue(const Parser::name_stack_t& name_stack, Parser& p, bool silent=false);
|
|
|
|
param_handle_t getHandleFromParam(const Param* param) const;
|
|
bool validateBlock(bool silent = false) const;
|
|
|
|
Param* getParamFromHandle(const param_handle_t param_handle)
|
|
{
|
|
if (param_handle == 0) return NULL;
|
|
U8* baseblock_address = reinterpret_cast<U8*>(this);
|
|
return reinterpret_cast<Param*>(baseblock_address + param_handle);
|
|
}
|
|
|
|
const Param* getParamFromHandle(const param_handle_t param_handle) const
|
|
{
|
|
const U8* baseblock_address = reinterpret_cast<const U8*>(this);
|
|
return reinterpret_cast<const Param*>(baseblock_address + param_handle);
|
|
}
|
|
|
|
void addSynonym(Param& param, const std::string& synonym);
|
|
|
|
// Blocks can override this to do custom tracking of changes
|
|
virtual void setLastChangedParam(const Param& last_param, bool user_provided);
|
|
|
|
const Param* getLastChangedParam() const { return mLastChangedParam ? getParamFromHandle(mLastChangedParam) : NULL; }
|
|
S32 getLastChangeVersion() const { return mChangeVersion; }
|
|
bool isDefault() const { return mChangeVersion == 0; }
|
|
|
|
bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack);
|
|
bool serializeBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), const BaseBlock* diff_block = NULL) const;
|
|
virtual bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t()) const;
|
|
|
|
const BlockDescriptor& getBlockDescriptor() const { return *mBlockDescriptor; }
|
|
BlockDescriptor& getBlockDescriptor() { return *mBlockDescriptor; }
|
|
|
|
// take all provided params from other and apply to self
|
|
bool overwriteFrom(const BaseBlock& other)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// take all provided params that are not already provided, and apply to self
|
|
bool fillFrom(const BaseBlock& other)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static void addParam(BlockDescriptor& block_data, const ParamDescriptor& param, const char* name);
|
|
protected:
|
|
void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size);
|
|
|
|
|
|
// take all provided params from other and apply to self
|
|
bool overwriteFromImpl(BlockDescriptor& block_data, const BaseBlock& other);
|
|
|
|
// take all provided params that are not already provided, and apply to self
|
|
bool fillFromImpl(BlockDescriptor& block_data, const BaseBlock& other);
|
|
|
|
// can be updated in getters
|
|
mutable param_handle_t mLastChangedParam;
|
|
mutable S32 mChangeVersion;
|
|
|
|
BlockDescriptor* mBlockDescriptor; // most derived block descriptor
|
|
|
|
static BlockDescriptor sBlockDescriptor;
|
|
|
|
private:
|
|
const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const;
|
|
ParamDescriptor* findParamDescriptor(param_handle_t handle);
|
|
};
|
|
|
|
|
|
template<typename T>
|
|
struct ParamIterator
|
|
{
|
|
typedef typename std::vector<T>::const_iterator const_iterator;
|
|
typedef typename std::vector<T>::iterator iterator;
|
|
};
|
|
|
|
// these templates allow us to distinguish between template parameters
|
|
// that derive from BaseBlock and those that don't
|
|
// this is supposedly faster than boost::is_convertible and the ilk
|
|
template<typename T, typename Void = void>
|
|
struct is_BaseBlock
|
|
: boost::false_type
|
|
{};
|
|
|
|
template<typename T>
|
|
struct is_BaseBlock<T, typename T::baseblock_base_class_t>
|
|
: boost::true_type
|
|
{};
|
|
|
|
// specialize for custom parsing/decomposition of specific classes
|
|
// e.g. TypedParam<LLRect> has left, top, right, bottom, etc...
|
|
template<typename T,
|
|
typename NAME_VALUE_LOOKUP = TypeValues<T>,
|
|
bool HAS_MULTIPLE_VALUES = false,
|
|
typename VALUE_IS_BLOCK = typename is_BaseBlock<T>::type>
|
|
class TypedParam
|
|
: public Param
|
|
{
|
|
public:
|
|
typedef const T& value_const_ref_t;
|
|
typedef value_const_ref_t value_assignment_t;
|
|
typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
|
|
typedef TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK> self_t;
|
|
|
|
TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
|
: Param(block_descriptor.mCurrentBlockPtr)
|
|
{
|
|
if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
|
|
{
|
|
ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
&mergeWith,
|
|
&deserializeParam,
|
|
&serializeParam,
|
|
validate_func,
|
|
&inspectParam,
|
|
min_count, max_count);
|
|
BaseBlock::addParam(block_descriptor, param_descriptor, name);
|
|
}
|
|
|
|
mData.mValue = value;
|
|
}
|
|
|
|
bool isProvided() const { return Param::getProvided(); }
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
|
|
{
|
|
self_t& typed_param = static_cast<self_t&>(param);
|
|
// no further names in stack, attempt to parse value now
|
|
if (name_stack.empty())
|
|
{
|
|
if (parser.readValue<T>(typed_param.mData.mValue))
|
|
{
|
|
typed_param.setProvided(true);
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
return true;
|
|
}
|
|
|
|
// try to parse a known named value
|
|
if(!NAME_VALUE_LOOKUP::empty())
|
|
{
|
|
// try to parse a known named value
|
|
std::string name;
|
|
if (parser.readValue<std::string>(name))
|
|
{
|
|
// try to parse a per type named value
|
|
if (NAME_VALUE_LOOKUP::get(name, typed_param.mData.mValue))
|
|
{
|
|
typed_param.mData.setKey(name);
|
|
typed_param.setProvided(true);
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
return true;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
|
|
{
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
if (!typed_param.isProvided()) return;
|
|
|
|
if (!name_stack.empty())
|
|
{
|
|
name_stack.back().second = parser.newParseGeneration();
|
|
}
|
|
|
|
std::string key = typed_param.mData.getKey();
|
|
|
|
// first try to write out name of name/value pair
|
|
|
|
if (!key.empty())
|
|
{
|
|
if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->mData.getKey(), key))
|
|
{
|
|
if (!parser.writeValue<std::string>(key, name_stack))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// then try to serialize value directly
|
|
else if (!diff_param || !ParamCompare<T>::equals(typed_param.get(), static_cast<const self_t*>(diff_param)->get())) {
|
|
if (!parser.writeValue<T>(typed_param.mData.mValue, name_stack))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
|
{
|
|
// tell parser about our actual type
|
|
parser.inspectValue<T>(name_stack, min_count, max_count, NULL);
|
|
// then tell it about string-based alternatives ("red", "blue", etc. for LLColor4)
|
|
if (NAME_VALUE_LOOKUP::getPossibleValues())
|
|
{
|
|
parser.inspectValue<std::string>(name_stack, min_count, max_count, NAME_VALUE_LOOKUP::getPossibleValues());
|
|
}
|
|
}
|
|
|
|
void set(value_assignment_t val, bool flag_as_provided = true)
|
|
{
|
|
mData.mValue = val;
|
|
mData.clearKey();
|
|
setProvided(flag_as_provided);
|
|
Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
|
|
}
|
|
|
|
void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
|
|
{
|
|
if (!isProvided())
|
|
{
|
|
set(val, flag_as_provided);
|
|
}
|
|
}
|
|
|
|
// implicit conversion
|
|
operator value_assignment_t() const { return get(); }
|
|
// explicit conversion
|
|
value_assignment_t operator()() const { return get(); }
|
|
|
|
protected:
|
|
value_assignment_t get() const
|
|
{
|
|
return mData.mValue;
|
|
}
|
|
|
|
static bool mergeWith(Param& dst, const Param& src, bool overwrite)
|
|
{
|
|
const self_t& src_typed_param = static_cast<const self_t&>(src);
|
|
self_t& dst_typed_param = static_cast<self_t&>(dst);
|
|
if (src_typed_param.isProvided()
|
|
&& (overwrite || !dst_typed_param.isProvided()))
|
|
{
|
|
dst_typed_param.mData.clearKey();
|
|
dst_typed_param = src_typed_param;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct Data : public key_cache_t
|
|
{
|
|
T mValue;
|
|
};
|
|
|
|
Data mData;
|
|
};
|
|
|
|
// parameter that is a block
|
|
template <typename T, typename NAME_VALUE_LOOKUP>
|
|
class TypedParam<T, NAME_VALUE_LOOKUP, false, boost::true_type>
|
|
: public T,
|
|
public Param
|
|
{
|
|
public:
|
|
typedef const T value_const_t;
|
|
typedef T value_t;
|
|
typedef value_const_t& value_const_ref_t;
|
|
typedef value_const_ref_t value_assignment_t;
|
|
typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
|
|
typedef TypedParam<T, NAME_VALUE_LOOKUP, false, boost::true_type> self_t;
|
|
|
|
TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
|
: Param(block_descriptor.mCurrentBlockPtr),
|
|
T(value)
|
|
{
|
|
if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
|
|
{
|
|
ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
&mergeWith,
|
|
&deserializeParam,
|
|
&serializeParam,
|
|
validate_func,
|
|
&inspectParam,
|
|
min_count, max_count);
|
|
BaseBlock::addParam(block_descriptor, param_descriptor, name);
|
|
}
|
|
}
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
|
|
{
|
|
self_t& typed_param = static_cast<self_t&>(param);
|
|
// attempt to parse block...
|
|
if(typed_param.deserializeBlock(parser, name_stack))
|
|
{
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
return true;
|
|
}
|
|
|
|
if(!NAME_VALUE_LOOKUP::empty())
|
|
{
|
|
// try to parse a known named value
|
|
std::string name;
|
|
if (parser.readValue<std::string>(name))
|
|
{
|
|
// try to parse a per type named value
|
|
if (NAME_VALUE_LOOKUP::get(name, typed_param))
|
|
{
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
typed_param.mData.setKey(name);
|
|
typed_param.mData.mKeyVersion = typed_param.getLastChangeVersion();
|
|
return true;
|
|
}
|
|
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
|
|
{
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
if (!name_stack.empty())
|
|
{
|
|
name_stack.back().second = parser.newParseGeneration();
|
|
}
|
|
|
|
std::string key = typed_param.mData.getKey();
|
|
if (!key.empty() && typed_param.mData.mKeyVersion == typed_param.getLastChangeVersion())
|
|
{
|
|
if (!parser.writeValue<std::string>(key, name_stack))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
typed_param.serializeBlock(parser, name_stack, static_cast<const self_t*>(diff_param));
|
|
}
|
|
}
|
|
|
|
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
|
{
|
|
// I am a param that is also a block, so just recurse into my contents
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
typed_param.inspectBlock(parser, name_stack);
|
|
}
|
|
|
|
// a param-that-is-a-block is provided when the user has set one of its child params
|
|
// *and* the block as a whole validates
|
|
bool isProvided() const
|
|
{
|
|
// only validate block when it hasn't already passed validation and user has supplied *some* value
|
|
if (Param::getProvided() && mData.mValidatedVersion < T::getLastChangeVersion())
|
|
{
|
|
// a sub-block is "provided" when it has been filled in enough to be valid
|
|
mData.mValidated = T::validateBlock(true);
|
|
mData.mValidatedVersion = T::getLastChangeVersion();
|
|
}
|
|
return Param::getProvided() && mData.mValidated;
|
|
}
|
|
|
|
// assign block contents to this param-that-is-a-block
|
|
void set(value_assignment_t val, bool flag_as_provided = true)
|
|
{
|
|
value_t::operator=(val);
|
|
mData.clearKey();
|
|
// force revalidation of block by clearing known provided version
|
|
// next call to isProvided() will update provision status based on validity
|
|
mData.mValidatedVersion = 0;
|
|
setProvided(flag_as_provided);
|
|
Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
|
|
}
|
|
|
|
void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
|
|
{
|
|
if (!isProvided())
|
|
{
|
|
set(val, flag_as_provided);
|
|
}
|
|
}
|
|
|
|
// propagate changed status up to enclosing block
|
|
/*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
|
|
{
|
|
T::setLastChangedParam(last_param, user_provided);
|
|
Param::enclosingBlock().setLastChangedParam(*this, user_provided);
|
|
if (user_provided)
|
|
{
|
|
// a child param has been explicitly changed
|
|
// so *some* aspect of this block is now provided
|
|
setProvided(true);
|
|
}
|
|
}
|
|
|
|
// implicit conversion
|
|
operator value_assignment_t() const { return get(); }
|
|
// explicit conversion
|
|
value_assignment_t operator()() const { return get(); }
|
|
|
|
protected:
|
|
value_assignment_t get() const
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
static bool mergeWith(Param& dst, const Param& src, bool overwrite)
|
|
{
|
|
const self_t& src_typed_param = static_cast<const self_t&>(src);
|
|
self_t& dst_typed_param = static_cast<self_t&>(dst);
|
|
if (overwrite)
|
|
{
|
|
if (dst_typed_param.T::overwriteFrom(src_typed_param))
|
|
{
|
|
dst_typed_param.mData.clearKey();
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dst_typed_param.T::fillFrom(src_typed_param))
|
|
{
|
|
dst_typed_param.mData.clearKey();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct Data : public key_cache_t
|
|
{
|
|
S32 mKeyVersion;
|
|
mutable S32 mValidatedVersion;
|
|
mutable bool mValidated; // lazy validation flag
|
|
|
|
Data()
|
|
: mKeyVersion(0),
|
|
mValidatedVersion(0),
|
|
mValidated(false)
|
|
{}
|
|
};
|
|
Data mData;
|
|
};
|
|
|
|
// container of non-block parameters
|
|
template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
|
|
class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::false_type>
|
|
: public Param
|
|
{
|
|
public:
|
|
typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::false_type> self_t;
|
|
typedef typename std::vector<VALUE_TYPE> container_t;
|
|
typedef const container_t& value_assignment_t;
|
|
|
|
typedef VALUE_TYPE value_t;
|
|
typedef value_t& value_ref_t;
|
|
typedef const value_t& value_const_ref_t;
|
|
|
|
typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
|
|
|
|
TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
|
: Param(block_descriptor.mCurrentBlockPtr),
|
|
mValues(value)
|
|
{
|
|
mCachedKeys.resize(mValues.size());
|
|
if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
|
|
{
|
|
ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
&mergeWith,
|
|
&deserializeParam,
|
|
&serializeParam,
|
|
validate_func,
|
|
&inspectParam,
|
|
min_count, max_count);
|
|
BaseBlock::addParam(block_descriptor, param_descriptor, name);
|
|
}
|
|
}
|
|
|
|
bool isProvided() const { return Param::getProvided(); }
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
|
|
{
|
|
self_t& typed_param = static_cast<self_t&>(param);
|
|
value_t value;
|
|
// no further names in stack, attempt to parse value now
|
|
if (name_stack.empty())
|
|
{
|
|
// attempt to read value directly
|
|
if (parser.readValue<value_t>(value))
|
|
{
|
|
typed_param.mValues.push_back(value);
|
|
// save an empty name/value key as a placeholder
|
|
typed_param.mCachedKeys.push_back(key_cache_t());
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
typed_param.setProvided(true);
|
|
return true;
|
|
}
|
|
|
|
// try to parse a known named value
|
|
if(!NAME_VALUE_LOOKUP::empty())
|
|
{
|
|
// try to parse a known named value
|
|
std::string name;
|
|
if (parser.readValue<std::string>(name))
|
|
{
|
|
// try to parse a per type named value
|
|
if (NAME_VALUE_LOOKUP::get(name, typed_param.mValues))
|
|
{
|
|
typed_param.mValues.push_back(value);
|
|
typed_param.mCachedKeys.push_back(key_cache_t());
|
|
typed_param.mCachedKeys.back().setKey(name);
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
typed_param.setProvided(true);
|
|
return true;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
|
|
{
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
if (!typed_param.isProvided() || name_stack.empty()) return;
|
|
|
|
typename container_t::const_iterator it = typed_param.mValues.begin();
|
|
for (typename std::vector<key_cache_t>::const_iterator key_it = typed_param.mCachedKeys.begin();
|
|
it != typed_param.mValues.end();
|
|
++key_it, ++it)
|
|
{
|
|
std::string key = key_it->get();
|
|
name_stack.back().second = parser.newParseGeneration();
|
|
|
|
if(!key.empty())
|
|
{
|
|
if(!parser.writeValue<std::string>(key, name_stack))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
// not parse via name values, write out value directly
|
|
else if (!parser.writeValue<VALUE_TYPE>(*it, name_stack))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
|
{
|
|
parser.inspectValue<VALUE_TYPE>(name_stack, min_count, max_count, NULL);
|
|
if (NAME_VALUE_LOOKUP::getPossibleValues())
|
|
{
|
|
parser.inspectValue<std::string>(name_stack, min_count, max_count, NAME_VALUE_LOOKUP::getPossibleValues());
|
|
}
|
|
}
|
|
|
|
void set(value_assignment_t val, bool flag_as_provided = true)
|
|
{
|
|
mValues = val;
|
|
mCachedKeys.clear();
|
|
mCachedKeys.resize(mValues.size());
|
|
setProvided(flag_as_provided);
|
|
Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
|
|
}
|
|
|
|
|
|
void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
|
|
{
|
|
if (!isProvided())
|
|
{
|
|
set(val, flag_as_provided);
|
|
}
|
|
}
|
|
|
|
value_ref_t add()
|
|
{
|
|
mValues.push_back(value_t());
|
|
mCachedKeys.push_back(key_cache_t());
|
|
setProvided(true);
|
|
return mValues.back();
|
|
}
|
|
|
|
void add(value_const_ref_t item)
|
|
{
|
|
mValues.push_back(item);
|
|
mCachedKeys.push_back(key_cache_t());
|
|
setProvided(true);
|
|
}
|
|
|
|
// implicit conversion
|
|
operator value_assignment_t() const { return self_t::get(); }
|
|
// explicit conversion
|
|
value_assignment_t operator()() const { return get(); }
|
|
|
|
bool hasNValidElements(S32 n) const
|
|
{
|
|
return mValues.size() >= n;
|
|
}
|
|
|
|
protected:
|
|
value_assignment_t get() const
|
|
{
|
|
return mValues;
|
|
}
|
|
|
|
static bool mergeWith(Param& dst, const Param& src, bool overwrite)
|
|
{
|
|
const self_t& src_typed_param = static_cast<const self_t&>(src);
|
|
self_t& dst_typed_param = static_cast<self_t&>(dst);
|
|
|
|
if (src_typed_param.isProvided()
|
|
&& (overwrite || !isProvided()))
|
|
{
|
|
dst_typed_param = src_typed_param;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
container_t mValues;
|
|
std::vector<key_cache_t> mCachedKeys;
|
|
};
|
|
|
|
// container of block parameters
|
|
template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
|
|
class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::true_type>
|
|
: public Param
|
|
{
|
|
public:
|
|
typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::true_type> self_t;
|
|
typedef typename std::vector<VALUE_TYPE> container_t;
|
|
typedef const container_t& value_assignment_t;
|
|
|
|
typedef VALUE_TYPE value_t;
|
|
typedef value_t& value_ref_t;
|
|
typedef const value_t& value_const_ref_t;
|
|
|
|
typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
|
|
|
|
TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
|
: Param(block_descriptor.mCurrentBlockPtr),
|
|
mValues(value),
|
|
mLastParamGeneration(0)
|
|
{
|
|
mCachedKeys.resize(mValues.size());
|
|
if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
|
|
{
|
|
ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
&mergeWith,
|
|
&deserializeParam,
|
|
&serializeParam,
|
|
validate_func,
|
|
&inspectParam,
|
|
min_count, max_count);
|
|
BaseBlock::addParam(block_descriptor, param_descriptor, name);
|
|
}
|
|
}
|
|
|
|
bool isProvided() const { return Param::getProvided(); }
|
|
|
|
value_ref_t operator[](S32 index) { return mValues[index]; }
|
|
value_const_ref_t operator[](S32 index) const { return mValues[index]; }
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
|
|
{
|
|
self_t& typed_param = static_cast<self_t&>(param);
|
|
if (generation != typed_param.mLastParamGeneration || typed_param.mValues.empty())
|
|
{
|
|
typed_param.mValues.push_back(value_t());
|
|
typed_param.mCachedKeys.push_back(Data());
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
typed_param.mLastParamGeneration = generation;
|
|
}
|
|
|
|
value_t& value = typed_param.mValues.back();
|
|
|
|
// attempt to parse block...
|
|
if(value.deserializeBlock(parser, name_stack))
|
|
{
|
|
typed_param.setProvided(true);
|
|
return true;
|
|
}
|
|
|
|
if(!NAME_VALUE_LOOKUP::empty())
|
|
{
|
|
// try to parse a known named value
|
|
std::string name;
|
|
if (parser.readValue<std::string>(name))
|
|
{
|
|
// try to parse a per type named value
|
|
if (NAME_VALUE_LOOKUP::get(name, value))
|
|
{
|
|
typed_param.mCachedKeys.back().setKey(name);
|
|
typed_param.mCachedKeys.back().mKeyVersion = value.getLastChangeVersion();
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
typed_param.setProvided(true);
|
|
return true;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
|
|
{
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
if (!typed_param.isProvided() || name_stack.empty()) return;
|
|
|
|
typename container_t::const_iterator it = typed_param.mValues.begin();
|
|
for (typename std::vector<Data>::const_iterator key_it = typed_param.mCachedKeys.begin();
|
|
it != typed_param.mValues.end();
|
|
++key_it, ++it)
|
|
{
|
|
name_stack.back().second = parser.newParseGeneration();
|
|
|
|
std::string key = key_it->getKey();
|
|
if (!key.empty() && key_it->mKeyVersion == it->getLastChangeVersion())
|
|
{
|
|
if(!parser.writeValue<std::string>(key, name_stack))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
// Not parsed via named values, write out value directly
|
|
// NOTE: currently we don't worry about removing default values in Multiple
|
|
else if (!it->serializeBlock(parser, name_stack, NULL))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
|
{
|
|
// I am a vector of blocks, so describe my contents recursively
|
|
value_t().inspectBlock(parser, name_stack);
|
|
}
|
|
|
|
void set(value_assignment_t val, bool flag_as_provided = true)
|
|
{
|
|
mValues = val;
|
|
mCachedKeys.clear();
|
|
mCachedKeys.resize(mValues.size());
|
|
setProvided(flag_as_provided);
|
|
Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
|
|
}
|
|
|
|
void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
|
|
{
|
|
if (!isProvided())
|
|
{
|
|
set(val, flag_as_provided);
|
|
}
|
|
}
|
|
|
|
value_ref_t add()
|
|
{
|
|
mValues.push_back(value_t());
|
|
mCachedKeys.push_back(Data());
|
|
setProvided(true);
|
|
return mValues.back();
|
|
}
|
|
|
|
void add(value_const_ref_t item)
|
|
{
|
|
mValues.push_back(item);
|
|
mCachedKeys.push_back(Data());
|
|
setProvided(true);
|
|
}
|
|
|
|
// implicit conversion
|
|
operator value_assignment_t() const { return self_t::get(); }
|
|
// explicit conversion
|
|
value_assignment_t operator()() const { return get(); }
|
|
|
|
U32 numValidElements() const
|
|
{
|
|
U32 count = 0;
|
|
for (typename container_t::const_iterator it = mValues.begin();
|
|
it != mValues.end();
|
|
++it)
|
|
{
|
|
if(it->validateBlock(true)) count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
protected:
|
|
value_assignment_t get() const
|
|
{
|
|
return mValues;
|
|
}
|
|
|
|
static bool mergeWith(Param& dst, const Param& src, bool overwrite)
|
|
{
|
|
const self_t& src_typed_param = static_cast<const self_t&>(src);
|
|
self_t& dst_typed_param = static_cast<self_t&>(dst);
|
|
|
|
if (src_typed_param.isProvided()
|
|
&& (overwrite || !dst_typed_param.isProvided()))
|
|
{
|
|
dst_typed_param = src_typed_param;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct Data : public key_cache_t
|
|
{
|
|
S32 mKeyVersion; // version of block for which key was last valid
|
|
|
|
Data() : mKeyVersion(0) {}
|
|
};
|
|
|
|
container_t mValues;
|
|
std::vector<Data> mCachedKeys;
|
|
|
|
S32 mLastParamGeneration;
|
|
};
|
|
|
|
template <typename DERIVED_BLOCK>
|
|
class Choice : public BaseBlock
|
|
{
|
|
typedef Choice<DERIVED_BLOCK> self_t;
|
|
typedef Choice<DERIVED_BLOCK> enclosing_block_t;
|
|
|
|
LOG_CLASS(self_t);
|
|
public:
|
|
// take all provided params from other and apply to self
|
|
bool overwriteFrom(const self_t& other)
|
|
{
|
|
mCurChoice = other.mCurChoice;
|
|
return BaseBlock::overwriteFromImpl(sBlockDescriptor, other);
|
|
}
|
|
|
|
// take all provided params that are not already provided, and apply to self
|
|
bool fillFrom(const self_t& other)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// clear out old choice when param has changed
|
|
/*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
|
|
{
|
|
param_handle_t changed_param_handle = BaseBlock::getHandleFromParam(&last_param);
|
|
// if we have a new choice...
|
|
if (changed_param_handle != mCurChoice)
|
|
{
|
|
// clear provided flag on previous choice
|
|
Param* previous_choice = BaseBlock::getParamFromHandle(mCurChoice);
|
|
if (previous_choice)
|
|
{
|
|
previous_choice->setProvided(false);
|
|
}
|
|
mCurChoice = changed_param_handle;
|
|
}
|
|
BaseBlock::setLastChangedParam(last_param, user_provided);
|
|
}
|
|
|
|
protected:
|
|
Choice()
|
|
: mCurChoice(0)
|
|
{
|
|
BaseBlock::init(sBlockDescriptor, BaseBlock::sBlockDescriptor, sizeof(DERIVED_BLOCK));
|
|
}
|
|
|
|
// Alternatives are mutually exclusive wrt other Alternatives in the same block.
|
|
// One alternative in a block will always have isChosen() == true.
|
|
// At most one alternative in a block will have isProvided() == true.
|
|
template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
|
|
class Alternative : public TypedParam<T, NAME_VALUE_LOOKUP, false>
|
|
{
|
|
public:
|
|
friend class Choice<DERIVED_BLOCK>;
|
|
|
|
typedef Alternative<T, NAME_VALUE_LOOKUP> self_t;
|
|
typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
|
|
typedef typename super_t::value_assignment_t value_assignment_t;
|
|
|
|
explicit Alternative(const char* name, value_assignment_t val = DefaultInitializer<T>::get())
|
|
: super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, NULL, 0, 1),
|
|
mOriginalValue(val)
|
|
{
|
|
// assign initial choice to first declared option
|
|
DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::sBlockDescriptor.mCurrentBlockPtr);
|
|
if (DERIVED_BLOCK::sBlockDescriptor.mInitializationState == BlockDescriptor::INITIALIZING
|
|
&& blockp->mCurChoice == 0)
|
|
{
|
|
blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this);
|
|
}
|
|
}
|
|
|
|
Alternative& operator=(value_assignment_t val)
|
|
{
|
|
super_t::set(val);
|
|
return *this;
|
|
}
|
|
|
|
void operator()(typename super_t::value_assignment_t val)
|
|
{
|
|
super_t::set(val);
|
|
}
|
|
|
|
operator value_assignment_t() const
|
|
{
|
|
if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this)
|
|
{
|
|
return super_t::get();
|
|
}
|
|
return mOriginalValue;
|
|
}
|
|
|
|
value_assignment_t operator()() const
|
|
{
|
|
if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this)
|
|
{
|
|
return super_t::get();
|
|
}
|
|
return mOriginalValue;
|
|
}
|
|
|
|
bool isChosen() const
|
|
{
|
|
return static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this;
|
|
}
|
|
|
|
private:
|
|
T mOriginalValue;
|
|
};
|
|
|
|
protected:
|
|
static BlockDescriptor sBlockDescriptor;
|
|
|
|
private:
|
|
param_handle_t mCurChoice;
|
|
|
|
const Param* getCurrentChoice() const
|
|
{
|
|
return BaseBlock::getParamFromHandle(mCurChoice);
|
|
}
|
|
};
|
|
|
|
template<typename DERIVED_BLOCK>
|
|
BlockDescriptor
|
|
Choice<DERIVED_BLOCK>::sBlockDescriptor;
|
|
|
|
//struct CardinalityConstraint
|
|
//{
|
|
// virtual std::pair<S32, S32> getRange() = 0;
|
|
//};
|
|
|
|
struct AnyAmount
|
|
{
|
|
static U32 minCount() { return 0; }
|
|
static U32 maxCount() { return U32_MAX; }
|
|
};
|
|
|
|
template<U32 MIN_AMOUNT>
|
|
struct AtLeast
|
|
{
|
|
static U32 minCount() { return MIN_AMOUNT; }
|
|
static U32 maxCount() { return U32_MAX; }
|
|
};
|
|
|
|
template<U32 MAX_AMOUNT>
|
|
struct AtMost
|
|
{
|
|
static U32 minCount() { return 0; }
|
|
static U32 maxCount() { return MAX_AMOUNT; }
|
|
};
|
|
|
|
template<U32 MIN_AMOUNT, U32 MAX_AMOUNT>
|
|
struct Between
|
|
{
|
|
static U32 minCount() { return MIN_AMOUNT; }
|
|
static U32 maxCount() { return MAX_AMOUNT; }
|
|
};
|
|
|
|
template<U32 EXACT_COUNT>
|
|
struct Exactly
|
|
{
|
|
static U32 minCount() { return EXACT_COUNT; }
|
|
static U32 maxCount() { return EXACT_COUNT; }
|
|
};
|
|
|
|
template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock>
|
|
class Block
|
|
: public BASE_BLOCK
|
|
{
|
|
typedef Block<DERIVED_BLOCK, BASE_BLOCK> self_t;
|
|
typedef Block<DERIVED_BLOCK, BASE_BLOCK> block_t;
|
|
|
|
public:
|
|
typedef BASE_BLOCK base_block_t;
|
|
|
|
// take all provided params from other and apply to self
|
|
bool overwriteFrom(const self_t& other)
|
|
{
|
|
return BaseBlock::overwriteFromImpl(sBlockDescriptor, other);
|
|
}
|
|
|
|
// take all provided params that are not already provided, and apply to self
|
|
bool fillFrom(const self_t& other)
|
|
{
|
|
return BaseBlock::fillFromImpl(sBlockDescriptor, other);
|
|
}
|
|
protected:
|
|
Block()
|
|
{
|
|
//#pragma message("Parsing LLInitParam::Block")
|
|
BaseBlock::init(sBlockDescriptor, BASE_BLOCK::sBlockDescriptor, sizeof(DERIVED_BLOCK));
|
|
}
|
|
|
|
//
|
|
// Nested classes for declaring parameters
|
|
//
|
|
template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
|
|
class Optional : public TypedParam<T, NAME_VALUE_LOOKUP, false>
|
|
{
|
|
public:
|
|
typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
|
|
typedef typename super_t::value_assignment_t value_assignment_t;
|
|
|
|
explicit Optional(const char* name = "", value_assignment_t val = DefaultInitializer<T>::get())
|
|
: super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, NULL, 0, 1)
|
|
{
|
|
//#pragma message("Parsing LLInitParam::Block::Optional")
|
|
}
|
|
|
|
Optional& operator=(value_assignment_t val)
|
|
{
|
|
set(val);
|
|
return *this;
|
|
}
|
|
|
|
DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
|
|
{
|
|
super_t::set(val);
|
|
return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
|
|
}
|
|
using super_t::operator();
|
|
};
|
|
|
|
template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
|
|
class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false>
|
|
{
|
|
public:
|
|
typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
|
|
typedef Mandatory<T, NAME_VALUE_LOOKUP> self_t;
|
|
typedef typename super_t::value_assignment_t value_assignment_t;
|
|
|
|
// mandatory parameters require a name to be parseable
|
|
explicit Mandatory(const char* name = "", value_assignment_t val = DefaultInitializer<T>::get())
|
|
: super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, &validate, 1, 1)
|
|
{}
|
|
|
|
Mandatory& operator=(value_assignment_t val)
|
|
{
|
|
set(val);
|
|
return *this;
|
|
}
|
|
|
|
DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
|
|
{
|
|
super_t::set(val);
|
|
return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
|
|
}
|
|
using super_t::operator();
|
|
|
|
static bool validate(const Param* p)
|
|
{
|
|
// valid only if provided
|
|
return static_cast<const self_t*>(p)->isProvided();
|
|
}
|
|
|
|
};
|
|
|
|
template <typename T, typename RANGE = AnyAmount, typename NAME_VALUE_LOOKUP = TypeValues<T> >
|
|
class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true>
|
|
{
|
|
public:
|
|
typedef TypedParam<T, NAME_VALUE_LOOKUP, true> super_t;
|
|
typedef Multiple<T, RANGE, NAME_VALUE_LOOKUP> self_t;
|
|
typedef typename super_t::container_t container_t;
|
|
typedef typename super_t::value_assignment_t value_assignment_t;
|
|
typedef typename container_t::iterator iterator;
|
|
typedef typename container_t::const_iterator const_iterator;
|
|
|
|
explicit Multiple(const char* name = "", value_assignment_t val = DefaultInitializer<container_t>::get())
|
|
: super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, &validate, RANGE::minCount(), RANGE::maxCount())
|
|
{}
|
|
|
|
using super_t::operator();
|
|
|
|
Multiple& operator=(value_assignment_t val)
|
|
{
|
|
set(val);
|
|
return *this;
|
|
}
|
|
|
|
DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
|
|
{
|
|
super_t::set(val);
|
|
return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
|
|
}
|
|
|
|
static bool validate(const Param* paramp)
|
|
{
|
|
U32 num_valid = ((super_t*)paramp)->numValidElements();
|
|
return RANGE::minCount() <= num_valid && num_valid <= RANGE::maxCount();
|
|
}
|
|
};
|
|
|
|
class Deprecated : public Param
|
|
{
|
|
public:
|
|
explicit Deprecated(const char* name)
|
|
: Param(DERIVED_BLOCK::sBlockDescriptor.mCurrentBlockPtr)
|
|
{
|
|
BlockDescriptor& block_descriptor = DERIVED_BLOCK::sBlockDescriptor;
|
|
if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
|
|
{
|
|
ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
NULL,
|
|
&deserializeParam,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0, S32_MAX);
|
|
BaseBlock::addParam(block_descriptor, param_descriptor, name);
|
|
}
|
|
}
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
|
|
{
|
|
if (name_stack.empty())
|
|
{
|
|
//std::string message = llformat("Deprecated value %s ignored", getName().c_str());
|
|
//parser.parserWarning(message);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
typedef Deprecated Ignored;
|
|
|
|
protected:
|
|
static BlockDescriptor sBlockDescriptor;
|
|
};
|
|
|
|
template<typename DERIVED_BLOCK, typename BASE_BLOCK>
|
|
BlockDescriptor
|
|
Block<DERIVED_BLOCK, BASE_BLOCK>::sBlockDescriptor;
|
|
|
|
template<typename T, typename DERIVED = TypedParam<T> >
|
|
class BlockValue
|
|
: public Block<TypedParam<T, TypeValues<T>, false> >,
|
|
public Param
|
|
{
|
|
public:
|
|
typedef BlockValue<T> self_t;
|
|
typedef Block<TypedParam<T, TypeValues<T>, false> > block_t;
|
|
typedef const T& value_const_ref_t;
|
|
typedef value_const_ref_t value_assignment_t;
|
|
typedef typename TypeValues<T>::KeyCache key_cache_t;
|
|
|
|
BlockValue(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
|
: Param(block_descriptor.mCurrentBlockPtr),
|
|
mData(value)
|
|
{
|
|
if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
|
|
{
|
|
ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
&mergeWith,
|
|
&deserializeParam,
|
|
&serializeParam,
|
|
validate_func,
|
|
&inspectParam,
|
|
min_count, max_count);
|
|
BaseBlock::addParam(block_descriptor, param_descriptor, name);
|
|
}
|
|
}
|
|
|
|
// implicit conversion
|
|
operator value_assignment_t() const { return get(); }
|
|
// explicit conversion
|
|
value_assignment_t operator()() const { return get(); }
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
|
|
{
|
|
self_t& typed_param = static_cast<self_t&>(param);
|
|
// type to apply parse direct value T
|
|
if (name_stack.empty())
|
|
{
|
|
if(parser.readValue<T>(typed_param.mData.mValue))
|
|
{
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
typed_param.setProvided(true);
|
|
typed_param.mData.mLastParamVersion = typed_param.BaseBlock::getLastChangeVersion();
|
|
return true;
|
|
}
|
|
|
|
if(!TypeValues<T>::empty())
|
|
{
|
|
// try to parse a known named value
|
|
std::string name;
|
|
if (parser.readValue<std::string>(name))
|
|
{
|
|
// try to parse a per type named value
|
|
if (TypeValues<T>::get(name, typed_param.mData.mValue))
|
|
{
|
|
typed_param.mData.setKey(name);
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
typed_param.setProvided(true);
|
|
typed_param.mData.mLastParamVersion = typed_param.BaseBlock::getLastChangeVersion();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// fall back on parsing block components for T
|
|
// if we deserialized at least one component...
|
|
if (typed_param.BaseBlock::deserializeBlock(parser, name_stack))
|
|
{
|
|
// ...our block is provided, and considered changed
|
|
typed_param.enclosingBlock().setLastChangedParam(param, true);
|
|
typed_param.setProvided(true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
|
|
{
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
|
|
if (!typed_param.isProvided()) return;
|
|
|
|
std::string key = typed_param.mData.getKey();
|
|
|
|
// first try to write out name of name/value pair
|
|
if (!key.empty())
|
|
{
|
|
if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->mData.getKey(), key))
|
|
{
|
|
if (!parser.writeValue<std::string>(key, name_stack))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// then try to serialize value directly
|
|
else if (!diff_param || !ParamCompare<T>::equals(typed_param.get(), (static_cast<const self_t*>(diff_param))->get()))
|
|
{
|
|
|
|
if (parser.writeValue<T>(typed_param.mData.mValue, name_stack))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//RN: *always* serialize provided components of BlockValue (don't pass diff_param on),
|
|
// since these tend to be viewed as the constructor arguments for the value T. It seems
|
|
// cleaner to treat the uniqueness of a BlockValue according to the generated value, and
|
|
// not the individual components. This way <color red="0" green="1" blue="0"/> will not
|
|
// be exported as <color green="1"/>, since it was probably the intent of the user to
|
|
// be specific about the RGB color values. This also fixes an issue where we distinguish
|
|
// between rect.left not being provided and rect.left being explicitly set to 0 (same as default)
|
|
typed_param.BaseBlock::serializeBlock(parser, name_stack, NULL);
|
|
}
|
|
}
|
|
|
|
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
|
{
|
|
// first, inspect with actual type...
|
|
parser.inspectValue<T>(name_stack, min_count, max_count, NULL);
|
|
if (TypeValues<T>::getPossibleValues())
|
|
{
|
|
//...then inspect with possible string values...
|
|
parser.inspectValue<std::string>(name_stack, min_count, max_count, TypeValues<T>::getPossibleValues());
|
|
}
|
|
// then recursively inspect contents...
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
typed_param.inspectBlock(parser, name_stack);
|
|
}
|
|
|
|
|
|
bool isProvided() const
|
|
{
|
|
// either param value provided directly or block is sufficiently filled in
|
|
// if cached value is stale, regenerate from params
|
|
if (Param::getProvided() && mData.mLastParamVersion < BaseBlock::getLastChangeVersion())
|
|
{
|
|
if (block_t::validateBlock(true))
|
|
{
|
|
mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock();
|
|
// clear stale keyword associated with old value
|
|
mData.clearKey();
|
|
mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
//block value incomplete, so not considered provided
|
|
// will attempt to revalidate on next call to isProvided()
|
|
return false;
|
|
}
|
|
}
|
|
// either no data provided, or we have a valid value in hand
|
|
return Param::getProvided();
|
|
}
|
|
|
|
void set(value_assignment_t val, bool flag_as_provided = true)
|
|
{
|
|
Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
|
|
// set param version number to be up to date, so we ignore block contents
|
|
mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
|
|
|
|
mData.mValue = val;
|
|
mData.clearKey();
|
|
setProvided(flag_as_provided);
|
|
}
|
|
|
|
void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
|
|
{
|
|
// don't override any user provided value
|
|
if (!isProvided())
|
|
{
|
|
set(val, flag_as_provided);
|
|
}
|
|
}
|
|
|
|
// propagate change status up to enclosing block
|
|
/*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
|
|
{
|
|
BaseBlock::setLastChangedParam(last_param, user_provided);
|
|
Param::enclosingBlock().setLastChangedParam(*this, user_provided);
|
|
if (user_provided)
|
|
{
|
|
setProvided(true); // some component provided
|
|
}
|
|
}
|
|
|
|
protected:
|
|
value_assignment_t get() const
|
|
{
|
|
if (mData.mLastParamVersion < BaseBlock::getLastChangeVersion() && block_t::validateBlock(true))
|
|
{
|
|
mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock();
|
|
mData.clearKey();
|
|
mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
|
|
}
|
|
|
|
return mData.mValue;
|
|
}
|
|
|
|
// mutable to allow lazy updates on get
|
|
struct Data : public key_cache_t
|
|
{
|
|
Data(const T& value)
|
|
: mValue(value),
|
|
mLastParamVersion(0)
|
|
{}
|
|
|
|
T mValue;
|
|
S32 mLastParamVersion;
|
|
};
|
|
|
|
mutable Data mData;
|
|
|
|
private:
|
|
static bool mergeWith(Param& dst, const Param& src, bool overwrite)
|
|
{
|
|
const self_t& src_param = static_cast<const self_t&>(src);
|
|
self_t& dst_typed_param = static_cast<self_t&>(dst);
|
|
|
|
if (src_param.isProvided()
|
|
&& (overwrite || !dst_typed_param.isProvided()))
|
|
{
|
|
// assign individual parameters
|
|
if (overwrite)
|
|
{
|
|
dst_typed_param.BaseBlock::overwriteFromImpl(block_t::sBlockDescriptor, src_param);
|
|
}
|
|
else
|
|
{
|
|
dst_typed_param.BaseBlock::fillFromImpl(block_t::sBlockDescriptor, src_param);
|
|
}
|
|
// then copy actual value
|
|
dst_typed_param.mData.mValue = src_param.get();
|
|
dst_typed_param.mData.clearKey();
|
|
dst_typed_param.setProvided(true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
bool ParamCompare<boost::function<void (const std::string &,void *)> >::equals(
|
|
const boost::function<void (const std::string &,void *)> &a,
|
|
const boost::function<void (const std::string &,void *)> &b);
|
|
|
|
template<>
|
|
bool ParamCompare<boost::function<void (const LLSD &,const LLSD &)> >::equals(
|
|
const boost::function<void (const LLSD &,const LLSD &)> &a,
|
|
const boost::function<void (const LLSD &,const LLSD &)> &b);
|
|
|
|
template<>
|
|
bool ParamCompare<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
|