/**
 * @file llxuiparser.cpp
 * @brief Utility functions for handling XUI structures in XML
 *
 * $LicenseInfo:firstyear=2003&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"

#include "llxuiparser.h"

#include "llxmlnode.h"
#include "llfasttimer.h"
#ifdef LL_USESYSTEMLIBS
#include <expat.h>
#else
#include "expat/expat.h"
#endif

#include <fstream>
#include <boost/tokenizer.hpp>
#include <boost/bind.hpp>
//#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/classic_core.hpp>

#include "lluicolor.h"
#include "v3math.h"
using namespace BOOST_SPIRIT_CLASSIC_NS;

const S32 MAX_STRING_ATTRIBUTE_SIZE = 40;

static  LLInitParam::Parser::parser_read_func_map_t sXSDReadFuncs;
static  LLInitParam::Parser::parser_write_func_map_t sXSDWriteFuncs;
static  LLInitParam::Parser::parser_inspect_func_map_t sXSDInspectFuncs;

static  LLInitParam::Parser::parser_read_func_map_t sSimpleXUIReadFuncs;
static  LLInitParam::Parser::parser_write_func_map_t sSimpleXUIWriteFuncs;
static  LLInitParam::Parser::parser_inspect_func_map_t sSimpleXUIInspectFuncs;

const char* NO_VALUE_MARKER = "no_value";

struct MaxOccursValues : public LLInitParam::TypeValuesHelper<U32, MaxOccursValues>
{
    static void declareValues()
    {
        declare("unbounded", U32_MAX);
    }
};

struct Occurs : public LLInitParam::Block<Occurs>
{
    Optional<U32>                   minOccurs;
    Optional<U32, MaxOccursValues>  maxOccurs;

    Occurs()
    :   minOccurs("minOccurs", 0),
        maxOccurs("maxOccurs", U32_MAX)

    {}
};

typedef enum
{
    USE_REQUIRED,
    USE_OPTIONAL
} EUse;

namespace LLInitParam
{
    template<>
    struct TypeValues<EUse> : public TypeValuesHelper<EUse>
    {
        static void declareValues()
        {
            declare("required", USE_REQUIRED);
            declare("optional", USE_OPTIONAL);
        }
    };
}

struct Element;
struct Group;
struct Sequence;

struct All : public LLInitParam::Block<All, Occurs>
{
    Multiple< Lazy<Element, IS_A_BLOCK> > elements;

    All()
    :   elements("element")
    {
        maxOccurs = 1;
    }
};

struct Attribute : public LLInitParam::Block<Attribute>
{
    Mandatory<std::string>  name,
                            type;
    Mandatory<EUse>         use;

    Attribute()
    :   name("name"),
        type("type"),
        use("use")
    {}
};

struct Any : public LLInitParam::Block<Any, Occurs>
{
    Optional<std::string> _namespace;

    Any()
    :   _namespace("namespace")
    {}
};

struct Choice : public LLInitParam::ChoiceBlock<Choice, Occurs>
{
    Alternative< Lazy<Element, IS_A_BLOCK> >    element;
    Alternative< Lazy<Group, IS_A_BLOCK> >      group;
    Alternative< Lazy<Choice, IS_A_BLOCK> >     choice;
    Alternative< Lazy<Sequence, IS_A_BLOCK> >   sequence;
    Alternative< Lazy<Any> >                    any;

    Choice()
    :   element("element"),
        group("group"),
        choice("choice"),
        sequence("sequence"),
        any("any")
    {}

};

struct Sequence : public LLInitParam::ChoiceBlock<Sequence, Occurs>
{
    Alternative< Lazy<Element, IS_A_BLOCK> >    element;
    Alternative< Lazy<Group, IS_A_BLOCK> >      group;
    Alternative< Lazy<Choice> >                 choice;
    Alternative< Lazy<Sequence, IS_A_BLOCK> >   sequence;
    Alternative< Lazy<Any> >                    any;
};

struct GroupContents : public LLInitParam::ChoiceBlock<GroupContents, Occurs>
{
    Alternative<All>        all;
    Alternative<Choice>     choice;
    Alternative<Sequence>   sequence;

    GroupContents()
    :   all("all"),
        choice("choice"),
        sequence("sequence")
    {}
};

struct Group : public LLInitParam::Block<Group, GroupContents>
{
    Optional<std::string>   name,
                            ref;

    Group()
    :   name("name"),
        ref("ref")
    {}
};

struct Restriction : public LLInitParam::Block<Restriction>
{
};

struct Extension : public LLInitParam::Block<Extension>
{
};

struct SimpleContent : public LLInitParam::ChoiceBlock<SimpleContent>
{
    Alternative<Restriction> restriction;
    Alternative<Extension> extension;

    SimpleContent()
    :   restriction("restriction"),
        extension("extension")
    {}
};

struct SimpleType : public LLInitParam::Block<SimpleType>
{
    // TODO
};

struct ComplexContent : public LLInitParam::Block<ComplexContent, SimpleContent>
{
    Optional<bool> mixed;

    ComplexContent()
    :   mixed("mixed", true)
    {}
};

struct ComplexTypeContents : public LLInitParam::ChoiceBlock<ComplexTypeContents>
{
    Alternative<SimpleContent>  simple_content;
    Alternative<ComplexContent> complex_content;
    Alternative<Group>          group;
    Alternative<All>            all;
    Alternative<Choice>         choice;
    Alternative<Sequence>       sequence;

    ComplexTypeContents()
    :   simple_content("simpleContent"),
        complex_content("complexContent"),
        group("group"),
        all("all"),
        choice("choice"),
        sequence("sequence")
    {}
};

struct ComplexType : public LLInitParam::Block<ComplexType, ComplexTypeContents>
{
    Optional<std::string>           name;
    Optional<bool>                  mixed;

    Multiple<Attribute>             attribute;
    Multiple< Lazy<Element, IS_A_BLOCK > >          elements;

    ComplexType()
    :   name("name"),
        attribute("xs:attribute"),
        elements("xs:element"),
        mixed("mixed")
    {
    }
};

struct ElementContents : public LLInitParam::ChoiceBlock<ElementContents, Occurs>
{
    Alternative<SimpleType>     simpleType;
    Alternative<ComplexType>    complexType;

    ElementContents()
    :   simpleType("simpleType"),
        complexType("complexType")
    {}
};

struct Element : public LLInitParam::Block<Element, ElementContents>
{
    Optional<std::string>   name,
                            ref,
                            type;

    Element()
    :   name("xs:name"),
        ref("xs:ref"),
        type("xs:type")
    {}
};

struct Schema : public LLInitParam::Block<Schema>
{
private:
    Mandatory<std::string>  targetNamespace,
                            xmlns,
                            xs;

public:
    Optional<std::string>   attributeFormDefault,
                            elementFormDefault;

    Mandatory<Element>      root_element;

    void setNameSpace(const std::string& ns) {targetNamespace = ns; xmlns = ns;}

    Schema(const std::string& ns = LLStringUtil::null)
    :   attributeFormDefault("attributeFormDefault"),
        elementFormDefault("elementFormDefault"),
        xs("xmlns:xs"),
        targetNamespace("targetNamespace"),
        xmlns("xmlns"),
        root_element("xs:element")
    {
        attributeFormDefault = "unqualified";
        elementFormDefault = "qualified";
        xs = "http://www.w3.org/2001/XMLSchema";
        if (!ns.empty())
        {
            setNameSpace(ns);
        };
    }
};

//
// LLXSDWriter
//
LLXSDWriter::LLXSDWriter()
: Parser(sXSDReadFuncs, sXSDWriteFuncs, sXSDInspectFuncs)
{
    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));
}

LLXSDWriter::~LLXSDWriter() {}

void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
{
    Schema schema(xml_namespace);

    schema.root_element.name = type_name;
    Choice& choice = schema.root_element.complexType.choice;

    choice.minOccurs = 0;
    choice.maxOccurs = "unbounded";

    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 choices for valid children
    if (widget_registryp)
    {
        // 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);
        }

        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);
}

static  LLInitParam::Parser::parser_read_func_map_t sXUIReadFuncs;
static  LLInitParam::Parser::parser_write_func_map_t sXUIWriteFuncs;
static  LLInitParam::Parser::parser_inspect_func_map_t sXUIInspectFuncs;

//
// LLXUIParser
//
LLXUIParser::LLXUIParser()
:   Parser(sXUIReadFuncs, sXUIWriteFuncs, sXUIInspectFuncs),
    mCurReadDepth(0)
{
    if (sXUIReadFuncs.empty())
    {
        registerParserFuncs<LLInitParam::Flag>(readFlag, writeFlag);
        registerParserFuncs<bool>(readBoolValue, writeBoolValue);
        registerParserFuncs<std::string>(readStringValue, writeStringValue);
        registerParserFuncs<U8>(readU8Value, writeU8Value);
        registerParserFuncs<S8>(readS8Value, writeS8Value);
        registerParserFuncs<U16>(readU16Value, writeU16Value);
        registerParserFuncs<S16>(readS16Value, writeS16Value);
        registerParserFuncs<U32>(readU32Value, writeU32Value);
        registerParserFuncs<S32>(readS32Value, writeS32Value);
        registerParserFuncs<F32>(readF32Value, writeF32Value);
        registerParserFuncs<F64>(readF64Value, writeF64Value);
        registerParserFuncs<LLVector3>(readVector3Value, writeVector3Value);
        registerParserFuncs<LLColor4>(readColor4Value, writeColor4Value);
        registerParserFuncs<LLUIColor>(readUIColorValue, writeUIColorValue);
        registerParserFuncs<LLUUID>(readUUIDValue, writeUUIDValue);
        registerParserFuncs<LLSD>(readSDValue, writeSDValue);
    }
}

static LLTrace::BlockTimerStatHandle FTM_PARSE_XUI("XUI Parsing");
const LLXMLNodePtr DUMMY_NODE = new LLXMLNode();

void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, const std::string& filename, bool silent)
{
    LL_RECORD_BLOCK_TIME(FTM_PARSE_XUI);
    mNameStack.clear();
    mRootNodeName = node->getName()->mString;
    mCurFileName = filename;
    mCurReadDepth = 0;
    setParseSilently(silent);

    if (node.isNull())
    {
        parserWarning("Invalid node");
    }
    else
    {
        readXUIImpl(node, block);
    }
}

bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
{
    typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
    boost::char_separator<char> sep(".");

    bool values_parsed = false;
    bool silent = mCurReadDepth > 0;

    if (nodep->getFirstChild().isNull()
        && nodep->mAttributes.empty()
        && nodep->getSanitizedValue().empty())
    {
        // empty node, just parse as flag
        mCurReadNode = DUMMY_NODE;
        return block.submitValue(mNameStack, *this, silent);
    }

    // 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"), true));
        // child nodes are not necessarily valid parameters (could be a child widget)
        // so don't complain once we've recursed
        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, true));
            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 (mNameStack.empty())
            {
                if (*name_token_it != mRootNodeName)
                {
                    childp = childp->getNextSibling();
                    continue;
                }
            }
            else if(mNameStack.back().first != *name_token_it)
            {
                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, true));
                num_tokens_pushed++;
            }
        }

        // recurse and visit children XML nodes
        if(readXUIImpl(childp, 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;
    bool silent = mCurReadDepth > 0;

    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, true));
            num_tokens_pushed++;
        }

        // child nodes are not necessarily valid attributes, so don't complain once we've recursed
        any_parsed |= block.submitValue(mNameStack, *this, silent);

        while(num_tokens_pushed-- > 0)
        {
            mNameStack.pop_back();
        }
    }

    return any_parsed;
}

void LLXUIParser::writeXUIImpl(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::predicate_rule_t rules, const LLInitParam::BaseBlock* diff_block)
{
    mWriteRootNode = node;
    name_stack_t name_stack = Parser::name_stack_t();
    block.serializeBlock(*this, name_stack, rules, diff_block);
    mOutNodes.clear();
}

// go from a stack of names to a specific XML node
LLXMLNodePtr LLXUIParser::getNode(name_stack_t& stack)
{
    LLXMLNodePtr out_node = mWriteRootNode;

    name_stack_t::iterator next_it = stack.begin();
    for (name_stack_t::iterator it = stack.begin();
        it != stack.end();
        it = next_it)
    {
        ++next_it;
        bool force_new_node = false;

        if (it->first.empty())
        {
            it->second = false;
            continue;
        }

        if (next_it != stack.end() && next_it->first.empty() && next_it->second)
        {
            force_new_node = true;
        }


        out_nodes_t::iterator found_it = mOutNodes.find(it->first);

        // node with this name not yet written
        if (found_it == mOutNodes.end() || it->second || force_new_node)
        {
            // make an attribute if we are the last element on the name stack
            bool is_attribute = next_it == stack.end();
            LLXMLNodePtr new_node = new LLXMLNode(it->first.c_str(), is_attribute);
            out_node->addChild(new_node);
            mOutNodes[it->first] = new_node;
            out_node = new_node;
            it->second = false;
        }
        else
        {
            out_node = found_it->second;
        }
    }

    return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node);
}

bool LLXUIParser::readFlag(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    return self.mCurReadNode == DUMMY_NODE;
}

bool LLXUIParser::writeFlag(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    // just create node
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    return node.notNull();
}

bool LLXUIParser::readBoolValue(Parser& parser, void* val_ptr)
{
    bool value;
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    bool success = self.mCurReadNode->getBoolValue(1, &value);
    *((bool*)val_ptr) = value;
    return success;
}

bool LLXUIParser::writeBoolValue(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        node->setBoolValue(*((bool*)val_ptr));
        return true;
    }
    return false;
}

bool LLXUIParser::readStringValue(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    *((std::string*)val_ptr) = self.mCurReadNode->getSanitizedValue();
    return true;
}

bool LLXUIParser::writeStringValue(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.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(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    return self.mCurReadNode->getByteValue(1, (U8*)val_ptr);
}

bool LLXUIParser::writeU8Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        node->setUnsignedValue(*((U8*)val_ptr));
        return true;
    }
    return false;
}

bool LLXUIParser::readS8Value(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    S32 value;
    if(self.mCurReadNode->getIntValue(1, &value))
    {
        *((S8*)val_ptr) = value;
        return true;
    }
    return false;
}

bool LLXUIParser::writeS8Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        node->setIntValue(*((S8*)val_ptr));
        return true;
    }
    return false;
}

bool LLXUIParser::readU16Value(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    U32 value;
    if(self.mCurReadNode->getUnsignedValue(1, &value))
    {
        *((U16*)val_ptr) = value;
        return true;
    }
    return false;
}

bool LLXUIParser::writeU16Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        node->setUnsignedValue(*((U16*)val_ptr));
        return true;
    }
    return false;
}

bool LLXUIParser::readS16Value(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    S32 value;
    if(self.mCurReadNode->getIntValue(1, &value))
    {
        *((S16*)val_ptr) = value;
        return true;
    }
    return false;
}

bool LLXUIParser::writeS16Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        node->setIntValue(*((S16*)val_ptr));
        return true;
    }
    return false;
}

bool LLXUIParser::readU32Value(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    return self.mCurReadNode->getUnsignedValue(1, (U32*)val_ptr);
}

bool LLXUIParser::writeU32Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        node->setUnsignedValue(*((U32*)val_ptr));
        return true;
    }
    return false;
}

bool LLXUIParser::readS32Value(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    return self.mCurReadNode->getIntValue(1, (S32*)val_ptr);
}

bool LLXUIParser::writeS32Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        node->setIntValue(*((S32*)val_ptr));
        return true;
    }
    return false;
}

bool LLXUIParser::readF32Value(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    return self.mCurReadNode->getFloatValue(1, (F32*)val_ptr);
}

bool LLXUIParser::writeF32Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        node->setFloatValue(*((F32*)val_ptr));
        return true;
    }
    return false;
}

bool LLXUIParser::readF64Value(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    return self.mCurReadNode->getDoubleValue(1, (F64*)val_ptr);
}

bool LLXUIParser::writeF64Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        node->setDoubleValue(*((F64*)val_ptr));
        return true;
    }
    return false;
}

bool LLXUIParser::readVector3Value(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLVector3* vecp = (LLVector3*)val_ptr;
    if(self.mCurReadNode->getFloatValue(3, vecp->mV) >= 3)
    {
        return true;
    }

    return false;
}

bool LLXUIParser::writeVector3Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        LLVector3 vector = *((LLVector3*)val_ptr);
        node->setFloatValue(3, vector.mV);
        return true;
    }
    return false;
}

bool LLXUIParser::readColor4Value(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLColor4* colorp = (LLColor4*)val_ptr;
    if(self.mCurReadNode->getFloatValue(4, colorp->mV) >= 3)
    {
        return true;
    }

    return false;
}

bool LLXUIParser::writeColor4Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        LLColor4 color = *((LLColor4*)val_ptr);
        node->setFloatValue(4, color.mV);
        return true;
    }
    return false;
}

bool LLXUIParser::readUIColorValue(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLUIColor* param = (LLUIColor*)val_ptr;
    LLColor4 color;
    bool success =  self.mCurReadNode->getFloatValue(4, color.mV) >= 3;
    if (success)
    {
        param->set(color);
        return true;
    }
    return false;
}

bool LLXUIParser::writeUIColorValue(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.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(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLUUID temp_id;
    // LLUUID::set is destructive, so use temporary value
    if (temp_id.set(self.mCurReadNode->getSanitizedValue()))
    {
        *(LLUUID*)(val_ptr) = temp_id;
        return true;
    }
    return false;
}

bool LLXUIParser::writeUUIDValue(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    LLXMLNodePtr node = self.getNode(stack);
    if (node.notNull())
    {
        node->setStringValue(((LLUUID*)val_ptr)->asString());
        return true;
    }
    return false;
}

bool LLXUIParser::readSDValue(Parser& parser, void* val_ptr)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);
    *((LLSD*)val_ptr) = LLSD(self.mCurReadNode->getSanitizedValue());
    return true;
}

bool LLXUIParser::writeSDValue(Parser& parser, const void* val_ptr, name_stack_t& stack)
{
    LLXUIParser& self = static_cast<LLXUIParser&>(parser);

    LLXMLNodePtr node = self.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)
{
    std::string warning_msg = llformat("%s:\t%s(%d)", message.c_str(), mCurFileName.c_str(), mCurReadNode->getLineNumber());
    Parser::parserWarning(warning_msg);
}

void LLXUIParser::parserError(const std::string& message)
{
    std::string error_msg = llformat("%s:\t%s(%d)", message.c_str(), mCurFileName.c_str(), mCurReadNode->getLineNumber());
    Parser::parserError(error_msg);
}


//
// LLSimpleXUIParser
//

struct ScopedFile
{
    ScopedFile( const std::string& filename, const char* accessmode )
    {
        mFile = LLFile::fopen(filename, accessmode);
    }

    ~ScopedFile()
    {
        fclose(mFile);
        mFile = NULL;
    }

    S32 getRemainingBytes()
    {
        if (!isOpen()) return 0;

        S32 cur_pos = ftell(mFile);
        fseek(mFile, 0L, SEEK_END);
        S32 file_size = ftell(mFile);
        fseek(mFile, cur_pos, SEEK_SET);
        return file_size - cur_pos;
    }

    bool isOpen() { return mFile != NULL; }

    LLFILE* mFile;
};
LLSimpleXUIParser::LLSimpleXUIParser(LLSimpleXUIParser::element_start_callback_t element_cb)
:   Parser(sSimpleXUIReadFuncs, sSimpleXUIWriteFuncs, sSimpleXUIInspectFuncs),
    mCurReadDepth(0),
    mElementCB(element_cb)
{
    if (sSimpleXUIReadFuncs.empty())
    {
        registerParserFuncs<LLInitParam::Flag>(readFlag);
        registerParserFuncs<bool>(readBoolValue);
        registerParserFuncs<std::string>(readStringValue);
        registerParserFuncs<U8>(readU8Value);
        registerParserFuncs<S8>(readS8Value);
        registerParserFuncs<U16>(readU16Value);
        registerParserFuncs<S16>(readS16Value);
        registerParserFuncs<U32>(readU32Value);
        registerParserFuncs<S32>(readS32Value);
        registerParserFuncs<F32>(readF32Value);
        registerParserFuncs<F64>(readF64Value);
        registerParserFuncs<LLColor4>(readColor4Value);
        registerParserFuncs<LLUIColor>(readUIColorValue);
        registerParserFuncs<LLUUID>(readUUIDValue);
        registerParserFuncs<LLSD>(readSDValue);
    }
}

LLSimpleXUIParser::~LLSimpleXUIParser()
{
}


bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBlock& block, bool silent)
{
    LL_RECORD_BLOCK_TIME(FTM_PARSE_XUI);

    mParser = XML_ParserCreate(NULL);
    XML_SetUserData(mParser, this);
    XML_SetElementHandler(          mParser,    startElementHandler, endElementHandler);
    XML_SetCharacterDataHandler(    mParser,    characterDataHandler);

    mOutputStack.push_back(std::make_pair(&block, 0));
    mNameStack.clear();
    mCurFileName = filename;
    mCurReadDepth = 0;
    setParseSilently(silent);

    ScopedFile file(filename, "rb");
    if( !file.isOpen() )
    {
        LL_WARNS("ReadXUI") << "Unable to open file " << filename << LL_ENDL;
        XML_ParserFree( mParser );
        return false;
    }

    S32 bytes_read = 0;

    S32 buffer_size = file.getRemainingBytes();
    void* buffer = XML_GetBuffer(mParser, buffer_size);
    if( !buffer )
    {
        LL_WARNS("ReadXUI") << "Unable to allocate XML buffer while reading file " << filename << LL_ENDL;
        XML_ParserFree( mParser );
        return false;
    }

    bytes_read = (S32)fread(buffer, 1, buffer_size, file.mFile);
    if( bytes_read <= 0 )
    {
        LL_WARNS("ReadXUI") << "Error while reading file  " << filename << LL_ENDL;
        XML_ParserFree( mParser );
        return false;
    }

    mEmptyLeafNode.push_back(false);

    if( !XML_ParseBuffer(mParser, bytes_read, true ) )
    {
        LL_WARNS("ReadXUI") << "Error while parsing file  " << filename << LL_ENDL;
        XML_ParserFree( mParser );
        return false;
    }

    mEmptyLeafNode.pop_back();

    XML_ParserFree( mParser );
    return true;
}

void LLSimpleXUIParser::startElementHandler(void *userData, const char *name, const char **atts)
{
    LLSimpleXUIParser* self = reinterpret_cast<LLSimpleXUIParser*>(userData);
    self->startElement(name, atts);
}

void LLSimpleXUIParser::endElementHandler(void *userData, const char *name)
{
    LLSimpleXUIParser* self = reinterpret_cast<LLSimpleXUIParser*>(userData);
    self->endElement(name);
}

void LLSimpleXUIParser::characterDataHandler(void *userData, const char *s, int len)
{
    LLSimpleXUIParser* self = reinterpret_cast<LLSimpleXUIParser*>(userData);
    self->characterData(s, len);
}

void LLSimpleXUIParser::characterData(const char *s, int len)
{
    mTextContents += std::string(s, len);
}

void LLSimpleXUIParser::startElement(const char *name, const char **atts)
{
    processText();

    typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
    boost::char_separator<char> sep(".");

    if (mElementCB)
    {
        LLInitParam::BaseBlock* blockp = mElementCB(*this, name);
        if (blockp)
        {
            mOutputStack.push_back(std::make_pair(blockp, 0));
        }
    }

    mOutputStack.back().second++;
    S32 num_tokens_pushed = 0;
    std::string child_name(name);

    if (mOutputStack.back().second == 1)
    {   // root node for this block
        mScope.push_back(child_name);
    }
    else
    {   // compound attribute
        if (child_name.find(".") == std::string::npos)
        {
            mNameStack.push_back(std::make_pair(child_name, true));
            num_tokens_pushed++;
            mScope.push_back(child_name);
        }
        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())
            {
                return;
            }

            // check for proper nesting
            if(!mScope.empty() && *name_token_it != mScope.back())
            {
                return;
            }

            // 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, true));
                num_tokens_pushed++;
            }
            mScope.push_back(mNameStack.back().first);
        }
    }

    // parent node is not empty
    mEmptyLeafNode.back() = false;
    // we are empty if we have no attributes
    mEmptyLeafNode.push_back(atts[0] == NULL);

    mTokenSizeStack.push_back(num_tokens_pushed);
    readAttributes(atts);

}

void LLSimpleXUIParser::endElement(const char *name)
{
    bool has_text = processText();

    // no text, attributes, or children
    if (!has_text && mEmptyLeafNode.back())
    {
        // submit this as a valueless name (even though there might be text contents we haven't seen yet)
        mCurAttributeValueBegin = NO_VALUE_MARKER;
        mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently);
    }

    if (--mOutputStack.back().second == 0)
    {
        if (mOutputStack.empty())
        {
            LL_ERRS("ReadXUI") << "Parameter block output stack popped while empty." << LL_ENDL;
        }
        mOutputStack.pop_back();
    }

    S32 num_tokens_to_pop = mTokenSizeStack.back();
    mTokenSizeStack.pop_back();
    while(num_tokens_to_pop-- > 0)
    {
        mNameStack.pop_back();
    }
    mScope.pop_back();
    mEmptyLeafNode.pop_back();
}

bool LLSimpleXUIParser::readAttributes(const char **atts)
{
    typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
    boost::char_separator<char> sep(".");

    bool any_parsed = false;
    for(S32 i = 0; atts[i] && atts[i+1]; i += 2 )
    {
        std::string attribute_name(atts[i]);
        mCurAttributeValueBegin = atts[i+1];

        S32 num_tokens_pushed = 0;
        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, true));
            num_tokens_pushed++;
        }

        // child nodes are not necessarily valid attributes, so don't complain once we've recursed
        any_parsed |= mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently);

        while(num_tokens_pushed-- > 0)
        {
            mNameStack.pop_back();
        }
    }
    return any_parsed;
}

bool LLSimpleXUIParser::processText()
{
    if (!mTextContents.empty())
    {
        LLStringUtil::trim(mTextContents);
        if (!mTextContents.empty())
        {
            mNameStack.push_back(std::make_pair(std::string("value"), true));
            mCurAttributeValueBegin = mTextContents.c_str();
            mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently);
            mNameStack.pop_back();
        }
        mTextContents.clear();
        return true;
    }
    return false;
}

/*virtual*/ std::string LLSimpleXUIParser::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 LLSimpleXUIParser::parserWarning(const std::string& message)
{
    std::string warning_msg = llformat("%s:\t%s",  message.c_str(), mCurFileName.c_str());
    Parser::parserWarning(warning_msg);
}

void LLSimpleXUIParser::parserError(const std::string& message)
{
    std::string error_msg = llformat("%s:\t%s",  message.c_str(), mCurFileName.c_str());
    Parser::parserError(error_msg);
}

bool LLSimpleXUIParser::readFlag(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    return self.mCurAttributeValueBegin == NO_VALUE_MARKER;
}

bool LLSimpleXUIParser::readBoolValue(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    if (!strcmp(self.mCurAttributeValueBegin, "true"))
    {
        *((bool*)val_ptr) = true;
        return true;
    }
    else if (!strcmp(self.mCurAttributeValueBegin, "false"))
    {
        *((bool*)val_ptr) = false;
        return true;
    }

    return false;
}

bool LLSimpleXUIParser::readStringValue(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    *((std::string*)val_ptr) = self.mCurAttributeValueBegin;
    return true;
}

bool LLSimpleXUIParser::readU8Value(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U8*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readS8Value(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S8*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readU16Value(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U16*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readS16Value(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S16*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readU32Value(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U32*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readS32Value(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S32*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readF32Value(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    return parse(self.mCurAttributeValueBegin, real_p[assign_a(*(F32*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readF64Value(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    return parse(self.mCurAttributeValueBegin, real_p[assign_a(*(F64*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readColor4Value(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    LLColor4 value;

    if (parse(self.mCurAttributeValueBegin, real_p[assign_a(value.mV[0])] >> real_p[assign_a(value.mV[1])] >> real_p[assign_a(value.mV[2])] >> real_p[assign_a(value.mV[3])], space_p).full)
    {
        *(LLColor4*)(val_ptr) = value;
        return true;
    }
    return false;
}

bool LLSimpleXUIParser::readUIColorValue(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    LLColor4 value;
    LLUIColor* colorp = (LLUIColor*)val_ptr;

    if (parse(self.mCurAttributeValueBegin, real_p[assign_a(value.mV[0])] >> real_p[assign_a(value.mV[1])] >> real_p[assign_a(value.mV[2])] >> real_p[assign_a(value.mV[3])], space_p).full)
    {
        colorp->set(value);
        return true;
    }
    return false;
}

bool LLSimpleXUIParser::readUUIDValue(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    LLUUID temp_id;
    // LLUUID::set is destructive, so use temporary value
    if (temp_id.set(std::string(self.mCurAttributeValueBegin)))
    {
        *(LLUUID*)(val_ptr) = temp_id;
        return true;
    }
    return false;
}

bool LLSimpleXUIParser::readSDValue(Parser& parser, void* val_ptr)
{
    LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
    *((LLSD*)val_ptr) = LLSD(self.mCurAttributeValueBegin);
    return true;
}