493 lines
15 KiB
C++
493 lines
15 KiB
C++
/**
|
|
* @file llinitparam.cpp
|
|
* @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$
|
|
*/
|
|
|
|
#include "linden_common.h"
|
|
|
|
#include "llinitparam.h"
|
|
#include "llformat.h"
|
|
|
|
|
|
namespace LLInitParam
|
|
{
|
|
|
|
predicate_rule_t default_parse_rules()
|
|
{
|
|
return ll_make_predicate(PROVIDED) && !ll_make_predicate(EMPTY);
|
|
}
|
|
|
|
//
|
|
// Param
|
|
//
|
|
Param::Param(BaseBlock* enclosing_block)
|
|
: mIsProvided(false)
|
|
{
|
|
const U8* my_addr = reinterpret_cast<const U8*>(this);
|
|
const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block);
|
|
U32 enclosing_block_offset = 0x7FFFffff & (U32)(my_addr - block_addr);
|
|
mEnclosingBlockOffsetLow = enclosing_block_offset & 0x0000ffff;
|
|
mEnclosingBlockOffsetHigh = (enclosing_block_offset & 0x007f0000) >> 16;
|
|
}
|
|
|
|
//
|
|
// ParamDescriptor
|
|
//
|
|
ParamDescriptor::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),
|
|
mInspectFunc(inspect_func),
|
|
mValidationFunc(validation_func),
|
|
mMinCount(min_count),
|
|
mMaxCount(max_count),
|
|
mUserData(NULL)
|
|
{}
|
|
|
|
ParamDescriptor::ParamDescriptor()
|
|
: mParamHandle(0),
|
|
mMergeFunc(NULL),
|
|
mDeserializeFunc(NULL),
|
|
mSerializeFunc(NULL),
|
|
mInspectFunc(NULL),
|
|
mValidationFunc(NULL),
|
|
mMinCount(0),
|
|
mMaxCount(0),
|
|
mUserData(NULL)
|
|
{}
|
|
|
|
ParamDescriptor::~ParamDescriptor()
|
|
{
|
|
delete mUserData;
|
|
}
|
|
|
|
//
|
|
// Parser
|
|
//
|
|
Parser::~Parser()
|
|
{}
|
|
|
|
void Parser::parserWarning(const std::string& message)
|
|
{
|
|
if (mParseSilently) return;
|
|
LL_WARNS() << message << LL_ENDL;
|
|
}
|
|
|
|
void Parser::parserError(const std::string& message)
|
|
{
|
|
if (mParseSilently) return;
|
|
LL_ERRS() << message << LL_ENDL;
|
|
}
|
|
|
|
|
|
//
|
|
// BlockDescriptor
|
|
//
|
|
void BlockDescriptor::aggregateBlockData(BlockDescriptor& src_block_data)
|
|
{
|
|
mNamedParams.insert(src_block_data.mNamedParams.begin(), src_block_data.mNamedParams.end());
|
|
std::copy(src_block_data.mUnnamedParams.begin(), src_block_data.mUnnamedParams.end(), std::back_inserter(mUnnamedParams));
|
|
std::copy(src_block_data.mValidationList.begin(), src_block_data.mValidationList.end(), std::back_inserter(mValidationList));
|
|
std::copy(src_block_data.mAllParams.begin(), src_block_data.mAllParams.end(), std::back_inserter(mAllParams));
|
|
}
|
|
|
|
void BlockDescriptor::addParam(const ParamDescriptorPtr in_param, const char* char_name)
|
|
{
|
|
// create a copy of the param descriptor in mAllParams
|
|
// so other data structures can store a pointer to it
|
|
mAllParams.push_back(in_param);
|
|
ParamDescriptorPtr param(mAllParams.back());
|
|
|
|
std::string name(char_name);
|
|
if ((size_t)param->mParamHandle > mMaxParamOffset)
|
|
{
|
|
LL_ERRS() << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << LL_ENDL;
|
|
}
|
|
|
|
if (name.empty())
|
|
{
|
|
mUnnamedParams.push_back(param);
|
|
}
|
|
else
|
|
{
|
|
// don't use insert, since we want to overwrite existing entries
|
|
mNamedParams[name] = param;
|
|
}
|
|
|
|
if (param->mValidationFunc)
|
|
{
|
|
mValidationList.push_back(std::make_pair(param->mParamHandle, param->mValidationFunc));
|
|
}
|
|
}
|
|
|
|
BlockDescriptor::BlockDescriptor()
|
|
: mMaxParamOffset(0),
|
|
mInitializationState(UNINITIALIZED),
|
|
mCurrentBlockPtr(NULL)
|
|
{}
|
|
|
|
// called by each derived class in least to most derived order
|
|
void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
|
|
{
|
|
descriptor.mCurrentBlockPtr = this;
|
|
descriptor.mMaxParamOffset = block_size;
|
|
|
|
switch(descriptor.mInitializationState)
|
|
{
|
|
case BlockDescriptor::UNINITIALIZED:
|
|
// copy params from base class here
|
|
descriptor.aggregateBlockData(base_descriptor);
|
|
|
|
descriptor.mInitializationState = BlockDescriptor::INITIALIZING;
|
|
break;
|
|
case BlockDescriptor::INITIALIZING:
|
|
descriptor.mInitializationState = BlockDescriptor::INITIALIZED;
|
|
break;
|
|
case BlockDescriptor::INITIALIZED:
|
|
// nothing to do
|
|
break;
|
|
}
|
|
}
|
|
|
|
param_handle_t BaseBlock::getHandleFromParam(const Param* param) const
|
|
{
|
|
const U8* param_address = reinterpret_cast<const U8*>(param);
|
|
const U8* baseblock_address = reinterpret_cast<const U8*>(this);
|
|
return (param_address - baseblock_address);
|
|
}
|
|
|
|
bool BaseBlock::submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent)
|
|
{
|
|
Parser::name_stack_range_t range = std::make_pair(name_stack.begin(), name_stack.end());
|
|
if (!deserializeBlock(p, range, true))
|
|
{
|
|
if (!silent)
|
|
{
|
|
std::string file_name = p.getCurrentFileName();
|
|
if(!file_name.empty())
|
|
{
|
|
file_name = "in file: " + file_name;
|
|
}
|
|
p.parserWarning(llformat("Failed to parse parameter \"%s\" %s", p.getCurrentElementName().c_str(), file_name.c_str()));
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool BaseBlock::validateBlock(bool emit_errors) const
|
|
{
|
|
// only validate block when it hasn't already passed validation with current data
|
|
if (!mValidated)
|
|
{
|
|
const BlockDescriptor& block_data = mostDerivedBlockDescriptor();
|
|
for (BlockDescriptor::param_validation_list_t::const_iterator it = block_data.mValidationList.begin(); it != block_data.mValidationList.end(); ++it)
|
|
{
|
|
const Param* param = getParamFromHandle(it->first);
|
|
if (!it->second(param))
|
|
{
|
|
if (emit_errors)
|
|
{
|
|
LL_WARNS() << "Invalid param \"" << getParamName(block_data, param) << "\"" << LL_ENDL;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
mValidated = true;
|
|
}
|
|
return mValidated;
|
|
}
|
|
|
|
bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const LLInitParam::BaseBlock* diff_block) const
|
|
{
|
|
bool serialized = false;
|
|
if (!predicate_rule.check(ll_make_predicate(PROVIDED, isProvided())))
|
|
{
|
|
return false;
|
|
}
|
|
// named param is one like LLView::Params::follows
|
|
// unnamed param is like LLView::Params::rect - implicit
|
|
const BlockDescriptor& block_data = mostDerivedBlockDescriptor();
|
|
|
|
for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin();
|
|
it != block_data.mUnnamedParams.end();
|
|
++it)
|
|
{
|
|
param_handle_t param_handle = (*it)->mParamHandle;
|
|
const Param* param = getParamFromHandle(param_handle);
|
|
ParamDescriptor::serialize_func_t serialize_func = (*it)->mSerializeFunc;
|
|
if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided())))
|
|
{
|
|
const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
|
|
serialized |= serialize_func(*param, parser, name_stack, predicate_rule, diff_param);
|
|
}
|
|
}
|
|
|
|
for(BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin();
|
|
it != block_data.mNamedParams.end();
|
|
++it)
|
|
{
|
|
param_handle_t param_handle = it->second->mParamHandle;
|
|
const Param* param = getParamFromHandle(param_handle);
|
|
ParamDescriptor::serialize_func_t serialize_func = it->second->mSerializeFunc;
|
|
if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided())))
|
|
{
|
|
// Ensure this param has not already been serialized
|
|
// Prevents <rect> from being serialized as its own tag.
|
|
bool duplicate = false;
|
|
for (BlockDescriptor::param_list_t::const_iterator it2 = block_data.mUnnamedParams.begin();
|
|
it2 != block_data.mUnnamedParams.end();
|
|
++it2)
|
|
{
|
|
if (param_handle == (*it2)->mParamHandle)
|
|
{
|
|
duplicate = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//FIXME: for now, don't attempt to serialize values under synonyms, as current parsers
|
|
// don't know how to detect them
|
|
if (duplicate)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
name_stack.push_back(std::make_pair(it->first, !duplicate));
|
|
const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
|
|
serialized |= serialize_func(*param, parser, name_stack, predicate_rule, diff_param);
|
|
name_stack.pop_back();
|
|
}
|
|
}
|
|
|
|
if (!serialized && predicate_rule.check(ll_make_predicate(EMPTY)))
|
|
{
|
|
serialized |= parser.writeValue(Flag(), name_stack);
|
|
}
|
|
// was anything serialized in this block?
|
|
return serialized;
|
|
}
|
|
|
|
bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack, S32 min_count, S32 max_count) const
|
|
{
|
|
// named param is one like LLView::Params::follows
|
|
// unnamed param is like LLView::Params::rect - implicit
|
|
const BlockDescriptor& block_data = mostDerivedBlockDescriptor();
|
|
|
|
for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin();
|
|
it != block_data.mUnnamedParams.end();
|
|
++it)
|
|
{
|
|
param_handle_t param_handle = (*it)->mParamHandle;
|
|
const Param* param = getParamFromHandle(param_handle);
|
|
ParamDescriptor::inspect_func_t inspect_func = (*it)->mInspectFunc;
|
|
if (inspect_func)
|
|
{
|
|
name_stack.push_back(std::make_pair("", true));
|
|
inspect_func(*param, parser, name_stack, (*it)->mMinCount, (*it)->mMaxCount);
|
|
name_stack.pop_back();
|
|
}
|
|
}
|
|
|
|
for(BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin();
|
|
it != block_data.mNamedParams.end();
|
|
++it)
|
|
{
|
|
param_handle_t param_handle = it->second->mParamHandle;
|
|
const Param* param = getParamFromHandle(param_handle);
|
|
ParamDescriptor::inspect_func_t inspect_func = it->second->mInspectFunc;
|
|
if (inspect_func)
|
|
{
|
|
// Ensure this param has not already been inspected
|
|
bool duplicate = false;
|
|
for (BlockDescriptor::param_list_t::const_iterator it2 = block_data.mUnnamedParams.begin();
|
|
it2 != block_data.mUnnamedParams.end();
|
|
++it2)
|
|
{
|
|
if (param_handle == (*it2)->mParamHandle)
|
|
{
|
|
duplicate = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
name_stack.push_back(std::make_pair(it->first, !duplicate));
|
|
inspect_func(*param, parser, name_stack, it->second->mMinCount, it->second->mMaxCount);
|
|
name_stack.pop_back();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool ignored)
|
|
{
|
|
BlockDescriptor& block_data = mostDerivedBlockDescriptor();
|
|
bool names_left = name_stack_range.first != name_stack_range.second;
|
|
|
|
bool new_name = names_left
|
|
? name_stack_range.first->second
|
|
: true;
|
|
|
|
if (names_left)
|
|
{
|
|
const std::string& top_name = name_stack_range.first->first;
|
|
|
|
BlockDescriptor::param_map_t::iterator found_it = block_data.mNamedParams.find(top_name);
|
|
if (found_it != block_data.mNamedParams.end())
|
|
{
|
|
// find pointer to member parameter from offset table
|
|
Param* paramp = getParamFromHandle(found_it->second->mParamHandle);
|
|
ParamDescriptor::deserialize_func_t deserialize_func = found_it->second->mDeserializeFunc;
|
|
|
|
Parser::name_stack_range_t new_name_stack(name_stack_range.first, name_stack_range.second);
|
|
++new_name_stack.first;
|
|
if (deserialize_func(*paramp, p, new_name_stack, new_name))
|
|
{
|
|
// value is no longer new, we know about it now
|
|
name_stack_range.first->second = false;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// try to parse unnamed parameters, in declaration order
|
|
for ( BlockDescriptor::param_list_t::iterator it = block_data.mUnnamedParams.begin();
|
|
it != block_data.mUnnamedParams.end();
|
|
++it)
|
|
{
|
|
Param* paramp = getParamFromHandle((*it)->mParamHandle);
|
|
ParamDescriptor::deserialize_func_t deserialize_func = (*it)->mDeserializeFunc;
|
|
|
|
if (deserialize_func && deserialize_func(*paramp, p, name_stack_range, new_name))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// if no match, and no names left on stack, this is just an existence assertion of this block
|
|
// verify by calling readValue with NoParamValue type, an inherently unparseable type
|
|
if (!names_left)
|
|
{
|
|
Flag no_value;
|
|
return p.readValue(no_value);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void BaseBlock::addSynonym(Param& param, const std::string& synonym)
|
|
{
|
|
BlockDescriptor& block_data = mostDerivedBlockDescriptor();
|
|
if (block_data.mInitializationState == BlockDescriptor::INITIALIZING)
|
|
{
|
|
param_handle_t handle = getHandleFromParam(¶m);
|
|
|
|
// check for invalid derivation from a paramblock (i.e. without using
|
|
// Block<T, Base_Class>
|
|
if ((size_t)handle > block_data.mMaxParamOffset)
|
|
{
|
|
LL_ERRS() << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << LL_ENDL;
|
|
}
|
|
|
|
ParamDescriptorPtr param_descriptor = findParamDescriptor(param);
|
|
if (param_descriptor)
|
|
{
|
|
if (synonym.empty())
|
|
{
|
|
block_data.mUnnamedParams.push_back(param_descriptor);
|
|
}
|
|
else
|
|
{
|
|
block_data.mNamedParams[synonym] = param_descriptor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const std::string& BaseBlock::getParamName(const BlockDescriptor& block_data, const Param* paramp) const
|
|
{
|
|
param_handle_t handle = getHandleFromParam(paramp);
|
|
for (BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin(); it != block_data.mNamedParams.end(); ++it)
|
|
{
|
|
if (it->second->mParamHandle == handle)
|
|
{
|
|
return it->first;
|
|
}
|
|
}
|
|
|
|
return LLStringUtil::null;
|
|
}
|
|
|
|
ParamDescriptorPtr BaseBlock::findParamDescriptor(const Param& param)
|
|
{
|
|
param_handle_t handle = getHandleFromParam(¶m);
|
|
BlockDescriptor& descriptor = mostDerivedBlockDescriptor();
|
|
BlockDescriptor::all_params_list_t::iterator end_it = descriptor.mAllParams.end();
|
|
for (BlockDescriptor::all_params_list_t::iterator it = descriptor.mAllParams.begin();
|
|
it != end_it;
|
|
++it)
|
|
{
|
|
if ((*it)->mParamHandle == handle) return *it;
|
|
}
|
|
return ParamDescriptorPtr();
|
|
}
|
|
|
|
// take all provided params from other and apply to self
|
|
// NOTE: this requires that "other" is of the same derived type as this
|
|
bool BaseBlock::mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite)
|
|
{
|
|
bool some_param_changed = false;
|
|
BlockDescriptor::all_params_list_t::const_iterator end_it = block_data.mAllParams.end();
|
|
for (BlockDescriptor::all_params_list_t::const_iterator it = block_data.mAllParams.begin();
|
|
it != end_it;
|
|
++it)
|
|
{
|
|
const Param* other_paramp = other.getParamFromHandle((*it)->mParamHandle);
|
|
ParamDescriptor::merge_func_t merge_func = (*it)->mMergeFunc;
|
|
if (merge_func)
|
|
{
|
|
Param* paramp = getParamFromHandle((*it)->mParamHandle);
|
|
llassert(paramp->getEnclosingBlockOffset() == (*it)->mParamHandle);
|
|
some_param_changed |= merge_func(*paramp, *other_paramp, overwrite);
|
|
}
|
|
}
|
|
return some_param_changed;
|
|
}
|
|
}
|