From c3cbd049859c058526ae9a07a5cbfa7e51085943 Mon Sep 17 00:00:00 2001
From: Adam Moss <moss@lindenlab.com>
Date: Fri, 21 Aug 2009 21:52:21 +0000
Subject: svn merge -r130238:130240
 svn+ssh://svn.lindenlab.com/svn/linden/branches/linux-updater-6

QAR-1771  Linux Viewer Autoupdater + XUI-parse refactoring
---
 indra/llxuixml/CMakeLists.txt  |   45 +
 indra/llxuixml/llinitparam.cpp |  524 ++++++++++++
 indra/llxuixml/llinitparam.h   | 1822 ++++++++++++++++++++++++++++++++++++++++
 indra/llxuixml/llregistry.h    |  347 ++++++++
 indra/llxuixml/lltrans.cpp     |  171 ++++
 indra/llxuixml/lltrans.h       |  111 +++
 indra/llxuixml/lluicolor.cpp   |   71 ++
 indra/llxuixml/lluicolor.h     |   45 +
 indra/llxuixml/llxuiparser.cpp |  968 +++++++++++++++++++++
 indra/llxuixml/llxuiparser.h   |  174 ++++
 10 files changed, 4278 insertions(+)
 create mode 100644 indra/llxuixml/CMakeLists.txt
 create mode 100644 indra/llxuixml/llinitparam.cpp
 create mode 100644 indra/llxuixml/llinitparam.h
 create mode 100644 indra/llxuixml/llregistry.h
 create mode 100644 indra/llxuixml/lltrans.cpp
 create mode 100644 indra/llxuixml/lltrans.h
 create mode 100644 indra/llxuixml/lluicolor.cpp
 create mode 100644 indra/llxuixml/lluicolor.h
 create mode 100644 indra/llxuixml/llxuiparser.cpp
 create mode 100644 indra/llxuixml/llxuiparser.h

(limited to 'indra/llxuixml')

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
-- 
cgit v1.2.3