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