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