* Copyright (c) 1999-2000, Eric Moon.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions, and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "MessageIO.h"
#include <BeBuild.h>
#include <Debug.h>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <vector>
#include <utility>
using namespace std;
__USE_CORTEX_NAMESPACE
const char* const MessageIO::s_element = "BMessage";
const char* _boolEl = "bool";
const char* _int8El = "int8";
const char* _int16El = "int16";
const char* _int32El = "int32";
const char* _int64El = "int64";
const char* _floatEl = "float";
const char* _doubleEl = "double";
const char* _stringEl = "string";
const char* _pointEl = "point";
const char* _rectEl = "rect";
MessageIO::~MessageIO() {
if(m_ownMessage && m_message)
delete m_message;
}
MessageIO::MessageIO() :
m_ownMessage(true),
m_message(0) {}
MessageIO::MessageIO(
const BMessage* message) :
m_ownMessage(false),
m_message(const_cast<BMessage*>(message)) {
ASSERT(m_message);
}
void MessageIO::setMessage(
BMessage* message) {
if(m_ownMessage && m_message)
delete m_message;
m_ownMessage = false;
m_message = message;
ASSERT(m_message);
}
void MessageIO::AddTo(
XML::DocumentType* docType) {
docType->addMapping(new Mapping<MessageIO>(s_element));
}
void MessageIO::xmlExportBegin(
ExportContext& context) const {
if(!m_message) {
context.reportError("No message data to export.\n");
return;
}
context.beginElement(s_element);
}
void MessageIO::xmlExportAttributes(
ExportContext& context) const {
if(m_message->what)
context.writeAttr("what", m_message->what);
if(m_name.Length())
context.writeAttr("name", m_name.String());
}
void MessageIO::xmlExportContent(
ExportContext& context) const {
ASSERT(m_message);
status_t err;
typedef vector<BString> field_set;
field_set fields;
#ifdef B_BEOS_VERSION_DANO
const
#endif
char* name;
type_code type;
int32 count;
for(
int32 n = 0;
m_message->GetInfo(B_ANY_TYPE, n, &name, &type, &count) == B_OK;
++n) {
fields.push_back(name);
}
if(!fields.size())
return;
context.beginContent();
bool done = false;
for(int32 n = 0; !done; ++n) {
done = true;
for(
uint32 fieldIndex = 0;
fieldIndex < fields.size();
++fieldIndex) {
if(m_message->GetInfo(
fields[fieldIndex].String(),
&type,
&count) < B_OK || n >= count)
continue;
done = false;
err = _exportField(
context,
m_message,
type,
fields[fieldIndex].String(),
n);
if(err < B_OK) {
BString errText;
errText << "Couldn't export field '" << fields[fieldIndex] <<
"' index " << n << ": " << strerror(err) << "\n";
context.reportError(errText.String());
return;
}
}
}
}
void MessageIO::xmlExportEnd(
ExportContext& context) const {
context.endElement();
}
void MessageIO::xmlImportBegin(
ImportContext& context) {
if(m_message) {
if(m_ownMessage)
delete m_message;
}
m_message = new BMessage();
m_name.SetTo("");
}
void MessageIO::xmlImportAttribute(
const char* key,
const char* value,
ImportContext& context) {
ASSERT(m_message);
if(!strcmp(key, "what"))
m_message->what = atol(value);
else if(!strcmp(key, "name"))
m_name.SetTo(value);
}
void MessageIO::xmlImportContent(
const char* data,
uint32 length,
ImportContext& context) {}
void MessageIO::xmlImportChild(
IPersistent* child,
ImportContext& context) {
ASSERT(m_message);
if(strcmp(context.element(), s_element) != 0) {
context.reportError("Unexpected child element.\n");
return;
}
MessageIO* childMessageIO = dynamic_cast<MessageIO*>(child);
ASSERT(childMessageIO);
m_message->AddMessage(
childMessageIO->m_name.String(),
childMessageIO->m_message);
}
void MessageIO::xmlImportComplete(
ImportContext& context) {}
void MessageIO::xmlImportChildBegin(
const char* name,
ImportContext& context) {
ASSERT(m_message);
if(strcmp(context.parentElement(), s_element) != 0) {
context.reportError("Unexpected parent element.\n");
return;
}
if(!_isValidMessageElement(context.element())) {
context.reportError("Invalid message field element.\n");
return;
}
m_fieldData.SetTo("");
}
void MessageIO::xmlImportChildAttribute(
const char* key,
const char* value,
ImportContext& context) {
if(!strcmp(key, "name"))
m_fieldName.SetTo(value);
if(!strcmp(key, "value"))
m_fieldData.SetTo(value);
}
void MessageIO::xmlImportChildContent(
const char* data,
uint32 length,
ImportContext& context) {
m_fieldData.Append(data, length);
}
void MessageIO::xmlImportChildComplete(
const char* name,
ImportContext& context) {
ASSERT(m_message);
status_t err = _importField(
m_message,
name,
m_fieldName.String(),
m_fieldData.String());
if(err < B_OK) {
context.reportWarning("Invalid field data.\n");
}
}
bool MessageIO::_isValidMessageElement(
const char* element) const {
if(!strcmp(element, _boolEl)) return true;
if(!strcmp(element, _int8El)) return true;
if(!strcmp(element, _int16El)) return true;
if(!strcmp(element, _int32El)) return true;
if(!strcmp(element, _int64El)) return true;
if(!strcmp(element, _floatEl)) return true;
if(!strcmp(element, _doubleEl)) return true;
if(!strcmp(element, _stringEl)) return true;
if(!strcmp(element, _pointEl)) return true;
if(!strcmp(element, _rectEl)) return true;
return false;
}
status_t MessageIO::_importField(
BMessage* message,
const char* element,
const char* name,
const char* data) {
while(*data && isspace(*data)) ++data;
if(!strcmp(element, _boolEl)) {
bool v;
if(!strcmp(data, "true") || !strcmp(data, "1"))
v = true;
else if(!strcmp(data, "false") || !strcmp(data, "0"))
v = false;
else
return B_BAD_VALUE;
return message->AddBool(name, v);
}
if(!strcmp(element, _int8El)) {
int8 v = atoi(data);
return message->AddInt8(name, v);
}
if(!strcmp(element, _int16El)) {
int16 v = atoi(data);
return message->AddInt16(name, v);
}
if(!strcmp(element, _int32El)) {
int32 v = atol(data);
return message->AddInt32(name, v);
}
if(!strcmp(element, _int64El)) {
int64 v = strtoll(data, 0, 10);
return message->AddInt64(name, v);
}
if(!strcmp(element, _floatEl)) {
float v = (float)atof(data);
return message->AddFloat(name, v);
}
if(!strcmp(element, _doubleEl)) {
double v = atof(data);
return message->AddDouble(name, v);
}
if(!strcmp(element, _stringEl)) {
return message->AddString(name, data);
}
if(!strcmp(element, _pointEl)) {
BPoint p;
const char* ystart = strchr(data, ',');
if(!ystart)
return B_BAD_VALUE;
++ystart;
if(!*ystart)
return B_BAD_VALUE;
p.x = (float)atof(data);
p.y = (float)atof(ystart);
return message->AddPoint(name, p);
}
if(!strcmp(element, _rectEl)) {
BRect r;
const char* topstart = strchr(data, ',');
if(!topstart)
return B_BAD_VALUE;
++topstart;
if(!*topstart)
return B_BAD_VALUE;
const char* rightstart = strchr(topstart, ',');
if(!rightstart)
return B_BAD_VALUE;
++rightstart;
if(!*rightstart)
return B_BAD_VALUE;
const char* bottomstart = strchr(rightstart, ',');
if(!bottomstart)
return B_BAD_VALUE;
++bottomstart;
if(!*bottomstart)
return B_BAD_VALUE;
r.left = (float)atof(data);
r.top = (float)atof(topstart);
r.right = (float)atof(rightstart);
r.bottom = (float)atof(bottomstart);
return message->AddRect(name, r);
}
return B_BAD_INDEX;
}
status_t MessageIO::_exportField(
ExportContext& context,
BMessage* message,
type_code type,
const char* name,
int32 index) const {
status_t err;
BString elementName;
BString content;
switch(type) {
case B_BOOL_TYPE: {
bool v;
err = message->FindBool(name, index, &v);
if(err < B_OK)
return err;
elementName = _boolEl;
content = (v ? "true" : "false");
break;
}
case B_INT8_TYPE: {
int8 v;
err = message->FindInt8(name, index, &v);
if(err < B_OK)
return err;
elementName = _int8El;
content << (int32)v;
break;
}
case B_INT16_TYPE: {
int16 v;
err = message->FindInt16(name, index, &v);
if(err < B_OK)
return err;
elementName = _int16El;
content << (int32)v;
break;
}
case B_INT32_TYPE: {
int32 v;
err = message->FindInt32(name, index, &v);
if(err < B_OK)
return err;
elementName = _int32El;
content << v;
break;
}
case B_INT64_TYPE: {
int64 v;
err = message->FindInt64(name, index, &v);
if(err < B_OK)
return err;
elementName = _int64El;
content << v;
break;
}
case B_FLOAT_TYPE: {
float v;
err = message->FindFloat(name, index, &v);
if(err < B_OK)
return err;
elementName = _floatEl;
content << v;
break;
}
case B_DOUBLE_TYPE: {
double v;
err = message->FindDouble(name, index, &v);
if(err < B_OK)
return err;
elementName = _doubleEl;
content << (float)v;
break;
}
case B_STRING_TYPE: {
const char* v;
err = message->FindString(name, index, &v);
if(err < B_OK)
return err;
elementName = _stringEl;
content = v;
break;
}
case B_POINT_TYPE: {
BPoint v;
err = message->FindPoint(name, index, &v);
if(err < B_OK)
return err;
elementName = _pointEl;
content << v.x << ", " << v.y;
break;
}
case B_RECT_TYPE: {
BRect v;
err = message->FindRect(name, index, &v);
if(err < B_OK)
return err;
elementName = _rectEl;
content << v.left << ", " << v.top << ", " <<
v.right << ", " << v.bottom;
break;
}
case B_MESSAGE_TYPE: {
BMessage m;
err = message->FindMessage(name, index, &m);
if(err < B_OK)
return err;
MessageIO io(&m);
io.m_name = name;
return context.writeObject(&io);
}
default:
return B_BAD_TYPE;
}
context.beginElement(elementName.String());
context.writeAttr("name", name);
context.writeAttr("value", content.String());
context.endElement();
return B_OK;
}