From 922cb5559b9f2f97279fa24cc9c5862c8b666495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 8 Mar 2005 10:23:43 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r2603, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/iem/iemxmlrpc/; revision=2604 --- xmlrpc++/src/XmlRpcValue.cpp | 611 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 611 insertions(+) create mode 100644 xmlrpc++/src/XmlRpcValue.cpp (limited to 'xmlrpc++/src/XmlRpcValue.cpp') diff --git a/xmlrpc++/src/XmlRpcValue.cpp b/xmlrpc++/src/XmlRpcValue.cpp new file mode 100644 index 0000000..d6cf3f0 --- /dev/null +++ b/xmlrpc++/src/XmlRpcValue.cpp @@ -0,0 +1,611 @@ + +#include "XmlRpcValue.h" +#include "XmlRpcException.h" +#include "XmlRpcUtil.h" +#include "base64.h" + +#ifndef MAKEDEPEND +# include +# include +# include +# include +#endif + +namespace XmlRpc { + + + static const char VALUE_TAG[] = ""; + static const char VALUE_ETAG[] = ""; + + static const char BOOLEAN_TAG[] = ""; + static const char BOOLEAN_ETAG[] = ""; + static const char DOUBLE_TAG[] = ""; + static const char DOUBLE_ETAG[] = ""; + static const char INT_TAG[] = ""; + static const char I4_TAG[] = ""; + static const char I4_ETAG[] = ""; + static const char STRING_TAG[] = ""; + static const char DATETIME_TAG[] = ""; + static const char DATETIME_ETAG[] = ""; + static const char BASE64_TAG[] = ""; + static const char BASE64_ETAG[] = ""; + + static const char ARRAY_TAG[] = ""; + static const char DATA_TAG[] = ""; + static const char DATA_ETAG[] = ""; + static const char ARRAY_ETAG[] = ""; + + static const char STRUCT_TAG[] = ""; + static const char MEMBER_TAG[] = ""; + static const char NAME_TAG[] = ""; + static const char NAME_ETAG[] = ""; + static const char MEMBER_ETAG[] = ""; + static const char STRUCT_ETAG[] = ""; + + + + // Format strings + std::string XmlRpcValue::_doubleFormat("%f"); + + + + // Clean up + void XmlRpcValue::invalidate() + { + switch (_type) { + case TypeString: delete _value.asString; break; + case TypeDateTime: delete _value.asTime; break; + case TypeBase64: delete _value.asBinary; break; + case TypeArray: delete _value.asArray; break; + case TypeStruct: delete _value.asStruct; break; + default: break; + } + _type = TypeInvalid; + _value.asBinary = 0; + } + + + // Type checking + void XmlRpcValue::assertTypeOrInvalid(Type t) + { + if (_type == TypeInvalid) + { + _type = t; + switch (_type) { // Ensure there is a valid value for the type + case TypeString: _value.asString = new std::string(); break; + case TypeDateTime: _value.asTime = new struct tm(); break; + case TypeBase64: _value.asBinary = new BinaryData(); break; + case TypeArray: _value.asArray = new ValueArray(); break; + case TypeStruct: _value.asStruct = new ValueStruct(); break; + default: _value.asBinary = 0; break; + } + } + else if (_type != t) + throw XmlRpcException("type error"); + } + + void XmlRpcValue::assertArray(int size) const + { + if (_type != TypeArray) + throw XmlRpcException("type error: expected an array"); + else if (int(_value.asArray->size()) < size) + throw XmlRpcException("range error: array index too large"); + } + + + void XmlRpcValue::assertArray(int size) + { + if (_type == TypeInvalid) { + _type = TypeArray; + _value.asArray = new ValueArray(size); + } else if (_type == TypeArray) { + if (int(_value.asArray->size()) < size) + _value.asArray->resize(size); + } else + throw XmlRpcException("type error: expected an array"); + } + + void XmlRpcValue::assertStruct() + { + if (_type == TypeInvalid) { + _type = TypeStruct; + _value.asStruct = new ValueStruct(); + } else if (_type != TypeStruct) + throw XmlRpcException("type error: expected a struct"); + } + + + // Operators + XmlRpcValue& XmlRpcValue::operator=(XmlRpcValue const& rhs) + { + if (this != &rhs) + { + invalidate(); + _type = rhs._type; + switch (_type) { + case TypeBoolean: _value.asBool = rhs._value.asBool; break; + case TypeInt: _value.asInt = rhs._value.asInt; break; + case TypeDouble: _value.asDouble = rhs._value.asDouble; break; + case TypeDateTime: _value.asTime = new struct tm(*rhs._value.asTime); break; + case TypeString: _value.asString = new std::string(*rhs._value.asString); break; + case TypeBase64: _value.asBinary = new BinaryData(*rhs._value.asBinary); break; + case TypeArray: _value.asArray = new ValueArray(*rhs._value.asArray); break; + case TypeStruct: _value.asStruct = new ValueStruct(*rhs._value.asStruct); break; + default: _value.asBinary = 0; break; + } + } + return *this; + } + + + // Predicate for tm equality + static bool tmEq(struct tm const& t1, struct tm const& t2) { + return t1.tm_sec == t2.tm_sec && t1.tm_min == t2.tm_min && + t1.tm_hour == t2.tm_hour && t1.tm_mday == t1.tm_mday && + t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year; + } + + bool XmlRpcValue::operator==(XmlRpcValue const& other) const + { + if (_type != other._type) + return false; + + switch (_type) { + case TypeBoolean: return ( !_value.asBool && !other._value.asBool) || + ( _value.asBool && other._value.asBool); + case TypeInt: return _value.asInt == other._value.asInt; + case TypeDouble: return _value.asDouble == other._value.asDouble; + case TypeDateTime: return tmEq(*_value.asTime, *other._value.asTime); + case TypeString: return *_value.asString == *other._value.asString; + case TypeBase64: return *_value.asBinary == *other._value.asBinary; + case TypeArray: return *_value.asArray == *other._value.asArray; + + // The map<>::operator== requires the definition of value< for kcc + case TypeStruct: //return *_value.asStruct == *other._value.asStruct; + { + if (_value.asStruct->size() != other._value.asStruct->size()) + return false; + + ValueStruct::const_iterator it1=_value.asStruct->begin(); + ValueStruct::const_iterator it2=other._value.asStruct->begin(); + while (it1 != _value.asStruct->end()) { + const XmlRpcValue& v1 = it1->second; + const XmlRpcValue& v2 = it2->second; + if ( ! (v1 == v2)) + return false; + it1++; + it2++; + } + return true; + } + default: break; + } + return true; // Both invalid values ... + } + + bool XmlRpcValue::operator!=(XmlRpcValue const& other) const + { + return !(*this == other); + } + + + // Works for strings, binary data, arrays, and structs. + int XmlRpcValue::size() const + { + switch (_type) { + case TypeString: return int(_value.asString->size()); + case TypeBase64: return int(_value.asBinary->size()); + case TypeArray: return int(_value.asArray->size()); + case TypeStruct: return int(_value.asStruct->size()); + default: break; + } + + throw XmlRpcException("type error"); + } + + // Checks for existence of struct member + bool XmlRpcValue::hasMember(const std::string& name) const + { + return _type == TypeStruct && _value.asStruct->find(name) != _value.asStruct->end(); + } + + // Set the value from xml. The chars at *offset into valueXml + // should be the start of a tag. Destroys any existing value. + bool XmlRpcValue::fromXml(std::string const& valueXml, int* offset) + { + int savedOffset = *offset; + + invalidate(); + if ( ! XmlRpcUtil::nextTagIs(VALUE_TAG, valueXml, offset)) + return false; // Not a value, offset not updated + + int afterValueOffset = *offset; + std::string typeTag = XmlRpcUtil::getNextTag(valueXml, offset); + bool result = false; + if (typeTag == BOOLEAN_TAG) + result = boolFromXml(valueXml, offset); + else if (typeTag == I4_TAG || typeTag == INT_TAG) + result = intFromXml(valueXml, offset); + else if (typeTag == DOUBLE_TAG) + result = doubleFromXml(valueXml, offset); + else if (typeTag.empty() || typeTag == STRING_TAG) + result = stringFromXml(valueXml, offset); + else if (typeTag == DATETIME_TAG) + result = timeFromXml(valueXml, offset); + else if (typeTag == BASE64_TAG) + result = binaryFromXml(valueXml, offset); + else if (typeTag == ARRAY_TAG) + result = arrayFromXml(valueXml, offset); + else if (typeTag == STRUCT_TAG) + result = structFromXml(valueXml, offset); + // Watch for empty/blank strings with no tag + else if (typeTag == VALUE_ETAG) + { + *offset = afterValueOffset; // back up & try again + result = stringFromXml(valueXml, offset); + } + + if (result) // Skip over the tag + XmlRpcUtil::findTag(VALUE_ETAG, valueXml, offset); + else // Unrecognized tag after + *offset = savedOffset; + + return result; + } + + // Encode the Value in xml + std::string XmlRpcValue::toXml() const + { + switch (_type) { + case TypeBoolean: return boolToXml(); + case TypeInt: return intToXml(); + case TypeDouble: return doubleToXml(); + case TypeString: return stringToXml(); + case TypeDateTime: return timeToXml(); + case TypeBase64: return binaryToXml(); + case TypeArray: return arrayToXml(); + case TypeStruct: return structToXml(); + default: break; + } + return std::string(); // Invalid value + } + + + // Boolean + bool XmlRpcValue::boolFromXml(std::string const& valueXml, int* offset) + { + const char* valueStart = valueXml.c_str() + *offset; + char* valueEnd; + long ivalue = strtol(valueStart, &valueEnd, 10); + if (valueEnd == valueStart || (ivalue != 0 && ivalue != 1)) + return false; + + _type = TypeBoolean; + _value.asBool = (ivalue == 1); + *offset += int(valueEnd - valueStart); + return true; + } + + std::string XmlRpcValue::boolToXml() const + { + std::string xml = VALUE_TAG; + xml += BOOLEAN_TAG; + xml += (_value.asBool ? "1" : "0"); + xml += BOOLEAN_ETAG; + xml += VALUE_ETAG; + return xml; + } + + // Int + bool XmlRpcValue::intFromXml(std::string const& valueXml, int* offset) + { + const char* valueStart = valueXml.c_str() + *offset; + char* valueEnd; + long ivalue = strtol(valueStart, &valueEnd, 10); + if (valueEnd == valueStart) + return false; + + _type = TypeInt; + _value.asInt = int(ivalue); + *offset += int(valueEnd - valueStart); + return true; + } + + std::string XmlRpcValue::intToXml() const + { + char buf[256]; + snprintf(buf, sizeof(buf)-1, "%d", _value.asInt); + buf[sizeof(buf)-1] = 0; + std::string xml = VALUE_TAG; + xml += I4_TAG; + xml += buf; + xml += I4_ETAG; + xml += VALUE_ETAG; + return xml; + } + + // Double + bool XmlRpcValue::doubleFromXml(std::string const& valueXml, int* offset) + { + const char* valueStart = valueXml.c_str() + *offset; + char* valueEnd; + double dvalue = strtod(valueStart, &valueEnd); + if (valueEnd == valueStart) + return false; + + _type = TypeDouble; + _value.asDouble = dvalue; + *offset += int(valueEnd - valueStart); + return true; + } + + std::string XmlRpcValue::doubleToXml() const + { + char buf[256]; + // this was fixed for windows use (frauenberger@iem.at) 12.10.2004 + snprintf(buf, sizeof(buf)-1, "%f" , _value.asDouble); + buf[sizeof(buf)-1] = 0; + + std::string xml = VALUE_TAG; + xml += DOUBLE_TAG; + xml += buf; + xml += DOUBLE_ETAG; + xml += VALUE_ETAG; + return xml; + } + + // String + bool XmlRpcValue::stringFromXml(std::string const& valueXml, int* offset) + { + size_t valueEnd = valueXml.find('<', *offset); + if (valueEnd == std::string::npos) + return false; // No end tag; + + _type = TypeString; + _value.asString = new std::string(XmlRpcUtil::xmlDecode(valueXml.substr(*offset, valueEnd-*offset))); + *offset += int(_value.asString->length()); + return true; + } + + std::string XmlRpcValue::stringToXml() const + { + std::string xml = VALUE_TAG; + //xml += STRING_TAG; optional + xml += XmlRpcUtil::xmlEncode(*_value.asString); + //xml += STRING_ETAG; + xml += VALUE_ETAG; + return xml; + } + + // DateTime (stored as a struct tm) + bool XmlRpcValue::timeFromXml(std::string const& valueXml, int* offset) + { + size_t valueEnd = valueXml.find('<', *offset); + if (valueEnd == std::string::npos) + return false; // No end tag; + + std::string stime = valueXml.substr(*offset, valueEnd-*offset); + + struct tm t; + if (sscanf(stime.c_str(),"%4d%2d%2dT%2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec) != 6) + return false; + + t.tm_isdst = -1; + _type = TypeDateTime; + _value.asTime = new struct tm(t); + *offset += int(stime.length()); + return true; + } + + std::string XmlRpcValue::timeToXml() const + { + struct tm* t = _value.asTime; + char buf[20]; + snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d", + t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); + buf[sizeof(buf)-1] = 0; + + std::string xml = VALUE_TAG; + xml += DATETIME_TAG; + xml += buf; + xml += DATETIME_ETAG; + xml += VALUE_ETAG; + return xml; + } + + + // Base64 + bool XmlRpcValue::binaryFromXml(std::string const& valueXml, int* offset) + { + size_t valueEnd = valueXml.find('<', *offset); + if (valueEnd == std::string::npos) + return false; // No end tag; + + _type = TypeBase64; + std::string asString = valueXml.substr(*offset, valueEnd-*offset); + _value.asBinary = new BinaryData(); + // check whether base64 encodings can contain chars xml encodes... + + // convert from base64 to binary + int iostatus = 0; + base64 decoder; + std::back_insert_iterator ins = std::back_inserter(*(_value.asBinary)); + decoder.get(asString.begin(), asString.end(), ins, iostatus); + + *offset += int(asString.length()); + return true; + } + + + std::string XmlRpcValue::binaryToXml() const + { + // convert to base64 + std::vector base64data; + int iostatus = 0; + base64 encoder; + std::back_insert_iterator > ins = std::back_inserter(base64data); + encoder.put(_value.asBinary->begin(), _value.asBinary->end(), ins, iostatus, base64<>::crlf()); + + // Wrap with xml + std::string xml = VALUE_TAG; + xml += BASE64_TAG; + xml.append(base64data.begin(), base64data.end()); + xml += BASE64_ETAG; + xml += VALUE_ETAG; + return xml; + } + + + // Array + bool XmlRpcValue::arrayFromXml(std::string const& valueXml, int* offset) + { + if ( ! XmlRpcUtil::nextTagIs(DATA_TAG, valueXml, offset)) + return false; + + _type = TypeArray; + _value.asArray = new ValueArray; + XmlRpcValue v; + while (v.fromXml(valueXml, offset)) + _value.asArray->push_back(v); // copy... + + // Skip the trailing + (void) XmlRpcUtil::nextTagIs(DATA_ETAG, valueXml, offset); + return true; + } + + + // In general, its preferable to generate the xml of each element of the + // array as it is needed rather than glomming up one big string. + std::string XmlRpcValue::arrayToXml() const + { + std::string xml = VALUE_TAG; + xml += ARRAY_TAG; + xml += DATA_TAG; + + int s = int(_value.asArray->size()); + for (int i=0; iat(i).toXml(); + + xml += DATA_ETAG; + xml += ARRAY_ETAG; + xml += VALUE_ETAG; + return xml; + } + + + // Struct + bool XmlRpcValue::structFromXml(std::string const& valueXml, int* offset) + { + _type = TypeStruct; + _value.asStruct = new ValueStruct; + + while (XmlRpcUtil::nextTagIs(MEMBER_TAG, valueXml, offset)) { + // name + const std::string name = XmlRpcUtil::parseTag(NAME_TAG, valueXml, offset); + // value + XmlRpcValue val(valueXml, offset); + if ( ! val.valid()) { + invalidate(); + return false; + } + const std::pair p(name, val); + _value.asStruct->insert(p); + + (void) XmlRpcUtil::nextTagIs(MEMBER_ETAG, valueXml, offset); + } + return true; + } + + + // In general, its preferable to generate the xml of each element + // as it is needed rather than glomming up one big string. + std::string XmlRpcValue::structToXml() const + { + std::string xml = VALUE_TAG; + xml += STRUCT_TAG; + + ValueStruct::const_iterator it; + for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) { + xml += MEMBER_TAG; + xml += NAME_TAG; + xml += XmlRpcUtil::xmlEncode(it->first); + xml += NAME_ETAG; + xml += it->second.toXml(); + xml += MEMBER_ETAG; + } + + xml += STRUCT_ETAG; + xml += VALUE_ETAG; + return xml; + } + + + + // Write the value without xml encoding it + std::ostream& XmlRpcValue::write(std::ostream& os) const { + switch (_type) { + default: break; + case TypeBoolean: os << _value.asBool; break; + case TypeInt: os << _value.asInt; break; + case TypeDouble: os << _value.asDouble; break; + case TypeString: os << *_value.asString; break; + case TypeDateTime: + { + struct tm* t = _value.asTime; + char buf[20]; + snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d", + t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); + buf[sizeof(buf)-1] = 0; + os << buf; + break; + } + case TypeBase64: + { + int iostatus = 0; + std::ostreambuf_iterator out(os); + base64 encoder; + encoder.put(_value.asBinary->begin(), _value.asBinary->end(), out, iostatus, base64<>::crlf()); + break; + } + case TypeArray: + { + int s = int(_value.asArray->size()); + os << '{'; + for (int i=0; i 0) os << ','; + _value.asArray->at(i).write(os); + } + os << '}'; + break; + } + case TypeStruct: + { + os << '['; + ValueStruct::const_iterator it; + for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) + { + if (it!=_value.asStruct->begin()) os << ','; + os << it->first << ':'; + it->second.write(os); + } + os << ']'; + break; + } + + } + + return os; + } + +} // namespace XmlRpc + + +// ostream +std::ostream& operator<<(std::ostream& os, XmlRpc::XmlRpcValue& v) +{ + // If you want to output in xml format: + //return os << v.toXml(); + return v.write(os); +} + -- cgit v1.2.1