summaryrefslogtreecommitdiff
path: root/indra/llxuixml
diff options
context:
space:
mode:
authorAdam Moss <moss@lindenlab.com>2009-08-21 21:52:21 +0000
committerAdam Moss <moss@lindenlab.com>2009-08-21 21:52:21 +0000
commitc3cbd049859c058526ae9a07a5cbfa7e51085943 (patch)
tree80f74a033a2f56f465cd4a67fa3e11a00bc18248 /indra/llxuixml
parent87be6648d50efc55785e2298a9ed6ec0546da564 (diff)
svn merge -r130238:130240 svn+ssh://svn.lindenlab.com/svn/linden/branches/linux-updater-6
QAR-1771 Linux Viewer Autoupdater + XUI-parse refactoring
Diffstat (limited to 'indra/llxuixml')
-rw-r--r--indra/llxuixml/CMakeLists.txt45
-rw-r--r--indra/llxuixml/llinitparam.cpp524
-rw-r--r--indra/llxuixml/llinitparam.h1822
-rw-r--r--indra/llxuixml/llregistry.h347
-rw-r--r--indra/llxuixml/lltrans.cpp171
-rw-r--r--indra/llxuixml/lltrans.h111
-rw-r--r--indra/llxuixml/lluicolor.cpp71
-rw-r--r--indra/llxuixml/lluicolor.h45
-rw-r--r--indra/llxuixml/llxuiparser.cpp968
-rw-r--r--indra/llxuixml/llxuiparser.h174
10 files changed, 4278 insertions, 0 deletions
diff --git a/indra/llxuixml/CMakeLists.txt b/indra/llxuixml/CMakeLists.txt
new file mode 100644
index 0000000000..daed4de6ce
--- /dev/null
+++ b/indra/llxuixml/CMakeLists.txt
@@ -0,0 +1,45 @@
+# -*- cmake -*-
+
+project(llxuixml)
+
+include(00-Common)
+include(LLCommon)
+include(LLMath)
+include(LLXML)
+
+include_directories(
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLXML_INCLUDE_DIRS}
+ )
+
+set(llxuixml_SOURCE_FILES
+ llinitparam.cpp
+ lltrans.cpp
+ lluicolor.cpp
+ llxuiparser.cpp
+ )
+
+set(llxuixml_HEADER_FILES
+ CMakeLists.txt
+
+ llinitparam.h
+ lltrans.h
+ llregistry.h
+ lluicolor.h
+ llxuiparser.h
+ )
+
+set_source_files_properties(${llxuixml_HEADER_FILES}
+ PROPERTIES HEADER_FILE_ONLY TRUE)
+
+list(APPEND llxuixml_SOURCE_FILES ${llxuixml_HEADER_FILES})
+
+add_library (llxuixml ${llxuixml_SOURCE_FILES})
+# Libraries on which this library depends, needed for Linux builds
+# Sort by high-level to low-level
+target_link_libraries(llxuixml
+ llxml
+ llcommon
+ llmath
+ )
diff --git a/indra/llxuixml/llinitparam.cpp b/indra/llxuixml/llinitparam.cpp
new file mode 100644
index 0000000000..d35e7b40f8
--- /dev/null
+++ b/indra/llxuixml/llinitparam.cpp
@@ -0,0 +1,524 @@
+/**
+ * @file llinitparam.cpp
+ * @brief parameter block abstraction for creating complex objects and
+ * parsing construction parameters from xml and LLSD
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ *
+ * Copyright (c) 2008-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llinitparam.h"
+
+
+namespace LLInitParam
+{
+ BlockDescriptor BaseBlock::sBlockDescriptor;
+
+ //
+ // 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);
+ mEnclosingBlockOffset = (S16)(block_addr - my_addr);
+ }
+
+ //
+ // Parser
+ //
+ Parser::~Parser()
+ {}
+
+ void Parser::parserWarning(const std::string& message)
+ {
+ if (mParseSilently) return;
+ llwarns << message << llendl;
+ }
+
+ void Parser::parserError(const std::string& message)
+ {
+ if (mParseSilently) return;
+ llerrs << message << llendl;
+ }
+
+
+ //
+ // BlockDescriptor
+ //
+ void BlockDescriptor::aggregateBlockData(BlockDescriptor& src_block_data)
+ {
+ mNamedParams.insert(src_block_data.mNamedParams.begin(), src_block_data.mNamedParams.end());
+ mSynonyms.insert(src_block_data.mSynonyms.begin(), src_block_data.mSynonyms.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));
+ }
+
+ //
+ // BaseBlock
+ //
+ BaseBlock::BaseBlock()
+ : mLastChangedParam(0),
+ mChangeVersion(0)
+ {}
+
+ BaseBlock::~BaseBlock()
+ {}
+
+ // called by each derived class in least to most derived order
+ void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
+ {
+ mBlockDescriptor = &descriptor;
+
+ 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(const Parser::name_stack_t& name_stack, Parser& p, bool silent)
+ {
+ if (!deserializeBlock(p, boost::make_iterator_range(name_stack.begin(), name_stack.end())))
+ {
+ if (!silent)
+ {
+ p.parserWarning(llformat("Failed to parse parameter \"%s\"", p.getCurrentElementName().c_str()));
+ }
+ return false;
+ }
+ return true;
+ }
+
+
+ bool BaseBlock::validateBlock(bool silent) const
+ {
+ const BlockDescriptor& block_data = getBlockDescriptor();
+ 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 (!silent)
+ {
+ llwarns << "Invalid param \"" << getParamName(block_data, param) << "\"" << llendl;
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t name_stack, const LLInitParam::BaseBlock* diff_block) const
+ {
+ // named param is one like LLView::Params::follows
+ // unnamed param is like LLView::Params::rect - implicit
+ const BlockDescriptor& block_data = getBlockDescriptor();
+
+ 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)
+ {
+ const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
+ // each param descriptor remembers its serial number
+ // so we can inspect the same param under different names
+ // and see that it has the same number
+ (*it)->mGeneration = parser.newParseGeneration();
+ name_stack.push_back(std::make_pair("", (*it)->mGeneration));
+ serialize_func(*param, parser, name_stack, diff_param);
+ 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::serialize_func_t serialize_func = it->second->mSerializeFunc;
+ if (serialize_func)
+ {
+ // 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;
+ }
+
+ if (!duplicate)
+ {
+ it->second->mGeneration = parser.newParseGeneration();
+ }
+
+ name_stack.push_back(std::make_pair(it->first, it->second->mGeneration));
+ const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
+ serialize_func(*param, parser, name_stack, diff_param);
+ name_stack.pop_back();
+ }
+ }
+
+ return true;
+ }
+
+ bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack) const
+ {
+ // named param is one like LLView::Params::follows
+ // unnamed param is like LLView::Params::rect - implicit
+ const BlockDescriptor& block_data = getBlockDescriptor();
+
+ 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)
+ {
+ (*it)->mGeneration = parser.newParseGeneration();
+ name_stack.push_back(std::make_pair("", (*it)->mGeneration));
+ 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;
+ }
+ }
+
+ if (!duplicate)
+ {
+ it->second->mGeneration = parser.newParseGeneration();
+ }
+ name_stack.push_back(std::make_pair(it->first, it->second->mGeneration));
+ inspect_func(*param, parser, name_stack, it->second->mMinCount, it->second->mMaxCount);
+ name_stack.pop_back();
+ }
+ }
+
+ for(BlockDescriptor::param_map_t::const_iterator it = block_data.mSynonyms.begin();
+ it != block_data.mSynonyms.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)
+ {
+ // use existing serial number for param
+ name_stack.push_back(std::make_pair(it->first, it->second->mGeneration));
+ 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)
+ {
+ BlockDescriptor& block_data = getBlockDescriptor();
+ bool names_left = !name_stack.empty();
+
+ if (names_left)
+ {
+ const std::string& top_name = name_stack.front().first;
+
+ ParamDescriptor::deserialize_func_t deserialize_func = NULL;
+ Param* paramp = NULL;
+
+ 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
+ paramp = getParamFromHandle(found_it->second->mParamHandle);
+ deserialize_func = found_it->second->mDeserializeFunc;
+ }
+ else
+ {
+ BlockDescriptor::param_map_t::iterator found_it = block_data.mSynonyms.find(top_name);
+ if (found_it != block_data.mSynonyms.end())
+ {
+ // find pointer to member parameter from offset table
+ paramp = getParamFromHandle(found_it->second->mParamHandle);
+ deserialize_func = found_it->second->mDeserializeFunc;
+ }
+ }
+
+ Parser::name_stack_range_t new_name_stack(++name_stack.begin(), name_stack.end());
+ if (deserialize_func)
+ {
+ return deserialize_func(*paramp, p, new_name_stack, name_stack.empty() ? -1 : name_stack.front().second);
+ }
+ }
+
+ // 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, name_stack.empty() ? -1 : name_stack.front().second))
+ {
+ mLastChangedParam = (*it)->mParamHandle;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ //static
+ void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptor& in_param, const char* char_name)
+ {
+ // create a copy of the paramdescriptor in allparams
+ // so other data structures can store a pointer to it
+ block_data.mAllParams.push_back(in_param);
+ ParamDescriptor& param(block_data.mAllParams.back());
+
+ std::string name(char_name);
+ if ((size_t)param.mParamHandle > block_data.mMaxParamOffset)
+ {
+ llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << llendl;
+ }
+
+ if (name.empty())
+ {
+ block_data.mUnnamedParams.push_back(&param);
+ }
+ else
+ {
+ // don't use insert, since we want to overwrite existing entries
+ block_data.mNamedParams[name] = &param;
+ }
+
+ if (param.mValidationFunc)
+ {
+ block_data.mValidationList.push_back(std::make_pair(param.mParamHandle, param.mValidationFunc));
+ }
+ }
+
+ void BaseBlock::addSynonym(Param& param, const std::string& synonym)
+ {
+ BlockDescriptor& block_data = getBlockDescriptor();
+ if (block_data.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ param_handle_t handle = getHandleFromParam(&param);
+
+ // check for invalid derivation from a paramblock (i.e. without using
+ // Block<T, Base_Class>
+ if ((size_t)handle > block_data.mMaxParamOffset)
+ {
+ llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << llendl;
+ }
+
+ ParamDescriptor* param_descriptor = findParamDescriptor(handle);
+ if (param_descriptor)
+ {
+ if (synonym.empty())
+ {
+ block_data.mUnnamedParams.push_back(param_descriptor);
+ }
+ else
+ {
+ block_data.mSynonyms[synonym] = param_descriptor;
+ }
+ }
+ }
+ }
+
+ void BaseBlock::setLastChangedParam(const Param& last_param, bool user_provided)
+ {
+ mLastChangedParam = getHandleFromParam(&last_param);
+ mChangeVersion++;
+ }
+
+ 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;
+ }
+ }
+
+ for (BlockDescriptor::param_map_t::const_iterator it = block_data.mSynonyms.begin(); it != block_data.mSynonyms.end(); ++it)
+ {
+ if (it->second->mParamHandle == handle)
+ {
+ return it->first;
+ }
+ }
+
+ return LLStringUtil::null;
+ }
+
+ ParamDescriptor* BaseBlock::findParamDescriptor(param_handle_t handle)
+ {
+ BlockDescriptor& descriptor = getBlockDescriptor();
+ 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 NULL;
+ }
+
+ // 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::overwriteFromImpl(BlockDescriptor& block_data, const BaseBlock& other)
+ {
+ bool 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);
+ param_changed |= merge_func(*paramp, *other_paramp, true);
+ mLastChangedParam = it->mParamHandle;
+ }
+ }
+ return param_changed;
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool BaseBlock::fillFromImpl(BlockDescriptor& block_data, const BaseBlock& other)
+ {
+ bool 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);
+ param_changed |= merge_func(*paramp, *other_paramp, false);
+ mLastChangedParam = it->mParamHandle;
+ }
+ }
+ return param_changed;
+ }
+
+
+ template<>
+ bool ParamCompare<boost::function<void (const std::string &,void *)> >::equals(
+ const boost::function<void (const std::string &,void *)> &a,
+ const boost::function<void (const std::string &,void *)> &b)
+ {
+ return false;
+ }
+
+ template<>
+ bool ParamCompare<boost::function<void (const LLSD &,const LLSD &)> >::equals(
+ const boost::function<void (const LLSD &,const LLSD &)> &a,
+ const boost::function<void (const LLSD &,const LLSD &)> &b)
+ {
+ return false;
+ }
+
+ template<>
+ bool ParamCompare<LLSD>::equals(const LLSD &a, const LLSD &b)
+ {
+ return false;
+ }
+}
diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h
new file mode 100644
index 0000000000..cb56049ae2
--- /dev/null
+++ b/indra/llxuixml/llinitparam.h
@@ -0,0 +1,1822 @@
+/**
+ * @file llinitparam.h
+ * @brief parameter block abstraction for creating complex objects and
+ * parsing construction parameters from xml and LLSD
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ *
+ * Copyright (c) 2008-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPARAM_H
+#define LL_LLPARAM_H
+
+#include <vector>
+
+#include <stddef.h>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/range/iterator_range.hpp>
+#include "llregistry.h"
+#include "llmemory.h"
+
+
+namespace LLInitParam
+{
+ template <typename T>
+ class ParamCompare {
+ public:
+ static bool equals(const T &a, const T &b);
+ };
+
+ template<class T>
+ bool ParamCompare<T>::equals(const T &a, const T&b)
+ {
+ return a == b;
+ }
+
+
+ // default constructor adaptor for InitParam Values
+ // constructs default instances of the given type, returned by const reference
+ template <typename T>
+ struct DefaultInitializer
+ {
+ typedef const T& T_const_ref;
+ // return reference to a single default instance of T
+ // built-in types will be initialized to zero, default constructor otherwise
+ static T_const_ref get() { static T t = T(); return t; }
+ };
+
+ // helper functions and classes
+ typedef ptrdiff_t param_handle_t;
+
+ template <typename T>
+ class TypeValues
+ {
+ public:
+ // empty default implemenation of key cache
+ class KeyCache
+ {
+ public:
+ void setKey(const std::string& key) {}
+ std::string getKey() const { return ""; }
+ void clearKey(){}
+ };
+
+ static bool get(const std::string& name, T& value)
+ {
+ return false;
+ }
+
+ static bool empty()
+ {
+ return true;
+ }
+
+ static std::vector<std::string>* getPossibleValues() { return NULL; }
+ };
+
+ template <typename T, typename DERIVED_TYPE = TypeValues<T> >
+ class TypeValuesHelper
+ : public LLRegistrySingleton<std::string, T, DERIVED_TYPE >
+ {
+ typedef LLRegistrySingleton<std::string, T, DERIVED_TYPE> super_t;
+ typedef LLSingleton<DERIVED_TYPE> singleton_t;
+ public:
+
+ //TODO: cache key by index to save on param block size
+ class KeyCache
+ {
+ public:
+ void setKey(const std::string& key)
+ {
+ mKey = key;
+ }
+
+ void clearKey()
+ {
+ mKey = "";
+ }
+
+ std::string getKey() const
+ {
+ return mKey;
+ }
+
+ private:
+ std::string mKey;
+ };
+
+ static bool get(const std::string& name, T& value)
+ {
+ if (!singleton_t::instance().exists(name)) return false;
+
+ value = *singleton_t::instance().getValue(name);
+ return true;
+ }
+
+ static bool empty()
+ {
+ return singleton_t::instance().LLRegistry<std::string, T>::empty();
+ }
+
+ //override this to add name value pairs
+ static void declareValues() {}
+
+ void initSingleton()
+ {
+ DERIVED_TYPE::declareValues();
+ }
+
+ static const std::vector<std::string>* getPossibleValues()
+ {
+ // in order to return a pointer to a member, we lazily
+ // evaluate the result and store it in mValues here
+ if (singleton_t::instance().mValues.empty())
+ {
+ typename super_t::Registrar::registry_map_t::const_iterator it;
+ for (it = super_t::defaultRegistrar().beginItems(); it != super_t::defaultRegistrar().endItems(); ++it)
+ {
+ singleton_t::instance().mValues.push_back(it->first);
+ }
+ }
+ return &singleton_t::instance().mValues;
+ }
+
+
+ protected:
+ static void declare(const std::string& name, const T& value)
+ {
+ super_t::defaultRegistrar().add(name, value);
+ }
+
+ private:
+ std::vector<std::string> mValues;
+ };
+
+ class Parser
+ {
+ LOG_CLASS(Parser);
+
+ public:
+
+ struct CompareTypeID
+ {
+ bool operator()(const std::type_info* lhs, const std::type_info* rhs) const
+ {
+ return lhs->before(*rhs);
+ }
+ };
+
+ typedef std::vector<std::pair<std::string, S32> > name_stack_t;
+ typedef boost::iterator_range<name_stack_t::const_iterator> name_stack_range_t;
+ typedef std::vector<std::string> possible_values_t;
+
+ typedef boost::function<bool (void*)> parser_read_func_t;
+ typedef boost::function<bool (const void*, const name_stack_t&)> parser_write_func_t;
+ typedef boost::function<void (const name_stack_t&, S32, S32, const possible_values_t*)> parser_inspect_func_t;
+
+ typedef std::map<const std::type_info*, parser_read_func_t, CompareTypeID> parser_read_func_map_t;
+ typedef std::map<const std::type_info*, parser_write_func_t, CompareTypeID> parser_write_func_map_t;
+ typedef std::map<const std::type_info*, parser_inspect_func_t, CompareTypeID> parser_inspect_func_map_t;
+
+ Parser()
+ : mParseSilently(false),
+ mParseGeneration(0)
+ {}
+ virtual ~Parser();
+
+ template <typename T> bool readValue(T& param)
+ {
+ parser_read_func_map_t::iterator found_it = mParserReadFuncs.find(&typeid(T));
+ if (found_it != mParserReadFuncs.end())
+ {
+ return found_it->second((void*)&param);
+ }
+ return false;
+ }
+
+ template <typename T> bool writeValue(const T& param, const name_stack_t& name_stack)
+ {
+ parser_write_func_map_t::iterator found_it = mParserWriteFuncs.find(&typeid(T));
+ if (found_it != mParserWriteFuncs.end())
+ {
+ return found_it->second((const void*)&param, name_stack);
+ }
+ return false;
+ }
+
+ // dispatch inspection to registered inspection functions, for each parameter in a param block
+ template <typename T> bool inspectValue(const name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values)
+ {
+ parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs.find(&typeid(T));
+ if (found_it != mParserInspectFuncs.end())
+ {
+ found_it->second(name_stack, min_count, max_count, possible_values);
+ return true;
+ }
+ return false;
+ }
+
+ virtual std::string getCurrentElementName() = 0;
+ virtual void parserWarning(const std::string& message);
+ virtual void parserError(const std::string& message);
+ void setParseSilently(bool silent) { mParseSilently = silent; }
+ bool getParseSilently() { return mParseSilently; }
+
+ S32 getParseGeneration() { return mParseGeneration; }
+ S32 newParseGeneration() { return ++mParseGeneration; }
+
+
+ protected:
+ template <typename T>
+ void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func)
+ {
+ mParserReadFuncs.insert(std::make_pair(&typeid(T), read_func));
+ mParserWriteFuncs.insert(std::make_pair(&typeid(T), write_func));
+ }
+
+ template <typename T>
+ void registerInspectFunc(parser_inspect_func_t inspect_func)
+ {
+ mParserInspectFuncs.insert(std::make_pair(&typeid(T), inspect_func));
+ }
+
+ bool mParseSilently;
+
+ private:
+ parser_read_func_map_t mParserReadFuncs;
+ parser_write_func_map_t mParserWriteFuncs;
+ parser_inspect_func_map_t mParserInspectFuncs;
+ S32 mParseGeneration;
+ };
+
+ class BaseBlock;
+
+ class Param
+ {
+ public:
+ // public to allow choice blocks to clear provided flag on stale choices
+ void setProvided(bool is_provided) { mIsProvided = is_provided; }
+
+ protected:
+ bool getProvided() const { return mIsProvided; }
+
+ Param(class BaseBlock* enclosing_block);
+
+ // store pointer to enclosing block as offset to reduce space and allow for quick copying
+ BaseBlock& enclosingBlock() const
+ {
+ const U8* my_addr = reinterpret_cast<const U8*>(this);
+ // get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class
+ return *const_cast<BaseBlock*>(
+ reinterpret_cast<const BaseBlock*>(my_addr + (ptrdiff_t)mEnclosingBlockOffset));
+ }
+
+ private:
+ friend class BaseBlock;
+
+ bool mIsProvided;
+ S16 mEnclosingBlockOffset;
+ };
+
+ // various callbacks and constraints associated with an individual param
+ struct ParamDescriptor
+ {
+ public:
+ typedef bool(*merge_func_t)(Param&, const Param&, bool);
+ typedef bool(*deserialize_func_t)(Param&, Parser&, const Parser::name_stack_range_t&, S32);
+ typedef void(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const Param* diff_param);
+ typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count);
+ typedef bool(*validation_func_t)(const Param*);
+
+ ParamDescriptor(param_handle_t p,
+ merge_func_t merge_func,
+ deserialize_func_t deserialize_func,
+ serialize_func_t serialize_func,
+ validation_func_t validation_func,
+ inspect_func_t inspect_func,
+ S32 min_count,
+ S32 max_count)
+ : mParamHandle(p),
+ mMergeFunc(merge_func),
+ mDeserializeFunc(deserialize_func),
+ mSerializeFunc(serialize_func),
+ mValidationFunc(validation_func),
+ mInspectFunc(inspect_func),
+ mMinCount(min_count),
+ mMaxCount(max_count),
+ mNumRefs(0)
+ {}
+
+ ParamDescriptor()
+ : mParamHandle(0),
+ mMergeFunc(NULL),
+ mDeserializeFunc(NULL),
+ mSerializeFunc(NULL),
+ mValidationFunc(NULL),
+ mInspectFunc(NULL),
+ mMinCount(0),
+ mMaxCount(0),
+ mGeneration(0),
+ mNumRefs(0)
+ {}
+
+ param_handle_t mParamHandle;
+
+ merge_func_t mMergeFunc;
+ deserialize_func_t mDeserializeFunc;
+ serialize_func_t mSerializeFunc;
+ inspect_func_t mInspectFunc;
+ validation_func_t mValidationFunc;
+ S32 mMinCount;
+ S32 mMaxCount;
+ S32 mGeneration;
+ S32 mNumRefs;
+ };
+
+ // each derived Block class keeps a static data structure maintaining offsets to various params
+ class BlockDescriptor
+ {
+ public:
+ BlockDescriptor()
+ : mMaxParamOffset(0),
+ mInitializationState(UNINITIALIZED)
+ {}
+
+ typedef enum e_initialization_state
+ {
+ UNINITIALIZED,
+ INITIALIZING,
+ INITIALIZED
+ } EInitializationState;
+
+ void aggregateBlockData(BlockDescriptor& src_block_data);
+
+ public:
+ typedef std::map<const std::string, ParamDescriptor*> param_map_t; // references param descriptors stored in mAllParams
+ typedef std::vector<ParamDescriptor*> param_list_t;
+
+ typedef std::list<ParamDescriptor> all_params_list_t;// references param descriptors stored in mAllParams
+ typedef std::vector<std::pair<param_handle_t, ParamDescriptor::validation_func_t> > param_validation_list_t;
+
+ param_map_t mNamedParams; // parameters with associated names
+ param_map_t mSynonyms; // parameters with alternate names
+ param_list_t mUnnamedParams; // parameters with_out_ associated names
+ param_validation_list_t mValidationList; // parameters that must be validated
+ all_params_list_t mAllParams; // all parameters, owns descriptors
+
+ size_t mMaxParamOffset;
+
+ EInitializationState mInitializationState; // whether or not static block data has been initialized
+ class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed
+ };
+
+ class BaseBlock
+ {
+ public:
+ // this typedef identifies derived classes as being blocks
+ typedef void baseblock_base_class_t;
+ LOG_CLASS(BaseBlock);
+ friend class Param;
+
+ BaseBlock();
+ virtual ~BaseBlock();
+ bool submitValue(const Parser::name_stack_t& name_stack, Parser& p, bool silent=false);
+
+ param_handle_t getHandleFromParam(const Param* param) const;
+ bool validateBlock(bool silent = false) const;
+
+ Param* getParamFromHandle(const param_handle_t param_handle)
+ {
+ if (param_handle == 0) return NULL;
+ U8* baseblock_address = reinterpret_cast<U8*>(this);
+ return reinterpret_cast<Param*>(baseblock_address + param_handle);
+ }
+
+ const Param* getParamFromHandle(const param_handle_t param_handle) const
+ {
+ const U8* baseblock_address = reinterpret_cast<const U8*>(this);
+ return reinterpret_cast<const Param*>(baseblock_address + param_handle);
+ }
+
+ void addSynonym(Param& param, const std::string& synonym);
+
+ // Blocks can override this to do custom tracking of changes
+ virtual void setLastChangedParam(const Param& last_param, bool user_provided);
+
+ const Param* getLastChangedParam() const { return mLastChangedParam ? getParamFromHandle(mLastChangedParam) : NULL; }
+ S32 getLastChangeVersion() const { return mChangeVersion; }
+
+ bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack);
+ bool serializeBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), const BaseBlock* diff_block = NULL) const;
+ virtual bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t()) const;
+
+ const BlockDescriptor& getBlockDescriptor() const { return *mBlockDescriptor; }
+ BlockDescriptor& getBlockDescriptor() { return *mBlockDescriptor; }
+
+ // take all provided params from other and apply to self
+ bool overwriteFrom(const BaseBlock& other)
+ {
+ return false;
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFrom(const BaseBlock& other)
+ {
+ return false;
+ }
+
+ static void addParam(BlockDescriptor& block_data, const ParamDescriptor& param, const char* name);
+ protected:
+ void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size);
+
+
+ // take all provided params from other and apply to self
+ bool overwriteFromImpl(BlockDescriptor& block_data, const BaseBlock& other);
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFromImpl(BlockDescriptor& block_data, const BaseBlock& other);
+
+ // can be updated in getters
+ mutable param_handle_t mLastChangedParam;
+ mutable S32 mChangeVersion;
+
+ BlockDescriptor* mBlockDescriptor; // most derived block descriptor
+
+ static BlockDescriptor sBlockDescriptor;
+
+ private:
+ const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const;
+ ParamDescriptor* findParamDescriptor(param_handle_t handle);
+ };
+
+
+ template<typename T>
+ struct ParamIterator
+ {
+ typedef typename std::vector<T>::const_iterator const_iterator;
+ typedef typename std::vector<T>::iterator iterator;
+ };
+
+ // these templates allow us to distinguish between template parameters
+ // that derive from BaseBlock and those that don't
+ // this is supposedly faster than boost::is_convertible and the ilk
+ template<typename T, typename Void = void>
+ struct is_BaseBlock
+ : boost::false_type
+ {};
+
+ template<typename T>
+ struct is_BaseBlock<T, typename T::baseblock_base_class_t>
+ : boost::true_type
+ {};
+
+ // specialize for custom parsing/decomposition of specific classes
+ // e.g. TypedParam<LLRect> has left, top, right, bottom, etc...
+ template<typename T,
+ typename NAME_VALUE_LOOKUP = TypeValues<T>,
+ bool HAS_MULTIPLE_VALUES = false,
+ typename VALUE_IS_BLOCK = typename is_BaseBlock<T>::type>
+ class TypedParam
+ : public Param
+ {
+ public:
+ typedef const T& value_const_ref_t;
+ typedef value_const_ref_t value_assignment_t;
+ typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK> self_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr)
+ {
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+
+ mData.mValue = value;
+ }
+
+ bool isProvided() const { return Param::getProvided(); }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ // no further names in stack, attempt to parse value now
+ if (name_stack.empty())
+ {
+ if (parser.readValue<T>(typed_param.mData.mValue))
+ {
+ typed_param.setProvided(true);
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ return true;
+ }
+
+ // try to parse a known named value
+ if(!NAME_VALUE_LOOKUP::empty())
+ {
+ // try to parse a known named value
+ std::string name;
+ if (parser.readValue<std::string>(name))
+ {
+ // try to parse a per type named value
+ if (NAME_VALUE_LOOKUP::get(name, typed_param.mData.mValue))
+ {
+ typed_param.mData.setKey(name);
+ typed_param.setProvided(true);
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ return true;
+ }
+
+ }
+ }
+ }
+ return false;
+ }
+
+ static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ if (!typed_param.isProvided()) return;
+
+ if (!name_stack.empty())
+ {
+ name_stack.back().second = parser.newParseGeneration();
+ }
+
+ std::string key = typed_param.mData.getKey();
+
+ // first try to write out name of name/value pair
+
+ if (!key.empty())
+ {
+ if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->mData.getKey(), key))
+ {
+ if (!parser.writeValue<std::string>(key, name_stack))
+ {
+ return;
+ }
+ }
+ }
+ // then try to serialize value directly
+ else if (!diff_param || !ParamCompare<T>::equals(typed_param.get(), static_cast<const self_t*>(diff_param)->get())) {
+ if (!parser.writeValue<T>(typed_param.mData.mValue, name_stack))
+ {
+ return;
+ }
+ }
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ // tell parser about our actual type
+ parser.inspectValue<T>(name_stack, min_count, max_count, NULL);
+ // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4)
+ if (NAME_VALUE_LOOKUP::getPossibleValues())
+ {
+ parser.inspectValue<std::string>(name_stack, min_count, max_count, NAME_VALUE_LOOKUP::getPossibleValues());
+ }
+ }
+
+ void set(value_assignment_t val, bool flag_as_provided = true)
+ {
+ mData.mValue = val;
+ mData.clearKey();
+ setProvided(flag_as_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+ }
+
+ void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+ {
+ if (!isProvided())
+ {
+ set(val, flag_as_provided);
+ }
+ }
+
+ // implicit conversion
+ operator value_assignment_t() const { return get(); }
+ // explicit conversion
+ value_assignment_t operator()() const { return get(); }
+
+ protected:
+ value_assignment_t get() const
+ {
+ return mData.mValue;
+ }
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+ if (src_typed_param.isProvided()
+ && (overwrite || !dst_typed_param.isProvided()))
+ {
+ dst_typed_param.mData.clearKey();
+ dst_typed_param = src_typed_param;
+ return true;
+ }
+ return false;
+ }
+
+ struct Data : public key_cache_t
+ {
+ T mValue;
+ };
+
+ Data mData;
+ };
+
+ // parameter that is a block
+ template <typename T, typename NAME_VALUE_LOOKUP>
+ class TypedParam<T, NAME_VALUE_LOOKUP, false, boost::true_type>
+ : public T,
+ public Param
+ {
+ public:
+ typedef const T value_const_t;
+ typedef T value_t;
+ typedef value_const_t& value_const_ref_t;
+ typedef value_const_ref_t value_assignment_t;
+ typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false, boost::true_type> self_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ T(value)
+ {
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+ }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ // attempt to parse block...
+ if(typed_param.deserializeBlock(parser, name_stack))
+ {
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ return true;
+ }
+
+ if(!NAME_VALUE_LOOKUP::empty())
+ {
+ // try to parse a known named value
+ std::string name;
+ if (parser.readValue<std::string>(name))
+ {
+ // try to parse a per type named value
+ if (NAME_VALUE_LOOKUP::get(name, typed_param))
+ {
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.mData.setKey(name);
+ typed_param.mData.mKeyVersion = typed_param.getLastChangeVersion();
+ return true;
+ }
+
+ }
+ }
+ return false;
+ }
+
+ static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ if (!name_stack.empty())
+ {
+ name_stack.back().second = parser.newParseGeneration();
+ }
+
+ std::string key = typed_param.mData.getKey();
+ if (!key.empty() && typed_param.mData.mKeyVersion == typed_param.getLastChangeVersion())
+ {
+ if (!parser.writeValue<std::string>(key, name_stack))
+ {
+ return;
+ }
+ }
+ else
+ {
+ typed_param.serializeBlock(parser, name_stack, static_cast<const self_t*>(diff_param));
+ }
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ // I am a param that is also a block, so just recurse into my contents
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ typed_param.inspectBlock(parser, name_stack);
+ }
+
+ // a param-that-is-a-block is provided when the user has set one of its child params
+ // *and* the block as a whole validates
+ bool isProvided() const
+ {
+ // only validate block when it hasn't already passed validation and user has supplied *some* value
+ if (Param::getProvided() && mData.mValidatedVersion < T::getLastChangeVersion())
+ {
+ // a sub-block is "provided" when it has been filled in enough to be valid
+ mData.mValidated = T::validateBlock(true);
+ mData.mValidatedVersion = T::getLastChangeVersion();
+ }
+ return Param::getProvided() && mData.mValidated;
+ }
+
+ // assign block contents to this param-that-is-a-block
+ void set(value_assignment_t val, bool flag_as_provided = true)
+ {
+ value_t::operator=(val);
+ mData.clearKey();
+ // force revalidation of block by clearing known provided version
+ // next call to isProvided() will update provision status based on validity
+ mData.mValidatedVersion = 0;
+ setProvided(flag_as_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+ }
+
+ void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+ {
+ if (!isProvided())
+ {
+ set(val, flag_as_provided);
+ }
+ }
+
+ // propagate changed status up to enclosing block
+ /*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
+ {
+ T::setLastChangedParam(last_param, user_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, user_provided);
+ if (user_provided)
+ {
+ // a child param has been explicitly changed
+ // so *some* aspect of this block is now provided
+ setProvided(true);
+ }
+ }
+
+ // implicit conversion
+ operator value_assignment_t() const { return get(); }
+ // explicit conversion
+ value_assignment_t operator()() const { return get(); }
+
+ protected:
+ value_assignment_t get() const
+ {
+ return *this;
+ }
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+ if (overwrite)
+ {
+ if (dst_typed_param.T::overwriteFrom(src_typed_param))
+ {
+ dst_typed_param.mData.clearKey();
+ return true;
+ }
+ }
+ else
+ {
+ if (dst_typed_param.T::fillFrom(src_typed_param))
+ {
+ dst_typed_param.mData.clearKey();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ struct Data : public key_cache_t
+ {
+ S32 mKeyVersion;
+ mutable S32 mValidatedVersion;
+ mutable bool mValidated; // lazy validation flag
+
+ Data()
+ : mKeyVersion(0),
+ mValidatedVersion(0),
+ mValidated(false)
+ {}
+ };
+ Data mData;
+ };
+
+ // container of non-block parameters
+ template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
+ class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::false_type>
+ : public Param
+ {
+ public:
+ typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::false_type> self_t;
+ typedef typename std::vector<VALUE_TYPE> container_t;
+ typedef const container_t& value_assignment_t;
+
+ typedef VALUE_TYPE value_t;
+ typedef value_t& value_ref_t;
+ typedef const value_t& value_const_ref_t;
+
+ typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ mValues(value)
+ {
+ mCachedKeys.resize(mValues.size());
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+ }
+
+ bool isProvided() const { return Param::getProvided(); }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ value_t value;
+ // no further names in stack, attempt to parse value now
+ if (name_stack.empty())
+ {
+ // attempt to read value directly
+ if (parser.readValue<value_t>(value))
+ {
+ typed_param.mValues.push_back(value);
+ // save an empty name/value key as a placeholder
+ typed_param.mCachedKeys.push_back(key_cache_t());
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ return true;
+ }
+
+ // try to parse a known named value
+ if(!NAME_VALUE_LOOKUP::empty())
+ {
+ // try to parse a known named value
+ std::string name;
+ if (parser.readValue<std::string>(name))
+ {
+ // try to parse a per type named value
+ if (NAME_VALUE_LOOKUP::get(name, typed_param.mValues))
+ {
+ typed_param.mValues.push_back(value);
+ typed_param.mCachedKeys.push_back(key_cache_t());
+ typed_param.mCachedKeys.back().setKey(name);
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ return true;
+ }
+
+ }
+ }
+ }
+ return false;
+ }
+
+ static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ if (!typed_param.isProvided() || name_stack.empty()) return;
+
+ typename container_t::const_iterator it = typed_param.mValues.begin();
+ for (typename std::vector<key_cache_t>::const_iterator key_it = typed_param.mCachedKeys.begin();
+ it != typed_param.mValues.end();
+ ++key_it, ++it)
+ {
+ std::string key = key_it->get();
+ name_stack.back().second = parser.newParseGeneration();
+
+ if(!key.empty())
+ {
+ if(!parser.writeValue<std::string>(key, name_stack))
+ {
+ return;
+ }
+ }
+ // not parse via name values, write out value directly
+ else if (!parser.writeValue<VALUE_TYPE>(*it, name_stack))
+ {
+ return;
+ }
+ }
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ parser.inspectValue<VALUE_TYPE>(name_stack, min_count, max_count, NULL);
+ if (NAME_VALUE_LOOKUP::getPossibleValues())
+ {
+ parser.inspectValue<std::string>(name_stack, min_count, max_count, NAME_VALUE_LOOKUP::getPossibleValues());
+ }
+ }
+
+ void set(value_assignment_t val, bool flag_as_provided = true)
+ {
+ mValues = val;
+ mCachedKeys.clear();
+ mCachedKeys.resize(mValues.size());
+ setProvided(flag_as_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+ }
+
+
+ void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+ {
+ if (!isProvided())
+ {
+ set(val, flag_as_provided);
+ }
+ }
+
+ value_ref_t add()
+ {
+ mValues.push_back(value_t());
+ mCachedKeys.push_back(key_cache_t());
+ setProvided(true);
+ return mValues.back();
+ }
+
+ void add(value_const_ref_t item)
+ {
+ mValues.push_back(item);
+ mCachedKeys.push_back(key_cache_t());
+ setProvided(true);
+ }
+
+ // implicit conversion
+ operator value_assignment_t() const { return self_t::get(); }
+ // explicit conversion
+ value_assignment_t operator()() const { return get(); }
+
+ bool hasNValidElements(S32 n) const
+ {
+ return mValues.size() >= n;
+ }
+
+ protected:
+ value_assignment_t get() const
+ {
+ return mValues;
+ }
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+ if (src_typed_param.isProvided()
+ && (overwrite || !isProvided()))
+ {
+ dst_typed_param = src_typed_param;
+ return true;
+ }
+ return false;
+ }
+
+ container_t mValues;
+ std::vector<key_cache_t> mCachedKeys;
+ };
+
+ // container of block parameters
+ template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
+ class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::true_type>
+ : public Param
+ {
+ public:
+ typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::true_type> self_t;
+ typedef typename std::vector<VALUE_TYPE> container_t;
+ typedef const container_t& value_assignment_t;
+
+ typedef VALUE_TYPE value_t;
+ typedef value_t& value_ref_t;
+ typedef const value_t& value_const_ref_t;
+
+ typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ mValues(value),
+ mLastParamGeneration(0)
+ {
+ mCachedKeys.resize(mValues.size());
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+ }
+
+ bool isProvided() const { return Param::getProvided(); }
+
+ value_ref_t operator[](S32 index) { return mValues[index]; }
+ value_const_ref_t operator[](S32 index) const { return mValues[index]; }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ if (generation != typed_param.mLastParamGeneration || typed_param.mValues.empty())
+ {
+ typed_param.mValues.push_back(value_t());
+ typed_param.mCachedKeys.push_back(Data());
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.mLastParamGeneration = generation;
+ }
+
+ value_t& value = typed_param.mValues.back();
+
+ // attempt to parse block...
+ if(value.deserializeBlock(parser, name_stack))
+ {
+ typed_param.setProvided(true);
+ return true;
+ }
+
+ if(!NAME_VALUE_LOOKUP::empty())
+ {
+ // try to parse a known named value
+ std::string name;
+ if (parser.readValue<std::string>(name))
+ {
+ // try to parse a per type named value
+ if (NAME_VALUE_LOOKUP::get(name, value))
+ {
+ typed_param.mCachedKeys.back().setKey(name);
+ typed_param.mCachedKeys.back().mKeyVersion = value.getLastChangeVersion();
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ return true;
+ }
+
+ }
+ }
+
+ return false;
+ }
+
+ static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ if (!typed_param.isProvided() || name_stack.empty()) return;
+
+ typename container_t::const_iterator it = typed_param.mValues.begin();
+ for (typename std::vector<Data>::const_iterator key_it = typed_param.mCachedKeys.begin();
+ it != typed_param.mValues.end();
+ ++key_it, ++it)
+ {
+ name_stack.back().second = parser.newParseGeneration();
+
+ std::string key = key_it->getKey();
+ if (!key.empty() && key_it->mKeyVersion == it->getLastChangeVersion())
+ {
+ if(!parser.writeValue<std::string>(key, name_stack))
+ {
+ return;
+ }
+ }
+ // Not parsed via named values, write out value directly
+ // NOTE: currently we don't worry about removing default values in Multiple
+ else if (!it->serializeBlock(parser, name_stack, NULL))
+ {
+ return;
+ }
+ }
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ // I am a vector of blocks, so describe my contents recursively
+ value_t().inspectBlock(parser, name_stack);
+ }
+
+ void set(value_assignment_t val, bool flag_as_provided = true)
+ {
+ mValues = val;
+ mCachedKeys.clear();
+ mCachedKeys.resize(mValues.size());
+ setProvided(flag_as_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+ }
+
+ void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+ {
+ if (!isProvided())
+ {
+ set(val, flag_as_provided);
+ }
+ }
+
+ value_ref_t add()
+ {
+ mValues.push_back(value_t());
+ mCachedKeys.push_back(Data());
+ setProvided(true);
+ return mValues.back();
+ }
+
+ void add(value_const_ref_t item)
+ {
+ mValues.push_back(item);
+ mCachedKeys.push_back(Data());
+ setProvided(true);
+ }
+
+ // implicit conversion
+ operator value_assignment_t() const { return self_t::get(); }
+ // explicit conversion
+ value_assignment_t operator()() const { return get(); }
+
+ U32 numValidElements() const
+ {
+ U32 count = 0;
+ for (typename container_t::const_iterator it = mValues.begin();
+ it != mValues.end();
+ ++it)
+ {
+ if(it->validateBlock(true)) count++;
+ }
+ return count;
+ }
+
+ protected:
+ value_assignment_t get() const
+ {
+ return mValues;
+ }
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+ if (src_typed_param.isProvided()
+ && (overwrite || !dst_typed_param.isProvided()))
+ {
+ dst_typed_param = src_typed_param;
+ return true;
+ }
+ return false;
+ }
+
+ struct Data : public key_cache_t
+ {
+ S32 mKeyVersion; // version of block for which key was last valid
+
+ Data() : mKeyVersion(0) {}
+ };
+
+ container_t mValues;
+ std::vector<Data> mCachedKeys;
+
+ S32 mLastParamGeneration;
+ };
+
+ template <typename DERIVED_BLOCK>
+ class Choice : public BaseBlock
+ {
+ typedef Choice<DERIVED_BLOCK> self_t;
+ typedef Choice<DERIVED_BLOCK> enclosing_block_t;
+
+ LOG_CLASS(self_t);
+ public:
+ // take all provided params from other and apply to self
+ bool overwriteFrom(const self_t& other)
+ {
+ mCurChoice = other.mCurChoice;
+ return BaseBlock::overwriteFromImpl(sBlockDescriptor, other);
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFrom(const self_t& other)
+ {
+ return false;
+ }
+
+ // clear out old choice when param has changed
+ /*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
+ {
+ param_handle_t changed_param_handle = BaseBlock::getHandleFromParam(&last_param);
+ // if we have a new choice...
+ if (changed_param_handle != mCurChoice)
+ {
+ // clear provided flag on previous choice
+ Param* previous_choice = BaseBlock::getParamFromHandle(mCurChoice);
+ if (previous_choice)
+ {
+ previous_choice->setProvided(false);
+ }
+ mCurChoice = changed_param_handle;
+ }
+ BaseBlock::setLastChangedParam(last_param, user_provided);
+ }
+
+ protected:
+ Choice()
+ : mCurChoice(0)
+ {
+ BaseBlock::init(sBlockDescriptor, BaseBlock::sBlockDescriptor, sizeof(DERIVED_BLOCK));
+ }
+
+ // Alternatives are mutually exclusive wrt other Alternatives in the same block.
+ // One alternative in a block will always have isChosen() == true.
+ // At most one alternative in a block will have isProvided() == true.
+ template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+ class Alternative : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+ {
+ public:
+ friend class Choice<DERIVED_BLOCK>;
+
+ typedef Alternative<T, NAME_VALUE_LOOKUP> self_t;
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
+ typedef typename super_t::value_assignment_t value_assignment_t;
+
+ explicit Alternative(const char* name, value_assignment_t val = DefaultInitializer<T>::get())
+ : super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, NULL, 0, 1),
+ mOriginalValue(val)
+ {
+ // assign initial choice to first declared option
+ DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::sBlockDescriptor.mCurrentBlockPtr);
+ if (DERIVED_BLOCK::sBlockDescriptor.mInitializationState == BlockDescriptor::INITIALIZING
+ && blockp->mCurChoice == 0)
+ {
+ blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this);
+ }
+ }
+
+ Alternative& operator=(value_assignment_t val)
+ {
+ super_t::set(val);
+ return *this;
+ }
+
+ void operator()(typename super_t::value_assignment_t val)
+ {
+ super_t::set(val);
+ }
+
+ operator value_assignment_t() const
+ {
+ if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this)
+ {
+ return super_t::get();
+ }
+ return mOriginalValue;
+ }
+
+ value_assignment_t operator()() const
+ {
+ if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this)
+ {
+ return super_t::get();
+ }
+ return mOriginalValue;
+ }
+
+ bool isChosen() const
+ {
+ return static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this;
+ }
+
+ private:
+ T mOriginalValue;
+ };
+
+ protected:
+ static BlockDescriptor sBlockDescriptor;
+
+ private:
+ param_handle_t mCurChoice;
+
+ const Param* getCurrentChoice() const
+ {
+ return BaseBlock::getParamFromHandle(mCurChoice);
+ }
+ };
+
+ template<typename DERIVED_BLOCK>
+ BlockDescriptor
+ Choice<DERIVED_BLOCK>::sBlockDescriptor;
+
+ //struct CardinalityConstraint
+ //{
+ // virtual std::pair<S32, S32> getRange() = 0;
+ //};
+
+ struct AnyAmount
+ {
+ static U32 minCount() { return 0; }
+ static U32 maxCount() { return U32_MAX; }
+ };
+
+ template<U32 MIN_AMOUNT>
+ struct AtLeast
+ {
+ static U32 minCount() { return MIN_AMOUNT; }
+ static U32 maxCount() { return U32_MAX; }
+ };
+
+ template<U32 MAX_AMOUNT>
+ struct AtMost
+ {
+ static U32 minCount() { return 0; }
+ static U32 maxCount() { return MAX_AMOUNT; }
+ };
+
+ template<U32 MIN_AMOUNT, U32 MAX_AMOUNT>
+ struct Between
+ {
+ static U32 minCount() { return MIN_AMOUNT; }
+ static U32 maxCount() { return MAX_AMOUNT; }
+ };
+
+ template<U32 EXACT_COUNT>
+ struct Exactly
+ {
+ static U32 minCount() { return EXACT_COUNT; }
+ static U32 maxCount() { return EXACT_COUNT; }
+ };
+
+ template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock>
+ class Block
+ : public BASE_BLOCK
+ {
+ typedef Block<DERIVED_BLOCK, BASE_BLOCK> self_t;
+ typedef Block<DERIVED_BLOCK, BASE_BLOCK> block_t;
+
+ public:
+ typedef BASE_BLOCK base_block_t;
+
+ // take all provided params from other and apply to self
+ bool overwriteFrom(const self_t& other)
+ {
+ return BaseBlock::overwriteFromImpl(sBlockDescriptor, other);
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFrom(const self_t& other)
+ {
+ return BaseBlock::fillFromImpl(sBlockDescriptor, other);
+ }
+ protected:
+ Block()
+ {
+ //#pragma message("Parsing LLInitParam::Block")
+ BaseBlock::init(sBlockDescriptor, BASE_BLOCK::sBlockDescriptor, sizeof(DERIVED_BLOCK));
+ }
+
+ //
+ // Nested classes for declaring parameters
+ //
+ template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+ class Optional : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+ {
+ public:
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
+ typedef typename super_t::value_assignment_t value_assignment_t;
+
+ explicit Optional(const char* name = "", value_assignment_t val = DefaultInitializer<T>::get())
+ : super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, NULL, 0, 1)
+ {
+ //#pragma message("Parsing LLInitParam::Block::Optional")
+ }
+
+ Optional& operator=(value_assignment_t val)
+ {
+ set(val);
+ return *this;
+ }
+
+ DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
+ {
+ super_t::set(val);
+ return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+ }
+ using super_t::operator();
+ };
+
+ template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+ class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+ {
+ public:
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
+ typedef Mandatory<T, NAME_VALUE_LOOKUP> self_t;
+ typedef typename super_t::value_assignment_t value_assignment_t;
+
+ // mandatory parameters require a name to be parseable
+ explicit Mandatory(const char* name = "", value_assignment_t val = DefaultInitializer<T>::get())
+ : super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, &validate, 1, 1)
+ {}
+
+ Mandatory& operator=(value_assignment_t val)
+ {
+ set(val);
+ return *this;
+ }
+
+ DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
+ {
+ super_t::set(val);
+ return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+ }
+ using super_t::operator();
+
+ static bool validate(const Param* p)
+ {
+ // valid only if provided
+ return static_cast<const self_t*>(p)->isProvided();
+ }
+
+ };
+
+ template <typename T, typename RANGE = AnyAmount, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+ class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true>
+ {
+ public:
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, true> super_t;
+ typedef Multiple<T, RANGE, NAME_VALUE_LOOKUP> self_t;
+ typedef typename super_t::container_t container_t;
+ typedef typename super_t::value_assignment_t value_assignment_t;
+ typedef typename container_t::iterator iterator;
+ typedef typename container_t::const_iterator const_iterator;
+
+ explicit Multiple(const char* name = "", value_assignment_t val = DefaultInitializer<container_t>::get())
+ : super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, &validate, RANGE::minCount(), RANGE::maxCount())
+ {}
+
+ using super_t::operator();
+
+ Multiple& operator=(value_assignment_t val)
+ {
+ set(val);
+ return *this;
+ }
+
+ DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
+ {
+ super_t::set(val);
+ return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+ }
+
+ static bool validate(const Param* paramp)
+ {
+ U32 num_valid = ((super_t*)paramp)->numValidElements();
+ return RANGE::minCount() <= num_valid && num_valid <= RANGE::maxCount();
+ }
+ };
+
+ class Deprecated : public Param
+ {
+ public:
+ explicit Deprecated(const char* name)
+ : Param(DERIVED_BLOCK::sBlockDescriptor.mCurrentBlockPtr)
+ {
+ BlockDescriptor& block_descriptor = DERIVED_BLOCK::sBlockDescriptor;
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ NULL,
+ &deserializeParam,
+ NULL,
+ NULL,
+ NULL,
+ 0, S32_MAX);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+ }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ if (name_stack.empty())
+ {
+ //std::string message = llformat("Deprecated value %s ignored", getName().c_str());
+ //parser.parserWarning(message);
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ typedef Deprecated Ignored;
+
+ protected:
+ static BlockDescriptor sBlockDescriptor;
+ };
+
+ template<typename DERIVED_BLOCK, typename BASE_BLOCK>
+ BlockDescriptor
+ Block<DERIVED_BLOCK, BASE_BLOCK>::sBlockDescriptor;
+
+ template<typename T, typename DERIVED = TypedParam<T> >
+ class BlockValue
+ : public Block<TypedParam<T, TypeValues<T>, false> >,
+ public Param
+ {
+ public:
+ typedef BlockValue<T> self_t;
+ typedef Block<TypedParam<T, TypeValues<T>, false> > block_t;
+ typedef const T& value_const_ref_t;
+ typedef value_const_ref_t value_assignment_t;
+ typedef typename TypeValues<T>::KeyCache key_cache_t;
+
+ BlockValue(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ mData(value)
+ {
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+ }
+
+ // implicit conversion
+ operator value_assignment_t() const { return get(); }
+ // explicit conversion
+ value_assignment_t operator()() const { return get(); }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ // type to apply parse direct value T
+ if (name_stack.empty())
+ {
+ if(parser.readValue<T>(typed_param.mData.mValue))
+ {
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ typed_param.mData.mLastParamVersion = typed_param.BaseBlock::getLastChangeVersion();
+ return true;
+ }
+
+ if(!TypeValues<T>::empty())
+ {
+ // try to parse a known named value
+ std::string name;
+ if (parser.readValue<std::string>(name))
+ {
+ // try to parse a per type named value
+ if (TypeValues<T>::get(name, typed_param.mData.mValue))
+ {
+ typed_param.mData.setKey(name);
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ typed_param.mData.mLastParamVersion = typed_param.BaseBlock::getLastChangeVersion();
+ return true;
+ }
+ }
+ }
+ }
+
+ // fall back on parsing block components for T
+ // if we deserialized at least one component...
+ if (typed_param.BaseBlock::deserializeBlock(parser, name_stack))
+ {
+ // ...our block is provided, and considered changed
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ return true;
+ }
+ return false;
+ }
+
+ static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+
+ if (!typed_param.isProvided()) return;
+
+ std::string key = typed_param.mData.getKey();
+
+ // first try to write out name of name/value pair
+ if (!key.empty())
+ {
+ if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->mData.getKey(), key))
+ {
+ if (!parser.writeValue<std::string>(key, name_stack))
+ {
+ return;
+ }
+ }
+ }
+ // then try to serialize value directly
+ else if (!diff_param || !ParamCompare<T>::equals(typed_param.get(), (static_cast<const self_t*>(diff_param))->get()))
+ {
+
+ if (parser.writeValue<T>(typed_param.mData.mValue, name_stack))
+ {
+ return;
+ }
+
+ //RN: *always* serialize provided components of BlockValue (don't pass diff_param on),
+ // since these tend to be viewed as the constructor arguments for the value T. It seems
+ // cleaner to treat the uniqueness of a BlockValue according to the generated value, and
+ // not the individual components. This way <color red="0" green="1" blue="0"/> will not
+ // be exported as <color green="1"/>, since it was probably the intent of the user to
+ // be specific about the RGB color values. This also fixes an issue where we distinguish
+ // between rect.left not being provided and rect.left being explicitly set to 0 (same as default)
+ typed_param.BaseBlock::serializeBlock(parser, name_stack, NULL);
+ }
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ // first, inspect with actual type...
+ parser.inspectValue<T>(name_stack, min_count, max_count, NULL);
+ if (TypeValues<T>::getPossibleValues())
+ {
+ //...then inspect with possible string values...
+ parser.inspectValue<std::string>(name_stack, min_count, max_count, TypeValues<T>::getPossibleValues());
+ }
+ // then recursively inspect contents...
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ typed_param.inspectBlock(parser, name_stack);
+ }
+
+
+ bool isProvided() const
+ {
+ // either param value provided directly or block is sufficiently filled in
+ // if cached value is stale, regenerate from params
+ if (Param::getProvided() && mData.mLastParamVersion < BaseBlock::getLastChangeVersion())
+ {
+ if (block_t::validateBlock(true))
+ {
+ mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock();
+ // clear stale keyword associated with old value
+ mData.clearKey();
+ mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
+ return true;
+ }
+ else
+ {
+ //block value incomplete, so not considered provided
+ // will attempt to revalidate on next call to isProvided()
+ return false;
+ }
+ }
+ // either no data provided, or we have a valid value in hand
+ return Param::getProvided();
+ }
+
+ void set(value_assignment_t val, bool flag_as_provided = true)
+ {
+ Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+ // set param version number to be up to date, so we ignore block contents
+ mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
+
+ mData.mValue = val;
+ mData.clearKey();
+ setProvided(flag_as_provided);
+ }
+
+ void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+ {
+ // don't override any user provided value
+ if (!isProvided())
+ {
+ set(val, flag_as_provided);
+ }
+ }
+
+ // propagate change status up to enclosing block
+ /*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
+ {
+ BaseBlock::setLastChangedParam(last_param, user_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, user_provided);
+ if (user_provided)
+ {
+ setProvided(true); // some component provided
+ }
+ }
+
+ protected:
+ value_assignment_t get() const
+ {
+ if (mData.mLastParamVersion < BaseBlock::getLastChangeVersion() && block_t::validateBlock(true))
+ {
+ mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock();
+ mData.clearKey();
+ mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
+ }
+
+ return mData.mValue;
+ }
+
+ // mutable to allow lazy updates on get
+ struct Data : public key_cache_t
+ {
+ Data(const T& value)
+ : mValue(value),
+ mLastParamVersion(0)
+ {}
+
+ T mValue;
+ S32 mLastParamVersion;
+ };
+
+ mutable Data mData;
+
+ private:
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+ if (src_param.isProvided()
+ && (overwrite || !dst_typed_param.isProvided()))
+ {
+ // assign individual parameters
+ if (overwrite)
+ {
+ dst_typed_param.BaseBlock::overwriteFromImpl(block_t::sBlockDescriptor, src_param);
+ }
+ else
+ {
+ dst_typed_param.BaseBlock::fillFromImpl(block_t::sBlockDescriptor, src_param);
+ }
+ // then copy actual value
+ dst_typed_param.mData.mValue = src_param.get();
+ dst_typed_param.mData.clearKey();
+ dst_typed_param.setProvided(true);
+ return true;
+ }
+ return false;
+ }
+ };
+
+ template<>
+ bool ParamCompare<boost::function<void (const std::string &,void *)> >::equals(
+ const boost::function<void (const std::string &,void *)> &a,
+ const boost::function<void (const std::string &,void *)> &b);
+
+ template<>
+ bool ParamCompare<boost::function<void (const LLSD &,const LLSD &)> >::equals(
+ const boost::function<void (const LLSD &,const LLSD &)> &a,
+ const boost::function<void (const LLSD &,const LLSD &)> &b);
+
+ template<>
+ bool ParamCompare<LLSD>::equals(const LLSD &a, const LLSD &b);
+}
+
+#endif // LL_LLPARAM_H
diff --git a/indra/llxuixml/llregistry.h b/indra/llxuixml/llregistry.h
new file mode 100644
index 0000000000..2c04d8c419
--- /dev/null
+++ b/indra/llxuixml/llregistry.h
@@ -0,0 +1,347 @@
+/**
+ * @file llregistry.h
+ * @brief template classes for registering name, value pairs in nested scopes, statically, etc.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLREGISTRY_H
+#define LL_LLREGISTRY_H
+
+#include <list>
+
+#include <boost/type_traits.hpp>
+#include "llsingleton.h"
+
+template <typename T>
+class LLRegistryDefaultComparator
+{
+ bool operator()(const T& lhs, const T& rhs) { return lhs < rhs; }
+};
+
+template <typename KEY, typename VALUE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
+class LLRegistry
+{
+public:
+ typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
+ typedef typename boost::add_reference<typename boost::add_const<KEY>::type>::type ref_const_key_t;
+ typedef typename boost::add_reference<typename boost::add_const<VALUE>::type>::type ref_const_value_t;
+ typedef typename boost::add_reference<VALUE>::type ref_value_t;
+ typedef typename boost::add_pointer<typename boost::add_const<VALUE>::type>::type ptr_const_value_t;
+ typedef typename boost::add_pointer<VALUE>::type ptr_value_t;
+
+ class Registrar
+ {
+ friend class LLRegistry<KEY, VALUE, COMPARATOR>;
+ public:
+ typedef typename std::map<KEY, VALUE> registry_map_t;
+
+ bool add(ref_const_key_t key, ref_const_value_t value)
+ {
+ if (mMap.insert(std::make_pair(key, value)).second == false)
+ {
+ llwarns << "Tried to register " << key << " but it was already registered!" << llendl;
+ return false;
+ }
+ return true;
+ }
+
+ void remove(ref_const_key_t key)
+ {
+ mMap.erase(key);
+ }
+
+ typename registry_map_t::const_iterator beginItems() const
+ {
+ return mMap.begin();
+ }
+
+ typename registry_map_t::const_iterator endItems() const
+ {
+ return mMap.end();
+ }
+
+ protected:
+ ptr_value_t getValue(ref_const_key_t key)
+ {
+ typename registry_map_t::iterator found_it = mMap.find(key);
+ if (found_it != mMap.end())
+ {
+ return &(found_it->second);
+ }
+ return NULL;
+ }
+
+ ptr_const_value_t getValue(ref_const_key_t key) const
+ {
+ typename registry_map_t::const_iterator found_it = mMap.find(key);
+ if (found_it != mMap.end())
+ {
+ return &(found_it->second);
+ }
+ return NULL;
+ }
+
+ // if the registry is used to store pointers, and null values are valid entries
+ // then use this function to check the existence of an entry
+ bool exists(ref_const_key_t key) const
+ {
+ return mMap.find(key) != mMap.end();
+ }
+
+ bool empty() const
+ {
+ return mMap.empty();
+ }
+
+ protected:
+ // use currentRegistrar() or defaultRegistrar()
+ Registrar() {}
+ ~Registrar() {}
+
+ private:
+ registry_map_t mMap;
+ };
+
+ typedef typename std::list<Registrar*> scope_list_t;
+ typedef typename std::list<Registrar*>::iterator scope_list_iterator_t;
+ typedef typename std::list<Registrar*>::const_iterator scope_list_const_iterator_t;
+
+ LLRegistry()
+ {}
+
+ ~LLRegistry() {}
+
+ ptr_value_t getValue(ref_const_key_t key)
+ {
+ for(scope_list_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ ptr_value_t valuep = (*it)->getValue(key);
+ if (valuep != NULL) return valuep;
+ }
+ return mDefaultRegistrar.getValue(key);
+ }
+
+ ptr_const_value_t getValue(ref_const_key_t key) const
+ {
+ for(scope_list_const_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ ptr_value_t valuep = (*it)->getValue(key);
+ if (valuep != NULL) return valuep;
+ }
+ return mDefaultRegistrar.getValue(key);
+ }
+
+ bool exists(ref_const_key_t key) const
+ {
+ for(scope_list_const_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ if ((*it)->exists(key)) return true;
+ }
+
+ return mDefaultRegistrar.exists(key);
+ }
+
+ bool empty() const
+ {
+ for(scope_list_const_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ if (!(*it)->empty()) return false;
+ }
+
+ return mDefaultRegistrar.empty();
+ }
+
+
+ Registrar& defaultRegistrar()
+ {
+ return mDefaultRegistrar;
+ }
+
+ const Registrar& defaultRegistrar() const
+ {
+ return mDefaultRegistrar;
+ }
+
+
+ Registrar& currentRegistrar()
+ {
+ if (!mActiveScopes.empty())
+ {
+ return *mActiveScopes.front();
+ }
+
+ return mDefaultRegistrar;
+ }
+
+ const Registrar& currentRegistrar() const
+ {
+ if (!mActiveScopes.empty())
+ {
+ return *mActiveScopes.front();
+ }
+
+ return mDefaultRegistrar;
+ }
+
+
+protected:
+ void addScope(Registrar* scope)
+ {
+ // newer scopes go up front
+ mActiveScopes.insert(mActiveScopes.begin(), scope);
+ }
+
+ void removeScope(Registrar* scope)
+ {
+ // O(N) but should be near the beggining and N should be small and this is safer than storing iterators
+ scope_list_iterator_t iter = std::find(mActiveScopes.begin(), mActiveScopes.end(), scope);
+ if (iter != mActiveScopes.end())
+ {
+ mActiveScopes.erase(iter);
+ }
+ }
+
+private:
+ scope_list_t mActiveScopes;
+ Registrar mDefaultRegistrar;
+};
+
+template <typename KEY, typename VALUE, typename DERIVED_TYPE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
+class LLRegistrySingleton
+ : public LLRegistry<KEY, VALUE, COMPARATOR>,
+ public LLSingleton<DERIVED_TYPE>
+{
+ friend class LLSingleton<DERIVED_TYPE>;
+public:
+ typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
+ typedef const KEY& ref_const_key_t;
+ typedef const VALUE& ref_const_value_t;
+ typedef VALUE* ptr_value_t;
+ typedef const VALUE* ptr_const_value_t;
+ typedef LLSingleton<DERIVED_TYPE> singleton_t;
+
+ class ScopedRegistrar : public registry_t::Registrar
+ {
+ public:
+ ScopedRegistrar(bool push_scope = true)
+ {
+ if (push_scope)
+ {
+ pushScope();
+ }
+ }
+
+ ~ScopedRegistrar()
+ {
+ if (!singleton_t::destroyed())
+ {
+ popScope();
+ }
+ }
+
+ void pushScope()
+ {
+ singleton_t::instance().addScope(this);
+ }
+
+ void popScope()
+ {
+ singleton_t::instance().removeScope(this);
+ }
+
+ ptr_value_t getValueFromScope(ref_const_key_t key)
+ {
+ return getValue(key);
+ }
+
+ ptr_const_value_t getValueFromScope(ref_const_key_t key) const
+ {
+ return getValue(key);
+ }
+
+ private:
+ typename std::list<typename registry_t::Registrar*>::iterator mListIt;
+ };
+
+ class StaticRegistrar : public registry_t::Registrar
+ {
+ public:
+ virtual ~StaticRegistrar() {}
+ StaticRegistrar(ref_const_key_t key, ref_const_value_t value)
+ {
+ singleton_t::instance().mStaticScope->add(key, value);
+ }
+ };
+
+ // convenience functions
+ typedef typename LLRegistry<KEY, VALUE, COMPARATOR>::Registrar& ref_registrar_t;
+ static ref_registrar_t currentRegistrar()
+ {
+ return singleton_t::instance().registry_t::currentRegistrar();
+ }
+
+ static ref_registrar_t defaultRegistrar()
+ {
+ return singleton_t::instance().registry_t::defaultRegistrar();
+ }
+
+ static ptr_value_t getValue(ref_const_key_t key)
+ {
+ return singleton_t::instance().registry_t::getValue(key);
+ }
+
+protected:
+ // DERIVED_TYPE needs to derive from LLRegistrySingleton
+ LLRegistrySingleton()
+ : mStaticScope(NULL)
+ {}
+
+ virtual void initSingleton()
+ {
+ mStaticScope = new ScopedRegistrar();
+ }
+
+ virtual ~LLRegistrySingleton()
+ {
+ delete mStaticScope;
+ }
+
+private:
+ ScopedRegistrar* mStaticScope;
+};
+
+#endif
diff --git a/indra/llxuixml/lltrans.cpp b/indra/llxuixml/lltrans.cpp
new file mode 100644
index 0000000000..db7421575c
--- /dev/null
+++ b/indra/llxuixml/lltrans.cpp
@@ -0,0 +1,171 @@
+/**
+ * @file lltrans.cpp
+ * @brief LLTrans implementation
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltrans.h"
+
+#include "llfasttimer.h" // for call count statistics
+#include "llxuiparser.h"
+
+#include <map>
+
+LLTrans::template_map_t LLTrans::sStringTemplates;
+LLStringUtil::format_map_t LLTrans::sDefaultArgs;
+
+struct StringDef : public LLInitParam::Block<StringDef>
+{
+ Mandatory<std::string> name;
+ Mandatory<std::string> value;
+
+ StringDef()
+ : name("name"),
+ value("value")
+ {}
+};
+
+struct StringTable : public LLInitParam::Block<StringTable>
+{
+ Multiple<StringDef> strings;
+ StringTable()
+ : strings("string")
+ {}
+};
+
+//static
+bool LLTrans::parseStrings(LLXMLNodePtr &root, const std::set<std::string>& default_args)
+{
+ std::string xml_filename = "(strings file)";
+ if (!root->hasName("strings"))
+ {
+ llerrs << "Invalid root node name in " << xml_filename
+ << ": was " << root->getName() << ", expected \"strings\"" << llendl;
+ }
+
+ StringTable string_table;
+ LLXUIParser::instance().readXUI(root, string_table);
+
+ if (!string_table.validateBlock())
+ {
+ llerrs << "Problem reading strings: " << xml_filename << llendl;
+ return false;
+ }
+
+ sStringTemplates.clear();
+ sDefaultArgs.clear();
+
+ for(LLInitParam::ParamIterator<StringDef>::const_iterator it = string_table.strings().begin();
+ it != string_table.strings().end();
+ ++it)
+ {
+ LLTransTemplate xml_template(it->name, it->value);
+ sStringTemplates[xml_template.mName] = xml_template;
+
+ std::set<std::string>::const_iterator iter = default_args.find(xml_template.mName);
+ if (iter != default_args.end())
+ {
+ std::string name = *iter;
+ if (name[0] != '[')
+ name = llformat("[%s]",name.c_str());
+ sDefaultArgs[name] = xml_template.mText;
+ }
+ }
+
+ return true;
+}
+
+
+//static
+bool LLTrans::parseLanguageStrings(LLXMLNodePtr &root)
+{
+ std::string xml_filename = "(language strings file)";
+ if (!root->hasName("strings"))
+ {
+ llerrs << "Invalid root node name in " << xml_filename
+ << ": was " << root->getName() << ", expected \"strings\"" << llendl;
+ }
+
+ StringTable string_table;
+ LLXUIParser::instance().readXUI(root, string_table);
+
+ if (!string_table.validateBlock())
+ {
+ llerrs << "Problem reading strings: " << xml_filename << llendl;
+ return false;
+ }
+
+ for(LLInitParam::ParamIterator<StringDef>::const_iterator it = string_table.strings().begin();
+ it != string_table.strings().end();
+ ++it)
+ {
+ // share the same map with parseStrings() so we can search the strings using the same getString() function.- angela
+ LLTransTemplate xml_template(it->name, it->value);
+ sStringTemplates[xml_template.mName] = xml_template;
+ }
+
+ return true;
+}
+
+
+
+static LLFastTimer::DeclareTimer FTM_GET_TRANS("Translate string");
+
+//static
+std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
+{
+ // Don't care about time as much as call count. Make sure we're not
+ // calling LLTrans::getString() in an inner loop. JC
+ LLFastTimer timer(FTM_GET_TRANS);
+
+ template_map_t::iterator iter = sStringTemplates.find(xml_desc);
+ if (iter != sStringTemplates.end())
+ {
+ std::string text = iter->second.mText;
+ LLStringUtil::format_map_t args = sDefaultArgs;
+ args.insert(msg_args.begin(), msg_args.end());
+ LLStringUtil::format(text, args);
+
+ return text;
+ }
+ else
+ {
+ LLSD args;
+ args["STRING_NAME"] = xml_desc;
+ LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
+
+ //LLNotifications::instance().add("MissingString", args); // *TODO: resurrect
+ //return xml_desc;
+
+ return "MissingString("+xml_desc+")";
+ }
+}
+
diff --git a/indra/llxuixml/lltrans.h b/indra/llxuixml/lltrans.h
new file mode 100644
index 0000000000..6423c88245
--- /dev/null
+++ b/indra/llxuixml/lltrans.h
@@ -0,0 +1,111 @@
+/**
+ * @file lltrans.h
+ * @brief LLTrans definition
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_TRANS_H
+#define LL_TRANS_H
+
+#include <map>
+
+#include "llstring.h"
+#include "llxmlnode.h"
+
+/**
+ * @brief String template loaded from strings.xml
+ */
+class LLTransTemplate
+{
+public:
+ LLTransTemplate(const std::string& name = LLStringUtil::null, const std::string& text = LLStringUtil::null) : mName(name), mText(text) {}
+
+ std::string mName;
+ std::string mText;
+};
+
+/**
+ * @brief Localized strings class
+ * This class is used to retrieve translations of strings used to build larger ones, as well as
+ * strings with a general usage that don't belong to any specific floater. For example,
+ * "Owner:", "Retrieving..." used in the place of a not yet known name, etc.
+ */
+class LLTrans
+{
+public:
+ LLTrans();
+
+ /**
+ * @brief Parses the xml root that holds the strings. Used once on startup
+// *FIXME * @param xml_filename Filename to parse
+ * @param default_args Set of strings (expected to be in the file) to use as default replacement args, e.g. "SECOND_LIFE"
+ * @returns true if the file was parsed successfully, true if something went wrong
+ */
+ static bool parseStrings(LLXMLNodePtr& root, const std::set<std::string>& default_args);
+
+ static bool parseLanguageStrings(LLXMLNodePtr &root);
+
+ /**
+ * @brief Returns a translated string
+ * @param xml_desc String's description
+ * @param args A list of substrings to replace in the string
+ * @returns Translated string
+ */
+ static std::string getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args);
+
+ /**
+ * @brief Returns a translated string
+ * @param xml_desc String's description
+ * @returns Translated string
+ */
+ static std::string getString(const std::string &xml_desc)
+ {
+ LLStringUtil::format_map_t empty;
+ return getString(xml_desc, empty);
+ }
+
+ // get the default args
+ static const LLStringUtil::format_map_t& getDefaultArgs()
+ {
+ return sDefaultArgs;
+ }
+
+ // insert default args into an arg list
+ static void getArgs(LLStringUtil::format_map_t& args)
+ {
+ args.insert(sDefaultArgs.begin(), sDefaultArgs.end());
+ }
+
+private:
+ typedef std::map<std::string, LLTransTemplate > template_map_t;
+ static template_map_t sStringTemplates;
+ static LLStringUtil::format_map_t sDefaultArgs;
+};
+
+#endif
diff --git a/indra/llxuixml/lluicolor.cpp b/indra/llxuixml/lluicolor.cpp
new file mode 100644
index 0000000000..ef0fa5d634
--- /dev/null
+++ b/indra/llxuixml/lluicolor.cpp
@@ -0,0 +1,71 @@
+/**
+ * @file lluicolor.cpp
+ * @brief brief LLUIColor class implementation file
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#include "lluicolor.h"
+
+LLUIColor::LLUIColor()
+ :mColorPtr(NULL)
+{
+}
+
+LLUIColor::LLUIColor(const LLColor4* color)
+ :mColorPtr(color)
+{
+}
+
+LLUIColor::LLUIColor(const LLColor4& color)
+ :mColor(color), mColorPtr(NULL)
+{
+}
+
+void LLUIColor::set(const LLColor4& color)
+{
+ mColor = color;
+ mColorPtr = NULL;
+}
+
+void LLUIColor::set(const LLColor4* color)
+{
+ mColorPtr = color;
+}
+
+const LLColor4& LLUIColor::get() const
+{
+ return (mColorPtr == NULL ? mColor : *mColorPtr);
+}
+
+LLUIColor::operator const LLColor4& () const
+{
+ return get();
+}
+
+const LLColor4& LLUIColor::operator()() const
+{
+ return get();
+}
+
+bool LLUIColor::isReference() const
+{
+ return mColorPtr != NULL;
+}
+
+namespace LLInitParam
+{
+ // used to detect equivalence with default values on export
+ template<>
+ class ParamCompare<LLUIColor>
+ {
+ public:
+ static bool equals(const LLUIColor &a, const LLUIColor &b)
+ {
+ // do not detect value equivalence, treat pointers to colors as distinct from color values
+ return (a.mColorPtr == NULL && b.mColorPtr == NULL ? a.mColor == b.mColor : a.mColorPtr == b.mColorPtr);
+ }
+ };
+}
diff --git a/indra/llxuixml/lluicolor.h b/indra/llxuixml/lluicolor.h
new file mode 100644
index 0000000000..365f61003b
--- /dev/null
+++ b/indra/llxuixml/lluicolor.h
@@ -0,0 +1,45 @@
+/**
+ * @file lluicolor.h
+ * @brief brief LLUIColor class header file
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLUICOLOR_H_
+#define LL_LLUICOLOR_H_
+
+#include "v4color.h"
+
+namespace LLInitParam
+{
+ template<typename T>
+ class ParamCompare;
+}
+
+class LLUIColor
+{
+public:
+ LLUIColor();
+ LLUIColor(const LLColor4* color);
+ LLUIColor(const LLColor4& color);
+
+ void set(const LLColor4& color);
+ void set(const LLColor4* color);
+
+ const LLColor4& get() const;
+
+ operator const LLColor4& () const;
+ const LLColor4& operator()() const;
+
+ bool isReference() const;
+
+private:
+ friend class LLInitParam::ParamCompare<LLUIColor>;
+
+ const LLColor4* mColorPtr;
+ LLColor4 mColor;
+};
+
+#endif
diff --git a/indra/llxuixml/llxuiparser.cpp b/indra/llxuixml/llxuiparser.cpp
new file mode 100644
index 0000000000..e1f61906e2
--- /dev/null
+++ b/indra/llxuixml/llxuiparser.cpp
@@ -0,0 +1,968 @@
+/**
+ * @file llxuiparser.cpp
+ * @brief Utility functions for handling XUI structures in XML
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ *
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llxuiparser.h"
+
+#include <fstream>
+#include <boost/tokenizer.hpp>
+
+#include "lluicolor.h"
+
+const S32 MAX_STRING_ATTRIBUTE_SIZE = 40;
+
+//
+// LLXSDWriter
+//
+LLXSDWriter::LLXSDWriter()
+{
+ registerInspectFunc<bool>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:boolean", _1, _2, _3, _4));
+ registerInspectFunc<std::string>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<U8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedByte", _1, _2, _3, _4));
+ registerInspectFunc<S8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedByte", _1, _2, _3, _4));
+ registerInspectFunc<U16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedShort", _1, _2, _3, _4));
+ registerInspectFunc<S16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedShort", _1, _2, _3, _4));
+ registerInspectFunc<U32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedInt", _1, _2, _3, _4));
+ registerInspectFunc<S32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:integer", _1, _2, _3, _4));
+ registerInspectFunc<F32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:float", _1, _2, _3, _4));
+ registerInspectFunc<F64>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:double", _1, _2, _3, _4));
+ registerInspectFunc<LLColor4>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<LLUIColor>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<LLUUID>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<LLSD>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+}
+
+void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
+{
+ mSchemaNode = node;
+ node->setName("xs:schema");
+ node->createChild("attributeFormDefault", true)->setStringValue("unqualified");
+ node->createChild("elementFormDefault", true)->setStringValue("qualified");
+ node->createChild("targetNamespace", true)->setStringValue(xml_namespace);
+ node->createChild("xmlns:xs", true)->setStringValue("http://www.w3.org/2001/XMLSchema");
+ node->createChild("xmlns", true)->setStringValue(xml_namespace);
+
+ node = node->createChild("xs:complexType", false);
+ node->createChild("name", true)->setStringValue(type_name);
+ node->createChild("mixed", true)->setStringValue("true");
+
+ mAttributeNode = node;
+ mElementNode = node->createChild("xs:choice", false);
+ mElementNode->createChild("minOccurs", true)->setStringValue("0");
+ mElementNode->createChild("maxOccurs", true)->setStringValue("unbounded");
+ block.inspectBlock(*this);
+
+ // duplicate element choices
+ LLXMLNodeList children;
+ mElementNode->getChildren("xs:element", children, FALSE);
+ for (LLXMLNodeList::iterator child_it = children.begin(); child_it != children.end(); ++child_it)
+ {
+ LLXMLNodePtr child_copy = child_it->second->deepCopy();
+ std::string child_name;
+ child_copy->getAttributeString("name", child_name);
+ child_copy->setAttributeString("name", type_name + "." + child_name);
+ mElementNode->addChild(child_copy);
+ }
+
+ LLXMLNodePtr element_declaration_node = mSchemaNode->createChild("xs:element", false);
+ element_declaration_node->createChild("name", true)->setStringValue(type_name);
+ element_declaration_node->createChild("type", true)->setStringValue(type_name);
+}
+
+void LLXSDWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values)
+{
+ name_stack_t non_empty_names;
+ std::string attribute_name;
+ for (name_stack_t::const_iterator it = stack.begin();
+ it != stack.end();
+ ++it)
+ {
+ const std::string& name = it->first;
+ if (!name.empty())
+ {
+ non_empty_names.push_back(*it);
+ }
+ }
+
+ for (name_stack_t::const_iterator it = non_empty_names.begin();
+ it != non_empty_names.end();
+ ++it)
+ {
+ if (!attribute_name.empty())
+ {
+ attribute_name += ".";
+ }
+ attribute_name += it->first;
+ }
+
+ // only flag non-nested attributes as mandatory, nested attributes have variant syntax
+ // that can't be properly constrained in XSD
+ // e.g. <foo mandatory.value="bar"/> vs <foo><mandatory value="bar"/></foo>
+ bool attribute_mandatory = min_count == 1 && max_count == 1 && non_empty_names.size() == 1;
+
+ // don't bother supporting "Multiple" params as xml attributes
+ if (max_count <= 1)
+ {
+ // add compound attribute to root node
+ addAttributeToSchema(mAttributeNode, attribute_name, type, attribute_mandatory, possible_values);
+ }
+
+ // now generated nested elements for compound attributes
+ if (non_empty_names.size() > 1 && !attribute_mandatory)
+ {
+ std::string element_name;
+
+ // traverse all but last element, leaving that as an attribute name
+ name_stack_t::const_iterator end_it = non_empty_names.end();
+ end_it--;
+
+ for (name_stack_t::const_iterator it = non_empty_names.begin();
+ it != end_it;
+ ++it)
+ {
+ if (it != non_empty_names.begin())
+ {
+ element_name += ".";
+ }
+ element_name += it->first;
+ }
+
+ std::string short_attribute_name = non_empty_names.back().first;
+
+ LLXMLNodePtr complex_type_node;
+
+ // find existing element node here, starting at tail of child list
+ if (mElementNode->mChildren.notNull())
+ {
+ for(LLXMLNodePtr element = mElementNode->mChildren->tail;
+ element.notNull();
+ element = element->mPrev)
+ {
+ std::string name;
+ if(element->getAttributeString("name", name) && name == element_name)
+ {
+ complex_type_node = element->mChildren->head;
+ break;
+ }
+ }
+ }
+ //create complex_type node
+ //
+ //<xs:element
+ // maxOccurs="1"
+ // minOccurs="0"
+ // name="name">
+ // <xs:complexType>
+ // </xs:complexType>
+ //</xs:element>
+ if(complex_type_node.isNull())
+ {
+ complex_type_node = mElementNode->createChild("xs:element", false);
+
+ complex_type_node->createChild("minOccurs", true)->setIntValue(min_count);
+ complex_type_node->createChild("maxOccurs", true)->setIntValue(max_count);
+ complex_type_node->createChild("name", true)->setStringValue(element_name);
+ complex_type_node = complex_type_node->createChild("xs:complexType", false);
+ }
+
+ addAttributeToSchema(complex_type_node, short_attribute_name, type, false, possible_values);
+ }
+}
+
+void LLXSDWriter::addAttributeToSchema(LLXMLNodePtr type_declaration_node, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values)
+{
+ if (!attribute_name.empty())
+ {
+ LLXMLNodePtr new_enum_type_node;
+ if (possible_values != NULL)
+ {
+ // custom attribute type, for example
+ //<xs:simpleType>
+ // <xs:restriction
+ // base="xs:string">
+ // <xs:enumeration
+ // value="a" />
+ // <xs:enumeration
+ // value="b" />
+ // </xs:restriction>
+ // </xs:simpleType>
+ new_enum_type_node = new LLXMLNode("xs:simpleType", false);
+
+ LLXMLNodePtr restriction_node = new_enum_type_node->createChild("xs:restriction", false);
+ restriction_node->createChild("base", true)->setStringValue("xs:string");
+
+ for (std::vector<std::string>::const_iterator it = possible_values->begin();
+ it != possible_values->end();
+ ++it)
+ {
+ LLXMLNodePtr enum_node = restriction_node->createChild("xs:enumeration", false);
+ enum_node->createChild("value", true)->setStringValue(*it);
+ }
+ }
+
+ string_set_t& attributes_written = mAttributesWritten[type_declaration_node];
+
+ string_set_t::iterator found_it = attributes_written.lower_bound(attribute_name);
+
+ // attribute not yet declared
+ if (found_it == attributes_written.end() || attributes_written.key_comp()(attribute_name, *found_it))
+ {
+ attributes_written.insert(found_it, attribute_name);
+
+ LLXMLNodePtr attribute_node = type_declaration_node->createChild("xs:attribute", false);
+
+ // attribute name
+ attribute_node->createChild("name", true)->setStringValue(attribute_name);
+
+ if (new_enum_type_node.notNull())
+ {
+ attribute_node->addChild(new_enum_type_node);
+ }
+ else
+ {
+ // simple attribute type
+ attribute_node->createChild("type", true)->setStringValue(type);
+ }
+
+ // required or optional
+ attribute_node->createChild("use", true)->setStringValue(mandatory ? "required" : "optional");
+ }
+ // attribute exists...handle collision of same name attributes with potentially different types
+ else
+ {
+ LLXMLNodePtr attribute_declaration;
+ if (type_declaration_node.notNull())
+ {
+ for(LLXMLNodePtr node = type_declaration_node->mChildren->tail;
+ node.notNull();
+ node = node->mPrev)
+ {
+ std::string name;
+ if (node->getAttributeString("name", name) && name == attribute_name)
+ {
+ attribute_declaration = node;
+ break;
+ }
+ }
+ }
+
+ bool new_type_is_enum = new_enum_type_node.notNull();
+ bool existing_type_is_enum = !attribute_declaration->hasAttribute("type");
+
+ // either type is enum, revert to string in collision
+ // don't bother to check for enum equivalence
+ if (new_type_is_enum || existing_type_is_enum)
+ {
+ if (attribute_declaration->hasAttribute("type"))
+ {
+ attribute_declaration->setAttributeString("type", "xs:string");
+ }
+ else
+ {
+ attribute_declaration->createChild("type", true)->setStringValue("xs:string");
+ }
+ attribute_declaration->deleteChildren("xs:simpleType");
+ }
+ else
+ {
+ // check for collision of different standard types
+ std::string existing_type;
+ attribute_declaration->getAttributeString("type", existing_type);
+ // if current type is not the same as the new type, revert to strnig
+ if (existing_type != type)
+ {
+ // ...than use most general type, string
+ attribute_declaration->setAttributeString("type", "string");
+ }
+ }
+ }
+ }
+}
+
+//
+// LLXUIXSDWriter
+//
+void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& path, const LLInitParam::BaseBlock& block)
+{
+ std::string file_name(path);
+ file_name += type_name + ".xsd";
+ LLXMLNodePtr root_nodep = new LLXMLNode();
+
+ LLXSDWriter::writeXSD(type_name, root_nodep, block, "http://www.lindenlab.com/xui");
+
+ // add includes for all possible children
+ const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
+ const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
+
+ // add include declarations for all valid children
+ for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
+ it != widget_registryp->currentRegistrar().endItems();
+ ++it)
+ {
+ std::string widget_name = it->first;
+ if (widget_name == type_name)
+ {
+ continue;
+ }
+ LLXMLNodePtr nodep = new LLXMLNode("xs:include", false);
+ nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd");
+
+ // add to front of schema
+ mSchemaNode->addChild(nodep, mSchemaNode);
+ }
+
+ // add choices for valid children
+ if (widget_registryp)
+ {
+ for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
+ it != widget_registryp->currentRegistrar().endItems();
+ ++it)
+ {
+ std::string widget_name = it->first;
+ //<xs:element name="widget_name" type="widget_name">
+ LLXMLNodePtr widget_node = mElementNode->createChild("xs:element", false);
+ widget_node->createChild("name", true)->setStringValue(widget_name);
+ widget_node->createChild("type", true)->setStringValue(widget_name);
+ }
+ }
+
+ LLFILE* xsd_file = LLFile::fopen(file_name.c_str(), "w");
+ LLXMLNode::writeHeaderToFile(xsd_file);
+ root_nodep->writeToFile(xsd_file);
+ fclose(xsd_file);
+}
+
+//
+// LLXUIParser
+//
+LLXUIParser::LLXUIParser()
+: mLastWriteGeneration(-1),
+ mCurReadDepth(0)
+{
+ registerParserFuncs<bool>(boost::bind(&LLXUIParser::readBoolValue, this, _1),
+ boost::bind(&LLXUIParser::writeBoolValue, this, _1, _2));
+ registerParserFuncs<std::string>(boost::bind(&LLXUIParser::readStringValue, this, _1),
+ boost::bind(&LLXUIParser::writeStringValue, this, _1, _2));
+ registerParserFuncs<U8>(boost::bind(&LLXUIParser::readU8Value, this, _1),
+ boost::bind(&LLXUIParser::writeU8Value, this, _1, _2));
+ registerParserFuncs<S8>(boost::bind(&LLXUIParser::readS8Value, this, _1),
+ boost::bind(&LLXUIParser::writeS8Value, this, _1, _2));
+ registerParserFuncs<U16>(boost::bind(&LLXUIParser::readU16Value, this, _1),
+ boost::bind(&LLXUIParser::writeU16Value, this, _1, _2));
+ registerParserFuncs<S16>(boost::bind(&LLXUIParser::readS16Value, this, _1),
+ boost::bind(&LLXUIParser::writeS16Value, this, _1, _2));
+ registerParserFuncs<U32>(boost::bind(&LLXUIParser::readU32Value, this, _1),
+ boost::bind(&LLXUIParser::writeU32Value, this, _1, _2));
+ registerParserFuncs<S32>(boost::bind(&LLXUIParser::readS32Value, this, _1),
+ boost::bind(&LLXUIParser::writeS32Value, this, _1, _2));
+ registerParserFuncs<F32>(boost::bind(&LLXUIParser::readF32Value, this, _1),
+ boost::bind(&LLXUIParser::writeF32Value, this, _1, _2));
+ registerParserFuncs<F64>(boost::bind(&LLXUIParser::readF64Value, this, _1),
+ boost::bind(&LLXUIParser::writeF64Value, this, _1, _2));
+ registerParserFuncs<LLColor4>(boost::bind(&LLXUIParser::readColor4Value, this, _1),
+ boost::bind(&LLXUIParser::writeColor4Value, this, _1, _2));
+ registerParserFuncs<LLUIColor>(boost::bind(&LLXUIParser::readUIColorValue, this, _1),
+ boost::bind(&LLXUIParser::writeUIColorValue, this, _1, _2));
+ registerParserFuncs<LLUUID>(boost::bind(&LLXUIParser::readUUIDValue, this, _1),
+ boost::bind(&LLXUIParser::writeUUIDValue, this, _1, _2));
+ registerParserFuncs<LLSD>(boost::bind(&LLXUIParser::readSDValue, this, _1),
+ boost::bind(&LLXUIParser::writeSDValue, this, _1, _2));
+}
+
+static LLFastTimer::DeclareTimer PARSE_XUI("XUI Parsing");
+
+void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent)
+{
+ LLFastTimer timer(PARSE_XUI);
+ mNameStack.clear();
+ mCurReadDepth = 0;
+ setParseSilently(silent);
+
+ if (node.isNull())
+ {
+ parserWarning("Invalid node");
+ }
+ else
+ {
+ readXUIImpl(node, std::string(node->getName()->mString), block);
+ }
+}
+
+bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, const std::string& scope, LLInitParam::BaseBlock& block)
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".");
+
+ bool values_parsed = false;
+
+ // submit attributes for current node
+ values_parsed |= readAttributes(nodep, block);
+
+ // treat text contents of xml node as "value" parameter
+ std::string text_contents = nodep->getSanitizedValue();
+ if (!text_contents.empty())
+ {
+ mCurReadNode = nodep;
+ mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration()));
+ // child nodes are not necessarily valid parameters (could be a child widget)
+ // so don't complain once we've recursed
+ bool silent = mCurReadDepth > 0;
+ if (!block.submitValue(mNameStack, *this, true))
+ {
+ mNameStack.pop_back();
+ block.submitValue(mNameStack, *this, silent);
+ }
+ else
+ {
+ mNameStack.pop_back();
+ }
+ }
+
+ // then traverse children
+ // child node must start with last name of parent node (our "scope")
+ // for example: "<button><button.param nested_param1="foo"><param.nested_param2 nested_param3="bar"/></button.param></button>"
+ // which equates to the following nesting:
+ // button
+ // param
+ // nested_param1
+ // nested_param2
+ // nested_param3
+ mCurReadDepth++;
+ for(LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();)
+ {
+ std::string child_name(childp->getName()->mString);
+ S32 num_tokens_pushed = 0;
+
+ // for non "dotted" child nodes check to see if child node maps to another widget type
+ // and if not, treat as a child element of the current node
+ // e.g. <button><rect left="10"/></button> will interpret <rect> as "button.rect"
+ // since there is no widget named "rect"
+ if (child_name.find(".") == std::string::npos)
+ {
+ mNameStack.push_back(std::make_pair(child_name, newParseGeneration()));
+ num_tokens_pushed++;
+ }
+ else
+ {
+ // parse out "dotted" name into individual tokens
+ tokenizer name_tokens(child_name, sep);
+
+ tokenizer::iterator name_token_it = name_tokens.begin();
+ if(name_token_it == name_tokens.end())
+ {
+ childp = childp->getNextSibling();
+ continue;
+ }
+
+ // check for proper nesting
+ if(!scope.empty() && *name_token_it != scope)
+ {
+ childp = childp->getNextSibling();
+ continue;
+ }
+
+ // now ignore first token
+ ++name_token_it;
+
+ // copy remaining tokens on to our running token list
+ for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push)
+ {
+ mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
+ num_tokens_pushed++;
+ }
+ }
+
+ // recurse and visit children XML nodes
+ if(readXUIImpl(childp, mNameStack.empty() ? scope : mNameStack.back().first, block))
+ {
+ // child node successfully parsed, remove from DOM
+
+ values_parsed = true;
+ LLXMLNodePtr node_to_remove = childp;
+ childp = childp->getNextSibling();
+
+ nodep->deleteChild(node_to_remove);
+ }
+ else
+ {
+ childp = childp->getNextSibling();
+ }
+
+ while(num_tokens_pushed-- > 0)
+ {
+ mNameStack.pop_back();
+ }
+ }
+ mCurReadDepth--;
+ return values_parsed;
+}
+
+bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".");
+
+ bool any_parsed = false;
+
+ for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin();
+ attribute_it != nodep->mAttributes.end();
+ ++attribute_it)
+ {
+ S32 num_tokens_pushed = 0;
+ std::string attribute_name(attribute_it->first->mString);
+ mCurReadNode = attribute_it->second;
+
+ tokenizer name_tokens(attribute_name, sep);
+ // copy remaining tokens on to our running token list
+ for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push)
+ {
+ mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
+ num_tokens_pushed++;
+ }
+
+ // child nodes are not necessarily valid attributes, so don't complain once we've recursed
+ bool silent = mCurReadDepth > 0;
+ any_parsed |= block.submitValue(mNameStack, *this, silent);
+
+ while(num_tokens_pushed-- > 0)
+ {
+ mNameStack.pop_back();
+ }
+ }
+
+ return any_parsed;
+}
+
+void LLXUIParser::writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::BaseBlock* diff_block)
+{
+ mWriteRootNode = node;
+ block.serializeBlock(*this, Parser::name_stack_t(), diff_block);
+ mOutNodes.clear();
+}
+
+// go from a stack of names to a specific XML node
+LLXMLNodePtr LLXUIParser::getNode(const name_stack_t& stack)
+{
+ name_stack_t name_stack;
+ for (name_stack_t::const_iterator it = stack.begin();
+ it != stack.end();
+ ++it)
+ {
+ if (!it->first.empty())
+ {
+ name_stack.push_back(*it);
+ }
+ }
+
+ LLXMLNodePtr out_node = mWriteRootNode;
+
+ name_stack_t::const_iterator next_it = name_stack.begin();
+ for (name_stack_t::const_iterator it = name_stack.begin();
+ it != name_stack.end();
+ it = next_it)
+ {
+ ++next_it;
+ if (it->first.empty())
+ {
+ continue;
+ }
+
+ out_nodes_t::iterator found_it = mOutNodes.lower_bound(it->second);
+
+ // node with this name not yet written
+ if (found_it == mOutNodes.end() || mOutNodes.key_comp()(found_it->first, it->second))
+ {
+ // make an attribute if we are the last element on the name stack
+ bool is_attribute = next_it == name_stack.end();
+ LLXMLNodePtr new_node = new LLXMLNode(it->first.c_str(), is_attribute);
+ out_node->addChild(new_node);
+ mOutNodes.insert(found_it, std::make_pair(it->second, new_node));
+ out_node = new_node;
+ }
+ else
+ {
+ out_node = found_it->second;
+ }
+ }
+
+ return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node);
+}
+
+
+bool LLXUIParser::readBoolValue(void* val_ptr)
+{
+ S32 value;
+ bool success = mCurReadNode->getBoolValue(1, &value);
+ *((bool*)val_ptr) = (value != FALSE);
+ return success;
+}
+
+bool LLXUIParser::writeBoolValue(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setBoolValue(*((bool*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readStringValue(void* val_ptr)
+{
+ *((std::string*)val_ptr) = mCurReadNode->getSanitizedValue();
+ return true;
+}
+
+bool LLXUIParser::writeStringValue(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ const std::string* string_val = reinterpret_cast<const std::string*>(val_ptr);
+ if (string_val->find('\n') != std::string::npos
+ || string_val->size() > MAX_STRING_ATTRIBUTE_SIZE)
+ {
+ // don't write strings with newlines into attributes
+ std::string attribute_name = node->getName()->mString;
+ LLXMLNodePtr parent_node = node->mParent;
+ parent_node->deleteChild(node);
+ // write results in text contents of node
+ if (attribute_name == "value")
+ {
+ // "value" is implicit, just write to parent
+ node = parent_node;
+ }
+ else
+ {
+ // create a child that is not an attribute, but with same name
+ node = parent_node->createChild(attribute_name.c_str(), false);
+ }
+ }
+ node->setStringValue(*string_val);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readU8Value(void* val_ptr)
+{
+ return mCurReadNode->getByteValue(1, (U8*)val_ptr);
+}
+
+bool LLXUIParser::writeU8Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setUnsignedValue(*((U8*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readS8Value(void* val_ptr)
+{
+ S32 value;
+ if(mCurReadNode->getIntValue(1, &value))
+ {
+ *((S8*)val_ptr) = value;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeS8Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setIntValue(*((S8*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readU16Value(void* val_ptr)
+{
+ U32 value;
+ if(mCurReadNode->getUnsignedValue(1, &value))
+ {
+ *((U16*)val_ptr) = value;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeU16Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setUnsignedValue(*((U16*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readS16Value(void* val_ptr)
+{
+ S32 value;
+ if(mCurReadNode->getIntValue(1, &value))
+ {
+ *((S16*)val_ptr) = value;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeS16Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setIntValue(*((S16*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readU32Value(void* val_ptr)
+{
+ return mCurReadNode->getUnsignedValue(1, (U32*)val_ptr);
+}
+
+bool LLXUIParser::writeU32Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setUnsignedValue(*((U32*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readS32Value(void* val_ptr)
+{
+ return mCurReadNode->getIntValue(1, (S32*)val_ptr);
+}
+
+bool LLXUIParser::writeS32Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setIntValue(*((S32*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readF32Value(void* val_ptr)
+{
+ return mCurReadNode->getFloatValue(1, (F32*)val_ptr);
+}
+
+bool LLXUIParser::writeF32Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setFloatValue(*((F32*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readF64Value(void* val_ptr)
+{
+ return mCurReadNode->getDoubleValue(1, (F64*)val_ptr);
+}
+
+bool LLXUIParser::writeF64Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setDoubleValue(*((F64*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readColor4Value(void* val_ptr)
+{
+ LLColor4* colorp = (LLColor4*)val_ptr;
+ if(mCurReadNode->getFloatValue(4, colorp->mV) >= 3)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool LLXUIParser::writeColor4Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ LLColor4 color = *((LLColor4*)val_ptr);
+ node->setFloatValue(4, color.mV);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readUIColorValue(void* val_ptr)
+{
+ LLUIColor* param = (LLUIColor*)val_ptr;
+ LLColor4 color;
+ bool success = mCurReadNode->getFloatValue(4, color.mV) >= 3;
+ if (success)
+ {
+ param->set(color);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeUIColorValue(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ LLUIColor color = *((LLUIColor*)val_ptr);
+ //RN: don't write out the color that is represented by a function
+ // rely on param block exporting to get the reference to the color settings
+ if (color.isReference()) return false;
+ node->setFloatValue(4, color.get().mV);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readUUIDValue(void* val_ptr)
+{
+ LLUUID temp_id;
+ // LLUUID::set is destructive, so use temporary value
+ if (temp_id.set(mCurReadNode->getSanitizedValue()))
+ {
+ *(LLUUID*)(val_ptr) = temp_id;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeUUIDValue(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setStringValue(((LLUUID*)val_ptr)->asString());
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readSDValue(void* val_ptr)
+{
+ *((LLSD*)val_ptr) = LLSD(mCurReadNode->getSanitizedValue());
+ return true;
+}
+
+bool LLXUIParser::writeSDValue(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ std::string string_val = ((LLSD*)val_ptr)->asString();
+ if (string_val.find('\n') != std::string::npos || string_val.size() > MAX_STRING_ATTRIBUTE_SIZE)
+ {
+ // don't write strings with newlines into attributes
+ std::string attribute_name = node->getName()->mString;
+ LLXMLNodePtr parent_node = node->mParent;
+ parent_node->deleteChild(node);
+ // write results in text contents of node
+ if (attribute_name == "value")
+ {
+ // "value" is implicit, just write to parent
+ node = parent_node;
+ }
+ else
+ {
+ node = parent_node->createChild(attribute_name.c_str(), false);
+ }
+ }
+
+ node->setStringValue(string_val);
+ return true;
+ }
+ return false;
+}
+
+/*virtual*/ std::string LLXUIParser::getCurrentElementName()
+{
+ std::string full_name;
+ for (name_stack_t::iterator it = mNameStack.begin();
+ it != mNameStack.end();
+ ++it)
+ {
+ full_name += it->first + "."; // build up dotted names: "button.param.nestedparam."
+ }
+
+ return full_name;
+}
+
+void LLXUIParser::parserWarning(const std::string& message)
+{
+#if 0 //#ifdef LL_WINDOWS
+ // use Visual Studo friendly formatting of output message for easy access to originating xml
+ llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
+ utf16str += '\n';
+ OutputDebugString(utf16str.c_str());
+#else
+ Parser::parserWarning(message);
+#endif
+}
+
+void LLXUIParser::parserError(const std::string& message)
+{
+#if 0 //#ifdef LL_WINDOWS
+ llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
+ utf16str += '\n';
+ OutputDebugString(utf16str.c_str());
+#else
+ Parser::parserError(message);
+#endif
+}
diff --git a/indra/llxuixml/llxuiparser.h b/indra/llxuixml/llxuiparser.h
new file mode 100644
index 0000000000..6f000f2422
--- /dev/null
+++ b/indra/llxuixml/llxuiparser.h
@@ -0,0 +1,174 @@
+/**
+ * @file llxuiparser.h
+ * @brief Utility functions for handling XUI structures in XML
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ *
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LLXUIPARSER_H
+#define LLXUIPARSER_H
+
+#include "llinitparam.h"
+#include "llxmlnode.h"
+#include "llfasttimer.h"
+#include "llregistry.h"
+
+#include <boost/function.hpp>
+#include <iosfwd>
+#include <stack>
+#include <set>
+
+
+
+class LLView;
+
+
+
+// lookup widget type by name
+class LLWidgetTypeRegistry
+: public LLRegistrySingleton<std::string, const std::type_info*, LLWidgetTypeRegistry>
+{};
+
+
+// global static instance for registering all widget types
+typedef boost::function<LLView* (LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)> LLWidgetCreatorFunc;
+
+typedef LLRegistry<std::string, LLWidgetCreatorFunc> widget_registry_t;
+
+class LLChildRegistryRegistry
+: public LLRegistrySingleton<const std::type_info*, widget_registry_t, LLChildRegistryRegistry>
+{};
+
+
+
+class LLXSDWriter : public LLInitParam::Parser
+{
+ LOG_CLASS(LLXSDWriter);
+public:
+ void writeXSD(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
+
+ /*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
+
+ LLXSDWriter();
+
+protected:
+ void writeAttribute(const std::string& type, const Parser::name_stack_t&, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values);
+ void addAttributeToSchema(LLXMLNodePtr nodep, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values);
+ LLXMLNodePtr mAttributeNode;
+ LLXMLNodePtr mElementNode;
+ LLXMLNodePtr mSchemaNode;
+
+ typedef std::set<std::string> string_set_t;
+ typedef std::map<LLXMLNodePtr, string_set_t> attributes_map_t;
+ attributes_map_t mAttributesWritten;
+};
+
+
+
+// NOTE: DOES NOT WORK YET
+// should support child widgets for XUI
+class LLXUIXSDWriter : public LLXSDWriter
+{
+public:
+ void writeXSD(const std::string& name, const std::string& path, const LLInitParam::BaseBlock& block);
+};
+
+
+
+class LLXUIParser : public LLInitParam::Parser, public LLSingleton<LLXUIParser>
+{
+LOG_CLASS(LLXUIParser);
+
+protected:
+ LLXUIParser();
+ friend class LLSingleton<LLXUIParser>;
+public:
+ typedef LLInitParam::Parser::name_stack_t name_stack_t;
+
+ /*virtual*/ std::string getCurrentElementName();
+ /*virtual*/ void parserWarning(const std::string& message);
+ /*virtual*/ void parserError(const std::string& message);
+
+ void readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent=false);
+ void writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const LLInitParam::BaseBlock* diff_block = NULL);
+
+private:
+ typedef std::list<std::pair<std::string, bool> > token_list_t;
+
+ bool readXUIImpl(LLXMLNodePtr node, const std::string& scope, LLInitParam::BaseBlock& block);
+ bool readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block);
+
+ //reader helper functions
+ bool readBoolValue(void* val_ptr);
+ bool readStringValue(void* val_ptr);
+ bool readU8Value(void* val_ptr);
+ bool readS8Value(void* val_ptr);
+ bool readU16Value(void* val_ptr);
+ bool readS16Value(void* val_ptr);
+ bool readU32Value(void* val_ptr);
+ bool readS32Value(void* val_ptr);
+ bool readF32Value(void* val_ptr);
+ bool readF64Value(void* val_ptr);
+ bool readColor4Value(void* val_ptr);
+ bool readUIColorValue(void* val_ptr);
+ bool readUUIDValue(void* val_ptr);
+ bool readSDValue(void* val_ptr);
+
+ //writer helper functions
+ bool writeBoolValue(const void* val_ptr, const name_stack_t&);
+ bool writeStringValue(const void* val_ptr, const name_stack_t&);
+ bool writeU8Value(const void* val_ptr, const name_stack_t&);
+ bool writeS8Value(const void* val_ptr, const name_stack_t&);
+ bool writeU16Value(const void* val_ptr, const name_stack_t&);
+ bool writeS16Value(const void* val_ptr, const name_stack_t&);
+ bool writeU32Value(const void* val_ptr, const name_stack_t&);
+ bool writeS32Value(const void* val_ptr, const name_stack_t&);
+ bool writeF32Value(const void* val_ptr, const name_stack_t&);
+ bool writeF64Value(const void* val_ptr, const name_stack_t&);
+ bool writeColor4Value(const void* val_ptr, const name_stack_t&);
+ bool writeUIColorValue(const void* val_ptr, const name_stack_t&);
+ bool writeUUIDValue(const void* val_ptr, const name_stack_t&);
+ bool writeSDValue(const void* val_ptr, const name_stack_t&);
+
+ LLXMLNodePtr getNode(const name_stack_t& stack);
+
+private:
+ Parser::name_stack_t mNameStack;
+ LLXMLNodePtr mCurReadNode;
+ // Root of the widget XML sub-tree, for example, "line_editor"
+ LLXMLNodePtr mWriteRootNode;
+
+ typedef std::map<S32, LLXMLNodePtr> out_nodes_t;
+ out_nodes_t mOutNodes;
+ S32 mLastWriteGeneration;
+ LLXMLNodePtr mLastWrittenChild;
+ S32 mCurReadDepth;
+};
+
+
+#endif //LLXUIPARSER_H