xref: /haiku/src/apps/cortex/Persistence/Wrappers/MessageIO.cpp (revision 1acbe440b8dd798953bec31d18ee589aa3f71b73)
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