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