diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt
index 535cfc010..be0a510b4 100644
--- a/indra/llxml/CMakeLists.txt
+++ b/indra/llxml/CMakeLists.txt
@@ -13,6 +13,7 @@ include_directories(
)
set(llxml_SOURCE_FILES
+ aixml.cpp
llcontrol.cpp
llxmlnode.cpp
llxmlparser.cpp
@@ -22,6 +23,7 @@ set(llxml_SOURCE_FILES
set(llxml_HEADER_FILES
CMakeLists.txt
+ aixml.h
llcontrol.h
llcontrolgroupreader.h
llxmlnode.h
diff --git a/indra/llxml/aixml.cpp b/indra/llxml/aixml.cpp
new file mode 100644
index 000000000..9f21cf56e
--- /dev/null
+++ b/indra/llxml/aixml.cpp
@@ -0,0 +1,609 @@
+/**
+ * @file aixml.cpp
+ * @brief XML serialization support.
+ *
+ * Copyright (c) 2013, Aleric Inglewood.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * 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.
+ *
+ * CHANGELOG
+ * and additional copyright holders.
+ *
+ * 30/07/2013
+ * Initial version, written by Aleric Inglewood @ SL
+ */
+
+#include "sys.h"
+#include "aixml.h"
+#include "llmd5.h"
+#include
+#include "aifile.h"
+
+//=============================================================================
+// Overview
+
+// The AIXML* classes provide an Object Oriented way to serialize objects
+// to and from an XML file.
+//
+// The following classes are provided:
+//
+// AIXMLRootElement - Write an object to a file (including XML declaration at the top).
+// AIXMLElement - Write an ojbect to an ostream (just one XML element).
+//
+// AIXMLParser - Read and deserialize an XML file written with AIXMLRootElement.
+// AIXMLElementParser - Read and deserialize an XML stream written with AIXMLElement.
+//
+// Classes that need to be written to and from XML would typically
+// supply two member functions. For example,
+
+#ifdef EXAMPLE_CODE // undefined
+
+class HelloWord {
+ public:
+ // Write object to XML.
+ void toXML(std::ostream& os, int indentation) const;
+ // Read object from XML.
+ HelloWord(AIXMLElementParser const& parser);
+
+ private:
+ // Example member variables...
+ Attribute1 mAttribute1;
+ Attribute2 mAttribute2;
+ // etc.
+ Custom1 mCustom;
+ std::vector mContainer;
+ LLDate mDate;
+ LLMD5 mMd5;
+ LLUUID mUUID;
+};
+
+// Typical serialization member function.
+void HelloWord::toXML(std::ostream& os, int indentation) const
+{
+ AIXMLElement tag(os, "helloworld", indentation);
+
+ // Zero or more attributes:
+ tag.attribute("attributename1", mAttribute1); // Uses operator<<(std::ostream&, Attribute1 const&) to write mAttribute1.
+ tag.attribute("attributename2", mAttribute2); // Uses operator<<(std::ostream&, Attribute2 const&) to write mAttribute2.
+ // etc.
+
+ // Zero or more child elements:
+ tag.child("tagname", mChild1);
+ tag.child(mCustom); // Calls mCustom.toXML() to insert the object.
+ tag.child(mContainer.begin(), mContainer.end()); // Calls tag.child(element) for each element of the container.
+ // Special allowed cases:
+ tag.child(mDate); // Uses "date" as tag name.
+ tag.child(mMd5); // Uses "md5" as tag name.
+ tag.child(mUUID); // Uses "uuid" as tag name.
+}
+
+// Typical deserialization member function.
+HelloWord::HelloWorld(AIXMLElementParser const& parser)
+{
+ // Zero or more attributes:
+ parser.attribute("attributename1", "foobar"); // Throws std::runtime_error is attributename1 is missing or does not have the value "foobar".
+ if (!parser.attribute("attributename2", mAttribute2)) // Reads value of attributename2 into mAttribute2 (throws if it could not be parsed).
+ {
+ throw std::runtime_error("..."); // Attribute was missing.
+ }
+
+ // Zero or more child elements:
+ parser.child("tagname", mChild1);
+ parser.child("custom1", mCustom);
+ parser.insert_children("custom2", mContainer);
+ // Special allowed cases:
+ parser.child(mDate);
+ parser.child(mMd5);
+ parser.child(mUUID);
+}
+
+// To actually write to an XML file one would do, for example:
+
+ LLFILE* fp = fopen(...);
+ AIXMLRootElement tag(fp, "rootname");
+ tag.attribute("version", "1.0");
+ tag.child(LLDate::now());
+ tag.child(mHelloWorld);
+
+// And to read it again,
+
+ AIXMLParser helloworld(filename, "description of file used for error reporting", "rootname", 1);
+ helloworld.attribute("version", "1.0");
+ helloworld.child("helloworld", mHelloWorld);
+
+// Of course, both would need to be in a try { } catch block.
+
+#endif // EXAMPLE_CODE
+
+// Do NOT change these - it would break old databases.
+char const* const DEFAULT_LLUUID_NAME = "uuid";
+char const* const DEFAULT_MD5STR_NAME = "md5";
+char const* const DEFAULT_LLDATE_NAME = "date";
+
+std::string const DEFAULT_MD5STR_ATTRIBUTE_NAME = DEFAULT_MD5STR_NAME;
+std::string const DEFAULT_LLUUID_ATTRIBUTE_NAME = DEFAULT_LLUUID_NAME;
+std::string const DEFAULT_LLDATE_ATTRIBUTE_NAME = DEFAULT_LLDATE_NAME;
+std::string const DEFAULT_VERSION_ATTRIBUTE_NAME = "version";
+
+struct xdigit {
+ bool isxdigit;
+ xdigit(void) : isxdigit(true) { }
+ void operator()(char c) { isxdigit = isxdigit && std::isxdigit(c); }
+ operator bool() const { return isxdigit; }
+};
+
+static bool is_valid_md5str(std::string const& str)
+{
+ return str.length() == MD5HEX_STR_BYTES && std::for_each(str.begin(), str.end(), xdigit());
+}
+
+// Conversion routine that is a lot more strict then LLStringUtil::convertToU32.
+// This version does not allow leading or trailing spaces, nor does it allow a leading minus sign.
+// Leading zeroes are not allowed except a 0 by itself.
+bool convertToU32strict(std::string const& str, U32& value)
+{
+ bool first = true;
+ value = 0;
+ for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
+ {
+ if (value == 0 && !first || !std::isdigit(*i)) // Reject leading zeroes and non-digits.
+ return false;
+ value = value * 10 + *i - '0';
+ first = false;
+ }
+ return !first; // Reject empty string.
+}
+
+typedef boost::tokenizer > boost_tokenizer;
+
+bool decode_version(std::string const& version, U32& major, U32& minor)
+{
+ boost_tokenizer tokens(version, boost::char_separator("", "."));
+ boost_tokenizer::const_iterator itTok = tokens.begin();
+ return itTok != tokens.end() && convertToU32strict(*itTok++, major) &&
+ itTok != tokens.end() && *itTok++ == "." &&
+ itTok != tokens.end() && convertToU32strict(*itTok, minor);
+}
+
+bool md5strFromXML(LLXmlTreeNode* node, std::string& md5str_out)
+{
+ static LLStdStringHandle const DEFAULT_MD5STR_ATTRIBUTE_NAME_HANDLE = LLXmlTree::addAttributeString(DEFAULT_MD5STR_ATTRIBUTE_NAME);
+ return node->getFastAttributeString(DEFAULT_MD5STR_ATTRIBUTE_NAME_HANDLE, md5str_out) && is_valid_md5str(md5str_out);
+}
+
+bool md5strFromXML(LLXmlTreeNode* node, std::string& md5str_out, std::string const& attribute_name)
+{
+ return node->getAttributeString(attribute_name, md5str_out) && is_valid_md5str(md5str_out);
+}
+
+bool UUIDFromXML(LLXmlTreeNode* node, LLUUID& uuid_out)
+{
+ static LLStdStringHandle const DEFAULT_LLUUID_ATTRIBUTE_NAME_HANDLE = LLXmlTree::addAttributeString(DEFAULT_LLUUID_ATTRIBUTE_NAME);
+ return node->getFastAttributeUUID(DEFAULT_LLUUID_ATTRIBUTE_NAME_HANDLE, uuid_out);
+}
+
+bool UUIDFromXML(LLXmlTreeNode* node, LLUUID& uuid_out, std::string const& attribute_name)
+{
+ return node->getAttributeUUID(attribute_name, uuid_out);
+}
+
+bool dateFromXML(LLXmlTreeNode* node, LLDate& date_out)
+{
+ static LLStdStringHandle const DEFAULT_LLDATE_ATTRIBUTE_NAME_HANDLE = LLXmlTree::addAttributeString(DEFAULT_LLDATE_ATTRIBUTE_NAME);
+ std::string date_s;
+ return node->getFastAttributeString(DEFAULT_LLDATE_ATTRIBUTE_NAME_HANDLE, date_s) && date_out.fromString(date_s);
+}
+
+bool dateFromXML(LLXmlTreeNode* node, LLDate& date_out, std::string const& attribute_name)
+{
+ std::string date_s;
+ return node->getAttributeString(attribute_name, date_s) && date_out.fromString(date_s);
+}
+
+bool versionFromXML(LLXmlTreeNode* node, U32& major_out, U32& minor_out)
+{
+ static LLStdStringHandle const DEFAULT_VERSION_ATTRIBUTE_NAME_HANDLE = LLXmlTree::addAttributeString(DEFAULT_VERSION_ATTRIBUTE_NAME);
+ major_out = minor_out = 0;
+ std::string version_s;
+ return node->getFastAttributeString(DEFAULT_VERSION_ATTRIBUTE_NAME_HANDLE, version_s) && decode_version(version_s, major_out, minor_out);
+}
+
+bool versionFromXML(LLXmlTreeNode* node, U32& major_out, U32& minor_out, std::string const& attribute_name)
+{
+ major_out = minor_out = 0;
+ std::string version_s;
+ return node->getAttributeString(attribute_name, version_s) && decode_version(version_s, major_out, minor_out);
+}
+
+//-----------------------------------------------------------------------------
+// AIXMLElement
+
+AIXMLElement::AIXMLElement(std::ostream& os, char const* name, int indentation) :
+ mOs(os), mName(name), mIndentation(indentation), mHasChildren(false)
+{
+ mOs << std::string(mIndentation, ' ') << '<' << mName;
+ if (!mOs.good())
+ {
+ THROW_ALERT("AIXMLElement_Failed_to_write_DATA", AIArgs("[DATA]", "<" + mName));
+ }
+}
+
+int AIXMLElement::open_child(void)
+{
+ if (!mHasChildren)
+ {
+ mOs << ">\n";
+ if (!mOs.good())
+ {
+ THROW_ALERT("AIXMLElement_closing_child_Failed_to_write_DATA", AIArgs("[DATA]", ">\\n"));
+ }
+ mHasChildren = true;
+ }
+ mIndentation += 2;
+ return mIndentation;
+}
+
+void AIXMLElement::close_child(void)
+{
+ mIndentation -= 2;
+}
+
+AIXMLElement::~AIXMLElement()
+{
+ if (mHasChildren)
+ {
+ mOs << std::string(mIndentation, ' ') << "" << mName << ">\n";
+ if (!mOs.good())
+ {
+ THROW_ALERT("AIXMLElement_closing_child_Failed_to_write_DATA",
+ AIArgs("[DATA]", "\\n" + std::string(mIndentation, ' ') + "" + mName + ">\\n"));
+ }
+ }
+ else
+ {
+ mOs << " />\n";
+ if (!mOs.good())
+ {
+ THROW_ALERT("AIXMLElement_closing_child_Failed_to_write_DATA", AIArgs("[DATA]", " />\\n"));
+ }
+ }
+}
+
+template<>
+void AIXMLElement::child(LLUUID const& element)
+{
+ open_child();
+ write_child(DEFAULT_LLUUID_NAME, element);
+ close_child();
+}
+
+template<>
+void AIXMLElement::child(LLMD5 const& element)
+{
+ open_child();
+ write_child(DEFAULT_MD5STR_NAME, element);
+ close_child();
+}
+
+template<>
+void AIXMLElement::child(LLDate const& element)
+{
+ open_child();
+ write_child(DEFAULT_LLDATE_NAME, element);
+ close_child();
+}
+
+//-----------------------------------------------------------------------------
+// AIXMLStream
+
+AIXMLStream::AIXMLStream(LLFILE* fp, bool standalone) : mOfs(fp)
+{
+ char const* sp = standalone ? " standalone=\"yes\"" : "";
+ int rc = fprintf(fp, "\n", sp);
+ if (rc < 0 || ferror(fp))
+ {
+ // I don't think that errno is set to anything else but EBADF here,
+ // so there is not really any informative message to add here.
+ THROW_MALERT("AIXMLStream_fprintf_failed_to_write_xml_header");
+ }
+}
+
+AIXMLStream::~AIXMLStream()
+{
+ if (mOfs.is_open())
+ {
+ mOfs.close();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// AIXMLParser
+
+AIXMLParser::AIXMLParser(std::string const& filename, char const* file_desc, std::string const& name, U32 major_version) :
+ AIXMLElementParser(mFilename, mFileDesc, major_version),
+ mFilename(filename), mFileDesc(file_desc)
+{
+ char const* error = NULL;
+ AIArgs args;
+ if (!mXmlTree.parseFile(filename, TRUE))
+ {
+ AIFile dummy(filename, "rb"); // Check if the file can be opened at all (throws with a more descriptive error if not).
+ error = "AIXMLParser_Cannot_parse_FILEDESC_FILENAME";
+ }
+ else
+ {
+ mNode = mXmlTree.getRoot();
+ if (!mNode)
+ {
+ error = "AIXMLParser_No_root_node_found_in_FILEDESC_FILENAME";
+ }
+ else if (!mNode->hasName(name))
+ {
+ error = "AIXMLParser_Missing_header_NAME_invalid_FILEDESC_FILENAME";
+ args("[NAME]", name);
+ }
+ else if (!versionFromXML(mNode, mVersionMajor, mVersionMinor))
+ {
+ error = "AIXMLParser_Invalid_or_missing_NAME_version_attribute_in_FILEDESC_FILENAME";
+ args("[NAME]", name);
+ }
+ else if (mVersionMajor != major_version)
+ {
+ error = "AIXMLParser_Incompatible_NAME_version_MAJOR_MINOR_in";
+ args("[NAME]", name)("[MAJOR]", llformat("%u", mVersionMajor))("[MINOR]", llformat("%u", mVersionMinor));
+ }
+ }
+ if (error)
+ {
+ THROW_MALERT(error, args("[FILEDESC]", mFileDesc)("[FILENAME]", mFilename));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// AIXMLElementParser
+
+template<>
+LLMD5 AIXMLElementParser::read_string(std::string const& value) const
+{
+ if (!is_valid_md5str(value))
+ {
+ THROW_MALERT("AIXMLElementParser_read_string_Invalid_MD5_VALUE_in_FILEDESC_FILENAME",
+ AIArgs("[VALUE]", value)("[FILEDESC]", mFileDesc)("[FILENAME]", mFilename));
+ }
+ unsigned char digest[16];
+ std::memset(digest, 0, sizeof(digest));
+ for (int i = 0; i < 32; ++i)
+ {
+ int x = value[i];
+ digest[i >> 1] += (x - (x & 0xf0) + (x >> 6) * 9) << ((~i & 1) << 2);
+ }
+ LLMD5 result;
+ result.clone(digest);
+ return result;
+}
+
+template<>
+LLDate AIXMLElementParser::read_string(std::string const& value) const
+{
+ LLDate result;
+ result.fromString(value);
+ return result;
+}
+
+template
+T AIXMLElementParser::read_integer(char const* type, std::string const& value) const
+{
+ long long int result;
+ sscanf(value.c_str(),"%lld", &result);
+ if (result < std::numeric_limits::min() || result > std::numeric_limits::max())
+ {
+ THROW_MALERT("AIXMLElementParser_read_integer_Invalid_TYPE_VALUE_in_FILEDESC_FILENAME",
+ AIArgs("[TYPE]", type)("[VALUE]", value)("FILEDESC", mFileDesc)("[FILENAME]", mFilename));
+ }
+ return result;
+}
+
+template<>
+U8 AIXMLElementParser::read_string(std::string const& value) const
+{
+ return read_integer("U8", value);
+}
+
+template<>
+S8 AIXMLElementParser::read_string(std::string const& value) const
+{
+ return read_integer("S8", value);
+}
+
+template<>
+U16 AIXMLElementParser::read_string(std::string const& value) const
+{
+ return read_integer("U16", value);
+}
+
+template<>
+S16 AIXMLElementParser::read_string(std::string const& value) const
+{
+ return read_integer("S16", value);
+}
+
+template<>
+U32 AIXMLElementParser::read_string(std::string const& value) const
+{
+ return read_integer("U32", value);
+}
+
+template<>
+S32 AIXMLElementParser::read_string(std::string const& value) const
+{
+ return read_integer("S32", value);
+}
+
+double read_float(std::string const& value)
+{
+ double result;
+ sscanf(value.c_str(),"%lf", &result);
+ return result;
+}
+
+template<>
+F32 AIXMLElementParser::read_string(std::string const& value) const
+{
+ return read_float(value);
+}
+
+template<>
+F64 AIXMLElementParser::read_string(std::string const& value) const
+{
+ return read_float(value);
+}
+
+template<>
+bool AIXMLElementParser::read_string(std::string const& value) const
+{
+ if (value == "true")
+ {
+ return true;
+ }
+ else if (value != "false")
+ {
+ THROW_MALERT("AIXMLElementParser_read_string_Invalid_boolean_VALUE_in_FILEDESC_FILENAME",
+ AIArgs("[VALUE]", value)("FILEDESC]", mFileDesc)("[FILENAME]", mFilename));
+ }
+ return false;
+}
+
+void AIXMLElementParser::attribute(char const* name, char const* required_value) const
+{
+ char const* error = NULL;
+ AIArgs args;
+ std::string value;
+ if (!mNode->getAttributeString(name, value))
+ {
+ error = "AIXMLElementParser_attribute_Missing_NAME_attribute_in_NODENAME_of_FILEDESC_FILENAME";
+ }
+ else if (value != required_value)
+ {
+ error = "AIXMLElementParser_attribute_Invalid_NAME_attribute_should_be_REQUIRED_in_NODENAME_of_FILEDESC_FILENAME";
+ args("[REQUIRED]", required_value);
+ }
+ if (error)
+ {
+ THROW_MALERT(error, args("[NAME]", name)("[NODENAME]", node_name())("[FILEDESC]", mFileDesc)("[FILENAME]", mFilename));
+ }
+}
+
+template<>
+LLUUID AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ LLUUID result;
+ if (!LLUUID::parseUUID(node->getContents(), &result))
+ {
+ THROW_MALERT("AIXMLElementParser_read_child_Invalid_uuid_in_FILEDESC_FILENAME",
+ AIArgs("[FILEDESC]", mFileDesc)("[FILENAME]", mFilename));
+ }
+ return result;
+}
+
+template<>
+LLMD5 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return read_string(node->getContents());
+}
+
+template<>
+LLDate AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ LLDate result;
+ if (!result.fromString(node->getContents()))
+ {
+ THROW_MALERT("AIXMLElementParser_read_child_Invalid_date_DATE_in_FILEDESC_FILENAME",
+ AIArgs("[DATE]", node->getContents())("[FILEDESC]", mFileDesc)("[FILENAME]", mFilename));
+ }
+ return result;
+}
+
+template<>
+S8 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return read_integer("S8", node->getContents());
+}
+
+template<>
+U8 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return read_integer("U8", node->getContents());
+}
+
+template<>
+S16 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return read_integer("S16", node->getContents());
+}
+
+template<>
+U16 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return read_integer("U16", node->getContents());
+}
+
+template<>
+S32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return read_integer("S32", node->getContents());
+}
+
+template<>
+U32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return read_integer("U32", node->getContents());
+}
+
+template<>
+F32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return read_string(node->getContents());
+}
+
+template<>
+F64 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return read_string(node->getContents());
+}
+
+template<>
+bool AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return read_string(node->getContents());
+}
+
+bool AIXMLElementParser::child(LLUUID& uuid) const
+{
+ return child(DEFAULT_LLUUID_NAME, uuid);
+}
+
+bool AIXMLElementParser::child(LLMD5& md5) const
+{
+ return child(DEFAULT_MD5STR_NAME, md5);
+}
+
+bool AIXMLElementParser::child(LLDate& date) const
+{
+ return child(DEFAULT_LLDATE_NAME, date);
+}
+
diff --git a/indra/llxml/aixml.h b/indra/llxml/aixml.h
new file mode 100644
index 000000000..1aeec74e1
--- /dev/null
+++ b/indra/llxml/aixml.h
@@ -0,0 +1,374 @@
+/**
+ * @file aixml.h
+ * @brief XML serialization support.
+ *
+ * Copyright (c) 2013, Aleric Inglewood.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * 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.
+ *
+ * CHANGELOG
+ * and additional copyright holders.
+ *
+ * 30/07/2013
+ * Initial version, written by Aleric Inglewood @ SL
+ */
+
+#ifndef AIXML_H
+#define AIXML_H
+
+#include "llxmltree.h"
+#include "llxmlnode.h"
+#include "llfile.h"
+#include
+#include "aialert.h"
+
+extern char const* const DEFAULT_LLUUID_NAME;
+extern char const* const DEFAULT_MD5STR_NAME;
+extern char const* const DEFAULT_LLDATE_NAME;
+
+class LLUUID;
+class LLMD5;
+class LLDate;
+
+bool md5strFromXML(LLXmlTreeNode* node, std::string& md5str_out);
+bool md5strFromXML(LLXmlTreeNode* node, std::string& md5str_out, std::string const& attribute_name);
+bool UUIDFromXML(LLXmlTreeNode* node, LLUUID& uuid_out);
+bool UUIDFromXML(LLXmlTreeNode* node, LLUUID& uuid_out, std::string const& attribute_name);
+bool dateFromXML(LLXmlTreeNode* node, LLDate& date_out);
+bool dateFromXML(LLXmlTreeNode* node, LLDate& date_out, std::string const& attribute_name);
+bool versionFromXML(LLXmlTreeNode* node, U32& major_out, U32& minor_out);
+bool versionFromXML(LLXmlTreeNode* node, U32& major_out, U32& minor_out, std::string const& attribute_name);
+
+class AIXMLElement
+{
+ private:
+ std::ostream& mOs;
+ std::string mName;
+ int mIndentation;
+ bool mHasChildren;
+
+ public:
+ AIXMLElement(std::ostream& os, char const* name, int indentation);
+ ~AIXMLElement();
+
+ template
+ void attribute(char const* name, T const& attribute);
+ template
+ void child(T const& element);
+ template
+ void child(char const* name, T const& element);
+ template
+ void child(FWD_ITERATOR i1, FWD_ITERATOR const& i2);
+
+ private:
+ template
+ void write_child(char const* name, T const& element);
+
+ int open_child(void);
+ void close_child(void);
+};
+
+template
+void AIXMLElement::attribute(char const* name, T const& attribute)
+{
+ std::ostringstream raw_attribute;
+ raw_attribute << attribute;
+ mOs << ' ' << name << "=\"" << LLXMLNode::escapeXML(raw_attribute.str()) << '"';
+ if (!mOs.good())
+ {
+ std::ostringstream ss;
+ ss << ' ' << name << "=\"" << LLXMLNode::escapeXML(raw_attribute.str()) << '"';
+ THROW_FALERT("AIXMLElement_attribute_Failed_to_write_DATA", AIArgs("[DATA]", ss.str()));
+ }
+}
+
+template
+void AIXMLElement::child(T const& element)
+{
+ open_child();
+ element.toXML(mOs, mIndentation);
+ if (!mOs.good()) // Normally toXML will already have thrown.
+ {
+ THROW_FALERT("AIXMLElement_child_Bad_ostream");
+ }
+ close_child();
+}
+
+template<>
+void AIXMLElement::child(LLUUID const& element);
+
+template<>
+void AIXMLElement::child(LLMD5 const& element);
+
+template<>
+void AIXMLElement::child(LLDate const& element);
+
+template
+void AIXMLElement::write_child(char const* name, T const& element)
+{
+ mOs << std::string(mIndentation, ' ') << '<' << name << '>' << element << "" << name << ">\n";
+ if (!mOs.good())
+ {
+ std::ostringstream ss;
+ ss << std::string(mIndentation, ' ') << '<' << name << '>' << element << "" << name << ">\\n";
+ THROW_FALERT("AIXMLElement_write_child_Failed_to_write_DATA", AIArgs("[DATA]", ss.str()));
+ }
+}
+
+template
+void AIXMLElement::child(char const* name, T const& element)
+{
+ open_child();
+ write_child(name, element);
+ close_child();
+}
+
+template
+void AIXMLElement::child(FWD_ITERATOR i1, FWD_ITERATOR const& i2)
+{
+ while (i1 != i2)
+ {
+ child(*i1++);
+ }
+}
+
+// Helper class for AIXMLRootElement.
+class AIXMLStream {
+ protected:
+ llofstream mOfs;
+ AIXMLStream(LLFILE* fp, bool standalone);
+ ~AIXMLStream();
+};
+
+// Class to write XML files.
+class AIXMLRootElement : public AIXMLStream, public AIXMLElement
+{
+ public:
+ AIXMLRootElement(LLFILE* fp, char const* name, bool standalone = true) : AIXMLStream(fp, standalone), AIXMLElement(mOfs, name, 0) { }
+};
+
+class AIXMLElementParser
+{
+ private:
+ U32 mVersion;
+ std::string const& mFilename;
+ std::string const& mFileDesc;
+
+ protected:
+ LLXmlTreeNode* mNode;
+
+ protected:
+ // Used by AIXMLParser, which initializes mNode directly.
+ AIXMLElementParser(std::string const& filename, std::string const& file_desc, U32 version) : mVersion(version), mFilename(filename), mFileDesc(file_desc) { }
+
+ // Used for error reporting.
+ virtual std::string node_name(void) const { return "node '" + mNode->getName() + "'"; }
+
+ // Parse the integer given as string 'value' and return it as type T (U8, S8, U16, S16, U32 or S32).
+ template
+ T read_integer(char const* type, std::string const& value) const;
+
+ // Parse the string 'value' and return it as type T.
+ template
+ T read_string(std::string const& value) const;
+
+ // Parse a child node and return it as type T.
+ template
+ T read_child(LLXmlTreeNode* node) const;
+
+ public:
+ // Constructor for child member functions.
+ AIXMLElementParser(std::string const& filename, std::string const& file_desc, U32 version, LLXmlTreeNode* node) : mVersion(version), mFilename(filename), mFileDesc(file_desc), mNode(node) { }
+
+ // Require the existence of some attribute with given value.
+ void attribute(char const* name, char const* required_value) const;
+
+ // Read attribute. Returns true if attribute was found.
+ template
+ bool attribute(char const* name, T& attribute) const;
+
+ // Read child element. Returns true if child was found.
+ template
+ bool child(char const* name, T& child) const;
+ // Read Linden types. Return true if the child was found.
+ bool child(LLUUID& uuid) const;
+ bool child(LLMD5& md5) const;
+ bool child(LLDate& date) const;
+
+ // Append all elements with name 'name' to container.
+ template
+ void push_back_children(char const* name, CONTAINER& container) const;
+
+ // Insert all elements with name 'name' into container.
+ template
+ void insert_children(char const* name, CONTAINER& container) const;
+
+ // Set version of this particular element (if not set mVersion will be the version of the parent, all the way up to the xml header with a version of 1).
+ void setVersion(U32 version) { mVersion = version; }
+
+ // Accessors.
+ std::string const& filename(void) const { return mFilename; }
+ std::string const& filedesc(void) const { return mFileDesc; }
+ U32 version(void) const { return mVersion; }
+};
+
+template
+inline T AIXMLElementParser::read_string(std::string const& value) const
+{
+ // Construct from string.
+ return T(value);
+}
+
+// Specializations.
+
+template<>
+LLMD5 AIXMLElementParser::read_string(std::string const& value) const;
+
+template<>
+LLDate AIXMLElementParser::read_string(std::string const& value) const;
+
+template<>
+U8 AIXMLElementParser::read_string(std::string const& value) const;
+
+template<>
+S8 AIXMLElementParser::read_string(std::string const& value) const;
+
+template<>
+U16 AIXMLElementParser::read_string(std::string const& value) const;
+
+template<>
+S16 AIXMLElementParser::read_string(std::string const& value) const;
+
+template<>
+U32 AIXMLElementParser::read_string(std::string const& value) const;
+
+template<>
+S32 AIXMLElementParser::read_string(std::string const& value) const;
+
+template<>
+F32 AIXMLElementParser::read_string(std::string const& value) const;
+
+template<>
+F64 AIXMLElementParser::read_string(std::string const& value) const;
+
+template<>
+bool AIXMLElementParser::read_string(std::string const& value) const;
+
+
+template
+bool AIXMLElementParser::attribute(char const* name, T& attribute) const
+{
+ std::string value;
+ if (!mNode->getAttributeString(name, value))
+ {
+ return false;
+ }
+ attribute = read_string(value);
+ return true;
+}
+
+template
+inline T AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return AIXMLElementParser(mFilename, mFileDesc, mVersion, node);
+}
+
+// Specializations.
+
+template<>
+inline std::string AIXMLElementParser::read_child(LLXmlTreeNode* node) const
+{
+ return node->getContents();
+}
+
+template<>
+LLMD5 AIXMLElementParser::read_child(LLXmlTreeNode* node) const;
+
+template<>
+LLUUID AIXMLElementParser::read_child(LLXmlTreeNode* node) const;
+
+template<>
+LLDate AIXMLElementParser::read_child(LLXmlTreeNode* node) const;
+
+template<>
+S32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const;
+
+template<>
+F32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const;
+
+template<>
+bool AIXMLElementParser::read_child(LLXmlTreeNode* node) const;
+
+
+template
+bool AIXMLElementParser::child(char const* name, T& child) const
+{
+ LLXmlTreeNode* node = mNode->getChildByName(name);
+ if (!node)
+ {
+ return false;
+ }
+ child = read_child(node);
+ return true;
+}
+
+template
+void AIXMLElementParser::insert_children(char const* name, CONTAINER& container) const
+{
+ for (LLXmlTreeNode* node = mNode->getFirstChild(); node; node = mNode->getNextChild())
+ {
+ if (!node->hasName(name))
+ continue;
+ container.insert(read_child(node));
+ }
+}
+
+template
+void AIXMLElementParser::push_back_children(char const* name, CONTAINER& container) const
+{
+ for (LLXmlTreeNode* node = mNode->getFirstChild(); node; node = mNode->getNextChild())
+ {
+ if (!node->hasName(name))
+ continue;
+ container.push_back(read_child(node));
+ }
+}
+
+// Class to read XML files.
+class AIXMLParser : public AIXMLElementParser
+{
+ private:
+ std::string mFilename;
+ std::string mFileDesc;
+
+ LLXmlTree mXmlTree;
+ U32 mVersionMajor;
+ U32 mVersionMinor;
+
+ public:
+ AIXMLParser(std::string const& filename, char const* file_desc, std::string const& name, U32 major_version);
+
+ U32 version_major(void) const { return mVersionMajor; }
+ U32 version_minor(void) const { return mVersionMinor; }
+
+ protected:
+ /*virtual*/ std::string node_name(void) const { return "root node"; }
+};
+
+#endif // AIXML_H
+
diff --git a/indra/newview/skins/default/xui/en-us/strings.xml b/indra/newview/skins/default/xui/en-us/strings.xml
index 980bbcaa1..8ed04b942 100644
--- a/indra/newview/skins/default/xui/en-us/strings.xml
+++ b/indra/newview/skins/default/xui/en-us/strings.xml
@@ -4375,4 +4375,29 @@ Try enclosing path to the editor with double quotes.
Failed to remove file [FILENAME]: [ERROR]
Failed to rename file [FILE] to [NEWFILE]: [ERROR]
+
+ Failed to write '[DATA]', writing attribute data.
+ ostream not good after calling child.toXML
+ Failed to write '[DATA]', writing child data.
+ Failed to write '[DATA]', opening new child.
+ Failed to write '[DATA]', closing previous child.
+
+
+ Failed to write XML header.
+
+
+ Cannot parse [FILEDESC] "[FILENAME]".
+ No root node found in [FILEDESC] "[FILENAME]".
+ Missing header '[NAME]' ; invalid [FILEDESC] "[FILENAME]".
+ Invalid or missing [NAME] 'version' attribute in [FILEDESC] "[FILENAME]".
+ Incompatible '[NAME]' version, [MAJOR].[MINOR], in [FILEDESC] "[FILENAME]".
+
+
+ Invalid MD5 ([VALUE]) in [FILEDESC] "[FILENAME]".
+ Invalid boolean ([VALUE]) in [FILEDESC] "[FILENAME]".
+ Missing '[NAME]' attribute in [NODENAME] of [FILEDESC] "[FILENAME]"."
+ Invalid '[NAME]' attribute (should be '[REQUIRED]') in [NODENAME] of [FILEDESC] "[FILENAME]".
+ Invalid UUID in [FILEDESC] "[FILENAME]".
+ Invalid DATE ([DATE]) in [FILEDESC] "[FILENAME]".
+