XmlRpcValue.cpp
Go to the documentation of this file.
00001 
00002 #include "XmlRpcValue.h"
00003 #include "XmlRpcException.h"
00004 #include "XmlRpcUtil.h"
00005 #include "base64.h"
00006 
00007 #ifndef MAKEDEPEND
00008 # include <iostream>
00009 # include <ostream>
00010 # include <stdlib.h>
00011 # include <stdio.h>
00012 #endif
00013 
00014 #include <sstream>
00015 
00016 namespace XmlRpc {
00017 
00018 
00019   static const char VALUE_TAG[]     = "<value>";
00020   static const char VALUE_ETAG[]    = "</value>";
00021 
00022   static const char BOOLEAN_TAG[]   = "<boolean>";
00023   static const char BOOLEAN_ETAG[]  = "</boolean>";
00024   static const char DOUBLE_TAG[]    = "<double>";
00025   static const char DOUBLE_ETAG[]   = "</double>";
00026   static const char INT_TAG[]       = "<int>";
00027   static const char I4_TAG[]        = "<i4>";
00028   static const char I4_ETAG[]       = "</i4>";
00029   static const char STRING_TAG[]    = "<string>";
00030   static const char DATETIME_TAG[]  = "<dateTime.iso8601>";
00031   static const char DATETIME_ETAG[] = "</dateTime.iso8601>";
00032   static const char BASE64_TAG[]    = "<base64>";
00033   static const char BASE64_ETAG[]   = "</base64>";
00034 
00035   static const char ARRAY_TAG[]     = "<array>";
00036   static const char DATA_TAG[]      = "<data>";
00037   static const char DATA_ETAG[]     = "</data>";
00038   static const char ARRAY_ETAG[]    = "</array>";
00039 
00040   static const char STRUCT_TAG[]    = "<struct>";
00041   static const char MEMBER_TAG[]    = "<member>";
00042   static const char NAME_TAG[]      = "<name>";
00043   static const char NAME_ETAG[]     = "</name>";
00044   static const char MEMBER_ETAG[]   = "</member>";
00045   static const char STRUCT_ETAG[]   = "</struct>";
00046 
00047 
00048       
00049   // Format strings
00050   std::string XmlRpcValue::_doubleFormat("%.16g");
00051 
00052 
00053 
00054   // Clean up
00055   void XmlRpcValue::invalidate()
00056   {
00057     switch (_type) {
00058       case TypeString:    delete _value.asString; break;
00059       case TypeDateTime:  delete _value.asTime;   break;
00060       case TypeBase64:    delete _value.asBinary; break;
00061       case TypeArray:     delete _value.asArray;  break;
00062       case TypeStruct:    delete _value.asStruct; break;
00063       default: break;
00064     }
00065     _type = TypeInvalid;
00066     _value.asBinary = 0;
00067   }
00068 
00069   
00070   // Type checking
00071   void XmlRpcValue::assertTypeOrInvalid(Type t)
00072   {
00073     if (_type == TypeInvalid)
00074     {
00075       _type = t;
00076       switch (_type) {    // Ensure there is a valid value for the type
00077         case TypeString:   _value.asString = new std::string(); break;
00078         case TypeDateTime: _value.asTime = new struct tm();     break;
00079         case TypeBase64:   _value.asBinary = new BinaryData();  break;
00080         case TypeArray:    _value.asArray = new ValueArray();   break;
00081         case TypeStruct:   _value.asStruct = new ValueStruct(); break;
00082         default:           _value.asBinary = 0; break;
00083       }
00084     }
00085     else if (_type != t)
00086       throw XmlRpcException("type error");
00087   }
00088 
00089   void XmlRpcValue::assertArray(int size) const
00090   {
00091     if (_type != TypeArray)
00092       throw XmlRpcException("type error: expected an array");
00093     else if (int(_value.asArray->size()) < size)
00094       throw XmlRpcException("range error: array index too large");
00095   }
00096 
00097 
00098   void XmlRpcValue::assertArray(int size)
00099   {
00100     if (_type == TypeInvalid) {
00101       _type = TypeArray;
00102       _value.asArray = new ValueArray(size);
00103     } else if (_type == TypeArray) {
00104       if (int(_value.asArray->size()) < size)
00105         _value.asArray->resize(size);
00106     } else
00107       throw XmlRpcException("type error: expected an array");
00108   }
00109 
00110   void XmlRpcValue::assertStruct()
00111   {
00112     if (_type == TypeInvalid) {
00113       _type = TypeStruct;
00114       _value.asStruct = new ValueStruct();
00115     } else if (_type != TypeStruct)
00116       throw XmlRpcException("type error: expected a struct");
00117   }
00118 
00119 
00120   // Operators
00121   XmlRpcValue& XmlRpcValue::operator=(XmlRpcValue const& rhs)
00122   {
00123     if (this != &rhs)
00124     {
00125       invalidate();
00126       _type = rhs._type;
00127       switch (_type) {
00128         case TypeBoolean:  _value.asBool = rhs._value.asBool; break;
00129         case TypeInt:      _value.asInt = rhs._value.asInt; break;
00130         case TypeDouble:   _value.asDouble = rhs._value.asDouble; break;
00131         case TypeDateTime: _value.asTime = new struct tm(*rhs._value.asTime); break;
00132         case TypeString:   _value.asString = new std::string(*rhs._value.asString); break;
00133         case TypeBase64:   _value.asBinary = new BinaryData(*rhs._value.asBinary); break;
00134         case TypeArray:    _value.asArray = new ValueArray(*rhs._value.asArray); break;
00135         case TypeStruct:   _value.asStruct = new ValueStruct(*rhs._value.asStruct); break;
00136         default:           _value.asBinary = 0; break;
00137       }
00138     }
00139     return *this;
00140   }
00141 
00142 
00143   // Predicate for tm equality
00144   static bool tmEq(struct tm const& t1, struct tm const& t2) {
00145     return t1.tm_sec == t2.tm_sec && t1.tm_min == t2.tm_min &&
00146             t1.tm_hour == t2.tm_hour && t1.tm_mday == t2.tm_mday &&
00147             t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year;
00148   }
00149 
00150   bool XmlRpcValue::operator==(XmlRpcValue const& other) const
00151   {
00152     if (_type != other._type)
00153       return false;
00154 
00155     switch (_type) {
00156       case TypeBoolean:  return ( !_value.asBool && !other._value.asBool) ||
00157                                 ( _value.asBool && other._value.asBool);
00158       case TypeInt:      return _value.asInt == other._value.asInt;
00159       case TypeDouble:   return _value.asDouble == other._value.asDouble;
00160       case TypeDateTime: return tmEq(*_value.asTime, *other._value.asTime);
00161       case TypeString:   return *_value.asString == *other._value.asString;
00162       case TypeBase64:   return *_value.asBinary == *other._value.asBinary;
00163       case TypeArray:    return *_value.asArray == *other._value.asArray;
00164 
00165       // The map<>::operator== requires the definition of value< for kcc
00166       case TypeStruct:   //return *_value.asStruct == *other._value.asStruct;
00167         {
00168           if (_value.asStruct->size() != other._value.asStruct->size())
00169             return false;
00170           
00171           ValueStruct::const_iterator it1=_value.asStruct->begin();
00172           ValueStruct::const_iterator it2=other._value.asStruct->begin();
00173           while (it1 != _value.asStruct->end()) {
00174             const XmlRpcValue& v1 = it1->second;
00175             const XmlRpcValue& v2 = it2->second;
00176             if ( ! (v1 == v2))
00177               return false;
00178             it1++;
00179             it2++;
00180           }
00181           return true;
00182         }
00183       default: break;
00184     }
00185     return true;    // Both invalid values ...
00186   }
00187 
00188   bool XmlRpcValue::operator!=(XmlRpcValue const& other) const
00189   {
00190     return !(*this == other);
00191   }
00192 
00193 
00194   // Works for strings, binary data, arrays, and structs.
00195   int XmlRpcValue::size() const
00196   {
00197     switch (_type) {
00198       case TypeString: return int(_value.asString->size());
00199       case TypeBase64: return int(_value.asBinary->size());
00200       case TypeArray:  return int(_value.asArray->size());
00201       case TypeStruct: return int(_value.asStruct->size());
00202       default: break;
00203     }
00204 
00205     throw XmlRpcException("type error");
00206   }
00207 
00208   // Checks for existence of struct member
00209   bool XmlRpcValue::hasMember(const std::string& name) const
00210   {
00211     return _type == TypeStruct && _value.asStruct->find(name) != _value.asStruct->end();
00212   }
00213 
00214   // Set the value from xml. The chars at *offset into valueXml 
00215   // should be the start of a <value> tag. Destroys any existing value.
00216   bool XmlRpcValue::fromXml(std::string const& valueXml, int* offset)
00217   {
00218     int savedOffset = *offset;
00219 
00220     invalidate();
00221     if ( ! XmlRpcUtil::nextTagIs(VALUE_TAG, valueXml, offset))
00222       return false;       // Not a value, offset not updated
00223 
00224         int afterValueOffset = *offset;
00225     std::string typeTag = XmlRpcUtil::getNextTag(valueXml, offset);
00226     bool result = false;
00227     if (typeTag == BOOLEAN_TAG)
00228       result = boolFromXml(valueXml, offset);
00229     else if (typeTag == I4_TAG || typeTag == INT_TAG)
00230       result = intFromXml(valueXml, offset);
00231     else if (typeTag == DOUBLE_TAG)
00232       result = doubleFromXml(valueXml, offset);
00233     else if (typeTag.empty() || typeTag == STRING_TAG)
00234       result = stringFromXml(valueXml, offset);
00235     else if (typeTag == DATETIME_TAG)
00236       result = timeFromXml(valueXml, offset);
00237     else if (typeTag == BASE64_TAG)
00238       result = binaryFromXml(valueXml, offset);
00239     else if (typeTag == ARRAY_TAG)
00240       result = arrayFromXml(valueXml, offset);
00241     else if (typeTag == STRUCT_TAG)
00242       result = structFromXml(valueXml, offset);
00243     // Watch for empty/blank strings with no <string>tag
00244     else if (typeTag == VALUE_ETAG)
00245     {
00246       *offset = afterValueOffset;   // back up & try again
00247       result = stringFromXml(valueXml, offset);
00248     }
00249 
00250     if (result)  // Skip over the </value> tag
00251       XmlRpcUtil::findTag(VALUE_ETAG, valueXml, offset);
00252     else        // Unrecognized tag after <value>
00253       *offset = savedOffset;
00254 
00255     return result;
00256   }
00257 
00258   // Encode the Value in xml
00259   std::string XmlRpcValue::toXml() const
00260   {
00261     switch (_type) {
00262       case TypeBoolean:  return boolToXml();
00263       case TypeInt:      return intToXml();
00264       case TypeDouble:   return doubleToXml();
00265       case TypeString:   return stringToXml();
00266       case TypeDateTime: return timeToXml();
00267       case TypeBase64:   return binaryToXml();
00268       case TypeArray:    return arrayToXml();
00269       case TypeStruct:   return structToXml();
00270       default: break;
00271     }
00272     return std::string();   // Invalid value
00273   }
00274 
00275 
00276   // Boolean
00277   bool XmlRpcValue::boolFromXml(std::string const& valueXml, int* offset)
00278   {
00279     const char* valueStart = valueXml.c_str() + *offset;
00280     char* valueEnd;
00281     long ivalue = strtol(valueStart, &valueEnd, 10);
00282     if (valueEnd == valueStart || (ivalue != 0 && ivalue != 1))
00283       return false;
00284 
00285     _type = TypeBoolean;
00286     _value.asBool = (ivalue == 1);
00287     *offset += int(valueEnd - valueStart);
00288     return true;
00289   }
00290 
00291   std::string XmlRpcValue::boolToXml() const
00292   {
00293     std::string xml = VALUE_TAG;
00294     xml += BOOLEAN_TAG;
00295     xml += (_value.asBool ? "1" : "0");
00296     xml += BOOLEAN_ETAG;
00297     xml += VALUE_ETAG;
00298     return xml;
00299   }
00300 
00301   // Int
00302   bool XmlRpcValue::intFromXml(std::string const& valueXml, int* offset)
00303   {
00304     const char* valueStart = valueXml.c_str() + *offset;
00305     char* valueEnd;
00306     long ivalue = strtol(valueStart, &valueEnd, 10);
00307     if (valueEnd == valueStart)
00308       return false;
00309 
00310     _type = TypeInt;
00311     _value.asInt = int(ivalue);
00312     *offset += int(valueEnd - valueStart);
00313     return true;
00314   }
00315 
00316   std::string XmlRpcValue::intToXml() const
00317   {
00318     char buf[256];
00319     snprintf(buf, sizeof(buf)-1, "%d", _value.asInt);
00320     buf[sizeof(buf)-1] = 0;
00321     std::string xml = VALUE_TAG;
00322     xml += I4_TAG;
00323     xml += buf;
00324     xml += I4_ETAG;
00325     xml += VALUE_ETAG;
00326     return xml;
00327   }
00328 
00329   // Double
00330   bool XmlRpcValue::doubleFromXml(std::string const& valueXml, int* offset)
00331   {
00332     const char* valueStart = valueXml.c_str() + *offset;
00333     char* valueEnd;
00334 
00335     // ticket #2438
00336     // push/pop the locale here. Value 123.45 can get read by strtod
00337     // as '123', if the locale expects a comma instead of dot.
00338     // if there are locale problems, silently continue.
00339     std::string tmplocale;
00340     char* locale_cstr = setlocale(LC_NUMERIC, 0);
00341     if (locale_cstr)
00342       {
00343         tmplocale = locale_cstr;
00344         setlocale(LC_NUMERIC, "POSIX");
00345       }
00346 
00347     double dvalue = strtod(valueStart, &valueEnd);
00348 
00349     if (tmplocale.size() > 0) {
00350       setlocale(LC_NUMERIC, tmplocale.c_str());
00351     }
00352 
00353     if (valueEnd == valueStart)
00354       return false;
00355 
00356     _type = TypeDouble;
00357     _value.asDouble = dvalue;
00358     *offset += int(valueEnd - valueStart);
00359     return true;
00360   }
00361 
00362   std::string XmlRpcValue::doubleToXml() const
00363   {
00364     // ticket #2438
00365     std::stringstream ss;
00366     ss.imbue(std::locale::classic()); // ensure we're using "C" locale for formatting floating-point (1.4 vs. 1,4, etc.)
00367     ss.precision(17);
00368     ss << _value.asDouble;
00369 
00370     std::string xml = VALUE_TAG;
00371     xml += DOUBLE_TAG;
00372     xml += ss.str();
00373     xml += DOUBLE_ETAG;
00374     xml += VALUE_ETAG;
00375     return xml;
00376   }
00377 
00378   // String
00379   bool XmlRpcValue::stringFromXml(std::string const& valueXml, int* offset)
00380   {
00381     size_t valueEnd = valueXml.find('<', *offset);
00382     if (valueEnd == std::string::npos)
00383       return false;     // No end tag;
00384 
00385     _type = TypeString;
00386     _value.asString = new std::string(XmlRpcUtil::xmlDecode(valueXml.substr(*offset, valueEnd-*offset)));
00387     *offset += int(_value.asString->length());
00388     return true;
00389   }
00390 
00391   std::string XmlRpcValue::stringToXml() const
00392   {
00393     std::string xml = VALUE_TAG;
00394     //xml += STRING_TAG; optional
00395     xml += XmlRpcUtil::xmlEncode(*_value.asString);
00396     //xml += STRING_ETAG;
00397     xml += VALUE_ETAG;
00398     return xml;
00399   }
00400 
00401   // DateTime (stored as a struct tm)
00402   bool XmlRpcValue::timeFromXml(std::string const& valueXml, int* offset)
00403   {
00404     size_t valueEnd = valueXml.find('<', *offset);
00405     if (valueEnd == std::string::npos)
00406       return false;     // No end tag;
00407 
00408     std::string stime = valueXml.substr(*offset, valueEnd-*offset);
00409 
00410     struct tm t;
00411 #ifdef _MSC_VER
00412     if (sscanf_s(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)
00413 #else
00414     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)
00415 #endif
00416       return false;
00417 
00418     t.tm_isdst = -1;
00419     _type = TypeDateTime;
00420     _value.asTime = new struct tm(t);
00421     *offset += int(stime.length());
00422     return true;
00423   }
00424 
00425   std::string XmlRpcValue::timeToXml() const
00426   {
00427     struct tm* t = _value.asTime;
00428     char buf[20];
00429     snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d", 
00430       t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
00431     buf[sizeof(buf)-1] = 0;
00432 
00433     std::string xml = VALUE_TAG;
00434     xml += DATETIME_TAG;
00435     xml += buf;
00436     xml += DATETIME_ETAG;
00437     xml += VALUE_ETAG;
00438     return xml;
00439   }
00440 
00441 
00442   // Base64
00443   bool XmlRpcValue::binaryFromXml(std::string const& valueXml, int* offset)
00444   {
00445     size_t valueEnd = valueXml.find('<', *offset);
00446     if (valueEnd == std::string::npos)
00447       return false;     // No end tag;
00448 
00449     _type = TypeBase64;
00450     std::string asString = valueXml.substr(*offset, valueEnd-*offset);
00451     _value.asBinary = new BinaryData();
00452     // check whether base64 encodings can contain chars xml encodes...
00453 
00454     // convert from base64 to binary
00455     int iostatus = 0;
00456           base64<char> decoder;
00457     std::back_insert_iterator<BinaryData> ins = std::back_inserter(*(_value.asBinary));
00458                 decoder.get(asString.begin(), asString.end(), ins, iostatus);
00459 
00460     *offset += int(asString.length());
00461     return true;
00462   }
00463 
00464 
00465   std::string XmlRpcValue::binaryToXml() const
00466   {
00467     // convert to base64
00468     std::vector<char> base64data;
00469     int iostatus = 0;
00470           base64<char> encoder;
00471     std::back_insert_iterator<std::vector<char> > ins = std::back_inserter(base64data);
00472                 encoder.put(_value.asBinary->begin(), _value.asBinary->end(), ins, iostatus, base64<>::crlf());
00473 
00474     // Wrap with xml
00475     std::string xml = VALUE_TAG;
00476     xml += BASE64_TAG;
00477     xml.append(base64data.begin(), base64data.end());
00478     xml += BASE64_ETAG;
00479     xml += VALUE_ETAG;
00480     return xml;
00481   }
00482 
00483 
00484   // Array
00485   bool XmlRpcValue::arrayFromXml(std::string const& valueXml, int* offset)
00486   {
00487     if ( ! XmlRpcUtil::nextTagIs(DATA_TAG, valueXml, offset))
00488       return false;
00489 
00490     _type = TypeArray;
00491     _value.asArray = new ValueArray;
00492     XmlRpcValue v;
00493     while (v.fromXml(valueXml, offset))
00494       _value.asArray->push_back(v);       // copy...
00495 
00496     // Skip the trailing </data>
00497     (void) XmlRpcUtil::nextTagIs(DATA_ETAG, valueXml, offset);
00498     return true;
00499   }
00500 
00501 
00502   // In general, its preferable to generate the xml of each element of the
00503   // array as it is needed rather than glomming up one big string.
00504   std::string XmlRpcValue::arrayToXml() const
00505   {
00506     std::string xml = VALUE_TAG;
00507     xml += ARRAY_TAG;
00508     xml += DATA_TAG;
00509 
00510     int s = int(_value.asArray->size());
00511     for (int i=0; i<s; ++i)
00512        xml += _value.asArray->at(i).toXml();
00513 
00514     xml += DATA_ETAG;
00515     xml += ARRAY_ETAG;
00516     xml += VALUE_ETAG;
00517     return xml;
00518   }
00519 
00520 
00521   // Struct
00522   bool XmlRpcValue::structFromXml(std::string const& valueXml, int* offset)
00523   {
00524     _type = TypeStruct;
00525     _value.asStruct = new ValueStruct;
00526 
00527     while (XmlRpcUtil::nextTagIs(MEMBER_TAG, valueXml, offset)) {
00528       // name
00529       const std::string name = XmlRpcUtil::parseTag(NAME_TAG, valueXml, offset);
00530       // value
00531       XmlRpcValue val(valueXml, offset);
00532       if ( ! val.valid()) {
00533         invalidate();
00534         return false;
00535       }
00536       const std::pair<const std::string, XmlRpcValue> p(name, val);
00537       _value.asStruct->insert(p);
00538 
00539       (void) XmlRpcUtil::nextTagIs(MEMBER_ETAG, valueXml, offset);
00540     }
00541     return true;
00542   }
00543 
00544 
00545   // In general, its preferable to generate the xml of each element
00546   // as it is needed rather than glomming up one big string.
00547   std::string XmlRpcValue::structToXml() const
00548   {
00549     std::string xml = VALUE_TAG;
00550     xml += STRUCT_TAG;
00551 
00552     ValueStruct::const_iterator it;
00553     for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) {
00554       xml += MEMBER_TAG;
00555       xml += NAME_TAG;
00556       xml += XmlRpcUtil::xmlEncode(it->first);
00557       xml += NAME_ETAG;
00558       xml += it->second.toXml();
00559       xml += MEMBER_ETAG;
00560     }
00561 
00562     xml += STRUCT_ETAG;
00563     xml += VALUE_ETAG;
00564     return xml;
00565   }
00566 
00567 
00568 
00569   // Write the value without xml encoding it
00570   std::ostream& XmlRpcValue::write(std::ostream& os) const {
00571     switch (_type) {
00572       default:           break;
00573       case TypeBoolean:  os << _value.asBool; break;
00574       case TypeInt:      os << _value.asInt; break;
00575       case TypeDouble:   os << _value.asDouble; break;
00576       case TypeString:   os << *_value.asString; break;
00577       case TypeDateTime:
00578         {
00579           struct tm* t = _value.asTime;
00580           char buf[20];
00581           snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d", 
00582             t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
00583           buf[sizeof(buf)-1] = 0;
00584           os << buf;
00585           break;
00586         }
00587       case TypeBase64:
00588         {
00589           int iostatus = 0;
00590           std::ostreambuf_iterator<char> out(os);
00591           base64<char> encoder;
00592           encoder.put(_value.asBinary->begin(), _value.asBinary->end(), out, iostatus, base64<>::crlf());
00593           break;
00594         }
00595       case TypeArray:
00596         {
00597           int s = int(_value.asArray->size());
00598           os << '{';
00599           for (int i=0; i<s; ++i)
00600           {
00601             if (i > 0) os << ',';
00602             _value.asArray->at(i).write(os);
00603           }
00604           os << '}';
00605           break;
00606         }
00607       case TypeStruct:
00608         {
00609           os << '[';
00610           ValueStruct::const_iterator it;
00611           for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it)
00612           {
00613             if (it!=_value.asStruct->begin()) os << ',';
00614             os << it->first << ':';
00615             it->second.write(os);
00616           }
00617           os << ']';
00618           break;
00619         }
00620       
00621     }
00622     
00623     return os;
00624   }
00625 
00626 } // namespace XmlRpc
00627 
00628 
00629 // ostream
00630 std::ostream& operator<<(std::ostream& os, XmlRpc::XmlRpcValue& v) 
00631 { 
00632   // If you want to output in xml format:
00633   //return os << v.toXml(); 
00634   return v.write(os);
00635 }
00636 


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley
autogenerated on Fri Aug 28 2015 12:33:06