⛏️ index : haiku.git

/*
 * 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(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
		"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;
}