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