haiku/src/kits/shared/JsonMessageWriter.cpp

538 lines
10 KiB
C++

/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#include "JsonMessageWriter.h"
namespace BPrivate {
/*! The class and sub-classes of it are used as a stack internal to the
BJsonMessageWriter class. As the JSON is parsed, the stack of these
internal listeners follows the stack of the JSON parsing in terms of
containers; arrays and objects.
*/
class BStackedMessageEventListener : public BJsonEventListener {
public:
BStackedMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
uint32 messageWhat);
BStackedMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message);
~BStackedMessageEventListener();
bool Handle(const BJsonEvent& event);
void HandleError(status_t status, int32 line,
const char* message);
void Complete();
void AddMessage(BMessage* value);
status_t ErrorStatus();
virtual const char* NextItemName() = 0;
BStackedMessageEventListener*
Parent();
protected:
void AddBool(bool value);
void AddNull();
void AddDouble(double value);
void AddString(const char* value);
virtual bool WillAdd();
virtual void DidAdd();
void SetStackedListenerOnWriter(
BStackedMessageEventListener*
stackedListener);
BJsonMessageWriter* fWriter;
bool fOwnsMessage;
BStackedMessageEventListener
*fParent;
BMessage* fMessage;
};
class BStackedArrayMessageEventListener : public BStackedMessageEventListener {
public:
BStackedArrayMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent);
BStackedArrayMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message);
~BStackedArrayMessageEventListener();
bool Handle(const BJsonEvent& event);
const char* NextItemName();
protected:
void DidAdd();
private:
uint32 fCount;
BString fNextItemName;
};
class BStackedObjectMessageEventListener : public BStackedMessageEventListener {
public:
BStackedObjectMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent);
BStackedObjectMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message);
~BStackedObjectMessageEventListener();
bool Handle(const BJsonEvent& event);
const char* NextItemName();
protected:
bool WillAdd();
void DidAdd();
private:
BString fNextItemName;
};
} // namespace BPrivate
using BPrivate::BStackedMessageEventListener;
using BPrivate::BStackedArrayMessageEventListener;
using BPrivate::BStackedObjectMessageEventListener;
// #pragma mark - BStackedMessageEventListener
BStackedMessageEventListener::BStackedMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
uint32 messageWhat)
{
fWriter = writer;
fParent = parent;
fOwnsMessage = true;
fMessage = new BMessage(messageWhat);
}
BStackedMessageEventListener::BStackedMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message)
{
fWriter = writer;
fParent = parent;
fOwnsMessage = false;
fMessage = message;
}
BStackedMessageEventListener::~BStackedMessageEventListener()
{
if (fOwnsMessage)
delete fMessage;
}
bool
BStackedMessageEventListener::Handle(const BJsonEvent& event)
{
if (fWriter->ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_NUMBER:
AddDouble(event.ContentDouble());
break;
case B_JSON_STRING:
AddString(event.Content());
break;
case B_JSON_TRUE:
AddBool(true);
break;
case B_JSON_FALSE:
AddBool(false);
break;
case B_JSON_NULL:
AddNull();
break;
case B_JSON_OBJECT_START:
{
SetStackedListenerOnWriter(new BStackedObjectMessageEventListener(
fWriter, this));
break;
}
case B_JSON_ARRAY_START:
{
SetStackedListenerOnWriter(new BStackedArrayMessageEventListener(
fWriter, this));
break;
}
default:
{
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
"unexpected type of json item to add to container");
return false;
}
}
return ErrorStatus() == B_OK;
}
void
BStackedMessageEventListener::HandleError(status_t status, int32 line,
const char* message)
{
fWriter->HandleError(status, line, message);
}
void
BStackedMessageEventListener::Complete()
{
// illegal state.
HandleError(JSON_EVENT_LISTENER_ANY_LINE, B_NOT_ALLOWED,
"Complete() called on stacked message listener");
}
void
BStackedMessageEventListener::AddMessage(BMessage* message)
{
if (WillAdd()) {
fMessage->AddMessage(NextItemName(), message);
DidAdd();
}
}
status_t
BStackedMessageEventListener::ErrorStatus()
{
return fWriter->ErrorStatus();
}
BStackedMessageEventListener*
BStackedMessageEventListener::Parent()
{
return fParent;
}
void
BStackedMessageEventListener::AddBool(bool value)
{
if (WillAdd()) {
fMessage->AddBool(NextItemName(), value);
DidAdd();
}
}
void
BStackedMessageEventListener::AddNull()
{
if (WillAdd()) {
fMessage->AddPointer(NextItemName(), (void*)NULL);
DidAdd();
}
}
void
BStackedMessageEventListener::AddDouble(double value)
{
if (WillAdd()) {
fMessage->AddDouble(NextItemName(), value);
DidAdd();
}
}
void
BStackedMessageEventListener::AddString(const char* value)
{
if (WillAdd()) {
fMessage->AddString(NextItemName(), value);
DidAdd();
}
}
bool
BStackedMessageEventListener::WillAdd()
{
return true;
}
void
BStackedMessageEventListener::DidAdd()
{
// noop - present for overriding
}
void
BStackedMessageEventListener::SetStackedListenerOnWriter(
BStackedMessageEventListener* stackedListener)
{
fWriter->SetStackedListener(stackedListener);
}
// #pragma mark - BStackedArrayMessageEventListener
BStackedArrayMessageEventListener::BStackedArrayMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent)
:
BStackedMessageEventListener(writer, parent, B_JSON_MESSAGE_WHAT_ARRAY)
{
fCount = 0;
}
BStackedArrayMessageEventListener::BStackedArrayMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message)
:
BStackedMessageEventListener(writer, parent, message)
{
message->what = B_JSON_MESSAGE_WHAT_ARRAY;
fCount = 0;
}
BStackedArrayMessageEventListener::~BStackedArrayMessageEventListener()
{
}
bool
BStackedArrayMessageEventListener::Handle(const BJsonEvent& event)
{
if (fWriter->ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_ARRAY_END:
{
if (fParent != NULL)
fParent->AddMessage(fMessage);
SetStackedListenerOnWriter(fParent);
delete this;
break;
}
default:
return BStackedMessageEventListener::Handle(event);
}
return true;
}
const char*
BStackedArrayMessageEventListener::NextItemName()
{
fNextItemName.SetToFormat("%" B_PRIu32, fCount);
return fNextItemName.String();
}
void
BStackedArrayMessageEventListener::DidAdd()
{
BStackedMessageEventListener::DidAdd();
fCount++;
}
// #pragma mark - BStackedObjectMessageEventListener
BStackedObjectMessageEventListener::BStackedObjectMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent)
:
BStackedMessageEventListener(writer, parent, B_JSON_MESSAGE_WHAT_OBJECT)
{
}
BStackedObjectMessageEventListener::BStackedObjectMessageEventListener(
BJsonMessageWriter* writer,
BStackedMessageEventListener* parent,
BMessage* message)
:
BStackedMessageEventListener(writer, parent, message)
{
message->what = B_JSON_MESSAGE_WHAT_OBJECT;
}
BStackedObjectMessageEventListener::~BStackedObjectMessageEventListener()
{
}
bool
BStackedObjectMessageEventListener::Handle(const BJsonEvent& event)
{
if (fWriter->ErrorStatus() != B_OK)
return false;
switch (event.EventType()) {
case B_JSON_OBJECT_END:
{
if (fParent != NULL)
fParent->AddMessage(fMessage);
SetStackedListenerOnWriter(fParent);
delete this;
break;
}
case B_JSON_OBJECT_NAME:
fNextItemName.SetTo(event.Content());
break;
default:
return BStackedMessageEventListener::Handle(event);
}
return true;
}
const char*
BStackedObjectMessageEventListener::NextItemName()
{
return fNextItemName.String();
}
bool
BStackedObjectMessageEventListener::WillAdd()
{
if (0 == fNextItemName.Length()) {
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
"missing name for adding value into an object");
return false;
}
return true;
}
void
BStackedObjectMessageEventListener::DidAdd()
{
BStackedMessageEventListener::DidAdd();
fNextItemName.SetTo("", 0);
}
// #pragma mark - BJsonMessageWriter
BJsonMessageWriter::BJsonMessageWriter(BMessage& message)
{
fTopLevelMessage = &message;
fStackedListener = NULL;
}
BJsonMessageWriter::~BJsonMessageWriter()
{
BStackedMessageEventListener* listener = fStackedListener;
while (listener != NULL) {
BStackedMessageEventListener* nextListener = listener->Parent();
delete listener;
listener = nextListener;
}
fStackedListener = NULL;
}
bool
BJsonMessageWriter::Handle(const BJsonEvent& event)
{
if (fErrorStatus != B_OK)
return false;
if (fStackedListener != NULL)
return fStackedListener->Handle(event);
else {
switch(event.EventType()) {
case B_JSON_OBJECT_START:
{
SetStackedListener(new BStackedObjectMessageEventListener(
this, NULL, fTopLevelMessage));
break;
}
case B_JSON_ARRAY_START:
{
fTopLevelMessage->what = B_JSON_MESSAGE_WHAT_ARRAY;
SetStackedListener(new BStackedArrayMessageEventListener(
this, NULL, fTopLevelMessage));
break;
}
default:
{
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
"a message object can only handle an object or an array"
"at the top level");
return false;
}
}
}
return true; // keep going
}
void
BJsonMessageWriter::Complete()
{
if (fStackedListener != NULL) {
HandleError(B_BAD_DATA, JSON_EVENT_LISTENER_ANY_LINE,
"unexpected end of input data processing structure");
}
}
void
BJsonMessageWriter::SetStackedListener(
BStackedMessageEventListener* listener)
{
fStackedListener = listener;
}