/* * 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. */ // MessageIO.cpp #include "MessageIO.h" #include #include #include #include #include #include #include using namespace std; __USE_CORTEX_NAMESPACE // -------------------------------------------------------- // // constants // -------------------------------------------------------- // 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"; // -------------------------------------------------------- // // *** ctor/dtor/accessor // -------------------------------------------------------- // MessageIO::~MessageIO() { if(m_ownMessage && m_message) delete m_message; } MessageIO::MessageIO() : m_ownMessage(true), m_message(0) {} // When given a message to export, this object does NOT take // responsibility for deleting it. It will, however, handle // deletion of an imported BMessage. MessageIO::MessageIO( const BMessage* message) : m_ownMessage(false), m_message(const_cast(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); } // -------------------------------------------------------- // // *** static setup method // -------------------------------------------------------- // // call this method to install hooks for the tags needed by // MessageIO into the given document type /*static*/ void MessageIO::AddTo( XML::DocumentType* docType) { docType->addMapping(new Mapping(s_element)); } // -------------------------------------------------------- // // EXPORT: // -------------------------------------------------------- // 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; // +++++ the approach: // 1) build a list of field names // 2) export fields sorted first by index, then name typedef vector 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; // found a field at the current index, so don't give up 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(); } // -------------------------------------------------------- // // IMPORT: // -------------------------------------------------------- // void MessageIO::xmlImportBegin( ImportContext& context) { // create the message 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(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) { // sanity checks 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"); } } // -------------------------------------------------------- // // implementation // -------------------------------------------------------- // 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) { // skip leading whitespace 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 = atoll(data); 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)) { // +++++ chomp leading/trailing whitespace? 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; // +++++ need adjustable precision! break; } case B_DOUBLE_TYPE: { double v; err = message->FindDouble(name, index, &v); if(err < B_OK) return err; elementName = _doubleEl; content << (float)v; // +++++ need adjustable precision! 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; // write child message MessageIO io(&m); io.m_name = name; return context.writeObject(&io); } default: return B_BAD_TYPE; } // spew the element context.beginElement(elementName.String()); context.writeAttr("name", name); context.writeAttr("value", content.String()); // context.beginContent(); // context.writeString(content); context.endElement(); return B_OK; } // END -- MessageIO.cpp --