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
~MessageIO()71 MessageIO::~MessageIO() {
72 if(m_ownMessage && m_message)
73 delete m_message;
74 }
75
MessageIO()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
MessageIO(const BMessage * message)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
setMessage(BMessage * message)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*/
AddTo(XML::DocumentType * docType)110 void MessageIO::AddTo(
111 XML::DocumentType* docType) {
112
113 docType->addMapping(new Mapping<MessageIO>(s_element));
114 }
115
116 // -------------------------------------------------------- //
117 // EXPORT:
118 // -------------------------------------------------------- //
119
xmlExportBegin(ExportContext & context) const120 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
xmlExportAttributes(ExportContext & context) const130 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
xmlExportContent(ExportContext & context) const139 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
xmlExportEnd(ExportContext & context) const208 void MessageIO::xmlExportEnd(
209 ExportContext& context) const {
210 context.endElement();
211 }
212
213
214 // -------------------------------------------------------- //
215 // IMPORT:
216 // -------------------------------------------------------- //
217
xmlImportBegin(ImportContext & context)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
xmlImportAttribute(const char * key,const char * value,ImportContext & context)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
xmlImportContent(const char * data,uint32 length,ImportContext & context)243 void MessageIO::xmlImportContent(
244 const char* data,
245 uint32 length,
246 ImportContext& context) {}
247
xmlImportChild(IPersistent * child,ImportContext & context)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
xmlImportComplete(ImportContext & context)267 void MessageIO::xmlImportComplete(
268 ImportContext& context) {}
269
xmlImportChildBegin(const char * name,ImportContext & context)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
xmlImportChildAttribute(const char * key,const char * value,ImportContext & context)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
xmlImportChildContent(const char * data,uint32 length,ImportContext & context)302 void MessageIO::xmlImportChildContent(
303 const char* data,
304 uint32 length,
305 ImportContext& context) {
306
307 m_fieldData.Append(data, length);
308 }
309
xmlImportChildComplete(const char * name,ImportContext & context)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
_isValidMessageElement(const char * element) const330 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
_importField(BMessage * message,const char * element,const char * name,const char * data)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
_exportField(ExportContext & context,BMessage * message,type_code type,const char * name,int32 index) const447 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