xref: /haiku/src/kits/shared/JsonMessageWriter.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 
10 namespace 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 
18 class BStackedMessageEventListener : public BJsonEventListener {
19 public:
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 
43 protected:
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 
64 class BStackedArrayMessageEventListener : public BStackedMessageEventListener {
65 public:
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 
79 protected:
80 				void			DidAdd();
81 
82 private:
83 				uint32			fCount;
84 				BString			fNextItemName;
85 
86 };
87 
88 
89 class BStackedObjectMessageEventListener : public BStackedMessageEventListener {
90 public:
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 
104 protected:
105 				bool			WillAdd();
106 				void			DidAdd();
107 private:
108 				BString			fNextItemName;
109 };
110 
111 } // namespace BPrivate
112 
113 using BPrivate::BStackedMessageEventListener;
114 using BPrivate::BStackedArrayMessageEventListener;
115 using BPrivate::BStackedObjectMessageEventListener;
116 
117 
118 // #pragma mark - BStackedMessageEventListener
119 
120 
121 BStackedMessageEventListener::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 
133 BStackedMessageEventListener::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 
145 BStackedMessageEventListener::~BStackedMessageEventListener()
146 {
147 	if (fOwnsMessage)
148 		delete fMessage;
149 }
150 
151 
152 bool
153 BStackedMessageEventListener::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 
206 void
207 BStackedMessageEventListener::HandleError(status_t status, int32 line,
208 	const char* message)
209 {
210 	fWriter->HandleError(status, line, message);
211 }
212 
213 
214 void
215 BStackedMessageEventListener::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 
223 void
224 BStackedMessageEventListener::AddMessage(BMessage* message)
225 {
226 	if (WillAdd()) {
227 		fMessage->AddMessage(NextItemName(), message);
228 		DidAdd();
229 	}
230 }
231 
232 
233 status_t
234 BStackedMessageEventListener::ErrorStatus()
235 {
236 	return fWriter->ErrorStatus();
237 }
238 
239 
240 BStackedMessageEventListener*
241 BStackedMessageEventListener::Parent()
242 {
243 	return fParent;
244 }
245 
246 
247 void
248 BStackedMessageEventListener::AddBool(bool value)
249 {
250 	if (WillAdd()) {
251 		fMessage->AddBool(NextItemName(), value);
252 		DidAdd();
253 	}
254 }
255 
256 void
257 BStackedMessageEventListener::AddNull()
258 {
259 	if (WillAdd()) {
260 		fMessage->AddPointer(NextItemName(), (void*)NULL);
261 		DidAdd();
262 	}
263 }
264 
265 void
266 BStackedMessageEventListener::AddDouble(double value)
267 {
268 	if (WillAdd()) {
269 		fMessage->AddDouble(NextItemName(), value);
270 		DidAdd();
271 	}
272 }
273 
274 void
275 BStackedMessageEventListener::AddString(const char* value)
276 {
277 	if (WillAdd()) {
278 		fMessage->AddString(NextItemName(), value);
279 		DidAdd();
280 	}
281 }
282 
283 
284 bool
285 BStackedMessageEventListener::WillAdd()
286 {
287 	return true;
288 }
289 
290 
291 void
292 BStackedMessageEventListener::DidAdd()
293 {
294 	// noop - present for overriding
295 }
296 
297 
298 void
299 BStackedMessageEventListener::SetStackedListenerOnWriter(
300 	BStackedMessageEventListener* stackedListener)
301 {
302 	fWriter->SetStackedListener(stackedListener);
303 }
304 
305 
306 // #pragma mark - BStackedArrayMessageEventListener
307 
308 
309 BStackedArrayMessageEventListener::BStackedArrayMessageEventListener(
310 	BJsonMessageWriter* writer,
311 	BStackedMessageEventListener* parent)
312 	:
313 	BStackedMessageEventListener(writer, parent, B_JSON_MESSAGE_WHAT_ARRAY)
314 {
315 	fCount = 0;
316 }
317 
318 
319 BStackedArrayMessageEventListener::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 
331 BStackedArrayMessageEventListener::~BStackedArrayMessageEventListener()
332 {
333 }
334 
335 
336 bool
337 BStackedArrayMessageEventListener::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 
360 const char*
361 BStackedArrayMessageEventListener::NextItemName()
362 {
363 	fNextItemName.SetToFormat("%" B_PRIu32, fCount);
364 	return fNextItemName.String();
365 }
366 
367 
368 void
369 BStackedArrayMessageEventListener::DidAdd()
370 {
371 	BStackedMessageEventListener::DidAdd();
372 	fCount++;
373 }
374 
375 
376 // #pragma mark - BStackedObjectMessageEventListener
377 
378 
379 BStackedObjectMessageEventListener::BStackedObjectMessageEventListener(
380 	BJsonMessageWriter* writer,
381 	BStackedMessageEventListener* parent)
382 	:
383 	BStackedMessageEventListener(writer, parent, B_JSON_MESSAGE_WHAT_OBJECT)
384 {
385 }
386 
387 
388 BStackedObjectMessageEventListener::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 
399 BStackedObjectMessageEventListener::~BStackedObjectMessageEventListener()
400 {
401 }
402 
403 
404 bool
405 BStackedObjectMessageEventListener::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 
432 const char*
433 BStackedObjectMessageEventListener::NextItemName()
434 {
435 	return fNextItemName.String();
436 }
437 
438 
439 bool
440 BStackedObjectMessageEventListener::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 
452 void
453 BStackedObjectMessageEventListener::DidAdd()
454 {
455 	BStackedMessageEventListener::DidAdd();
456 	fNextItemName.SetTo("", 0);
457 }
458 
459 
460 // #pragma mark - BJsonMessageWriter
461 
462 
463 BJsonMessageWriter::BJsonMessageWriter(BMessage& message)
464 {
465 	fTopLevelMessage = &message;
466 	fStackedListener = NULL;
467 }
468 
469 
470 BJsonMessageWriter::~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 
484 bool
485 BJsonMessageWriter::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 
523 void
524 BJsonMessageWriter::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 
533 void
534 BJsonMessageWriter::SetStackedListener(
535 	BStackedMessageEventListener* listener)
536 {
537 	fStackedListener = listener;
538 }