1/*
2 * Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6
7#include "StandardMetaDataJsonEventListener.h"
8
9#include "Logger.h"
10
11
12#define KEY_CREATE_TIMESTAMP "createTimestamp"
13#define KEY_DATA_MODIFIED_TIMESTAMP "dataModifiedTimestamp"
14
15
16class SmdStackedEventListener : public BJsonEventListener {
17public:
18								SmdStackedEventListener(
19									StandardMetaDataJsonEventListener*
20										mainListener,
21									SmdStackedEventListener* parent);
22								~SmdStackedEventListener();
23
24				void			HandleError(status_t status, int32 line,
25									const char* message);
26				void			Complete();
27
28				status_t		ErrorStatus();
29
30				SmdStackedEventListener*
31								Parent();
32				StandardMetaData*
33								MetaData();
34
35protected:
36				void			SetStackedListenerMainListener(
37									SmdStackedEventListener* stackedListener);
38
39			StandardMetaDataJsonEventListener*
40								fMainListener;
41			SmdStackedEventListener*
42								fParent;
43};
44
45
46class SmdStackedArrayEventListener : public SmdStackedEventListener {
47public:
48								SmdStackedArrayEventListener(
49									StandardMetaDataJsonEventListener*
50										mainListener,
51									SmdStackedEventListener* parent);
52								~SmdStackedArrayEventListener();
53
54				bool			Handle(const BJsonEvent& event);
55
56};
57
58
59class SmdStackedObjectMessageEventListener : public SmdStackedEventListener {
60public:
61								SmdStackedObjectMessageEventListener(
62									BStringList* jsonPathObjectNames,
63									StandardMetaDataJsonEventListener*
64										mainListener,
65									SmdStackedEventListener* parent);
66								~SmdStackedObjectMessageEventListener();
67
68				bool			Handle(const BJsonEvent& event);
69
70private:
71			BStringList*		fJsonPathObjectNames;
72			BString				fNextItemName;
73};
74
75
76// #pragma mark - SmdStackedEventListener
77
78SmdStackedEventListener::SmdStackedEventListener(
79	StandardMetaDataJsonEventListener* mainListener,
80	SmdStackedEventListener* parent)
81{
82	fMainListener = mainListener;
83	fParent = parent;
84}
85
86
87SmdStackedEventListener::~SmdStackedEventListener()
88{
89}
90
91
92void
93SmdStackedEventListener::HandleError(status_t status, int32 line,
94	const char* message)
95{
96	fMainListener->HandleError(status, line, message);
97}
98
99
100void
101SmdStackedEventListener::Complete()
102{
103	fMainListener->Complete();
104}
105
106
107status_t
108SmdStackedEventListener::ErrorStatus()
109{
110	return fMainListener->ErrorStatus();
111}
112
113
114StandardMetaData*
115SmdStackedEventListener::MetaData()
116{
117	return fMainListener->MetaData();
118}
119
120
121SmdStackedEventListener*
122SmdStackedEventListener::Parent()
123{
124	return fParent;
125}
126
127
128void
129SmdStackedEventListener::SetStackedListenerMainListener(
130	SmdStackedEventListener* stackedListener)
131{
132	fMainListener->SetStackedListener(stackedListener);
133}
134
135
136// #pragma mark - SmdStackedArrayEventListener
137
138
139SmdStackedArrayEventListener::SmdStackedArrayEventListener(
140	StandardMetaDataJsonEventListener* mainListener,
141	SmdStackedEventListener* parent)
142	:
143	SmdStackedEventListener(mainListener, parent)
144{
145}
146
147
148SmdStackedArrayEventListener::~SmdStackedArrayEventListener()
149{
150}
151
152
153bool
154SmdStackedArrayEventListener::Handle(const BJsonEvent& event)
155{
156	if (ErrorStatus() != B_OK)
157		return false;
158
159	switch (event.EventType()) {
160
161		case B_JSON_NUMBER:
162		case B_JSON_STRING:
163		case B_JSON_TRUE:
164		case B_JSON_FALSE:
165		case B_JSON_NULL:
166			// ignore these atomic types in an array.
167			break;
168
169		case B_JSON_OBJECT_START:
170			SetStackedListenerMainListener(
171				new SmdStackedObjectMessageEventListener(NULL, fMainListener,
172					this));
173			break;
174
175		case B_JSON_ARRAY_START:
176			SetStackedListenerMainListener(new SmdStackedArrayEventListener(
177				fMainListener, this));
178			break;
179
180		case B_JSON_ARRAY_END:
181			SetStackedListenerMainListener(fParent);
182			delete this;
183			break;
184
185		case B_JSON_OBJECT_END:
186		case B_JSON_OBJECT_NAME:
187			HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
188				"illegal state when processing events in an array");
189			return false;
190	}
191
192	return true;
193}
194
195
196// #pragma mark - SmdStackedObjectMessageEventListener
197
198SmdStackedObjectMessageEventListener::SmdStackedObjectMessageEventListener(
199	BStringList* jsonPathObjectNames,
200	StandardMetaDataJsonEventListener* mainListener,
201	SmdStackedEventListener* parent)
202	:
203	SmdStackedEventListener(mainListener, parent)
204{
205	fJsonPathObjectNames = jsonPathObjectNames;
206}
207
208
209SmdStackedObjectMessageEventListener::~SmdStackedObjectMessageEventListener()
210{
211	if (fJsonPathObjectNames != NULL)
212		delete fJsonPathObjectNames;
213}
214
215
216bool
217SmdStackedObjectMessageEventListener::Handle(const BJsonEvent& event)
218{
219	if (ErrorStatus() != B_OK)
220		return false;
221
222	switch (event.EventType()) {
223
224		case B_JSON_OBJECT_NAME:
225			fNextItemName = event.Content();
226			break;
227
228		case B_JSON_NUMBER:
229			if (fJsonPathObjectNames != NULL
230				&& fJsonPathObjectNames->IsEmpty()) {
231
232				if (fNextItemName == KEY_CREATE_TIMESTAMP) {
233					MetaData()->SetCreateTimestamp(
234						event.ContentInteger());
235				}
236
237				if (fNextItemName == KEY_DATA_MODIFIED_TIMESTAMP) {
238					MetaData()->SetDataModifiedTimestamp(
239						event.ContentInteger());
240				}
241			}
242			break;
243
244		case B_JSON_STRING:
245		case B_JSON_TRUE:
246		case B_JSON_FALSE:
247		case B_JSON_NULL:
248			// ignore these atomic types as they are not required to fill the
249			// data structure.
250			break;
251
252		case B_JSON_OBJECT_START:
253		{
254			BStringList* nextJsonPathObjectNames = NULL;
255
256			// if this next object is on the path then remove it from the
257			// path and carry on down.  If it's not on the path then just
258			// drop the path from the next object.
259
260			if (fJsonPathObjectNames != NULL
261				&& !fJsonPathObjectNames->IsEmpty()
262				&& fNextItemName == fJsonPathObjectNames->StringAt(0)) {
263				nextJsonPathObjectNames = new BStringList(*fJsonPathObjectNames);
264				nextJsonPathObjectNames->Remove(0);
265			}
266
267			SetStackedListenerMainListener(
268				new SmdStackedObjectMessageEventListener(nextJsonPathObjectNames,
269					fMainListener, this));
270			break;
271		}
272
273		case B_JSON_ARRAY_START:
274			SetStackedListenerMainListener(new SmdStackedArrayEventListener(
275				fMainListener, this));
276			break;
277
278		case B_JSON_OBJECT_END:
279			SetStackedListenerMainListener(fParent);
280			delete this;
281			break;
282
283		case B_JSON_ARRAY_END:
284			HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
285				"illegal state when processing events in an array");
286			return false;
287	}
288
289	return true;
290}
291
292
293// #pragma mark - StandardMetaDataJsonEventListener
294
295
296StandardMetaDataJsonEventListener::StandardMetaDataJsonEventListener(
297	const BString& jsonPath,
298	StandardMetaData& metaData)
299{
300	fMetaData = &metaData;
301	SetJsonPath(jsonPath);
302	fErrorStatus = B_OK;
303	fStackedListener = NULL;
304}
305
306
307StandardMetaDataJsonEventListener::~StandardMetaDataJsonEventListener()
308{
309	if (fStackedListener != NULL)
310		delete fStackedListener;
311}
312
313
314void
315StandardMetaDataJsonEventListener::SetJsonPath(const BString& jsonPath)
316{
317	jsonPath.Split(".", true, fJsonPathObjectNames);
318
319	if (fJsonPathObjectNames.IsEmpty()) {
320		HandleError(B_BAD_VALUE, JSON_EVENT_LISTENER_ANY_LINE,
321			"json path required");
322	} else {
323		if (fJsonPathObjectNames.First() != "$") {
324			HandleError(B_BAD_VALUE, JSON_EVENT_LISTENER_ANY_LINE,
325				"illegal json path; should start with '$");
326		}
327	}
328}
329
330
331void
332StandardMetaDataJsonEventListener::SetStackedListener(
333	SmdStackedEventListener *listener)
334{
335	fStackedListener = listener;
336}
337
338
339bool
340StandardMetaDataJsonEventListener::Handle(const BJsonEvent& event)
341{
342	if (fErrorStatus != B_OK)
343		return false;
344
345		// best to exit early if the parsing has obtained all of the required
346		// data already.
347
348	if (fMetaData->IsPopulated())
349		return false;
350
351	if (fStackedListener != NULL)
352		return fStackedListener->Handle(event);
353
354		// the first thing that comes in must be an object container.  It is
355		// bad data if it does not start with an object container.
356
357	switch (event.EventType()) {
358
359		case B_JSON_OBJECT_START:
360		{
361			BStringList* jsonPathObjectNames = new BStringList(
362				fJsonPathObjectNames);
363			jsonPathObjectNames->Remove(0);
364
365			SetStackedListener(
366				new SmdStackedObjectMessageEventListener(
367					jsonPathObjectNames, this, NULL)
368			);
369		}
370		break;
371
372		default:
373			HandleError(B_BAD_DATA, JSON_EVENT_LISTENER_ANY_LINE,
374				"the top level element must be an object");
375			return false;
376
377	}
378
379	return true;
380}
381
382
383void
384StandardMetaDataJsonEventListener::HandleError(status_t status, int32 line,
385	const char* message)
386{
387	HDERROR("an error has arisen processing the standard "
388		"meta data; %s", message);
389	fErrorStatus = status;
390}
391
392
393void
394StandardMetaDataJsonEventListener::Complete()
395{
396}
397
398
399status_t
400StandardMetaDataJsonEventListener::ErrorStatus()
401{
402	return fErrorStatus;
403}
404
405
406StandardMetaData*
407StandardMetaDataJsonEventListener::MetaData()
408{
409	return fMetaData;
410}