2843 lines
77 KiB
C++
2843 lines
77 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=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#ifndef LL_LLPARAM_H
|
|
#define LL_LLPARAM_H
|
|
|
|
#include <vector>
|
|
#include <list>
|
|
#ifndef BOOST_FUNCTION_HPP_INCLUDED
|
|
#include <boost/function.hpp>
|
|
#define BOOST_FUNCTION_HPP_INCLUDED
|
|
#endif
|
|
#include <boost/shared_ptr.hpp>
|
|
#include <boost/type_traits/is_convertible.hpp>
|
|
#include <boost/type_traits/is_enum.hpp>
|
|
#include <boost/unordered_map.hpp>
|
|
|
|
#include "llerror.h"
|
|
#include "llstl.h"
|
|
#include "llpredicate.h"
|
|
#include "llsd.h"
|
|
|
|
namespace LLTypeTags
|
|
{
|
|
template <typename INNER_TYPE, int _SORT_ORDER>
|
|
struct TypeTagBase
|
|
{
|
|
typedef void is_tag_t;
|
|
typedef INNER_TYPE inner_t;
|
|
static const int SORT_ORDER=_SORT_ORDER;
|
|
};
|
|
|
|
template <int VAL1, int VAL2>
|
|
struct GreaterThan
|
|
{
|
|
static const bool value = VAL1 > VAL2;
|
|
};
|
|
|
|
template<typename ITEM, typename REST, bool NEEDS_SWAP = GreaterThan<ITEM::SORT_ORDER, REST::SORT_ORDER>::value >
|
|
struct Swap
|
|
{
|
|
typedef typename ITEM::template Cons<REST>::value_t value_t;
|
|
};
|
|
|
|
template<typename ITEM, typename REST>
|
|
struct Swap<ITEM, REST, true>
|
|
{
|
|
typedef typename REST::template Cons<Swap<ITEM, typename REST::inner_t>::value_t>::value_t value_t;
|
|
};
|
|
|
|
template<typename T, typename SORTABLE = void>
|
|
struct IsSortable
|
|
{
|
|
static const bool value = false;
|
|
};
|
|
|
|
template<typename T>
|
|
struct IsSortable<T, typename T::is_tag_t>
|
|
{
|
|
static const bool value = true;
|
|
};
|
|
|
|
template<typename ITEM, typename REST, bool IS_REST_SORTABLE = IsSortable<REST>::value>
|
|
struct InsertInto
|
|
{
|
|
typedef typename ITEM::template Cons<REST>::value_t value_t;
|
|
};
|
|
|
|
template<typename ITEM, typename REST>
|
|
struct InsertInto <ITEM, REST, true>
|
|
{
|
|
typedef typename Swap<ITEM, REST>::value_t value_t;
|
|
};
|
|
|
|
template<typename T, bool SORTABLE = IsSortable<T>::value>
|
|
struct Sorted
|
|
{
|
|
typedef T value_t;
|
|
};
|
|
|
|
template<typename T>
|
|
struct Sorted <T, true>
|
|
{
|
|
typedef typename InsertInto<T, typename Sorted<typename T::inner_t>::value_t>::value_t value_t;
|
|
};
|
|
}
|
|
|
|
namespace LLInitParam
|
|
{
|
|
// used to indicate no matching value to a given name when parsing
|
|
struct Flag{};
|
|
|
|
template<typename T> const T& defaultValue() { static T value; return value; }
|
|
|
|
// wraps comparison operator between any 2 values of the same type
|
|
// specialize to handle cases where equality isn't defined well, or at all
|
|
template <typename T, bool IS_BOOST_FUNCTION = boost::is_convertible<T, boost::function_base>::value >
|
|
struct ParamCompare
|
|
{
|
|
static bool equals(const T &a, const T &b)
|
|
{
|
|
return a == b;
|
|
}
|
|
};
|
|
|
|
// boost function types are not comparable
|
|
template<typename T>
|
|
struct ParamCompare<T, true>
|
|
{
|
|
static bool equals(const T&a, const T &b)
|
|
{
|
|
return false;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ParamCompare<LLSD, false>
|
|
{
|
|
static bool equals(const LLSD &a, const LLSD &b) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ParamCompare<Flag, false>
|
|
{
|
|
static bool equals(const Flag& a, const Flag& b) { return false; }
|
|
};
|
|
|
|
|
|
// helper functions and classes
|
|
typedef ptrdiff_t param_handle_t;
|
|
struct IS_A_BLOCK {};
|
|
struct NOT_BLOCK {};
|
|
|
|
// these templates allow us to distinguish between template parameters
|
|
// that derive from BaseBlock and those that don't
|
|
template<typename T, typename BLOCK_IDENTIFIER = void>
|
|
struct IsBlock
|
|
{
|
|
typedef NOT_BLOCK value_t;
|
|
};
|
|
|
|
template<typename T>
|
|
struct IsBlock<T, typename T::baseblock_base_class_t>
|
|
{
|
|
typedef IS_A_BLOCK value_t;
|
|
};
|
|
|
|
// ParamValue class directly manages the wrapped value
|
|
// by holding on to a copy (scalar params)
|
|
// or deriving from it (blocks)
|
|
// has specializations for custom value behavior
|
|
// and "tag" values like Lazy and Atomic
|
|
template<typename T, typename VALUE_IS_BLOCK = typename IsBlock<T>::value_t>
|
|
class ParamValue
|
|
{
|
|
typedef ParamValue<T, VALUE_IS_BLOCK> self_t;
|
|
|
|
public:
|
|
typedef T default_value_t;
|
|
typedef T value_t;
|
|
|
|
ParamValue(): mValue() {}
|
|
ParamValue(const default_value_t& other) : mValue(other) {}
|
|
|
|
void setValue(const value_t& val)
|
|
{
|
|
mValue = val;
|
|
}
|
|
|
|
const value_t& getValue() const
|
|
{
|
|
return mValue;
|
|
}
|
|
|
|
T& getValue()
|
|
{
|
|
return mValue;
|
|
}
|
|
|
|
bool isValid() const { return true; }
|
|
|
|
protected:
|
|
T mValue;
|
|
};
|
|
|
|
template<typename T>
|
|
class ParamValue<T, IS_A_BLOCK>
|
|
: public T
|
|
{
|
|
typedef ParamValue<T, IS_A_BLOCK> self_t;
|
|
public:
|
|
typedef T default_value_t;
|
|
typedef T value_t;
|
|
|
|
ParamValue()
|
|
: T()
|
|
{}
|
|
|
|
ParamValue(const default_value_t& other)
|
|
: T(other)
|
|
{}
|
|
|
|
void setValue(const value_t& val)
|
|
{
|
|
*this = val;
|
|
}
|
|
|
|
const value_t& getValue() const
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
T& getValue()
|
|
{
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
|
|
// empty default implementation of key cache
|
|
// leverages empty base class optimization
|
|
template <typename T>
|
|
class TypeValues
|
|
: public ParamValue<typename LLTypeTags::Sorted<T>::value_t>
|
|
{
|
|
private:
|
|
struct Inaccessable{};
|
|
public:
|
|
typedef std::map<std::string, T> value_name_map_t;
|
|
typedef Inaccessable name_t;
|
|
typedef TypeValues<T> type_value_t;
|
|
typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t;
|
|
typedef typename param_value_t::value_t value_t;
|
|
|
|
TypeValues(const typename param_value_t::value_t& val)
|
|
: param_value_t(val)
|
|
{}
|
|
|
|
void setValueName(const std::string& key) {}
|
|
std::string getValueName() const { return ""; }
|
|
std::string calcValueName(const value_t& value) const { return ""; }
|
|
void clearValueName() const {}
|
|
|
|
static bool getValueFromName(const std::string& name, value_t& value)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static bool valueNamesExist()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static std::vector<std::string>* getPossibleValues()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
void assignNamedValue(const Inaccessable& name)
|
|
{}
|
|
|
|
operator const value_t&() const
|
|
{
|
|
return param_value_t::getValue();
|
|
}
|
|
|
|
const value_t& operator()() const
|
|
{
|
|
return param_value_t::getValue();
|
|
}
|
|
|
|
static value_name_map_t* getValueNames() {return NULL;}
|
|
};
|
|
|
|
// helper class to implement name value lookups
|
|
// and caching of last used name
|
|
template <typename T, typename DERIVED_TYPE = TypeValues<T>, bool IS_SPECIALIZED = true >
|
|
class TypeValuesHelper
|
|
: public ParamValue<typename LLTypeTags::Sorted<T>::value_t>
|
|
{
|
|
typedef TypeValuesHelper<T, DERIVED_TYPE, IS_SPECIALIZED> self_t;
|
|
public:
|
|
typedef typename std::map<std::string, T> value_name_map_t;
|
|
typedef std::string name_t;
|
|
typedef self_t type_value_t;
|
|
typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t;
|
|
typedef typename param_value_t::value_t value_t;
|
|
|
|
TypeValuesHelper(const typename param_value_t::value_t& val)
|
|
: param_value_t(val)
|
|
{}
|
|
|
|
//TODO: cache key by index to save on param block size
|
|
void setValueName(const std::string& value_name)
|
|
{
|
|
mValueName = value_name;
|
|
}
|
|
|
|
std::string getValueName() const
|
|
{
|
|
return mValueName;
|
|
}
|
|
|
|
std::string calcValueName(const value_t& value) const
|
|
{
|
|
value_name_map_t* map = getValueNames();
|
|
for (typename value_name_map_t::iterator it = map->begin(), end_it = map->end();
|
|
it != end_it;
|
|
++it)
|
|
{
|
|
if (ParamCompare<T>::equals(it->second, value))
|
|
{
|
|
return it->first;
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
void clearValueName() const
|
|
{
|
|
mValueName.clear();
|
|
}
|
|
|
|
static bool getValueFromName(const std::string& name, value_t& value)
|
|
{
|
|
value_name_map_t* map = getValueNames();
|
|
typename value_name_map_t::iterator found_it = map->find(name);
|
|
if (found_it == map->end()) return false;
|
|
|
|
value = found_it->second;
|
|
return true;
|
|
}
|
|
|
|
static bool valueNamesExist()
|
|
{
|
|
return !getValueNames()->empty();
|
|
}
|
|
|
|
static value_name_map_t* getValueNames()
|
|
{
|
|
static value_name_map_t sMap;
|
|
static bool sInitialized = false;
|
|
|
|
if (!sInitialized)
|
|
{
|
|
sInitialized = true;
|
|
DERIVED_TYPE::declareValues();
|
|
}
|
|
return &sMap;
|
|
}
|
|
|
|
static std::vector<std::string>* getPossibleValues()
|
|
{
|
|
static std::vector<std::string> sValues;
|
|
|
|
value_name_map_t* map = getValueNames();
|
|
for (typename value_name_map_t::iterator it = map->begin(), end_it = map->end();
|
|
it != end_it;
|
|
++it)
|
|
{
|
|
sValues.push_back(it->first);
|
|
}
|
|
return &sValues;
|
|
}
|
|
|
|
static void declare(const std::string& name, const value_t& value)
|
|
{
|
|
(*getValueNames())[name] = value;
|
|
}
|
|
|
|
void operator ()(const std::string& name)
|
|
{
|
|
*this = name;
|
|
}
|
|
|
|
void assignNamedValue(const std::string& name)
|
|
{
|
|
if (getValueFromName(name, param_value_t::getValue()))
|
|
{
|
|
setValueName(name);
|
|
}
|
|
}
|
|
|
|
operator const value_t&() const
|
|
{
|
|
return param_value_t::getValue();
|
|
}
|
|
|
|
const value_t& operator()() const
|
|
{
|
|
return param_value_t::getValue();
|
|
}
|
|
|
|
protected:
|
|
static void getName(const std::string& name, const value_t& value)
|
|
{}
|
|
|
|
mutable std::string mValueName;
|
|
};
|
|
|
|
// string types can support custom named values, but need
|
|
// to disambiguate in code between a string that is a named value
|
|
// and a string that is a name
|
|
template <typename DERIVED_TYPE>
|
|
class TypeValuesHelper<std::string, DERIVED_TYPE, true>
|
|
: public TypeValuesHelper<std::string, DERIVED_TYPE, false>
|
|
{
|
|
public:
|
|
typedef TypeValuesHelper<std::string, DERIVED_TYPE, true> self_t;
|
|
typedef TypeValuesHelper<std::string, DERIVED_TYPE, false> base_t;
|
|
typedef std::string value_t;
|
|
typedef std::string name_t;
|
|
typedef self_t type_value_t;
|
|
|
|
TypeValuesHelper(const std::string& val)
|
|
: base_t::TypeValuesHelper(val) // <alchemy/> Fix invalid blah gcc
|
|
{}
|
|
|
|
void operator ()(const std::string& name)
|
|
{
|
|
*this = name;
|
|
}
|
|
|
|
self_t& operator =(const std::string& name)
|
|
{
|
|
if (base_t::getValueFromName(name, ParamValue<std::string>::getValue()))
|
|
{
|
|
base_t::setValueName(name);
|
|
}
|
|
else
|
|
{
|
|
ParamValue<std::string>::setValue(name);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
operator const value_t&() const
|
|
{
|
|
return ParamValue<std::string>::getValue();
|
|
}
|
|
|
|
const value_t& operator()() const
|
|
{
|
|
return ParamValue<std::string>::getValue();
|
|
}
|
|
|
|
};
|
|
|
|
// parser base class with mechanisms for registering readers/writers/inspectors of different types
|
|
class LL_COMMON_API Parser
|
|
{
|
|
LOG_CLASS(Parser);
|
|
public:
|
|
typedef std::vector<std::pair<std::string, bool> > name_stack_t;
|
|
typedef std::pair<name_stack_t::iterator, name_stack_t::iterator> name_stack_range_t;
|
|
typedef std::vector<std::string> possible_values_t;
|
|
|
|
typedef bool (*parser_read_func_t)(Parser& parser, void* output);
|
|
typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&);
|
|
typedef boost::function<void (name_stack_t&, S32, S32, const possible_values_t*)> parser_inspect_func_t;
|
|
|
|
typedef std::map<const std::type_info*, parser_read_func_t> parser_read_func_map_t;
|
|
typedef std::map<const std::type_info*, parser_write_func_t> parser_write_func_map_t;
|
|
typedef std::map<const std::type_info*, parser_inspect_func_t> parser_inspect_func_map_t;
|
|
|
|
public:
|
|
|
|
Parser(parser_read_func_map_t& read_map, parser_write_func_map_t& write_map, parser_inspect_func_map_t& inspect_map)
|
|
: mParseSilently(false),
|
|
mParserReadFuncs(&read_map),
|
|
mParserWriteFuncs(&write_map),
|
|
mParserInspectFuncs(&inspect_map)
|
|
{}
|
|
|
|
virtual ~Parser();
|
|
|
|
template <typename T> bool readValue(T& param, typename boost::disable_if<boost::is_enum<T> >::type* dummy = 0)
|
|
{
|
|
parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T));
|
|
if (found_it != mParserReadFuncs->end())
|
|
{
|
|
return found_it->second(*this, (void*)¶m);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename T> bool readValue(T& param, typename boost::enable_if<boost::is_enum<T> >::type* dummy = 0)
|
|
{
|
|
parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T));
|
|
if (found_it != mParserReadFuncs->end())
|
|
{
|
|
return found_it->second(*this, (void*)¶m);
|
|
}
|
|
else
|
|
{
|
|
found_it = mParserReadFuncs->find(&typeid(S32));
|
|
if (found_it != mParserReadFuncs->end())
|
|
{
|
|
S32 int_value;
|
|
bool parsed = found_it->second(*this, (void*)&int_value);
|
|
param = (T)int_value;
|
|
return parsed;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T> bool writeValue(const T& param, 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(*this, (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(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; }
|
|
|
|
protected:
|
|
template <typename T>
|
|
void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func = NULL)
|
|
{
|
|
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;
|
|
};
|
|
|
|
class Param;
|
|
|
|
enum ESerializePredicates
|
|
{
|
|
PROVIDED,
|
|
REQUIRED,
|
|
VALID,
|
|
HAS_DEFAULT_VALUE,
|
|
EMPTY
|
|
};
|
|
|
|
typedef LLPredicate::Rule<ESerializePredicates> predicate_rule_t;
|
|
|
|
LL_COMMON_API predicate_rule_t default_parse_rules();
|
|
|
|
// various callbacks and constraints associated with an individual param
|
|
struct LL_COMMON_API ParamDescriptor
|
|
{
|
|
struct UserData
|
|
{
|
|
virtual ~UserData() {}
|
|
};
|
|
|
|
typedef bool(*merge_func_t)(Param&, const Param&, bool);
|
|
typedef bool(*deserialize_func_t)(Param&, Parser&, Parser::name_stack_range_t&, bool);
|
|
typedef bool(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const predicate_rule_t rules, 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);
|
|
|
|
ParamDescriptor();
|
|
~ParamDescriptor();
|
|
|
|
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 mNumRefs;
|
|
UserData* mUserData;
|
|
};
|
|
|
|
typedef boost::shared_ptr<ParamDescriptor> ParamDescriptorPtr;
|
|
|
|
// each derived Block class keeps a static data structure maintaining offsets to various params
|
|
class LL_COMMON_API BlockDescriptor
|
|
{
|
|
public:
|
|
BlockDescriptor();
|
|
|
|
typedef enum e_initialization_state
|
|
{
|
|
UNINITIALIZED,
|
|
INITIALIZING,
|
|
INITIALIZED
|
|
} EInitializationState;
|
|
|
|
void aggregateBlockData(BlockDescriptor& src_block_data);
|
|
void addParam(ParamDescriptorPtr param, const char* name);
|
|
|
|
typedef boost::unordered_map<const std::string, ParamDescriptorPtr> param_map_t;
|
|
typedef std::vector<ParamDescriptorPtr> param_list_t;
|
|
typedef std::list<ParamDescriptorPtr> all_params_list_t;
|
|
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_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
|
|
};
|
|
|
|
//TODO: implement in terms of owned_ptr
|
|
template<typename T>
|
|
class LazyValue
|
|
{
|
|
public:
|
|
LazyValue()
|
|
: mPtr(NULL)
|
|
{}
|
|
|
|
~LazyValue()
|
|
{
|
|
delete mPtr;
|
|
}
|
|
|
|
LazyValue(const T& value)
|
|
{
|
|
mPtr = new T(value);
|
|
}
|
|
|
|
LazyValue(const LazyValue& other)
|
|
: mPtr(NULL)
|
|
{
|
|
*this = other;
|
|
}
|
|
|
|
LazyValue& operator = (const LazyValue& other)
|
|
{
|
|
if (!other.mPtr)
|
|
{
|
|
delete mPtr;
|
|
mPtr = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (!mPtr)
|
|
{
|
|
mPtr = new T(*other.mPtr);
|
|
}
|
|
else
|
|
{
|
|
*mPtr = *(other.mPtr);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const LazyValue& other) const
|
|
{
|
|
if (empty() || other.empty()) return false;
|
|
return *mPtr == *other.mPtr;
|
|
}
|
|
|
|
bool empty() const
|
|
{
|
|
return mPtr == NULL;
|
|
}
|
|
|
|
void set(const T& other)
|
|
{
|
|
if (!mPtr)
|
|
{
|
|
mPtr = new T(other);
|
|
}
|
|
else
|
|
{
|
|
*mPtr = other;
|
|
}
|
|
}
|
|
|
|
const T& get() const
|
|
{
|
|
return *ensureInstance();
|
|
}
|
|
|
|
T& get()
|
|
{
|
|
return *ensureInstance();
|
|
}
|
|
|
|
operator const T&() const
|
|
{
|
|
return get();
|
|
}
|
|
|
|
private:
|
|
// lazily allocate an instance of T
|
|
T* ensureInstance() const
|
|
{
|
|
if (mPtr == NULL)
|
|
{
|
|
mPtr = new T();
|
|
}
|
|
return mPtr;
|
|
}
|
|
|
|
private:
|
|
|
|
mutable T* mPtr;
|
|
};
|
|
|
|
// root class of all parameter blocks
|
|
|
|
class LL_COMMON_API BaseBlock
|
|
{
|
|
public:
|
|
// lift block tags into baseblock namespace so derived classes do not need to qualify them
|
|
typedef LLInitParam::IS_A_BLOCK IS_A_BLOCK;
|
|
typedef LLInitParam::NOT_BLOCK NOT_A_BLOCK;
|
|
|
|
template<typename T>
|
|
struct Sequential : public LLTypeTags::TypeTagBase<T, 2>
|
|
{
|
|
template <typename S> struct Cons { typedef Sequential<ParamValue<S> > value_t; };
|
|
template <typename S> struct Cons<Sequential<S> > { typedef Sequential<S> value_t; };
|
|
};
|
|
|
|
template<typename T>
|
|
struct Atomic : public LLTypeTags::TypeTagBase<T, 1>
|
|
{
|
|
template <typename S> struct Cons { typedef Atomic<ParamValue<S> > value_t; };
|
|
template <typename S> struct Cons<Atomic<S> > { typedef Atomic<S> value_t; };
|
|
};
|
|
|
|
template<typename T, typename BLOCK_T = typename IsBlock<T>::value_t >
|
|
struct Lazy : public LLTypeTags::TypeTagBase<T, 0>
|
|
{
|
|
template <typename S> struct Cons
|
|
{
|
|
typedef Lazy<ParamValue<S, BLOCK_T>, BLOCK_T> value_t;
|
|
};
|
|
template <typename S> struct Cons<Lazy<S, IS_A_BLOCK> >
|
|
{
|
|
typedef Lazy<S, IS_A_BLOCK> value_t;
|
|
};
|
|
template <typename S> struct Cons<Lazy<S, NOT_A_BLOCK> >
|
|
{
|
|
typedef Lazy<S, BLOCK_T> value_t;
|
|
};
|
|
};
|
|
|
|
// "Multiple" constraint types, put here in root class to avoid ambiguity during use
|
|
struct AnyAmount
|
|
{
|
|
enum { minCount = 0 };
|
|
enum { maxCount = U32_MAX };
|
|
};
|
|
|
|
template<U32 MIN_AMOUNT>
|
|
struct AtLeast
|
|
{
|
|
enum { minCount = MIN_AMOUNT };
|
|
enum { maxCount = U32_MAX };
|
|
};
|
|
|
|
template<U32 MAX_AMOUNT>
|
|
struct AtMost
|
|
{
|
|
enum { minCount = 0 };
|
|
enum { maxCount = MAX_AMOUNT };
|
|
};
|
|
|
|
template<U32 MIN_AMOUNT, U32 MAX_AMOUNT>
|
|
struct Between
|
|
{
|
|
enum { minCount = MIN_AMOUNT };
|
|
enum { maxCount = MAX_AMOUNT };
|
|
};
|
|
|
|
template<U32 EXACT_COUNT>
|
|
struct Exactly
|
|
{
|
|
enum { minCount = EXACT_COUNT };
|
|
enum { maxCount = EXACT_COUNT };
|
|
};
|
|
|
|
// this typedef identifies derived classes as being blocks
|
|
typedef void baseblock_base_class_t;
|
|
LOG_CLASS(BaseBlock);
|
|
friend class Param;
|
|
|
|
BaseBlock()
|
|
: mValidated(false),
|
|
mParamProvided(false)
|
|
{}
|
|
|
|
virtual ~BaseBlock() {}
|
|
bool submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent=false);
|
|
|
|
param_handle_t getHandleFromParam(const Param* param) const;
|
|
bool validateBlock(bool emit_errors = true) const;
|
|
|
|
bool isProvided() const
|
|
{
|
|
return mParamProvided;
|
|
}
|
|
|
|
bool isValid() const
|
|
{
|
|
return validateBlock(false);
|
|
}
|
|
|
|
|
|
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 paramChanged(const Param& changed_param, bool user_provided)
|
|
{
|
|
if (user_provided)
|
|
{
|
|
// a child param has been explicitly changed
|
|
// so *some* aspect of this block is now provided
|
|
mValidated = false;
|
|
mParamProvided = true;
|
|
}
|
|
}
|
|
|
|
bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
|
|
bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t rule, const BaseBlock* diff_block = NULL) const;
|
|
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const;
|
|
|
|
virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); }
|
|
virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); }
|
|
|
|
// 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;
|
|
}
|
|
|
|
ParamDescriptorPtr findParamDescriptor(const Param& param);
|
|
|
|
// take all provided params from other and apply to self
|
|
bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite);
|
|
|
|
static BlockDescriptor& getBlockDescriptor()
|
|
{
|
|
static BlockDescriptor sBlockDescriptor;
|
|
return sBlockDescriptor;
|
|
}
|
|
|
|
protected:
|
|
void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size);
|
|
|
|
|
|
bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite)
|
|
{
|
|
return mergeBlock(block_data, source, overwrite);
|
|
}
|
|
|
|
mutable bool mValidated; // lazy validation flag
|
|
bool mParamProvided;
|
|
|
|
private:
|
|
const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const;
|
|
};
|
|
|
|
class LL_COMMON_API Param
|
|
{
|
|
public:
|
|
void setProvided(bool is_provided = true)
|
|
{
|
|
mIsProvided = is_provided;
|
|
enclosingBlock().paramChanged(*this, is_provided);
|
|
}
|
|
|
|
Param& operator =(const Param& other)
|
|
{
|
|
mIsProvided = other.mIsProvided;
|
|
// don't change mEnclosingblockoffset
|
|
return *this;
|
|
}
|
|
protected:
|
|
|
|
bool anyProvided() const { return mIsProvided; }
|
|
|
|
Param(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)getEnclosingBlockOffset()));
|
|
}
|
|
|
|
U32 getEnclosingBlockOffset() const
|
|
{
|
|
return ((U32)mEnclosingBlockOffsetHigh << 16) | (U32)mEnclosingBlockOffsetLow;
|
|
}
|
|
|
|
private:
|
|
friend class BaseBlock;
|
|
|
|
//24 bits for member offset field and 1 bit for provided flag
|
|
U16 mEnclosingBlockOffsetLow;
|
|
U8 mEnclosingBlockOffsetHigh:7;
|
|
U8 mIsProvided:1;
|
|
|
|
};
|
|
|
|
template<typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
|
|
struct ParamIterator
|
|
{
|
|
typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::const_iterator const_iterator;
|
|
typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::iterator iterator;
|
|
};
|
|
|
|
// wrapper for parameter with a known type
|
|
// specialized to handle 4 cases:
|
|
// simple "scalar" value
|
|
// parameter that is itself a block
|
|
// multiple scalar values, stored in a vector
|
|
// multiple blocks, stored in a vector
|
|
template<typename T,
|
|
typename NAME_VALUE_LOOKUP = TypeValues<T>,
|
|
bool HAS_MULTIPLE_VALUES = false,
|
|
typename VALUE_IS_BLOCK = typename IsBlock<ParamValue<typename LLTypeTags::Sorted<T>::value_t> >::value_t>
|
|
class TypedParam
|
|
: public Param,
|
|
public NAME_VALUE_LOOKUP::type_value_t
|
|
{
|
|
protected:
|
|
typedef TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK> self_t;
|
|
typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t;
|
|
typedef typename param_value_t::default_value_t default_value_t;
|
|
typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
|
|
public:
|
|
typedef typename param_value_t::value_t value_t;
|
|
|
|
using named_value_t::operator();
|
|
|
|
TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
|
: Param(block_descriptor.mCurrentBlockPtr),
|
|
named_value_t(value)
|
|
{
|
|
if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING))
|
|
{
|
|
init(block_descriptor, validate_func, min_count, max_count, name);
|
|
}
|
|
}
|
|
|
|
bool isProvided() const { return Param::anyProvided(); }
|
|
|
|
bool isValid() const { return true; }
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
|
{
|
|
self_t& typed_param = static_cast<self_t&>(param);
|
|
// no further names in stack, attempt to parse value now
|
|
if (name_stack_range.first == name_stack_range.second)
|
|
{
|
|
std::string name;
|
|
|
|
// try to parse a known named value
|
|
if(named_value_t::valueNamesExist()
|
|
&& parser.readValue(name)
|
|
&& named_value_t::getValueFromName(name, typed_param.getValue()))
|
|
{
|
|
typed_param.setValueName(name);
|
|
typed_param.setProvided();
|
|
return true;
|
|
}
|
|
// try to read value directly
|
|
else if (parser.readValue(typed_param.getValue()))
|
|
{
|
|
typed_param.clearValueName();
|
|
typed_param.setProvided();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
|
|
{
|
|
bool serialized = false;
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
const self_t* diff_typed_param = static_cast<const self_t*>(diff_param);
|
|
|
|
LLPredicate::Value<ESerializePredicates> predicate;
|
|
if (diff_typed_param && ParamCompare<T>::equals(typed_param.getValue(), diff_typed_param->getValue()))
|
|
{
|
|
predicate.set(HAS_DEFAULT_VALUE);
|
|
}
|
|
|
|
predicate.set(VALID, typed_param.isValid());
|
|
predicate.set(PROVIDED, typed_param.anyProvided());
|
|
predicate.set(EMPTY, false);
|
|
|
|
if (!predicate_rule.check(predicate)) return false;
|
|
|
|
if (!name_stack.empty())
|
|
{
|
|
name_stack.back().second = true;
|
|
}
|
|
|
|
std::string key = typed_param.getValueName();
|
|
|
|
// first try to write out name of name/value pair
|
|
|
|
if (!key.empty())
|
|
{
|
|
if (!diff_typed_param || !ParamCompare<std::string>::equals(diff_typed_param->getValueName(), key))
|
|
{
|
|
serialized = parser.writeValue(key, name_stack);
|
|
}
|
|
}
|
|
// then try to serialize value directly
|
|
else if (!diff_typed_param || ParamCompare<T>::equals(typed_param.getValue(), diff_typed_param->getValue()))
|
|
{
|
|
serialized = parser.writeValue(typed_param.getValue(), name_stack);
|
|
if (!serialized)
|
|
{
|
|
std::string calculated_key = typed_param.calcValueName(typed_param.getValue());
|
|
if (calculated_key.size()
|
|
&& (!diff_typed_param
|
|
|| !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), calculated_key)))
|
|
{
|
|
serialized = parser.writeValue(calculated_key, name_stack);
|
|
}
|
|
}
|
|
}
|
|
return serialized;
|
|
}
|
|
|
|
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 (named_value_t::getPossibleValues())
|
|
{
|
|
parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues());
|
|
}
|
|
}
|
|
|
|
void set(const value_t& val, bool flag_as_provided = true)
|
|
{
|
|
named_value_t::clearValueName();
|
|
named_value_t::setValue(val);
|
|
setProvided(flag_as_provided);
|
|
}
|
|
|
|
self_t& operator =(const typename named_value_t::name_t& name)
|
|
{
|
|
named_value_t::assignNamedValue(name);
|
|
return *this;
|
|
}
|
|
|
|
protected:
|
|
|
|
self_t& operator =(const self_t& other)
|
|
{
|
|
param_value_t::operator =(other);
|
|
Param::operator =(other);
|
|
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 (src_typed_param.isProvided()
|
|
&& (overwrite || !dst_typed_param.isProvided()))
|
|
{
|
|
dst_typed_param.set(src_typed_param.getValue());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
private:
|
|
void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name )
|
|
{
|
|
ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor(
|
|
block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
&mergeWith,
|
|
&deserializeParam,
|
|
&serializeParam,
|
|
validate_func,
|
|
&inspectParam,
|
|
min_count, max_count));
|
|
block_descriptor.addParam(param_descriptor, name);
|
|
}
|
|
};
|
|
|
|
// parameter that is a block
|
|
template <typename BLOCK_T, typename NAME_VALUE_LOOKUP>
|
|
class TypedParam<BLOCK_T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK>
|
|
: public Param,
|
|
public NAME_VALUE_LOOKUP::type_value_t
|
|
{
|
|
protected:
|
|
typedef ParamValue<typename LLTypeTags::Sorted<BLOCK_T>::value_t> param_value_t;
|
|
typedef typename param_value_t::default_value_t default_value_t;
|
|
typedef TypedParam<BLOCK_T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK> self_t;
|
|
typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
|
|
public:
|
|
using named_value_t::operator();
|
|
typedef typename param_value_t::value_t value_t;
|
|
|
|
TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
|
: Param(block_descriptor.mCurrentBlockPtr),
|
|
named_value_t(value)
|
|
{
|
|
if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING))
|
|
{
|
|
init(block_descriptor, validate_func, min_count, max_count, name);
|
|
}
|
|
}
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
|
{
|
|
self_t& typed_param = static_cast<self_t&>(param);
|
|
|
|
if (name_stack_range.first == name_stack_range.second)
|
|
{ // try to parse a known named value
|
|
std::string name;
|
|
|
|
if(named_value_t::valueNamesExist()
|
|
&& parser.readValue(name)
|
|
&& named_value_t::getValueFromName(name, typed_param.getValue()))
|
|
{
|
|
typed_param.setValueName(name);
|
|
typed_param.setProvided();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(typed_param.deserializeBlock(parser, name_stack_range, new_name))
|
|
{ // attempt to parse block...
|
|
typed_param.clearValueName();
|
|
typed_param.setProvided();
|
|
return true;
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
|
|
{
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
|
|
LLPredicate::Value<ESerializePredicates> predicate;
|
|
|
|
predicate.set(VALID, typed_param.isValid());
|
|
predicate.set(PROVIDED, typed_param.anyProvided());
|
|
|
|
if (!predicate_rule.check(predicate)) return false;
|
|
|
|
if (!name_stack.empty())
|
|
{
|
|
name_stack.back().second = true;
|
|
}
|
|
|
|
std::string key = typed_param.getValueName();
|
|
if (!key.empty())
|
|
{
|
|
if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), key))
|
|
{
|
|
parser.writeValue(key, name_stack);
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return typed_param.serializeBlock(parser, name_stack, predicate_rule, static_cast<const self_t*>(diff_param));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
|
{
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
|
|
// tell parser about our actual type
|
|
parser.inspectValue<value_t>(name_stack, min_count, max_count, NULL);
|
|
// then tell it about string-based alternatives ("red", "blue", etc. for LLColor4)
|
|
if (named_value_t::getPossibleValues())
|
|
{
|
|
parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues());
|
|
}
|
|
|
|
typed_param.inspectBlock(parser, name_stack, min_count, max_count);
|
|
}
|
|
|
|
// 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
|
|
{
|
|
return Param::anyProvided() && isValid();
|
|
}
|
|
|
|
bool isValid() const
|
|
{
|
|
return param_value_t::isValid();
|
|
}
|
|
|
|
// assign block contents to this param-that-is-a-block
|
|
void set(const value_t& val, bool flag_as_provided = true)
|
|
{
|
|
named_value_t::setValue(val);
|
|
named_value_t::clearValueName();
|
|
setProvided(flag_as_provided);
|
|
}
|
|
|
|
self_t& operator =(const typename named_value_t::name_t& name)
|
|
{
|
|
named_value_t::assignNamedValue(name);
|
|
return *this;
|
|
}
|
|
|
|
// propagate changed status up to enclosing block
|
|
/*virtual*/ void paramChanged(const Param& changed_param, bool user_provided)
|
|
{
|
|
param_value_t::paramChanged(changed_param, user_provided);
|
|
|
|
if (user_provided)
|
|
{
|
|
setProvided();
|
|
named_value_t::clearValueName();
|
|
}
|
|
else
|
|
{
|
|
Param::enclosingBlock().paramChanged(*this, user_provided);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
|
|
self_t& operator =(const self_t& other)
|
|
{
|
|
param_value_t::operator =(other);
|
|
Param::operator =(other);
|
|
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 (src_typed_param.anyProvided())
|
|
{
|
|
if (dst_typed_param.mergeBlockParam(src_typed_param.isProvided(), dst_typed_param.isProvided(), param_value_t::getBlockDescriptor(), src_typed_param, overwrite))
|
|
{
|
|
dst_typed_param.clearValueName();
|
|
dst_typed_param.setProvided(true);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name )
|
|
{
|
|
ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor(
|
|
block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
&mergeWith,
|
|
&deserializeParam,
|
|
&serializeParam,
|
|
validate_func,
|
|
&inspectParam,
|
|
min_count, max_count));
|
|
block_descriptor.addParam(param_descriptor, name);
|
|
}
|
|
};
|
|
|
|
// list of non-block parameters
|
|
template <typename MULTI_VALUE_T, typename NAME_VALUE_LOOKUP>
|
|
class TypedParam<MULTI_VALUE_T, NAME_VALUE_LOOKUP, true, NOT_BLOCK>
|
|
: public Param
|
|
{
|
|
protected:
|
|
typedef TypedParam<MULTI_VALUE_T, NAME_VALUE_LOOKUP, true, NOT_BLOCK> self_t;
|
|
typedef ParamValue<typename LLTypeTags::Sorted<MULTI_VALUE_T>::value_t> param_value_t;
|
|
typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t;
|
|
typedef container_t default_value_t;
|
|
typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
|
|
|
|
public:
|
|
typedef typename param_value_t::value_t value_t;
|
|
|
|
TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
|
: Param(block_descriptor.mCurrentBlockPtr),
|
|
mMinCount(min_count),
|
|
mMaxCount(max_count)
|
|
{
|
|
std::copy(value.begin(), value.end(), std::back_inserter(mValues));
|
|
|
|
if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING))
|
|
{
|
|
init(block_descriptor, validate_func, min_count, max_count, name);
|
|
|
|
}
|
|
}
|
|
|
|
bool isProvided() const { return Param::anyProvided() && isValid(); }
|
|
|
|
bool isValid() const
|
|
{
|
|
size_t num_elements = numValidElements();
|
|
return mMinCount < num_elements && num_elements < mMaxCount;
|
|
}
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
|
{
|
|
Parser::name_stack_range_t new_name_stack_range(name_stack_range);
|
|
self_t& typed_param = static_cast<self_t&>(param);
|
|
value_t value;
|
|
|
|
// pop first element if empty string
|
|
if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty())
|
|
{
|
|
++new_name_stack_range.first;
|
|
}
|
|
|
|
// no further names in stack, attempt to parse value now
|
|
if (new_name_stack_range.first == new_name_stack_range.second)
|
|
{
|
|
std::string name;
|
|
|
|
// try to parse a known named value
|
|
if(named_value_t::valueNamesExist()
|
|
&& parser.readValue(name)
|
|
&& named_value_t::getValueFromName(name, value))
|
|
{
|
|
typed_param.add(value);
|
|
typed_param.mValues.back().setValueName(name);
|
|
return true;
|
|
}
|
|
else if (parser.readValue(value)) // attempt to read value directly
|
|
{
|
|
typed_param.add(value);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
|
|
{
|
|
bool serialized = false;
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
|
|
LLPredicate::Value<ESerializePredicates> predicate;
|
|
|
|
predicate.set(REQUIRED, typed_param.mMinCount > 0);
|
|
predicate.set(VALID, typed_param.isValid());
|
|
predicate.set(PROVIDED, typed_param.anyProvided());
|
|
predicate.set(EMPTY, typed_param.mValues.empty());
|
|
|
|
if (!predicate_rule.check(predicate)) return false;
|
|
|
|
for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end();
|
|
it != end_it;
|
|
++it)
|
|
{
|
|
std::string key = it->getValueName();
|
|
name_stack.push_back(std::make_pair(std::string(), true));
|
|
|
|
if(key.empty())
|
|
// not parsed via name values, write out value directly
|
|
{
|
|
bool value_written = parser.writeValue(*it, name_stack);
|
|
if (!value_written)
|
|
{
|
|
std::string calculated_key = it->calcValueName(it->getValue());
|
|
if (parser.writeValue(calculated_key, name_stack))
|
|
{
|
|
serialized = true;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(parser.writeValue(key, name_stack))
|
|
{
|
|
serialized = true;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return serialized;
|
|
}
|
|
|
|
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
|
{
|
|
parser.inspectValue<MULTI_VALUE_T>(name_stack, min_count, max_count, NULL);
|
|
if (named_value_t::getPossibleValues())
|
|
{
|
|
parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues());
|
|
}
|
|
}
|
|
|
|
void set(const container_t& val, bool flag_as_provided = true)
|
|
{
|
|
mValues = val;
|
|
setProvided(flag_as_provided);
|
|
}
|
|
|
|
param_value_t& add()
|
|
{
|
|
mValues.push_back(value_t());
|
|
Param::setProvided();
|
|
return mValues.back();
|
|
}
|
|
|
|
self_t& add(const value_t& item)
|
|
{
|
|
mValues.push_back(item);
|
|
setProvided();
|
|
return *this;
|
|
}
|
|
|
|
self_t& add(const typename named_value_t::name_t& name)
|
|
{
|
|
value_t value;
|
|
|
|
// try to parse a per type named value
|
|
if (named_value_t::getValueFromName(name, value))
|
|
{
|
|
add(value);
|
|
mValues.back().setValueName(name);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
// implicit conversion
|
|
operator const container_t&() const { return mValues; }
|
|
// explicit conversion
|
|
const container_t& operator()() const { return mValues; }
|
|
|
|
typedef typename container_t::iterator iterator;
|
|
typedef typename container_t::const_iterator const_iterator;
|
|
iterator begin() { return mValues.begin(); }
|
|
iterator end() { return mValues.end(); }
|
|
const_iterator begin() const { return mValues.begin(); }
|
|
const_iterator end() const { return mValues.end(); }
|
|
bool empty() const { return mValues.empty(); }
|
|
size_t size() const { return mValues.size(); }
|
|
|
|
size_t numValidElements() const
|
|
{
|
|
return mValues.size();
|
|
}
|
|
|
|
protected:
|
|
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)
|
|
{
|
|
std::copy(src_typed_param.begin(), src_typed_param.end(), std::back_inserter(dst_typed_param.mValues));
|
|
}
|
|
else
|
|
{
|
|
container_t new_values(src_typed_param.mValues);
|
|
std::copy(dst_typed_param.begin(), dst_typed_param.end(), std::back_inserter(new_values));
|
|
std::swap(dst_typed_param.mValues, new_values);
|
|
}
|
|
|
|
if (src_typed_param.begin() != src_typed_param.end())
|
|
{
|
|
dst_typed_param.setProvided();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
container_t mValues;
|
|
size_t mMinCount,
|
|
mMaxCount;
|
|
|
|
private:
|
|
void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name )
|
|
{
|
|
ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor(
|
|
block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
&mergeWith,
|
|
&deserializeParam,
|
|
&serializeParam,
|
|
validate_func,
|
|
&inspectParam,
|
|
min_count, max_count));
|
|
block_descriptor.addParam(param_descriptor, name);
|
|
}
|
|
};
|
|
|
|
// list of block parameters
|
|
template <typename MULTI_BLOCK_T, typename NAME_VALUE_LOOKUP>
|
|
class TypedParam<MULTI_BLOCK_T, NAME_VALUE_LOOKUP, true, IS_A_BLOCK>
|
|
: public Param
|
|
{
|
|
protected:
|
|
typedef TypedParam<MULTI_BLOCK_T, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> self_t;
|
|
typedef ParamValue<typename LLTypeTags::Sorted<MULTI_BLOCK_T>::value_t> param_value_t;
|
|
typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t;
|
|
typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
|
|
typedef container_t default_value_t;
|
|
typedef typename container_t::iterator iterator;
|
|
typedef typename container_t::const_iterator const_iterator;
|
|
public:
|
|
typedef typename param_value_t::value_t value_t;
|
|
|
|
TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
|
: Param(block_descriptor.mCurrentBlockPtr),
|
|
mMinCount(min_count),
|
|
mMaxCount(max_count)
|
|
{
|
|
std::copy(value.begin(), value.end(), back_inserter(mValues));
|
|
|
|
if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING))
|
|
{
|
|
init(block_descriptor, validate_func, min_count, max_count, name);
|
|
}
|
|
}
|
|
|
|
bool isProvided() const { return Param::anyProvided() && isValid(); }
|
|
|
|
bool isValid() const
|
|
{
|
|
size_t num_elements = numValidElements();
|
|
return mMinCount < num_elements && num_elements < mMaxCount;
|
|
}
|
|
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
|
{
|
|
Parser::name_stack_range_t new_name_stack_range(name_stack_range);
|
|
self_t& typed_param = static_cast<self_t&>(param);
|
|
bool new_value = false;
|
|
bool new_array_value = false;
|
|
|
|
// pop first element if empty string
|
|
if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty())
|
|
{
|
|
new_array_value = new_name_stack_range.first->second;
|
|
++new_name_stack_range.first;
|
|
}
|
|
|
|
if (new_name || new_array_value || typed_param.mValues.empty())
|
|
{
|
|
new_value = true;
|
|
typed_param.mValues.push_back(value_t());
|
|
}
|
|
param_value_t& value = typed_param.mValues.back();
|
|
|
|
if (new_name_stack_range.first == new_name_stack_range.second)
|
|
{ // try to parse a known named value
|
|
std::string name;
|
|
|
|
if(named_value_t::valueNamesExist()
|
|
&& parser.readValue(name)
|
|
&& named_value_t::getValueFromName(name, value.getValue()))
|
|
{
|
|
typed_param.mValues.back().setValueName(name);
|
|
typed_param.setProvided();
|
|
if (new_array_value)
|
|
{
|
|
name_stack_range.first->second = false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// attempt to parse block...
|
|
if(value.deserializeBlock(parser, new_name_stack_range, new_name))
|
|
{
|
|
typed_param.setProvided();
|
|
if (new_array_value)
|
|
{
|
|
name_stack_range.first->second = false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
if (new_value)
|
|
{ // failed to parse new value, pop it off
|
|
typed_param.mValues.pop_back();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
|
|
{
|
|
bool serialized = false;
|
|
const self_t& typed_param = static_cast<const self_t&>(param);
|
|
LLPredicate::Value<ESerializePredicates> predicate;
|
|
|
|
predicate.set(REQUIRED, typed_param.mMinCount > 0);
|
|
predicate.set(VALID, typed_param.isValid());
|
|
predicate.set(PROVIDED, typed_param.anyProvided());
|
|
predicate.set(EMPTY, typed_param.mValues.empty());
|
|
|
|
if (!predicate_rule.check(predicate)) return false;
|
|
|
|
for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end();
|
|
it != end_it;
|
|
++it)
|
|
{
|
|
name_stack.push_back(std::make_pair(std::string(), true));
|
|
|
|
std::string key = it->getValueName();
|
|
if (!key.empty())
|
|
{
|
|
serialized |= parser.writeValue(key, name_stack);
|
|
}
|
|
// Not parsed via named values, write out value directly
|
|
// NOTE: currently we don't do diffing of Multiples
|
|
else
|
|
{
|
|
serialized = it->serializeBlock(parser, name_stack, predicate_rule, NULL);
|
|
}
|
|
|
|
name_stack.pop_back();
|
|
}
|
|
|
|
if (!serialized && predicate_rule.check(ll_make_predicate(EMPTY)))
|
|
{
|
|
serialized |= parser.writeValue(Flag(), name_stack);
|
|
}
|
|
|
|
return serialized;
|
|
}
|
|
|
|
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
|
{
|
|
const param_value_t& value_param = param_value_t(value_t());
|
|
|
|
// tell parser about our actual type
|
|
parser.inspectValue<value_t>(name_stack, min_count, max_count, NULL);
|
|
// then tell it about string-based alternatives ("red", "blue", etc. for LLColor4)
|
|
if (named_value_t::getPossibleValues())
|
|
{
|
|
parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues());
|
|
}
|
|
|
|
value_param.inspectBlock(parser, name_stack, min_count, max_count);
|
|
}
|
|
|
|
void set(const container_t& val, bool flag_as_provided = true)
|
|
{
|
|
mValues = val;
|
|
setProvided(flag_as_provided);
|
|
}
|
|
|
|
param_value_t& add()
|
|
{
|
|
mValues.push_back(value_t());
|
|
setProvided();
|
|
return mValues.back();
|
|
}
|
|
|
|
self_t& add(const value_t& item)
|
|
{
|
|
mValues.push_back(item);
|
|
setProvided();
|
|
return *this;
|
|
}
|
|
|
|
self_t& add(const typename named_value_t::name_t& name)
|
|
{
|
|
value_t value;
|
|
|
|
// try to parse a per type named value
|
|
if (named_value_t::getValueFromName(name, value))
|
|
{
|
|
add(value);
|
|
mValues.back().setValueName(name);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// implicit conversion
|
|
operator const container_t&() const { return mValues; }
|
|
// explicit conversion
|
|
const container_t& operator()() const { return mValues; }
|
|
|
|
iterator begin() { return mValues.begin(); }
|
|
iterator end() { return mValues.end(); }
|
|
const_iterator begin() const { return mValues.begin(); }
|
|
const_iterator end() const { return mValues.end(); }
|
|
bool empty() const { return mValues.empty(); }
|
|
size_t size() const { return mValues.size(); }
|
|
|
|
size_t numValidElements() const
|
|
{
|
|
size_t count = 0;
|
|
for (const_iterator it = mValues.begin(), end_it = mValues.end();
|
|
it != end_it;
|
|
++it)
|
|
{
|
|
if(it->isValid()) count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
protected:
|
|
|
|
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)
|
|
{
|
|
std::copy(src_typed_param.begin(), src_typed_param.end(), std::back_inserter(dst_typed_param.mValues));
|
|
}
|
|
else
|
|
{
|
|
container_t new_values(src_typed_param.mValues);
|
|
std::copy(dst_typed_param.begin(), dst_typed_param.end(), std::back_inserter(new_values));
|
|
std::swap(dst_typed_param.mValues, new_values);
|
|
}
|
|
|
|
if (src_typed_param.begin() != src_typed_param.end())
|
|
{
|
|
dst_typed_param.setProvided();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
container_t mValues;
|
|
size_t mMinCount,
|
|
mMaxCount;
|
|
|
|
private:
|
|
void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name )
|
|
{
|
|
ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor(
|
|
block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
&mergeWith,
|
|
&deserializeParam,
|
|
&serializeParam,
|
|
validate_func,
|
|
&inspectParam,
|
|
min_count, max_count));
|
|
block_descriptor.addParam(param_descriptor, name);
|
|
}
|
|
};
|
|
|
|
template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock>
|
|
class ChoiceBlock : public BASE_BLOCK
|
|
{
|
|
typedef ChoiceBlock<DERIVED_BLOCK, BASE_BLOCK> self_t;
|
|
typedef ChoiceBlock<DERIVED_BLOCK, BASE_BLOCK> enclosing_block_t;
|
|
typedef BASE_BLOCK base_block_t;
|
|
|
|
LOG_CLASS(self_t);
|
|
public:
|
|
// take all provided params from other and apply to self
|
|
bool overwriteFrom(const self_t& other)
|
|
{
|
|
return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, true);
|
|
}
|
|
|
|
// take all provided params that are not already provided, and apply to self
|
|
bool fillFrom(const self_t& other)
|
|
{
|
|
return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, false);
|
|
}
|
|
|
|
bool mergeBlockParam(bool source_provided, bool dest_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite)
|
|
{
|
|
bool source_override = source_provided && (overwrite || !dest_provided);
|
|
|
|
if (source_override || source.mCurChoice == mCurChoice)
|
|
{
|
|
return mergeBlock(block_data, source, overwrite);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// merge with other block
|
|
bool mergeBlock(BlockDescriptor& block_data, const self_t& other, bool overwrite)
|
|
{
|
|
mCurChoice = other.mCurChoice;
|
|
return base_block_t::mergeBlock(getBlockDescriptor(), other, overwrite);
|
|
}
|
|
|
|
// clear out old choice when param has changed
|
|
/*virtual*/ void paramChanged(const Param& changed_param, bool user_provided)
|
|
{
|
|
param_handle_t changed_param_handle = base_block_t::getHandleFromParam(&changed_param);
|
|
// if we have a new choice...
|
|
if (changed_param_handle != mCurChoice)
|
|
{
|
|
// clear provided flag on previous choice
|
|
Param* previous_choice = base_block_t::getParamFromHandle(mCurChoice);
|
|
if (previous_choice)
|
|
{
|
|
previous_choice->setProvided(false);
|
|
}
|
|
mCurChoice = changed_param_handle;
|
|
}
|
|
base_block_t::paramChanged(changed_param, user_provided);
|
|
}
|
|
|
|
virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); }
|
|
virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); }
|
|
|
|
protected:
|
|
ChoiceBlock()
|
|
: mCurChoice(0)
|
|
{
|
|
BaseBlock::init(getBlockDescriptor(), base_block_t::getBlockDescriptor(), 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 = typename TypeValues<T>::type_value_t >
|
|
class Alternative : public TypedParam<T, NAME_VALUE_LOOKUP, false>
|
|
{
|
|
typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
|
|
typedef typename super_t::value_t value_t;
|
|
typedef typename super_t::default_value_t default_value_t;
|
|
|
|
public:
|
|
friend class ChoiceBlock<DERIVED_BLOCK>;
|
|
|
|
using super_t::operator =;
|
|
|
|
explicit Alternative(const char* name = "", const default_value_t& val = defaultValue<default_value_t>())
|
|
: super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1),
|
|
mOriginalValue(val)
|
|
{
|
|
// assign initial choice to first declared option
|
|
DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr);
|
|
if (LL_UNLIKELY(DERIVED_BLOCK::getBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING))
|
|
{
|
|
if(blockp->mCurChoice == 0)
|
|
{
|
|
blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void choose()
|
|
{
|
|
static_cast<enclosing_block_t&>(Param::enclosingBlock()).paramChanged(*this, true);
|
|
}
|
|
|
|
void chooseAs(const value_t& val)
|
|
{
|
|
super_t::set(val);
|
|
}
|
|
|
|
void operator =(const value_t& val)
|
|
{
|
|
super_t::set(val);
|
|
}
|
|
|
|
void operator()(const value_t& val)
|
|
{
|
|
super_t::set(val);
|
|
}
|
|
|
|
operator const value_t&() const
|
|
{
|
|
return (*this)();
|
|
}
|
|
|
|
const value_t& operator()() const
|
|
{
|
|
if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this)
|
|
{
|
|
return super_t::getValue();
|
|
}
|
|
return mOriginalValue;
|
|
}
|
|
|
|
bool isChosen() const
|
|
{
|
|
return static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this;
|
|
}
|
|
|
|
private:
|
|
default_value_t mOriginalValue;
|
|
};
|
|
|
|
public:
|
|
static BlockDescriptor& getBlockDescriptor()
|
|
{
|
|
static BlockDescriptor sBlockDescriptor;
|
|
return sBlockDescriptor;
|
|
}
|
|
|
|
private:
|
|
param_handle_t mCurChoice;
|
|
|
|
const Param* getCurrentChoice() const
|
|
{
|
|
return base_block_t::getParamFromHandle(mCurChoice);
|
|
}
|
|
};
|
|
|
|
template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock>
|
|
class Block
|
|
: public BASE_BLOCK
|
|
{
|
|
typedef Block<DERIVED_BLOCK, BASE_BLOCK> self_t;
|
|
|
|
protected:
|
|
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 static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, true);
|
|
}
|
|
|
|
// take all provided params that are not already provided, and apply to self
|
|
bool fillFrom(const self_t& other)
|
|
{
|
|
return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, false);
|
|
}
|
|
|
|
virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); }
|
|
virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); }
|
|
|
|
protected:
|
|
Block()
|
|
{
|
|
//#pragma message("Parsing LLInitParam::Block")
|
|
BaseBlock::init(getBlockDescriptor(), BASE_BLOCK::getBlockDescriptor(), sizeof(DERIVED_BLOCK));
|
|
}
|
|
|
|
//
|
|
// Nested classes for declaring parameters
|
|
//
|
|
template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t >
|
|
class Optional : public TypedParam<T, NAME_VALUE_LOOKUP, false>
|
|
{
|
|
typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
|
|
typedef typename super_t::value_t value_t;
|
|
typedef typename super_t::default_value_t default_value_t;
|
|
|
|
public:
|
|
using super_t::operator();
|
|
using super_t::operator =;
|
|
|
|
explicit Optional(const char* name = "", const default_value_t& val = defaultValue<default_value_t>())
|
|
: super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1)
|
|
{
|
|
//#pragma message("Parsing LLInitParam::Block::Optional")
|
|
}
|
|
|
|
Optional& operator =(const value_t& val)
|
|
{
|
|
super_t::set(val);
|
|
return *this;
|
|
}
|
|
|
|
DERIVED_BLOCK& operator()(const value_t& val)
|
|
{
|
|
super_t::set(val);
|
|
return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
|
|
}
|
|
};
|
|
|
|
template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t >
|
|
class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false>
|
|
{
|
|
typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
|
|
typedef Mandatory<T, NAME_VALUE_LOOKUP> self_t;
|
|
typedef typename super_t::value_t value_t;
|
|
typedef typename super_t::default_value_t default_value_t;
|
|
|
|
public:
|
|
using super_t::operator();
|
|
using super_t::operator =;
|
|
|
|
// mandatory parameters require a name to be parseable
|
|
explicit Mandatory(const char* name = "", const default_value_t& val = defaultValue<default_value_t>())
|
|
: super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, &validate, 1, 1)
|
|
{}
|
|
|
|
Mandatory& operator =(const value_t& val)
|
|
{
|
|
super_t::set(val);
|
|
return *this;
|
|
}
|
|
|
|
DERIVED_BLOCK& operator()(const value_t& val)
|
|
{
|
|
super_t::set(val);
|
|
return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
|
|
}
|
|
|
|
static bool validate(const Param* p)
|
|
{
|
|
// valid only if provided
|
|
return static_cast<const self_t*>(p)->isProvided();
|
|
}
|
|
|
|
};
|
|
|
|
template <typename T, typename RANGE = BaseBlock::AnyAmount, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t >
|
|
class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true>
|
|
{
|
|
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_t value_t;
|
|
|
|
public:
|
|
typedef typename super_t::iterator iterator;
|
|
typedef typename super_t::const_iterator const_iterator;
|
|
|
|
explicit Multiple(const char* name = "")
|
|
: super_t(DERIVED_BLOCK::getBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount)
|
|
{}
|
|
|
|
Multiple& operator =(const container_t& val)
|
|
{
|
|
super_t::set(val);
|
|
return *this;
|
|
}
|
|
|
|
DERIVED_BLOCK& operator()(const container_t& val)
|
|
{
|
|
super_t::set(val);
|
|
return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
|
|
}
|
|
|
|
static bool validate(const Param* paramp)
|
|
{
|
|
size_t num_valid = ((super_t*)paramp)->numValidElements();
|
|
return RANGE::minCount <= num_valid && num_valid <= RANGE::maxCount;
|
|
}
|
|
};
|
|
|
|
// can appear in data files, but will ignored during parsing
|
|
// cannot read or write in code
|
|
class Ignored : public Param
|
|
{
|
|
public:
|
|
explicit Ignored(const char* name)
|
|
: Param(DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr)
|
|
{
|
|
BlockDescriptor& block_descriptor = DERIVED_BLOCK::getBlockDescriptor();
|
|
if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING))
|
|
{
|
|
ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor(
|
|
block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
|
|
NULL,
|
|
&deserializeParam,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0, S32_MAX));
|
|
block_descriptor.addParam(param_descriptor, name);
|
|
}
|
|
}
|
|
|
|
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
|
{
|
|
if (name_stack_range.first == name_stack_range.second)
|
|
{
|
|
//std::string message = llformat("Deprecated value %s ignored", getName().c_str());
|
|
//parser.parserWarning(message);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// can appear in data files, or be written to in code, but data will be ignored
|
|
// cannot be read in code
|
|
class Deprecated : public Ignored
|
|
{
|
|
public:
|
|
explicit Deprecated(const char* name) : Ignored(name) {}
|
|
|
|
// dummy writer interfaces
|
|
template<typename T>
|
|
Deprecated& operator =(const T& val)
|
|
{
|
|
// do nothing
|
|
return *this;
|
|
}
|
|
|
|
template<typename T>
|
|
DERIVED_BLOCK& operator()(const T& val)
|
|
{
|
|
// do nothing
|
|
return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
|
|
}
|
|
|
|
template<typename T>
|
|
void set(const T& val, bool flag_as_provided = true)
|
|
{
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
public:
|
|
static BlockDescriptor& getBlockDescriptor()
|
|
{
|
|
static BlockDescriptor sBlockDescriptor;
|
|
return sBlockDescriptor;
|
|
}
|
|
|
|
protected:
|
|
template <typename T, typename NAME_VALUE_LOOKUP, bool multiple, typename is_block>
|
|
void changeDefault(TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>& param,
|
|
const typename TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>::value_t& value)
|
|
{
|
|
if (!param.isProvided())
|
|
{
|
|
param.set(value, false);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
template<typename T, typename BLOCK_T>
|
|
struct IsBlock<ParamValue<BaseBlock::Lazy<T, BaseBlock::IS_A_BLOCK>, BLOCK_T >, void>
|
|
{
|
|
typedef IS_A_BLOCK value_t;
|
|
};
|
|
|
|
template<typename T, typename BLOCK_T>
|
|
struct IsBlock<ParamValue<BaseBlock::Lazy<T, BaseBlock::NOT_A_BLOCK>, BLOCK_T >, void>
|
|
{
|
|
typedef NOT_BLOCK value_t;
|
|
};
|
|
|
|
template<typename T, typename BLOCK_IDENTIFIER>
|
|
struct IsBlock<ParamValue<BaseBlock::Atomic<T>, typename IsBlock<BaseBlock::Atomic<T> >::value_t >, BLOCK_IDENTIFIER>
|
|
{
|
|
typedef typename IsBlock<T>::value_t value_t;
|
|
};
|
|
|
|
template<typename T, typename BLOCK_IDENTIFIER>
|
|
struct IsBlock<ParamValue<BaseBlock::Sequential<T>, typename IsBlock<BaseBlock::Sequential<T> >::value_t >, BLOCK_IDENTIFIER>
|
|
{
|
|
typedef typename IsBlock<T>::value_t value_t;
|
|
};
|
|
|
|
|
|
template<typename T>
|
|
struct InnerMostType
|
|
{
|
|
typedef T value_t;
|
|
};
|
|
|
|
template<typename T>
|
|
struct InnerMostType<ParamValue<T, NOT_BLOCK> >
|
|
{
|
|
typedef typename InnerMostType<T>::value_t value_t;
|
|
};
|
|
|
|
template<typename T>
|
|
struct InnerMostType<ParamValue<T, IS_A_BLOCK> >
|
|
{
|
|
typedef typename InnerMostType<T>::value_t value_t;
|
|
};
|
|
|
|
template<typename T, typename BLOCK_T>
|
|
class ParamValue <BaseBlock::Atomic<T>, BLOCK_T>
|
|
{
|
|
typedef ParamValue <BaseBlock::Atomic<T>, BLOCK_T> self_t;
|
|
|
|
public:
|
|
typedef typename InnerMostType<T>::value_t value_t;
|
|
typedef T default_value_t;
|
|
|
|
ParamValue()
|
|
: mValue()
|
|
{}
|
|
|
|
ParamValue(const default_value_t& value)
|
|
: mValue(value)
|
|
{}
|
|
|
|
void setValue(const value_t& val)
|
|
{
|
|
mValue.setValue(val);
|
|
}
|
|
|
|
const value_t& getValue() const
|
|
{
|
|
return mValue.getValue();
|
|
}
|
|
|
|
value_t& getValue()
|
|
{
|
|
return mValue.getValue();
|
|
}
|
|
|
|
bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
|
{
|
|
if (new_name)
|
|
{
|
|
resetToDefault();
|
|
}
|
|
return mValue.deserializeBlock(p, name_stack_range, new_name);
|
|
}
|
|
|
|
bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const
|
|
{
|
|
const BaseBlock* base_block = diff_block
|
|
? &(diff_block->mValue)
|
|
: NULL;
|
|
return mValue.serializeBlock(p, name_stack, predicate_rule, base_block);
|
|
}
|
|
|
|
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
|
|
{
|
|
return mValue.inspectBlock(p, name_stack, min_count, max_count);
|
|
}
|
|
|
|
bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite)
|
|
{
|
|
if ((overwrite && source_provided) // new values coming in on top or...
|
|
|| (!overwrite && !dst_provided)) // values being pushed under with nothing already there
|
|
{
|
|
// clear away what is there and take the new stuff as a whole
|
|
resetToDefault();
|
|
return mValue.mergeBlock(block_data, source.getValue(), overwrite);
|
|
}
|
|
return mValue.mergeBlock(block_data, source.getValue(), overwrite);
|
|
}
|
|
|
|
bool validateBlock(bool emit_errors = true) const
|
|
{
|
|
return mValue.validateBlock(emit_errors);
|
|
}
|
|
|
|
bool isValid() const
|
|
{
|
|
return validateBlock(false);
|
|
}
|
|
|
|
static BlockDescriptor& getBlockDescriptor()
|
|
{
|
|
return value_t::getBlockDescriptor();
|
|
}
|
|
|
|
|
|
private:
|
|
void resetToDefault()
|
|
{
|
|
static T default_value;
|
|
mValue = default_value;
|
|
}
|
|
|
|
T mValue;
|
|
};
|
|
|
|
template<typename T>
|
|
class ParamValue <BaseBlock::Sequential<T>, IS_A_BLOCK>
|
|
{
|
|
typedef ParamValue <BaseBlock::Sequential<T>, IS_A_BLOCK> self_t;
|
|
|
|
public:
|
|
typedef typename InnerMostType<T>::value_t value_t;
|
|
typedef T default_value_t;
|
|
|
|
ParamValue()
|
|
: mValue()
|
|
{
|
|
mCurParam = getBlockDescriptor().mAllParams.begin();
|
|
}
|
|
|
|
ParamValue(const default_value_t& value)
|
|
: mValue(value)
|
|
{
|
|
mCurParam = getBlockDescriptor().mAllParams.begin();
|
|
}
|
|
|
|
void setValue(const value_t& val)
|
|
{
|
|
mValue.setValue(val);
|
|
}
|
|
|
|
const value_t& getValue() const
|
|
{
|
|
return mValue.getValue();
|
|
}
|
|
|
|
value_t& getValue()
|
|
{
|
|
return mValue.getValue();
|
|
}
|
|
|
|
bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
|
{
|
|
if (new_name)
|
|
{
|
|
mCurParam = getBlockDescriptor().mAllParams.begin();
|
|
}
|
|
if (name_stack_range.first == name_stack_range.second
|
|
&& mCurParam != getBlockDescriptor().mAllParams.end())
|
|
{
|
|
// deserialize to mCurParam
|
|
ParamDescriptor& pd = *(*mCurParam);
|
|
ParamDescriptor::deserialize_func_t deserialize_func = pd.mDeserializeFunc;
|
|
Param* paramp = mValue.getParamFromHandle(pd.mParamHandle);
|
|
|
|
if (deserialize_func
|
|
&& paramp
|
|
&& deserialize_func(*paramp, p, name_stack_range, new_name))
|
|
{
|
|
++mCurParam;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return mValue.deserializeBlock(p, name_stack_range, new_name);
|
|
}
|
|
}
|
|
|
|
bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const
|
|
{
|
|
const BaseBlock* base_block = diff_block
|
|
? &(diff_block->mValue)
|
|
: NULL;
|
|
return mValue.serializeBlock(p, name_stack, predicate_rule, base_block);
|
|
}
|
|
|
|
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
|
|
{
|
|
return mValue.inspectBlock(p, name_stack, min_count, max_count);
|
|
}
|
|
|
|
bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite)
|
|
{
|
|
return mValue.mergeBlock(block_data, source.getValue(), overwrite);
|
|
}
|
|
|
|
bool validateBlock(bool emit_errors = true) const
|
|
{
|
|
return mValue.validateBlock(emit_errors);
|
|
}
|
|
|
|
bool isValid() const
|
|
{
|
|
return validateBlock(false);
|
|
}
|
|
|
|
static BlockDescriptor& getBlockDescriptor()
|
|
{
|
|
return value_t::getBlockDescriptor();
|
|
}
|
|
|
|
private:
|
|
|
|
BlockDescriptor::all_params_list_t::iterator mCurParam;
|
|
T mValue;
|
|
};
|
|
|
|
template<typename T>
|
|
class ParamValue <BaseBlock::Sequential<T>, NOT_BLOCK>
|
|
: public T
|
|
{
|
|
typedef ParamValue <BaseBlock::Sequential<T>, NOT_BLOCK> self_t;
|
|
|
|
public:
|
|
typedef typename InnerMostType<T>::value_t value_t;
|
|
typedef T default_value_t;
|
|
|
|
ParamValue()
|
|
: T()
|
|
{}
|
|
|
|
ParamValue(const default_value_t& value)
|
|
: T(value.getValue())
|
|
{}
|
|
|
|
bool isValid() const { return true; }
|
|
};
|
|
|
|
template<typename T, typename BLOCK_T>
|
|
class ParamValue <BaseBlock::Lazy<T, IS_A_BLOCK>, BLOCK_T>
|
|
{
|
|
typedef ParamValue <BaseBlock::Lazy<T, IS_A_BLOCK>, BLOCK_T> self_t;
|
|
|
|
public:
|
|
typedef typename InnerMostType<T>::value_t value_t;
|
|
typedef LazyValue<T> default_value_t;
|
|
|
|
ParamValue()
|
|
: mValue()
|
|
{}
|
|
|
|
ParamValue(const default_value_t& other)
|
|
: mValue(other)
|
|
{}
|
|
|
|
ParamValue(const T& value)
|
|
: mValue(value)
|
|
{}
|
|
|
|
void setValue(const value_t& val)
|
|
{
|
|
mValue.set(val);
|
|
}
|
|
|
|
const value_t& getValue() const
|
|
{
|
|
return mValue.get().getValue();
|
|
}
|
|
|
|
value_t& getValue()
|
|
{
|
|
return mValue.get().getValue();
|
|
}
|
|
|
|
bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
|
{
|
|
return mValue.get().deserializeBlock(p, name_stack_range, new_name);
|
|
}
|
|
|
|
bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const
|
|
{
|
|
if (mValue.empty()) return false;
|
|
|
|
const BaseBlock* base_block = (diff_block && !diff_block->mValue.empty())
|
|
? &(diff_block->mValue.get().getValue())
|
|
: NULL;
|
|
return mValue.get().serializeBlock(p, name_stack, predicate_rule, base_block);
|
|
}
|
|
|
|
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
|
|
{
|
|
return mValue.get().inspectBlock(p, name_stack, min_count, max_count);
|
|
}
|
|
|
|
bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite)
|
|
{
|
|
return source.mValue.empty() || mValue.get().mergeBlock(block_data, source.getValue(), overwrite);
|
|
}
|
|
|
|
bool validateBlock(bool emit_errors = true) const
|
|
{
|
|
return mValue.empty() || mValue.get().validateBlock(emit_errors);
|
|
}
|
|
|
|
bool isValid() const
|
|
{
|
|
return validateBlock(false);
|
|
}
|
|
|
|
static BlockDescriptor& getBlockDescriptor()
|
|
{
|
|
return value_t::getBlockDescriptor();
|
|
}
|
|
|
|
private:
|
|
LazyValue<T> mValue;
|
|
};
|
|
|
|
template<typename T, typename BLOCK_T>
|
|
class ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T>
|
|
{
|
|
typedef ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T> self_t;
|
|
|
|
public:
|
|
typedef typename InnerMostType<T>::value_t value_t;
|
|
typedef LazyValue<T> default_value_t;
|
|
|
|
ParamValue()
|
|
: mValue()
|
|
{}
|
|
|
|
ParamValue(const default_value_t& other)
|
|
: mValue(other)
|
|
{}
|
|
|
|
ParamValue(const T& value)
|
|
: mValue(value)
|
|
{}
|
|
|
|
void setValue(const value_t& val)
|
|
{
|
|
mValue.set(val);
|
|
}
|
|
|
|
const value_t& getValue() const
|
|
{
|
|
return mValue.get().getValue();
|
|
}
|
|
|
|
value_t& getValue()
|
|
{
|
|
return mValue.get().getValue();
|
|
}
|
|
|
|
bool isValid() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
LazyValue<T> mValue;
|
|
};
|
|
|
|
template <>
|
|
class ParamValue <LLSD, NOT_BLOCK>
|
|
: public BaseBlock
|
|
{
|
|
public:
|
|
typedef LLSD value_t;
|
|
typedef LLSD default_value_t;
|
|
|
|
ParamValue()
|
|
{}
|
|
|
|
ParamValue(const default_value_t& other)
|
|
: mValue(other)
|
|
{}
|
|
|
|
void setValue(const value_t& val) { mValue = val; }
|
|
|
|
const value_t& getValue() const { return mValue; }
|
|
LLSD& getValue() { return mValue; }
|
|
|
|
// block param interface
|
|
LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
|
|
LL_COMMON_API bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const BaseBlock* diff_block = NULL) const;
|
|
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
|
|
{
|
|
//TODO: implement LLSD params as schema type Any
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
static void serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack);
|
|
|
|
LLSD mValue;
|
|
};
|
|
|
|
template<typename T>
|
|
class CustomParamValue
|
|
: public Block<ParamValue<T> >
|
|
{
|
|
public:
|
|
typedef enum e_value_age
|
|
{
|
|
VALUE_NEEDS_UPDATE, // mValue needs to be refreshed from the block parameters
|
|
VALUE_AUTHORITATIVE, // mValue holds the authoritative value (which has been replicated to the block parameters via updateBlockFromValue)
|
|
BLOCK_AUTHORITATIVE // mValue is derived from the block parameters, which are authoritative
|
|
} EValueAge;
|
|
|
|
typedef TypeValues<T> derived_t;
|
|
typedef CustomParamValue<T> self_t;
|
|
typedef Block<ParamValue<T> > block_t;
|
|
typedef T default_value_t;
|
|
typedef T value_t;
|
|
typedef void baseblock_base_class_t;
|
|
|
|
|
|
CustomParamValue(const default_value_t& value = T())
|
|
: mValue(value),
|
|
mValueAge(VALUE_AUTHORITATIVE)
|
|
{}
|
|
|
|
bool deserializeBlock(Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
|
{
|
|
derived_t& typed_param = static_cast<derived_t&>(*this);
|
|
// try to parse direct value T
|
|
if (name_stack_range.first == name_stack_range.second)
|
|
{
|
|
if(parser.readValue(typed_param.mValue))
|
|
{
|
|
typed_param.mValueAge = VALUE_AUTHORITATIVE;
|
|
typed_param.updateBlockFromValue(false);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// fall back on parsing block components for T
|
|
return typed_param.BaseBlock::deserializeBlock(parser, name_stack_range, new_name);
|
|
}
|
|
|
|
bool serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const BaseBlock* diff_block = NULL) const
|
|
{
|
|
const derived_t& typed_param = static_cast<const derived_t&>(*this);
|
|
const derived_t* diff_param = static_cast<const derived_t*>(diff_block);
|
|
|
|
//std::string key = typed_param.getValueName();
|
|
|
|
//// first try to write out name of name/value pair
|
|
//if (!key.empty())
|
|
//{
|
|
// if (!diff_param || !ParamCompare<std::string>::equals(diff_param->getValueName(), key))
|
|
// {
|
|
// return parser.writeValue(key, name_stack);
|
|
// }
|
|
//}
|
|
// then try to serialize value directly
|
|
if (!diff_param || !ParamCompare<T>::equals(typed_param.getValue(), diff_param->getValue()))
|
|
{
|
|
|
|
if (parser.writeValue(typed_param.getValue(), name_stack))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
//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)
|
|
|
|
if (typed_param.mValueAge == VALUE_AUTHORITATIVE)
|
|
{
|
|
// if the value is authoritative but the parser doesn't accept the value type
|
|
// go ahead and make a copy, and splat the value out to its component params
|
|
// and serialize those params
|
|
derived_t copy(typed_param);
|
|
copy.updateBlockFromValue(true);
|
|
return copy.block_t::serializeBlock(parser, name_stack, predicate_rule, NULL);
|
|
}
|
|
else
|
|
{
|
|
return block_t::serializeBlock(parser, name_stack, predicate_rule, NULL);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool validateBlock(bool emit_errors = true) const
|
|
{
|
|
if (mValueAge == VALUE_NEEDS_UPDATE)
|
|
{
|
|
if (block_t::validateBlock(emit_errors))
|
|
{
|
|
// clear stale keyword associated with old value
|
|
mValueAge = BLOCK_AUTHORITATIVE;
|
|
static_cast<derived_t*>(const_cast<self_t*>(this))->updateValueFromBlock();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
//block value incomplete, so not considered provided
|
|
// will attempt to revalidate on next call to isProvided()
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we have a valid value in hand
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// propagate change status up to enclosing block
|
|
/*virtual*/ void paramChanged(const Param& changed_param, bool user_provided)
|
|
{
|
|
BaseBlock::paramChanged(changed_param, user_provided);
|
|
if (user_provided)
|
|
{
|
|
// a parameter changed, so our value is out of date
|
|
mValueAge = VALUE_NEEDS_UPDATE;
|
|
}
|
|
}
|
|
|
|
void setValue(const value_t& val)
|
|
{
|
|
// set param version number to be up to date, so we ignore block contents
|
|
mValueAge = VALUE_AUTHORITATIVE;
|
|
mValue = val;
|
|
static_cast<derived_t*>(this)->updateBlockFromValue(false);
|
|
}
|
|
|
|
const value_t& getValue() const
|
|
{
|
|
validateBlock(true);
|
|
return mValue;
|
|
}
|
|
|
|
T& getValue()
|
|
{
|
|
validateBlock(true);
|
|
return mValue;
|
|
}
|
|
|
|
protected:
|
|
|
|
// use this from within updateValueFromBlock() to set the value without making it authoritative
|
|
void updateValue(const value_t& value)
|
|
{
|
|
mValue = value;
|
|
}
|
|
|
|
bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite)
|
|
{
|
|
bool source_override = source_provided && (overwrite || !dst_provided);
|
|
|
|
const derived_t& src_typed_param = static_cast<const derived_t&>(source);
|
|
|
|
if (source_override && src_typed_param.mValueAge == VALUE_AUTHORITATIVE)
|
|
{
|
|
// copy value over
|
|
setValue(src_typed_param.getValue());
|
|
return true;
|
|
}
|
|
// merge individual parameters into destination
|
|
if (mValueAge == VALUE_AUTHORITATIVE)
|
|
{
|
|
static_cast<derived_t*>(this)->updateBlockFromValue(dst_provided);
|
|
}
|
|
return mergeBlock(block_data, source, overwrite);
|
|
}
|
|
|
|
bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& source, bool overwrite)
|
|
{
|
|
return block_t::mergeBlock(block_data, source, overwrite);
|
|
}
|
|
|
|
private:
|
|
mutable T mValue;
|
|
mutable EValueAge mValueAge;
|
|
};
|
|
}
|
|
|
|
|
|
#endif // LL_LLPARAM_H
|