1 // MessageIO.cpp 2 3 #include "MessageIO.h" 4 5 #include <Debug.h> 6 7 #include <cstdlib> 8 #include <cstring> 9 #include <cctype> 10 11 #include <vector> 12 #include <utility> 13 14 using namespace std; 15 16 __USE_CORTEX_NAMESPACE 17 18 // -------------------------------------------------------- // 19 // constants 20 // -------------------------------------------------------- // 21 22 const char* const MessageIO::s_element = "BMessage"; 23 24 const char* _boolEl = "bool"; 25 const char* _int8El = "int8"; 26 const char* _int16El = "int16"; 27 const char* _int32El = "int32"; 28 const char* _int64El = "int64"; 29 const char* _floatEl = "float"; 30 const char* _doubleEl = "double"; 31 const char* _stringEl = "string"; 32 const char* _pointEl = "point"; 33 const char* _rectEl = "rect"; 34 35 // -------------------------------------------------------- // 36 // *** ctor/dtor/accessor 37 // -------------------------------------------------------- // 38 39 MessageIO::~MessageIO() { 40 if(m_ownMessage && m_message) 41 delete m_message; 42 } 43 44 MessageIO::MessageIO() : 45 m_ownMessage(true), 46 m_message(0) {} 47 48 // When given a message to export, this object does NOT take 49 // responsibility for deleting it. It will, however, handle 50 // deletion of an imported BMessage. 51 52 MessageIO::MessageIO( 53 const BMessage* message) : 54 m_ownMessage(false), 55 m_message(const_cast<BMessage*>(message)) { 56 57 ASSERT(m_message); 58 } 59 60 void MessageIO::setMessage( 61 BMessage* message) { 62 63 if(m_ownMessage && m_message) 64 delete m_message; 65 m_ownMessage = false; 66 m_message = message; 67 68 ASSERT(m_message); 69 } 70 71 // -------------------------------------------------------- // 72 // *** static setup method 73 // -------------------------------------------------------- // 74 // call this method to install hooks for the tags needed by 75 // MessageIO into the given document type 76 77 /*static*/ 78 void MessageIO::AddTo( 79 XML::DocumentType* docType) { 80 81 docType->addMapping(new Mapping<MessageIO>(s_element)); 82 } 83 84 // -------------------------------------------------------- // 85 // EXPORT: 86 // -------------------------------------------------------- // 87 88 void MessageIO::xmlExportBegin( 89 ExportContext& context) const { 90 91 if(!m_message) { 92 context.reportError("No message data to export.\n"); 93 return; 94 } 95 context.beginElement(s_element); 96 } 97 98 void MessageIO::xmlExportAttributes( 99 ExportContext& context) const { 100 101 if(m_message->what) 102 context.writeAttr("what", m_message->what); 103 if(m_name.Length()) 104 context.writeAttr("name", m_name.String()); 105 } 106 107 void MessageIO::xmlExportContent( 108 ExportContext& context) const { 109 110 111 ASSERT(m_message); 112 status_t err; 113 114 // +++++ the approach: 115 // 1) build a list of field names 116 // 2) export fields sorted first by index, then name 117 118 typedef vector<BString> field_set; 119 field_set fields; 120 121 char* name; 122 type_code type; 123 int32 count; 124 for( 125 int32 n = 0; 126 m_message->GetInfo(B_ANY_TYPE, n, &name, &type, &count) == B_OK; 127 ++n) { 128 fields.push_back(name); 129 } 130 131 if(!fields.size()) 132 return; 133 134 context.beginContent(); 135 136 bool done = false; 137 for(int32 n = 0; !done; ++n) { 138 139 done = true; 140 141 for( 142 uint32 fieldIndex = 0; 143 fieldIndex < fields.size(); 144 ++fieldIndex) { 145 146 if(m_message->GetInfo( 147 fields[fieldIndex].String(), 148 &type, 149 &count) < B_OK || n >= count) 150 continue; 151 152 // found a field at the current index, so don't give up 153 done = false; 154 155 err = _exportField( 156 context, 157 m_message, 158 type, 159 fields[fieldIndex].String(), 160 n); 161 162 if(err < B_OK) { 163 BString errText; 164 errText << "Couldn't export field '" << fields[fieldIndex] << 165 "' index " << n << ": " << strerror(err) << "\n"; 166 context.reportError(errText.String()); 167 return; 168 } 169 } 170 } 171 } 172 173 void MessageIO::xmlExportEnd( 174 ExportContext& context) const { 175 context.endElement(); 176 } 177 178 179 // -------------------------------------------------------- // 180 // IMPORT: 181 // -------------------------------------------------------- // 182 183 void MessageIO::xmlImportBegin( 184 ImportContext& context) { 185 186 // create the message 187 if(m_message) { 188 if(m_ownMessage) 189 delete m_message; 190 } 191 m_message = new BMessage(); 192 m_name.SetTo(""); 193 } 194 195 void MessageIO::xmlImportAttribute( 196 const char* key, 197 const char* value, 198 ImportContext& context) { 199 200 ASSERT(m_message); 201 202 if(!strcmp(key, "what")) 203 m_message->what = atol(value); 204 else if(!strcmp(key, "name")) 205 m_name.SetTo(value); 206 } 207 208 void MessageIO::xmlImportContent( 209 const char* data, 210 uint32 length, 211 ImportContext& context) {} 212 213 void MessageIO::xmlImportChild( 214 IPersistent* child, 215 ImportContext& context) { 216 217 ASSERT(m_message); 218 219 if(strcmp(context.element(), s_element) != 0) { 220 context.reportError("Unexpected child element.\n"); 221 return; 222 } 223 224 MessageIO* childMessageIO = dynamic_cast<MessageIO*>(child); 225 ASSERT(childMessageIO); 226 227 m_message->AddMessage( 228 childMessageIO->m_name.String(), 229 childMessageIO->m_message); 230 } 231 232 void MessageIO::xmlImportComplete( 233 ImportContext& context) {} 234 235 void MessageIO::xmlImportChildBegin( 236 const char* name, 237 ImportContext& context) { 238 239 // sanity checks 240 241 ASSERT(m_message); 242 243 if(strcmp(context.parentElement(), s_element) != 0) { 244 context.reportError("Unexpected parent element.\n"); 245 return; 246 } 247 248 if(!_isValidMessageElement(context.element())) { 249 context.reportError("Invalid message field element.\n"); 250 return; 251 } 252 253 m_fieldData.SetTo(""); 254 } 255 256 void MessageIO::xmlImportChildAttribute( 257 const char* key, 258 const char* value, 259 ImportContext& context) { 260 261 if(!strcmp(key, "name")) 262 m_fieldName.SetTo(value); 263 if(!strcmp(key, "value")) 264 m_fieldData.SetTo(value); 265 } 266 267 void MessageIO::xmlImportChildContent( 268 const char* data, 269 uint32 length, 270 ImportContext& context) { 271 272 m_fieldData.Append(data, length); 273 } 274 275 void MessageIO::xmlImportChildComplete( 276 const char* name, 277 ImportContext& context) { 278 279 ASSERT(m_message); 280 281 status_t err = _importField( 282 m_message, 283 name, 284 m_fieldName.String(), 285 m_fieldData.String()); 286 if(err < B_OK) { 287 context.reportWarning("Invalid field data.\n"); 288 } 289 } 290 291 // -------------------------------------------------------- // 292 // implementation 293 // -------------------------------------------------------- // 294 295 bool MessageIO::_isValidMessageElement( 296 const char* element) const { 297 298 if(!strcmp(element, _boolEl)) return true; 299 if(!strcmp(element, _int8El)) return true; 300 if(!strcmp(element, _int16El)) return true; 301 if(!strcmp(element, _int32El)) return true; 302 if(!strcmp(element, _int64El)) return true; 303 if(!strcmp(element, _floatEl)) return true; 304 if(!strcmp(element, _doubleEl)) return true; 305 if(!strcmp(element, _stringEl)) return true; 306 if(!strcmp(element, _pointEl)) return true; 307 if(!strcmp(element, _rectEl)) return true; 308 309 return false; 310 } 311 312 status_t MessageIO::_importField( 313 BMessage* message, 314 const char* element, 315 const char* name, 316 const char* data) { 317 318 // skip leading whitespace 319 while(*data && isspace(*data)) ++data; 320 321 if(!strcmp(element, _boolEl)) { 322 bool v; 323 if(!strcmp(data, "true") || !strcmp(data, "1")) 324 v = true; 325 else if(!strcmp(data, "false") || !strcmp(data, "0")) 326 v = false; 327 else 328 return B_BAD_VALUE; 329 return message->AddBool(name, v); 330 } 331 332 if(!strcmp(element, _int8El)) { 333 int8 v = atoi(data); 334 return message->AddInt8(name, v); 335 } 336 if(!strcmp(element, _int16El)) { 337 int16 v = atoi(data); 338 return message->AddInt16(name, v); 339 } 340 if(!strcmp(element, _int32El)) { 341 int32 v = atol(data); 342 return message->AddInt32(name, v); 343 } 344 if(!strcmp(element, _int64El)) { 345 // int64 v = atoll(data); 346 int64 v = strtoll(data, 0, 10); 347 return message->AddInt64(name, v); 348 } 349 if(!strcmp(element, _floatEl)) { 350 float v = (float)atof(data); 351 return message->AddFloat(name, v); 352 } 353 if(!strcmp(element, _doubleEl)) { 354 double v = atof(data); 355 return message->AddDouble(name, v); 356 } 357 358 if(!strcmp(element, _stringEl)) { 359 // +++++ chomp leading/trailing whitespace? 360 361 return message->AddString(name, data); 362 } 363 364 if(!strcmp(element, _pointEl)) { 365 BPoint p; 366 const char* ystart = strchr(data, ','); 367 if(!ystart) 368 return B_BAD_VALUE; 369 ++ystart; 370 if(!*ystart) 371 return B_BAD_VALUE; 372 p.x = (float)atof(data); 373 p.y = (float)atof(ystart); 374 375 return message->AddPoint(name, p); 376 } 377 378 if(!strcmp(element, _rectEl)) { 379 BRect r; 380 const char* topstart = strchr(data, ','); 381 if(!topstart) 382 return B_BAD_VALUE; 383 ++topstart; 384 if(!*topstart) 385 return B_BAD_VALUE; 386 387 const char* rightstart = strchr(topstart, ','); 388 if(!rightstart) 389 return B_BAD_VALUE; 390 ++rightstart; 391 if(!*rightstart) 392 return B_BAD_VALUE; 393 394 const char* bottomstart = strchr(rightstart, ','); 395 if(!bottomstart) 396 return B_BAD_VALUE; 397 ++bottomstart; 398 if(!*bottomstart) 399 return B_BAD_VALUE; 400 401 r.left = (float)atof(data); 402 r.top = (float)atof(topstart); 403 r.right = (float)atof(rightstart); 404 r.bottom = (float)atof(bottomstart); 405 406 return message->AddRect(name, r); 407 } 408 409 return B_BAD_INDEX; 410 } 411 412 status_t MessageIO::_exportField( 413 ExportContext& context, 414 BMessage* message, 415 type_code type, 416 const char* name, 417 int32 index) const { 418 419 status_t err; 420 BString elementName; 421 BString content; 422 423 switch(type) { 424 case B_BOOL_TYPE: { 425 bool v; 426 err = message->FindBool(name, index, &v); 427 if(err < B_OK) 428 return err; 429 elementName = _boolEl; 430 content = (v ? "true" : "false"); 431 break; 432 } 433 434 case B_INT8_TYPE: { 435 int8 v; 436 err = message->FindInt8(name, index, &v); 437 if(err < B_OK) 438 return err; 439 elementName = _int8El; 440 content << (int32)v; 441 break; 442 } 443 444 case B_INT16_TYPE: { 445 int16 v; 446 err = message->FindInt16(name, index, &v); 447 if(err < B_OK) 448 return err; 449 elementName = _int16El; 450 content << (int32)v; 451 break; 452 } 453 454 case B_INT32_TYPE: { 455 int32 v; 456 err = message->FindInt32(name, index, &v); 457 if(err < B_OK) 458 return err; 459 elementName = _int32El; 460 content << v; 461 break; 462 } 463 464 case B_INT64_TYPE: { 465 int64 v; 466 err = message->FindInt64(name, index, &v); 467 if(err < B_OK) 468 return err; 469 elementName = _int64El; 470 content << v; 471 break; 472 } 473 474 case B_FLOAT_TYPE: { 475 float v; 476 err = message->FindFloat(name, index, &v); 477 if(err < B_OK) 478 return err; 479 elementName = _floatEl; 480 content << v; // +++++ need adjustable precision! 481 break; 482 } 483 484 case B_DOUBLE_TYPE: { 485 double v; 486 err = message->FindDouble(name, index, &v); 487 if(err < B_OK) 488 return err; 489 elementName = _doubleEl; 490 content << (float)v; // +++++ need adjustable precision! 491 break; 492 } 493 494 case B_STRING_TYPE: { 495 const char* v; 496 err = message->FindString(name, index, &v); 497 if(err < B_OK) 498 return err; 499 elementName = _stringEl; 500 content = v; 501 break; 502 } 503 504 case B_POINT_TYPE: { 505 BPoint v; 506 err = message->FindPoint(name, index, &v); 507 if(err < B_OK) 508 return err; 509 elementName = _pointEl; 510 content << v.x << ", " << v.y; 511 break; 512 } 513 514 case B_RECT_TYPE: { 515 BRect v; 516 err = message->FindRect(name, index, &v); 517 if(err < B_OK) 518 return err; 519 elementName = _rectEl; 520 content << v.left << ", " << v.top << ", " << 521 v.right << ", " << v.bottom; 522 break; 523 } 524 525 case B_MESSAGE_TYPE: { 526 BMessage m; 527 err = message->FindMessage(name, index, &m); 528 if(err < B_OK) 529 return err; 530 531 // write child message 532 MessageIO io(&m); 533 io.m_name = name; 534 return context.writeObject(&io); 535 } 536 537 default: 538 return B_BAD_TYPE; 539 } 540 541 // spew the element 542 context.beginElement(elementName.String()); 543 context.writeAttr("name", name); 544 context.writeAttr("value", content.String()); 545 // context.beginContent(); 546 // context.writeString(content); 547 context.endElement(); 548 549 return B_OK; 550 } 551 // END -- MessageIO.cpp -- 552