/** * @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(this); const U8* block_addr = reinterpret_cast(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), mValidationFunc(validation_func), mInspectFunc(inspect_func), mMinCount(min_count), mMaxCount(max_count), mUserData(NULL) {} ParamDescriptor::ParamDescriptor() : mParamHandle(0), mMergeFunc(NULL), mDeserializeFunc(NULL), mSerializeFunc(NULL), mValidationFunc(NULL), mInspectFunc(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" << 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(param); const U8* baseblock_address = reinterpret_cast(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) { p.parserWarning(llformat("Failed to parse parameter \"%s\"", p.getCurrentElementName().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 (const BlockDescriptor::param_validation_list_t::value_type& pair : block_data.mValidationList) { const Param* param = getParamFromHandle(pair.first); if (!pair.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 (const ParamDescriptorPtr& ptr : block_data.mUnnamedParams) { param_handle_t param_handle = ptr->mParamHandle; const Param* param = getParamFromHandle(param_handle); ParamDescriptor::serialize_func_t serialize_func = ptr->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 (const BlockDescriptor::param_map_t::value_type& pair : block_data.mNamedParams) { param_handle_t param_handle = pair.second->mParamHandle; const Param* param = getParamFromHandle(param_handle); ParamDescriptor::serialize_func_t serialize_func = pair.second->mSerializeFunc; if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided()))) { // Ensure this param has not already been serialized // Prevents from being serialized as its own tag. bool duplicate = false; for (const ParamDescriptorPtr& ptr : block_data.mUnnamedParams) { if (param_handle == ptr->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(pair.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 (const ParamDescriptorPtr& ptr : block_data.mUnnamedParams) { param_handle_t param_handle = ptr->mParamHandle; const Param* param = getParamFromHandle(param_handle); ParamDescriptor::inspect_func_t inspect_func = ptr->mInspectFunc; if (inspect_func) { name_stack.push_back(std::make_pair("", true)); inspect_func(*param, parser, name_stack, ptr->mMinCount, ptr->mMaxCount); name_stack.pop_back(); } } for(const BlockDescriptor::param_map_t::value_type& pair : block_data.mNamedParams) { param_handle_t param_handle = pair.second->mParamHandle; const Param* param = getParamFromHandle(param_handle); ParamDescriptor::inspect_func_t inspect_func = pair.second->mInspectFunc; if (inspect_func) { // Ensure this param has not already been inspected bool duplicate = false; for (const ParamDescriptorPtr &ptr : block_data.mUnnamedParams) { if (param_handle == ptr->mParamHandle) { duplicate = true; break; } } name_stack.push_back(std::make_pair(pair.first, !duplicate)); inspect_func(*param, parser, name_stack, pair.second->mMinCount, pair.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 (ParamDescriptorPtr& ptr : block_data.mUnnamedParams) { Param* paramp = getParamFromHandle(ptr->mParamHandle); ParamDescriptor::deserialize_func_t deserialize_func = ptr->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 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" << 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(); for (ParamDescriptorPtr& ptr : descriptor.mAllParams) { if (ptr->mParamHandle == handle) return ptr; } 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; for (const ParamDescriptorPtr& ptr : block_data.mAllParams) { const Param* other_paramp = other.getParamFromHandle(ptr->mParamHandle); ParamDescriptor::merge_func_t merge_func = ptr->mMergeFunc; if (merge_func) { Param* paramp = getParamFromHandle(ptr->mParamHandle); llassert(paramp->getEnclosingBlockOffset() == ptr->mParamHandle); some_param_changed |= merge_func(*paramp, *other_paramp, overwrite); } } return some_param_changed; } }