XmlRpcValue.cc

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