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