diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llcommon/llsdserialize_xml.cpp |
Print done when done.
Diffstat (limited to 'indra/llcommon/llsdserialize_xml.cpp')
-rw-r--r-- | indra/llcommon/llsdserialize_xml.cpp | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp new file mode 100644 index 0000000000..2824d0f73c --- /dev/null +++ b/indra/llcommon/llsdserialize_xml.cpp @@ -0,0 +1,706 @@ +/** + * @file llsdserialize_xml.cpp + * @brief XML parsers and formatters for LLSD + * + * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" +#include "llsdserialize_xml.h" + +#include <iostream> +#include <deque> + +#include "apr-1/apr_base64.h" + +extern "C" +{ +#include "expat/expat.h" +} + +/** + * LLSDXMLFormatter + */ +LLSDXMLFormatter::LLSDXMLFormatter() +{ +} + +// virtual +LLSDXMLFormatter::~LLSDXMLFormatter() +{ +} + +// virtual +S32 LLSDXMLFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const +{ + std::streamsize old_precision = ostr.precision(25); + + LLString post = ""; + if (options & LLSDFormatter::OPTIONS_PRETTY) + { + post = "\n"; + } + ostr << "<llsd>" << post; + S32 rv = format_impl(data, ostr, options, 1); + ostr << "</llsd>\n"; + + ostr.precision(old_precision); + return rv; +} + +S32 LLSDXMLFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 options, U32 level) const +{ + S32 format_count = 1; + LLString pre = ""; + LLString post = ""; + + if (options & LLSDFormatter::OPTIONS_PRETTY) + { + for (U32 i = 0; i < level; i++) + { + pre += " "; + } + post = "\n"; + } + + switch(data.type()) + { + case LLSD::TypeMap: + if(0 == data.size()) + { + ostr << pre << "<map />" << post; + } + else + { + ostr << pre << "<map>" << post; + LLSD::map_const_iterator iter = data.beginMap(); + LLSD::map_const_iterator end = data.endMap(); + for(; iter != end; ++iter) + { + ostr << pre << "<key>" << escapeString((*iter).first) << "</key>" << post; + format_count += format_impl((*iter).second, ostr, options, level + 1); + } + ostr << pre << "</map>" << post; + } + break; + + case LLSD::TypeArray: + if(0 == data.size()) + { + ostr << pre << "<array />" << post; + } + else + { + ostr << pre << "<array>" << post; + LLSD::array_const_iterator iter = data.beginArray(); + LLSD::array_const_iterator end = data.endArray(); + for(; iter != end; ++iter) + { + format_count += format_impl(*iter, ostr, options, level + 1); + } + ostr << pre << "</array>" << post; + } + break; + + case LLSD::TypeUndefined: + ostr << pre << "<undef />" << post; + break; + + case LLSD::TypeBoolean: + ostr << pre << "<boolean>"; + if(mBoolAlpha || +#if( LL_WINDOWS || __GNUC__ > 2) + (ostr.flags() & std::ios::boolalpha) +#else + (ostr.flags() & 0x0100) +#endif + ) + { + ostr << (data.asBoolean() ? "true" : "false"); + } + else + { + ostr << (data.asBoolean() ? 1 : 0); + } + ostr << "</boolean>" << post; + break; + + case LLSD::TypeInteger: + ostr << pre << "<integer>" << data.asInteger() << "</integer>" << post; + break; + + case LLSD::TypeReal: + ostr << pre << "<real>"; + if(mRealFormat.empty()) + { + ostr << data.asReal(); + } + else + { + formatReal(data.asReal(), ostr); + } + ostr << "</real>" << post; + break; + + case LLSD::TypeUUID: + if(data.asUUID().isNull()) ostr << pre << "<uuid />" << post; + else ostr << pre << "<uuid>" << data.asUUID() << "</uuid>" << post; + break; + + case LLSD::TypeString: + if(data.asString().empty()) ostr << pre << "<string />" << post; + else ostr << pre << "<string>" << escapeString(data.asString()) <<"</string>" << post; + break; + + case LLSD::TypeDate: + ostr << pre << "<date>" << data.asDate() << "</date>" << post; + break; + + case LLSD::TypeURI: + ostr << pre << "<uri>" << escapeString(data.asString()) << "</uri>" << post; + break; + + case LLSD::TypeBinary: + { + LLSD::Binary buffer = data.asBinary(); + if(buffer.empty()) + { + ostr << pre << "<binary />" << post; + } + else + { + // *FIX: memory inefficient. + ostr << pre << "<binary encoding=\"base64\">"; + int b64_buffer_length = apr_base64_encode_len(buffer.size()); + char* b64_buffer = new char[b64_buffer_length]; + b64_buffer_length = apr_base64_encode_binary( + b64_buffer, + &buffer[0], + buffer.size()); + ostr.write(b64_buffer, b64_buffer_length - 1); + delete[] b64_buffer; + ostr << "</binary>" << post; + } + break; + } + default: + // *NOTE: This should never happen. + ostr << pre << "<undef />" << post; + break; + } + return format_count; +} + +// static +std::string LLSDXMLFormatter::escapeString(const std::string& in) +{ + std::ostringstream out; + std::string::const_iterator it = in.begin(); + std::string::const_iterator end = in.end(); + for(; it != end; ++it) + { + switch((*it)) + { + case '<': + out << "<"; + break; + case '>': + out << ">"; + break; + case '&': + out << "&"; + break; + case '\'': + out << "'"; + break; + case '"': + out << """; + break; + default: + out << (*it); + break; + } + } + return out.str(); +} + + + +class LLSDXMLParser::Impl +{ +public: + Impl(); + ~Impl(); + + LLSD parse(std::istream& input); + + void parsePart(const char *buf, int len); + +private: + void reset(); + + void startElementHandler(const XML_Char* name, const XML_Char** attributes); + void endElementHandler(const XML_Char* name); + void characterDataHandler(const XML_Char* data, int length); + + static void sStartElementHandler( + void* userData, const XML_Char* name, const XML_Char** attributes); + static void sEndElementHandler( + void* userData, const XML_Char* name); + static void sCharacterDataHandler( + void* userData, const XML_Char* data, int length); + + void startSkipping(); + + enum Element { + ELEMENT_LLSD, + ELEMENT_UNDEF, + ELEMENT_BOOL, + ELEMENT_INTEGER, + ELEMENT_REAL, + ELEMENT_STRING, + ELEMENT_UUID, + ELEMENT_DATE, + ELEMENT_URI, + ELEMENT_BINARY, + ELEMENT_MAP, + ELEMENT_ARRAY, + ELEMENT_KEY, + ELEMENT_UNKNOWN + }; + static Element readElement(const XML_Char* name); + + static const XML_Char* findAttribute(const XML_Char* name, const XML_Char** pairs); + + + XML_Parser mParser; + + LLSD mResult; + + bool mInLLSDElement; + bool mGracefullStop; + + typedef std::deque<LLSD*> LLSDRefStack; + LLSDRefStack mStack; + + int mDepth; + bool mSkipping; + int mSkipThrough; + + std::string mCurrentKey; + std::ostringstream mCurrentContent; + + bool mPreStaged; +}; + + +LLSDXMLParser::Impl::Impl() +{ + mParser = XML_ParserCreate(NULL); + mPreStaged = false; + reset(); +} + +LLSDXMLParser::Impl::~Impl() +{ + XML_ParserFree(mParser); +} + +bool is_eol(char c) +{ + return (c == '\n' || c == '\r'); +} + +void clear_eol(std::istream& input) +{ + char c = input.peek(); + while (input.good() && is_eol(c)) + { + input.get(c); + c = input.peek(); + } +} + +static unsigned get_till_eol(std::istream& input, char *buf, unsigned bufsize) +{ + unsigned count = 0; + while (count < bufsize && input.good()) + { + input.get(buf[count]); + count++; + if (is_eol(buf[count - 1])) + break; + } + return count; +} + +LLSD LLSDXMLParser::Impl::parse(std::istream& input) +{ + reset(); + XML_Status status; + + static const int BUFFER_SIZE = 1024; + void* buffer = NULL; + int count = 0; + while (input.good() && !input.eof()) + { + buffer = XML_GetBuffer(mParser, BUFFER_SIZE); + + /* + * If we happened to end our last buffer right at the end of the llsd, but the + * stream is still going we will get a null buffer here. Check for mGracefullStop. + */ + if (!buffer) + { + break; + } + count = get_till_eol(input, (char *)buffer, BUFFER_SIZE); + if (!count) + { + break; + } + status = XML_ParseBuffer(mParser, count, false); + + if (status == XML_STATUS_ERROR) + { + break; + } + } + + // FIXME: This code is buggy - if the stream was empty or not good, there + // is not buffer to parse, both the call to XML_ParseBuffer and the buffer + // manipulations are illegal + // futhermore, it isn't clear that the expat buffer semantics are preserved + + status = XML_ParseBuffer(mParser, 0, true); + if (status == XML_STATUS_ERROR && !mGracefullStop) + { + ((char*) buffer)[count? count - 1 : 0] = '\0'; + llinfos << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR parsing:" << (char*) buffer << llendl; + return LLSD(); + } + + clear_eol(input); + return mResult; +} + +void LLSDXMLParser::Impl::reset() +{ + if (mPreStaged) + { + mPreStaged = false; + return; + } + + mResult.clear(); + + mInLLSDElement = false; + mDepth = 0; + + mGracefullStop = false; + + mStack.clear(); + + mSkipping = false; + +#if( LL_WINDOWS || __GNUC__ > 2) + mCurrentKey.clear(); +#else + mCurrentKey = std::string(); +#endif + + + XML_ParserReset(mParser, "utf-8"); + XML_SetUserData(mParser, this); + XML_SetElementHandler(mParser, sStartElementHandler, sEndElementHandler); + XML_SetCharacterDataHandler(mParser, sCharacterDataHandler); +} + + +void LLSDXMLParser::Impl::startSkipping() +{ + mSkipping = true; + mSkipThrough = mDepth; +} + +const XML_Char* +LLSDXMLParser::Impl::findAttribute(const XML_Char* name, const XML_Char** pairs) +{ + while (NULL != pairs && NULL != *pairs) + { + if(0 == strcmp(name, *pairs)) + { + return *(pairs + 1); + } + pairs += 2; + } + return NULL; +} + +void LLSDXMLParser::Impl::parsePart(const char *buf, int len) +{ + void * buffer = XML_GetBuffer(mParser, len); + if (buffer != NULL && buf != NULL) + { + memcpy(buffer, buf, len); + } + XML_ParseBuffer(mParser, len, false); + + mPreStaged = true; +} + +void LLSDXMLParser::Impl::startElementHandler(const XML_Char* name, const XML_Char** attributes) +{ + mDepth += 1; + if (mSkipping) + { + return; + } + + Element element = readElement(name); + mCurrentContent.str(""); + + switch (element) + { + case ELEMENT_LLSD: + if (mInLLSDElement) { return startSkipping(); } + mInLLSDElement = true; + return; + + case ELEMENT_KEY: + if (mStack.empty() || !(mStack.back()->isMap())) + { + return startSkipping(); + } + return; + + case ELEMENT_BINARY: + { + const XML_Char* encoding = findAttribute("encoding", attributes); + if(encoding && strcmp("base64", encoding) != 0) { return startSkipping(); } + break; + } + + default: + // all rest are values, fall through + ; + } + + + if (!mInLLSDElement) { return startSkipping(); } + + if (mStack.empty()) + { + mStack.push_back(&mResult); + } + else if (mStack.back()->isMap()) + { + if (mCurrentKey.empty()) { return startSkipping(); } + + LLSD& map = *mStack.back(); + LLSD& newElement = map[mCurrentKey]; + mStack.push_back(&newElement); + +#if( LL_WINDOWS || __GNUC__ > 2) + mCurrentKey.clear(); +#else + mCurrentKey = std::string(); +#endif + } + else if (mStack.back()->isArray()) + { + LLSD& array = *mStack.back(); + array.append(LLSD()); + LLSD& newElement = array[array.size()-1]; + mStack.push_back(&newElement); + } + else { + // improperly nested value in a non-structure + return startSkipping(); + } + + switch (element) + { + case ELEMENT_MAP: + *mStack.back() = LLSD::emptyMap(); + break; + + case ELEMENT_ARRAY: + *mStack.back() = LLSD::emptyArray(); + break; + + default: + // all the other values will be set in the end element handler + ; + } +} + +void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name) +{ + mDepth -= 1; + if (mSkipping) + { + if (mDepth < mSkipThrough) + { + mSkipping = false; + } + return; + } + + Element element = readElement(name); + + switch (element) + { + case ELEMENT_LLSD: + if (mInLLSDElement) + { + mInLLSDElement = false; + mGracefullStop = true; + XML_StopParser(mParser, false); + } + return; + + case ELEMENT_KEY: + mCurrentKey = mCurrentContent.str(); + return; + + default: + // all rest are values, fall through + ; + } + + if (!mInLLSDElement) { return; } + + LLSD& value = *mStack.back(); + mStack.pop_back(); + + std::string content = mCurrentContent.str(); + mCurrentContent.str(""); + + switch (element) + { + case ELEMENT_UNDEF: + value.clear(); + break; + + case ELEMENT_BOOL: + value = content == "true" || content == "1"; + break; + + case ELEMENT_INTEGER: + value = LLSD(content).asInteger(); + break; + + case ELEMENT_REAL: + value = LLSD(content).asReal(); + break; + + case ELEMENT_STRING: + value = content; + break; + + case ELEMENT_UUID: + value = LLSD(content).asUUID(); + break; + + case ELEMENT_DATE: + value = LLSD(content).asDate(); + break; + + case ELEMENT_URI: + value = LLSD(content).asURI(); + break; + + case ELEMENT_BINARY: + { + S32 len = apr_base64_decode_len(content.c_str()); + std::vector<U8> data; + data.resize(len); + len = apr_base64_decode_binary(&data[0], content.c_str()); + data.resize(len); + value = data; + break; + } + + case ELEMENT_UNKNOWN: + value.clear(); + break; + + default: + // other values, map and array, have already been set + break; + } +} + +void LLSDXMLParser::Impl::characterDataHandler(const XML_Char* data, int length) +{ + mCurrentContent.write(data, length); +} + + +void LLSDXMLParser::Impl::sStartElementHandler( + void* userData, const XML_Char* name, const XML_Char** attributes) +{ + ((LLSDXMLParser::Impl*)userData)->startElementHandler(name, attributes); +} + +void LLSDXMLParser::Impl::sEndElementHandler( + void* userData, const XML_Char* name) +{ + ((LLSDXMLParser::Impl*)userData)->endElementHandler(name); +} + +void LLSDXMLParser::Impl::sCharacterDataHandler( + void* userData, const XML_Char* data, int length) +{ + ((LLSDXMLParser::Impl*)userData)->characterDataHandler(data, length); +} + + +LLSDXMLParser::Impl::Element LLSDXMLParser::Impl::readElement(const XML_Char* name) +{ + if (strcmp(name, "llsd") == 0) { return ELEMENT_LLSD; } + if (strcmp(name, "undef") == 0) { return ELEMENT_UNDEF; } + if (strcmp(name, "boolean") == 0) { return ELEMENT_BOOL; } + if (strcmp(name, "integer") == 0) { return ELEMENT_INTEGER; } + if (strcmp(name, "real") == 0) { return ELEMENT_REAL; } + if (strcmp(name, "string") == 0) { return ELEMENT_STRING; } + if (strcmp(name, "uuid") == 0) { return ELEMENT_UUID; } + if (strcmp(name, "date") == 0) { return ELEMENT_DATE; } + if (strcmp(name, "uri") == 0) { return ELEMENT_URI; } + if (strcmp(name, "binary") == 0) { return ELEMENT_BINARY; } + if (strcmp(name, "map") == 0) { return ELEMENT_MAP; } + if (strcmp(name, "array") == 0) { return ELEMENT_ARRAY; } + if (strcmp(name, "key") == 0) { return ELEMENT_KEY; } + + return ELEMENT_UNKNOWN; +} + + + + + + + +LLSDXMLParser::LLSDXMLParser() + : impl(* new Impl) +{ +} + +LLSDXMLParser::~LLSDXMLParser() +{ + delete &impl; +} + +void LLSDXMLParser::parsePart(const char *buf, int len) +{ + impl.parsePart(buf, len); +} + +// virtual +S32 LLSDXMLParser::parse(std::istream& input, LLSD& data) const +{ + data = impl.parse(input); + return 0; +} |