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