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, ' ') << "\n"; + if (!mOs.good()) + { + THROW_ALERT("AIXMLElement_closing_child_Failed_to_write_DATA", + AIArgs("[DATA]", "\\n" + std::string(mIndentation, ' ') + "\\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 << "\n"; + if (!mOs.good()) + { + std::ostringstream ss; + ss << std::string(mIndentation, ' ') << '<' << name << '>' << element << "\\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]". +