1/*
2 * Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "JsonMessageWriter.h"
8
9
10namespace BPrivate {
11
12/*! The class and sub-classes of it are used as a stack internal to the
13    BJsonMessageWriter class.  As the JSON is parsed, the stack of these
14    internal listeners follows the stack of the JSON parsing in terms of
15    containers; arrays and objects.
16*/
17
18class BStackedMessageEventListener : public BJsonEventListener {
19public:
20								BStackedMessageEventListener(
21									BJsonMessageWriter* writer,
22									BStackedMessageEventListener* parent,
23									uint32 messageWhat);
24								BStackedMessageEventListener(
25									BJsonMessageWriter* writer,
26									BStackedMessageEventListener* parent,
27									BMessage* message);
28								~BStackedMessageEventListener();
29
30				bool			Handle(const BJsonEvent& event);
31				void			HandleError(status_t status, int32 line,
32									const char* message);
33				void			Complete();
34
35				void			AddMessage(BMessage* value);
36
37				status_t		ErrorStatus();
38		virtual	const char*		NextItemName() = 0;
39
40				BStackedMessageEventListener*
41								Parent();
42
43protected:
44				void			AddBool(bool value);
45				void			AddNull();
46				void			AddDouble(double value);
47				void			AddString(const char* value);
48
49		virtual bool			WillAdd();
50		virtual void			DidAdd();
51
52				void			SetStackedListenerOnWriter(
53									BStackedMessageEventListener*
54									stackedListener);
55
56			BJsonMessageWriter*	fWriter;
57			bool				fOwnsMessage;
58			BStackedMessageEventListener
59								*fParent;
60			BMessage*			fMessage;
61};
62
63
64class BStackedArrayMessageEventListener : public BStackedMessageEventListener {
65public:
66								BStackedArrayMessageEventListener(
67									BJsonMessageWriter* writer,
68									BStackedMessageEventListener* parent);
69								BStackedArrayMessageEventListener(
70									BJsonMessageWriter* writer,
71									BStackedMessageEventListener* parent,
72									BMessage* message);
73								~BStackedArrayMessageEventListener();
74
75				bool			Handle(const BJsonEvent& event);
76
77				const char*		NextItemName();
78
79protected:
80				void			DidAdd();
81
82private:
83				uint32			fCount;
84				BString			fNextItemName;
85
86};
87
88
89class BStackedObjectMessageEventListener : public BStackedMessageEventListener {
90public:
91								BStackedObjectMessageEventListener(
92									BJsonMessageWriter* writer,
93									BStackedMessageEventListener* parent);
94								BStackedObjectMessageEventListener(
95									BJsonMessageWriter* writer,
96									BStackedMessageEventListener* parent,
97									BMessage* message);
98								~BStackedObjectMessageEventListener();
99
100				bool			Handle(const BJsonEvent& event);
101
102				const char*		NextItemName();
103
104protected:
105				bool			WillAdd();
106				void			DidAdd();
107private:
108				BString			fNextItemName;
109};
110
111} // namespace BPrivate
112
113using BPrivate::BStackedMessageEventListener;
114using BPrivate::BStackedArrayMessageEventListener;
115using BPrivate::BStackedObjectMessageEventListener;
116
117
118// #pragma mark - BStackedMessageEventListener
119
120
121BStackedMessageEventListener::BStackedMessageEventListener(
122	BJsonMessageWriter* writer,
123	BStackedMessageEventListener* parent,
124	uint32 messageWhat)
125{
126	fWriter = writer;
127	fParent = parent;
128	fOwnsMessage = true;
129	fMessage = new BMessage(messageWhat);
130}
131
132
133BStackedMessageEventListener::BStackedMessageEventListener(
134	BJsonMessageWriter* writer,
135	BStackedMessageEventListener* parent,
136	BMessage* message)
137{
138	fWriter = writer;
139	fParent = parent;
140	fOwnsMessage = false;
141	fMessage = message;
142}
143
144
145BStackedMessageEventListener::~BStackedMessageEventListener()
146{
147	if (fOwnsMessage)
148		delete fMessage;
149}
150
151
152bool
153BStackedMessageEventListener::Handle(const BJsonEvent& event)
154{
155	if (fWriter->ErrorStatus() != B_OK)
156		return false;
157
158	switch (event.EventType()) {
159
160		case B_JSON_NUMBER:
161			AddDouble(event.ContentDouble());
162			break;
163
164		case B_JSON_STRING:
165			AddString(event.Content());
166			break;
167
168		case B_JSON_TRUE:
169			AddBool(true);
170			break;
171
172		case B_JSON_FALSE:
173			AddBool(false);
174			break;
175
176		case B_JSON_NULL:
177			AddNull();
178			break;
179
180		case B_JSON_OBJECT_START:
181		{
182			SetStackedListenerOnWriter(new BStackedObjectMessageEventListener(
183				fWriter, this));
184			break;
185		}
186
187		case B_JSON_ARRAY_START:
188		{
189			SetStackedListenerOnWriter(new BStackedArrayMessageEventListener(
190				fWriter, this));
191			break;
192		}
193
194		default:
195		{
196			HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
197				"unexpected type of json item to add to container");
198			return false;
199		}
200	}
201
202	return ErrorStatus() == B_OK;
203}
204
205
206void
207BStackedMessageEventListener::HandleError(status_t status, int32 line,
208	const char* message)
209{
210	fWriter->HandleError(status, line, message);
211}
212
213
214void
215BStackedMessageEventListener::Complete()
216{
217	// illegal state.
218	HandleError(JSON_EVENT_LISTENER_ANY_LINE, B_NOT_ALLOWED,
219		"Complete() called on stacked message listener");
220}
221
222
223void
224BStackedMessageEventListener::AddMessage(BMessage* message)
225{
226	if (WillAdd()) {
227		fMessage->AddMessage(NextItemName(), message);
228		DidAdd();
229	}
230}
231
232
233status_t
234BStackedMessageEventListener::ErrorStatus()
235{
236	return fWriter->ErrorStatus();
237}
238
239
240BStackedMessageEventListener*
241BStackedMessageEventListener::Parent()
242{
243	return fParent;
244}
245
246
247void
248BStackedMessageEventListener::AddBool(bool value)
249{
250	if (WillAdd()) {
251		fMessage->AddBool(NextItemName(), value);
252		DidAdd();
253	}
254}
255
256void
257BStackedMessageEventListener::AddNull()
258{
259	if (WillAdd()) {
260		fMessage->AddPointer(NextItemName(), (void*)NULL);
261		DidAdd();
262	}
263}
264
265void
266BStackedMessageEventListener::AddDouble(double value)
267{
268	if (WillAdd()) {
269		fMessage->AddDouble(NextItemName(), value);
270		DidAdd();
271	}
272}
273
274void
275BStackedMessageEventListener::AddString(const char* value)
276{
277	if (WillAdd()) {
278		fMessage->AddString(NextItemName(), value);
279		DidAdd();
280	}
281}
282
283
284bool
285BStackedMessageEventListener::WillAdd()
286{
287	return true;
288}
289
290
291void
292BStackedMessageEventListener::DidAdd()
293{
294	// noop - present for overriding
295}
296
297
298void
299BStackedMessageEventListener::SetStackedListenerOnWriter(
300	BStackedMessageEventListener* stackedListener)
301{
302	fWriter->SetStackedListener(stackedListener);
303}
304
305
306// #pragma mark - BStackedArrayMessageEventListener
307
308
309BStackedArrayMessageEventListener::BStackedArrayMessageEventListener(
310	BJsonMessageWriter* writer,
311	BStackedMessageEventListener* parent)
312	:
313	BStackedMessageEventListener(writer, parent, B_JSON_MESSAGE_WHAT_ARRAY)
314{
315	fCount = 0;
316}
317
318
319BStackedArrayMessageEventListener::BStackedArrayMessageEventListener(
320	BJsonMessageWriter* writer,
321	BStackedMessageEventListener* parent,
322	BMessage* message)
323	:
324	BStackedMessageEventListener(writer, parent, message)
325{
326	message->what = B_JSON_MESSAGE_WHAT_ARRAY;
327	fCount = 0;
328}
329
330
331BStackedArrayMessageEventListener::~BStackedArrayMessageEventListener()
332{
333}
334
335
336bool
337BStackedArrayMessageEventListener::Handle(const BJsonEvent& event)
338{
339	if (fWriter->ErrorStatus() != B_OK)
340		return false;
341
342	switch (event.EventType()) {
343		case B_JSON_ARRAY_END:
344		{
345			if (fParent != NULL)
346				fParent->AddMessage(fMessage);
347			SetStackedListenerOnWriter(fParent);
348			delete this;
349			break;
350		}
351
352		default:
353			return BStackedMessageEventListener::Handle(event);
354	}
355
356	return true;
357}
358
359
360const char*
361BStackedArrayMessageEventListener::NextItemName()
362{
363	fNextItemName.SetToFormat("%" B_PRIu32, fCount);
364	return fNextItemName.String();
365}
366
367
368void
369BStackedArrayMessageEventListener::DidAdd()
370{
371	BStackedMessageEventListener::DidAdd();
372	fCount++;
373}
374
375
376// #pragma mark - BStackedObjectMessageEventListener
377
378
379BStackedObjectMessageEventListener::BStackedObjectMessageEventListener(
380	BJsonMessageWriter* writer,
381	BStackedMessageEventListener* parent)
382	:
383	BStackedMessageEventListener(writer, parent, B_JSON_MESSAGE_WHAT_OBJECT)
384{
385}
386
387
388BStackedObjectMessageEventListener::BStackedObjectMessageEventListener(
389	BJsonMessageWriter* writer,
390	BStackedMessageEventListener* parent,
391	BMessage* message)
392	:
393	BStackedMessageEventListener(writer, parent, message)
394{
395	message->what = B_JSON_MESSAGE_WHAT_OBJECT;
396}
397
398
399BStackedObjectMessageEventListener::~BStackedObjectMessageEventListener()
400{
401}
402
403
404bool
405BStackedObjectMessageEventListener::Handle(const BJsonEvent& event)
406{
407	if (fWriter->ErrorStatus() != B_OK)
408		return false;
409
410	switch (event.EventType()) {
411		case B_JSON_OBJECT_END:
412		{
413			if (fParent != NULL)
414				fParent->AddMessage(fMessage);
415			SetStackedListenerOnWriter(fParent);
416			delete this;
417			break;
418		}
419
420		case B_JSON_OBJECT_NAME:
421			fNextItemName.SetTo(event.Content());
422			break;
423
424		default:
425			return BStackedMessageEventListener::Handle(event);
426	}
427
428	return true;
429}
430
431
432const char*
433BStackedObjectMessageEventListener::NextItemName()
434{
435	return fNextItemName.String();
436}
437
438
439bool
440BStackedObjectMessageEventListener::WillAdd()
441{
442	if (0 == fNextItemName.Length()) {
443		HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
444				"missing name for adding value into an object");
445		return false;
446	}
447
448	return true;
449}
450
451
452void
453BStackedObjectMessageEventListener::DidAdd()
454{
455	BStackedMessageEventListener::DidAdd();
456	fNextItemName.SetTo("", 0);
457}
458
459
460// #pragma mark - BJsonMessageWriter
461
462
463BJsonMessageWriter::BJsonMessageWriter(BMessage& message)
464{
465	fTopLevelMessage = &message;
466	fStackedListener = NULL;
467}
468
469
470BJsonMessageWriter::~BJsonMessageWriter()
471{
472	BStackedMessageEventListener* listener = fStackedListener;
473
474	while (listener != NULL) {
475		BStackedMessageEventListener* nextListener = listener->Parent();
476		delete listener;
477		listener = nextListener;
478	}
479
480	fStackedListener = NULL;
481}
482
483
484bool
485BJsonMessageWriter::Handle(const BJsonEvent& event)
486{
487	if (fErrorStatus != B_OK)
488		return false;
489
490	if (fStackedListener != NULL)
491		return fStackedListener->Handle(event);
492	else {
493		switch(event.EventType()) {
494			case B_JSON_OBJECT_START:
495			{
496				SetStackedListener(new BStackedObjectMessageEventListener(
497					this, NULL, fTopLevelMessage));
498				break;
499			}
500
501			case B_JSON_ARRAY_START:
502			{
503				fTopLevelMessage->what = B_JSON_MESSAGE_WHAT_ARRAY;
504				SetStackedListener(new BStackedArrayMessageEventListener(
505					this, NULL, fTopLevelMessage));
506				break;
507			}
508
509			default:
510			{
511				HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
512					"a message object can only handle an object or an array"
513					"at the top level");
514				return false;
515			}
516		}
517	}
518
519	return true; // keep going
520}
521
522
523void
524BJsonMessageWriter::Complete()
525{
526	if (fStackedListener != NULL) {
527		HandleError(B_BAD_DATA, JSON_EVENT_LISTENER_ANY_LINE,
528			"unexpected end of input data processing structure");
529	}
530}
531
532
533void
534BJsonMessageWriter::SetStackedListener(
535	BStackedMessageEventListener* listener)
536{
537	fStackedListener = listener;
538}