Add AIXMLRootElement, AIXMLElement, AIXMLParser and AIXMLElementParser
The AIXML* classes provide an Object Oriented way to serialize objects to and from an XML file. Documentation is added at the top of aixml.cpp These classes will be used by AIMultiGrid.
This commit is contained in:
@@ -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
|
||||
|
||||
609
indra/llxml/aixml.cpp
Normal file
609
indra/llxml/aixml.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <boost/tokenizer.hpp>
|
||||
#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<Custom2> 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::char_separator<char> > boost_tokenizer;
|
||||
|
||||
bool decode_version(std::string const& version, U32& major, U32& minor)
|
||||
{
|
||||
boost_tokenizer tokens(version, boost::char_separator<char>("", "."));
|
||||
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, "<?xml version=\"1.0\" encoding=\"utf-8\"%s ?>\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<typename T>
|
||||
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<T>::min() || result > std::numeric_limits<T>::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>("U8", value);
|
||||
}
|
||||
|
||||
template<>
|
||||
S8 AIXMLElementParser::read_string(std::string const& value) const
|
||||
{
|
||||
return read_integer<S8>("S8", value);
|
||||
}
|
||||
|
||||
template<>
|
||||
U16 AIXMLElementParser::read_string(std::string const& value) const
|
||||
{
|
||||
return read_integer<U16>("U16", value);
|
||||
}
|
||||
|
||||
template<>
|
||||
S16 AIXMLElementParser::read_string(std::string const& value) const
|
||||
{
|
||||
return read_integer<S16>("S16", value);
|
||||
}
|
||||
|
||||
template<>
|
||||
U32 AIXMLElementParser::read_string(std::string const& value) const
|
||||
{
|
||||
return read_integer<U32>("U32", value);
|
||||
}
|
||||
|
||||
template<>
|
||||
S32 AIXMLElementParser::read_string(std::string const& value) const
|
||||
{
|
||||
return read_integer<S32>("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<LLMD5>(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>("S8", node->getContents());
|
||||
}
|
||||
|
||||
template<>
|
||||
U8 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
|
||||
{
|
||||
return read_integer<U8>("U8", node->getContents());
|
||||
}
|
||||
|
||||
template<>
|
||||
S16 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
|
||||
{
|
||||
return read_integer<S16>("S16", node->getContents());
|
||||
}
|
||||
|
||||
template<>
|
||||
U16 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
|
||||
{
|
||||
return read_integer<U16>("U16", node->getContents());
|
||||
}
|
||||
|
||||
template<>
|
||||
S32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
|
||||
{
|
||||
return read_integer<S32>("S32", node->getContents());
|
||||
}
|
||||
|
||||
template<>
|
||||
U32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
|
||||
{
|
||||
return read_integer<U32>("U32", node->getContents());
|
||||
}
|
||||
|
||||
template<>
|
||||
F32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
|
||||
{
|
||||
return read_string<F32>(node->getContents());
|
||||
}
|
||||
|
||||
template<>
|
||||
F64 AIXMLElementParser::read_child(LLXmlTreeNode* node) const
|
||||
{
|
||||
return read_string<F64>(node->getContents());
|
||||
}
|
||||
|
||||
template<>
|
||||
bool AIXMLElementParser::read_child(LLXmlTreeNode* node) const
|
||||
{
|
||||
return read_string<bool>(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);
|
||||
}
|
||||
|
||||
374
indra/llxml/aixml.h
Normal file
374
indra/llxml/aixml.h
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <sstream>
|
||||
#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<typename T>
|
||||
void attribute(char const* name, T const& attribute);
|
||||
template<typename T>
|
||||
void child(T const& element);
|
||||
template<typename T>
|
||||
void child(char const* name, T const& element);
|
||||
template<typename FWD_ITERATOR>
|
||||
void child(FWD_ITERATOR i1, FWD_ITERATOR const& i2);
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
void write_child(char const* name, T const& element);
|
||||
|
||||
int open_child(void);
|
||||
void close_child(void);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
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<typename T>
|
||||
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<typename T>
|
||||
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<typename T>
|
||||
void AIXMLElement::child(char const* name, T const& element)
|
||||
{
|
||||
open_child();
|
||||
write_child(name, element);
|
||||
close_child();
|
||||
}
|
||||
|
||||
template<typename FWD_ITERATOR>
|
||||
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<typename T>
|
||||
T read_integer(char const* type, std::string const& value) const;
|
||||
|
||||
// Parse the string 'value' and return it as type T.
|
||||
template<typename T>
|
||||
T read_string(std::string const& value) const;
|
||||
|
||||
// Parse a child node and return it as type T.
|
||||
template<typename T>
|
||||
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<typename T>
|
||||
bool attribute(char const* name, T& attribute) const;
|
||||
|
||||
// Read child element. Returns true if child was found.
|
||||
template<typename T>
|
||||
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<typename CONTAINER>
|
||||
void push_back_children(char const* name, CONTAINER& container) const;
|
||||
|
||||
// Insert all elements with name 'name' into container.
|
||||
template<typename CONTAINER>
|
||||
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<typename T>
|
||||
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<typename T>
|
||||
bool AIXMLElementParser::attribute(char const* name, T& attribute) const
|
||||
{
|
||||
std::string value;
|
||||
if (!mNode->getAttributeString(name, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
attribute = read_string<T>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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<typename T>
|
||||
bool AIXMLElementParser::child(char const* name, T& child) const
|
||||
{
|
||||
LLXmlTreeNode* node = mNode->getChildByName(name);
|
||||
if (!node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
child = read_child<T>(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename CONTAINER>
|
||||
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<typename CONTAINER::value_type>(node));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename CONTAINER>
|
||||
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<typename CONTAINER::value_type>(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
|
||||
|
||||
@@ -4375,4 +4375,29 @@ Try enclosing path to the editor with double quotes.
|
||||
<string name="AIFile_remove_Failed_to_remove_FILENAME">Failed to remove file [FILENAME]: [ERROR]</string>
|
||||
<string name="AIFile_rename_Failed_to_rename_FILE_to_NEWFILE">Failed to rename file [FILE] to [NEWFILE]: [ERROR]</string>
|
||||
|
||||
<!-- AIXMLElement exception alerts -->
|
||||
<string name="AIXMLElement_attribute_Failed_to_write_DATA">Failed to write '[DATA]', writing attribute data.</string>
|
||||
<string name="AIXMLElement_child_Bad_ostream">ostream not good after calling child.toXML</string>
|
||||
<string name="AIXMLElement_write_child_Failed_to_write_DATA">Failed to write '[DATA]', writing child data.</string>
|
||||
<string name="AIXMLElement_Failed_to_write_DATA">Failed to write '[DATA]', opening new child.</string>
|
||||
<string name="AIXMLElement_closing_child_Failed_to_write_DATA">Failed to write '[DATA]', closing previous child.</string>
|
||||
|
||||
<!-- AIXMLStream exception alerts -->
|
||||
<string name="AIXMLStream_fprintf_failed_to_write_xml_header">Failed to write XML header.</string>
|
||||
|
||||
<!-- AIXMLParser exception alerts -->
|
||||
<string name="AIXMLParser_Cannot_parse_FILEDESC_FILENAME">Cannot parse [FILEDESC] "[FILENAME]".</string>
|
||||
<string name="AIXMLParser_No_root_node_found_in_FILEDESC_FILENAME">No root node found in [FILEDESC] "[FILENAME]".</string>
|
||||
<string name="AIXMLParser_Missing_header_NAME_invalid_FILEDESC_FILENAME">Missing header '[NAME]' ; invalid [FILEDESC] "[FILENAME]".</string>
|
||||
<string name="AIXMLParser_Invalid_or_missing_NAME_version_attribute_in_FILEDESC_FILENAME">Invalid or missing [NAME] 'version' attribute in [FILEDESC] "[FILENAME]".</string>
|
||||
<string name="AIXMLParser_Incompatible_NAME_version_MAJOR_MINOR_in">Incompatible '[NAME]' version, [MAJOR].[MINOR], in [FILEDESC] "[FILENAME]".</string>
|
||||
|
||||
<!-- AIXMLElementParser exception alerts -->
|
||||
<string name="AIXMLElementParser_read_string_Invalid_MD5_VALUE_in_FILEDESC_FILENAME">Invalid MD5 ([VALUE]) in [FILEDESC] "[FILENAME]".</string>
|
||||
<string name="AIXMLElementParser_read_string_Invalid_boolean_VALUE_in_FILEDESC_FILENAME">Invalid boolean ([VALUE]) in [FILEDESC] "[FILENAME]".</string>
|
||||
<string name="AIXMLElementParser_attribute_Missing_NAME_attribute_in_NODENAME_of_FILEDESC_FILENAME">Missing '[NAME]' attribute in [NODENAME] of [FILEDESC] "[FILENAME]".</string>"
|
||||
<string name="AIXMLElementParser_attribute_Invalid_NAME_attribute_should_be_REQUIRED_in_NODENAME_of_FILEDESC_FILENAME">Invalid '[NAME]' attribute (should be '[REQUIRED]') in [NODENAME] of [FILEDESC] "[FILENAME]".</string>
|
||||
<string name="AIXMLElementParser_read_child_Invalid_uuid_in_FILEDESC_FILENAME">Invalid UUID in [FILEDESC] "[FILENAME]".</string>
|
||||
<string name="AIXMLElementParser_read_child_Invalid_date_DATE_in_FILEDESC_FILENAME">Invalid DATE ([DATE]) in [FILEDESC] "[FILENAME]".</string>
|
||||
|
||||
</strings>
|
||||
|
||||
Reference in New Issue
Block a user