1#!/usr/bin/python
2
3# =====================================
4# Copyright 2017-2023, Andrew Lindesay
5# Distributed under the terms of the MIT License.
6# =====================================
7
8# This simple tool will read a JSON schema and will then generate a
9# listener for use the 'BJson' class in the Haiku system.  This
10# allows data conforming to the schema to be able to be parsed.
11
12import string
13import json
14import argparse
15import os
16import ustache
17import hdsjsonschemacommon
18
19HEADER_TEMPLATE = """
20/*
21 * Generated Listener Object for {{toplevel.cppname}}
22 */
23
24#ifndef GEN_JSON_SCHEMA_PARSER__{{toplevel.cppnameupper}}_H
25#define GEN_JSON_SCHEMA_PARSER__{{toplevel.cppnameupper}}_H
26
27#include <JsonEventListener.h>
28#include <ObjectList.h>
29
30#include "{{toplevel.cppname}}.h"
31
32class AbstractStacked{{toplevel.cppname}}JsonListener;
33
34class AbstractMain{{toplevel.cppname}}JsonListener : public BJsonEventListener {
35friend class AbstractStacked{{toplevel.cppname}}JsonListener;
36public:
37    AbstractMain{{toplevel.cppname}}JsonListener();
38    virtual ~AbstractMain{{toplevel.cppname}}JsonListener();
39
40    void HandleError(status_t status, int32 line, const char* message);
41    void Complete();
42    status_t ErrorStatus();
43
44protected:
45    void SetStackedListener(
46        AbstractStacked{{toplevel.cppname}}JsonListener* listener);
47    status_t fErrorStatus;
48    AbstractStacked{{toplevel.cppname}}JsonListener* fStackedListener;
49};
50
51/*! Use this listener when you want to parse some JSON data that contains
52    just a single instance of {{toplevel.cppname}}.
53*/
54class Single{{toplevel.cppname}}JsonListener
55    : public AbstractMain{{toplevel.cppname}}JsonListener {
56friend class AbstractStacked{{toplevel.cppname}}JsonListener;
57public:
58    Single{{toplevel.cppname}}JsonListener();
59    virtual ~Single{{toplevel.cppname}}JsonListener();
60
61    bool Handle(const BJsonEvent& event);
62    {{toplevel.cppname}}* Target();
63
64private:
65    {{toplevel.cppname}}* fTarget;
66};
67
68/*! Concrete sub-classes of this class are able to respond to each
69    {{toplevel.cppname}}* instance as
70    it is parsed from the bulk container.  When the stream is
71    finished, the Complete() method is invoked.
72
73    Note that the item object will be deleted after the Handle method
74    is invoked.  The Handle method need not take responsibility
75    for deleting the item itself.
76*/
77class {{toplevel.cppname}}Listener {
78public:
79    virtual bool Handle({{toplevel.cppname}}* item) = 0;
80    virtual void Complete() = 0;
81};
82
83/*! Use this listener, together with an instance of a concrete
84    subclass of {{toplevel.cppname}}Listener
85    in order to parse the JSON data in a specific "bulk
86    container" format.  Each time that an instance of
87    {{toplevel.cppname}}
88    is parsed, the instance item listener will be invoked.
89*/
90class BulkContainer{{toplevel.cppname}}JsonListener
91    : public AbstractMain{{toplevel.cppname}}JsonListener {
92friend class AbstractStacked{{toplevel.cppname}}JsonListener;
93public:
94    BulkContainer{{toplevel.cppname}}JsonListener(
95        {{toplevel.cppname}}Listener* itemListener);
96    ~BulkContainer{{toplevel.cppname}}JsonListener();
97
98    bool Handle(const BJsonEvent& event);
99
100private:
101    {{toplevel.cppname}}Listener* fItemListener;
102};
103
104#endif // GEN_JSON_SCHEMA_PARSER__{{toplevel.cppnameupper}}_H
105"""
106
107IMPLEMENTATION_TEMPLATE = """
108/*
109 * Generated Listener Object for {{toplevel.cppname}}
110 */
111
112#include "{{toplevel.cppname}}JsonListener.h"
113#include <ObjectList.h>
114
115#include <stdio.h>
116
117// #pragma mark - private interfaces for the stacked listeners
118
119
120/*! This class is the top level of the stacked listeners.  The stack structure
121    is maintained in a linked list and sub-classes implement specific behaviors
122    depending where in the parse tree the stacked listener is working at.
123*/
124class AbstractStacked{{toplevel.cppname}}JsonListener : public BJsonEventListener {
125public:
126    AbstractStacked{{toplevel.cppname}}JsonListener(
127        AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
128        AbstractStacked{{toplevel.cppname}}JsonListener* parent);
129    ~AbstractStacked{{toplevel.cppname}}JsonListener();
130
131    void HandleError(status_t status, int32 line, const char* message);
132    void Complete();
133
134    status_t ErrorStatus();
135
136    AbstractStacked{{toplevel.cppname}}JsonListener* Parent();
137
138    virtual bool WillPop();
139
140protected:
141    AbstractMain{{toplevel.cppname}}JsonListener* fMainListener;
142
143    bool Pop();
144    void Push(AbstractStacked{{toplevel.cppname}}JsonListener* stackedListener);
145
146
147private:
148    AbstractStacked{{toplevel.cppname}}JsonListener* fParent;
149};
150
151class GeneralArrayStacked{{toplevel.cppname}}JsonListener : public AbstractStacked{{toplevel.cppname}}JsonListener {
152public:
153    GeneralArrayStacked{{toplevel.cppname}}JsonListener(
154        AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
155        AbstractStacked{{toplevel.cppname}}JsonListener* parent);
156    ~GeneralArrayStacked{{toplevel.cppname}}JsonListener();
157
158    bool Handle(const BJsonEvent& event);
159};
160
161class GeneralObjectStacked{{toplevel.cppname}}JsonListener : public AbstractStacked{{toplevel.cppname}}JsonListener {
162public:
163    GeneralObjectStacked{{toplevel.cppname}}JsonListener(
164        AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
165        AbstractStacked{{toplevel.cppname}}JsonListener* parent);
166    ~GeneralObjectStacked{{toplevel.cppname}}JsonListener();
167
168    bool Handle(const BJsonEvent& event);
169};
170
171
172/*! Sometimes attributes of objects are able to be arrays of strings.  This
173    listener will parse and return the array of strings.
174*/
175
176class StringList{{toplevel.cppname}}JsonListener : public AbstractStacked{{toplevel.cppname}}JsonListener {
177public:
178    StringList{{toplevel.cppname}}JsonListener(
179        AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
180        AbstractStacked{{toplevel.cppname}}JsonListener* parent);
181    ~StringList{{toplevel.cppname}}JsonListener();
182
183    bool Handle(const BJsonEvent& event);
184    BObjectList<BString>* Target();
185
186protected:
187    BObjectList<BString>* fTarget;
188};
189
190{{#allobjs}}
191class {{cppname}}_Stacked{{toplevelcppname}}JsonListener : public AbstractStacked{{toplevelcppname}}JsonListener {
192public:
193    {{cppname}}_Stacked{{toplevelcppname}}JsonListener(
194        AbstractMain{{toplevelcppname}}JsonListener* mainListener,
195        AbstractStacked{{toplevelcppname}}JsonListener* parent);
196    ~{{cppname}}_Stacked{{toplevelcppname}}JsonListener();
197
198    bool Handle(const BJsonEvent& event);
199    {{cppname}}* Target();
200
201protected:
202    {{cppname}}* fTarget;
203    uint16 fNextItemBitmask;
204};
205
206class {{cppname}}_List_Stacked{{toplevelcppname}}JsonListener : public AbstractStacked{{toplevelcppname}}JsonListener {
207public:
208    {{cppname}}_List_Stacked{{toplevelcppname}}JsonListener(
209        AbstractMain{{toplevelcppname}}JsonListener* mainListener,
210        AbstractStacked{{toplevelcppname}}JsonListener* parent);
211    ~{{cppname}}_List_Stacked{{toplevelcppname}}JsonListener();
212
213    bool Handle(const BJsonEvent& event);
214
215    BObjectList<{{cppname}}>* Target();
216        // list of {{cppname}} pointers
217
218private:
219    BObjectList<{{cppname}}>* fTarget;
220};
221{{/allobjs}}
222
223
224class ItemEmittingStacked{{toplevel.cppname}}JsonListener : public {{toplevel.cppname}}_Stacked{{toplevel.cppname}}JsonListener {
225public:
226    ItemEmittingStacked{{toplevel.cppname}}JsonListener(
227        AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
228        AbstractStacked{{toplevel.cppname}}JsonListener* parent,
229        {{toplevel.cppname}}Listener* itemListener);
230    ~ItemEmittingStacked{{toplevel.cppname}}JsonListener();
231
232    bool WillPop();
233
234private:
235    {{toplevel.cppname}}Listener* fItemListener;
236};
237
238
239class BulkContainerStacked{{toplevel.cppname}}JsonListener : public AbstractStacked{{toplevel.cppname}}JsonListener {
240public:
241    BulkContainerStacked{{toplevel.cppname}}JsonListener(
242        AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
243        AbstractStacked{{toplevel.cppname}}JsonListener* parent,
244        {{toplevel.cppname}}Listener* itemListener);
245    ~BulkContainerStacked{{toplevel.cppname}}JsonListener();
246
247    bool Handle(const BJsonEvent& event);
248
249private:
250    uint16 fNextItemIsItems;
251    {{toplevel.cppname}}Listener* fItemListener;
252};
253
254
255class BulkContainerItemsStacked{{toplevel.cppname}}JsonListener : public AbstractStacked{{toplevel.cppname}}JsonListener {
256public:
257    BulkContainerItemsStacked{{toplevel.cppname}}JsonListener(
258        AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
259        AbstractStacked{{toplevel.cppname}}JsonListener* parent,
260        {{toplevel.cppname}}Listener* itemListener);
261    ~BulkContainerItemsStacked{{toplevel.cppname}}JsonListener();
262
263    bool Handle(const BJsonEvent& event);
264    bool WillPop();
265
266private:
267    {{toplevel.cppname}}Listener* fItemListener;
268};
269
270// #pragma mark - implementations for the stacked listeners
271
272AbstractStacked{{toplevel.cppname}}JsonListener::AbstractStacked{{toplevel.cppname}}JsonListener (
273    AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
274    AbstractStacked{{toplevel.cppname}}JsonListener* parent)
275{
276    fMainListener = mainListener;
277    fParent = parent;
278}
279
280AbstractStacked{{toplevel.cppname}}JsonListener::~AbstractStacked{{toplevel.cppname}}JsonListener()
281{
282}
283
284void
285AbstractStacked{{toplevel.cppname}}JsonListener::HandleError(status_t status, int32 line, const char* message)
286{
287    fMainListener->HandleError(status, line, message);
288}
289
290void
291AbstractStacked{{toplevel.cppname}}JsonListener::Complete()
292{
293   fMainListener->Complete();
294}
295
296status_t
297AbstractStacked{{toplevel.cppname}}JsonListener::ErrorStatus()
298{
299    return fMainListener->ErrorStatus();
300}
301
302AbstractStacked{{toplevel.cppname}}JsonListener*
303AbstractStacked{{toplevel.cppname}}JsonListener::Parent()
304{
305    return fParent;
306}
307
308void
309AbstractStacked{{toplevel.cppname}}JsonListener::Push(AbstractStacked{{toplevel.cppname}}JsonListener* stackedListener)
310{
311    fMainListener->SetStackedListener(stackedListener);
312}
313
314bool
315AbstractStacked{{toplevel.cppname}}JsonListener::WillPop()
316{
317    return true;
318}
319
320bool
321AbstractStacked{{toplevel.cppname}}JsonListener::Pop()
322{
323    bool result = WillPop();
324    fMainListener->SetStackedListener(fParent);
325    return result;
326}
327
328GeneralObjectStacked{{toplevel.cppname}}JsonListener::GeneralObjectStacked{{toplevel.cppname}}JsonListener(
329    AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
330    AbstractStacked{{toplevel.cppname}}JsonListener* parent)
331    :
332    AbstractStacked{{toplevel.cppname}}JsonListener(mainListener, parent)
333{
334}
335
336GeneralObjectStacked{{toplevel.cppname}}JsonListener::~GeneralObjectStacked{{toplevel.cppname}}JsonListener()
337{
338}
339
340bool
341GeneralObjectStacked{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
342{
343    switch (event.EventType()) {
344        case B_JSON_OBJECT_NAME:
345        case B_JSON_NUMBER:
346        case B_JSON_STRING:
347        case B_JSON_TRUE:
348        case B_JSON_FALSE:
349        case B_JSON_NULL:
350            // ignore
351            break;
352        case B_JSON_OBJECT_START:
353            Push(new GeneralObjectStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
354            break;
355        case B_JSON_ARRAY_START:
356            Push(new GeneralArrayStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
357            break;
358        case B_JSON_ARRAY_END:
359            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected end of array");
360            break;
361        case B_JSON_OBJECT_END:
362        {
363            bool status = Pop() && (ErrorStatus() == B_OK);
364            delete this;
365            return status;
366        }
367    }
368
369    return ErrorStatus() == B_OK;
370}
371
372GeneralArrayStacked{{toplevel.cppname}}JsonListener::GeneralArrayStacked{{toplevel.cppname}}JsonListener(
373    AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
374    AbstractStacked{{toplevel.cppname}}JsonListener* parent)
375    :
376    AbstractStacked{{toplevel.cppname}}JsonListener(mainListener, parent)
377{
378}
379
380GeneralArrayStacked{{toplevel.cppname}}JsonListener::~GeneralArrayStacked{{toplevel.cppname}}JsonListener()
381{
382}
383
384bool
385GeneralArrayStacked{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
386{
387    switch (event.EventType()) {
388        case B_JSON_OBJECT_NAME:
389        case B_JSON_NUMBER:
390        case B_JSON_STRING:
391        case B_JSON_TRUE:
392        case B_JSON_FALSE:
393        case B_JSON_NULL:
394            // ignore
395            break;
396        case B_JSON_OBJECT_START:
397            Push(new GeneralObjectStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
398            break;
399        case B_JSON_ARRAY_START:
400            Push(new GeneralArrayStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
401            break;
402        case B_JSON_OBJECT_END:
403            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected end of object");
404            break;
405        case B_JSON_ARRAY_END:
406        {
407            bool status = Pop() && (ErrorStatus() == B_OK);
408            delete this;
409            return status;
410        }
411    }
412
413
414    return ErrorStatus() == B_OK;
415}
416
417StringList{{toplevel.cppname}}JsonListener::StringList{{toplevel.cppname}}JsonListener(
418    AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
419    AbstractStacked{{toplevel.cppname}}JsonListener* parent)
420    :
421    AbstractStacked{{toplevel.cppname}}JsonListener(mainListener, parent)
422{
423    fTarget = new BObjectList<BString>();
424}
425
426
427StringList{{toplevel.cppname}}JsonListener::~StringList{{toplevel.cppname}}JsonListener()
428{
429}
430
431
432BObjectList<BString>*
433StringList{{toplevel.cppname}}JsonListener::Target()
434{
435    return fTarget;
436}
437
438
439bool
440StringList{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
441{
442    switch (event.EventType()) {
443        case B_JSON_ARRAY_END:
444        {
445            bool status = Pop() && (ErrorStatus() == B_OK);
446            delete this;
447            return status;
448        }
449        case B_JSON_STRING:
450        {
451            fTarget->AddItem(new BString(event.Content()));
452            break;
453        }
454        default:
455            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
456                "illegal state - unexpected json event parsing a string array");
457            break;
458    }
459
460    return ErrorStatus() == B_OK;
461}
462
463{{#allobjs}}
464{{cppname}}_Stacked{{toplevelcppname}}JsonListener::{{cppname}}_Stacked{{toplevelcppname}}JsonListener(
465    AbstractMain{{toplevelcppname}}JsonListener* mainListener,
466    AbstractStacked{{toplevelcppname}}JsonListener* parent)
467    :
468    AbstractStacked{{toplevelcppname}}JsonListener(mainListener, parent)
469{
470    fTarget = new {{cppname}}();
471}
472
473
474{{cppname}}_Stacked{{toplevelcppname}}JsonListener::~{{cppname}}_Stacked{{toplevelcppname}}JsonListener()
475{
476}
477
478
479{{cppname}}*
480{{cppname}}_Stacked{{toplevelcppname}}JsonListener::Target()
481{
482    return fTarget;
483}
484
485
486bool
487{{cppname}}_Stacked{{toplevelcppname}}JsonListener::Handle(const BJsonEvent& event)
488{
489    switch (event.EventType()) {
490        case B_JSON_ARRAY_END:
491            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected start of array");
492            break;
493        case B_JSON_OBJECT_NAME:
494            fNextItemBitmask = 0;
495{{#propertyarray}}            if (0 == strcmp(event.Content(), "{{name}}"))
496                fNextItemBitmask = {{cppobjectname}}::k{{property.cppname}}Bitmask;
497{{/propertyarray}}
498            break;
499        case B_JSON_OBJECT_END:
500        {
501            bool status = Pop() && (ErrorStatus() == B_OK);
502            delete this;
503            return status;
504        }
505        case B_JSON_STRING:
506        {
507{{#propertyarray}}{{#property.isstring}}            if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
508                fTarget->Set{{property.cppname}}(new BString(event.Content()));
509{{/property.isstring}}{{/propertyarray}}
510            fNextItemBitmask = 0;
511            break;
512        }
513        case B_JSON_TRUE:
514{{#propertyarray}}{{#property.isboolean}}            if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
515                fTarget->Set{{property.cppname}}(true);
516{{/property.isboolean}}{{/propertyarray}}
517            fNextItemBitmask = 0;
518            break;
519        case B_JSON_FALSE:
520{{#propertyarray}}{{#property.isboolean}}            if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
521                fTarget->Set{{property.cppname}}(false);
522{{/property.isboolean}}
523{{/propertyarray}}
524            fNextItemBitmask = 0;
525            break;
526        case B_JSON_NULL:
527        {
528{{#propertyarray}}{{^property.isarray}}            if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
529                fTarget->Set{{property.cppname}}Null();
530{{/property.isarray}}{{/propertyarray}}
531            fNextItemBitmask = 0;
532            break;
533        }
534        case B_JSON_NUMBER:
535        {
536{{#propertyarray}}{{#property.isinteger}}            if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
537                fTarget->Set{{property.cppname}}(event.ContentInteger());
538{{/property.isinteger}}
539{{#property.isnumber}}            if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
540                fTarget->Set{{property.cppname}}(event.ContentDouble());
541{{/property.isnumber}}
542{{/propertyarray}}
543            fNextItemBitmask = 0;
544            break;
545        }
546        case B_JSON_OBJECT_START:
547        {
548{{#propertyarray}}{{#property.isobject}}            if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask) {
549                GeneralObjectStacked{{toplevelcppname}}JsonListener* nextListener
550                    = new GeneralObjectStacked{{toplevelcppname}}JsonListener(fMainListener, this);
551                Push(nextListener);
552                fNextItemBitmask = 0;
553                break;
554            }
555{{/property.isobject}}{{/propertyarray}}
556            // a general consumer that will clear any spurious data that was not
557            // part of the schema.
558            GeneralObjectStacked{{toplevelcppname}}JsonListener* nextListener
559                = new GeneralObjectStacked{{toplevelcppname}}JsonListener(fMainListener, this);
560            Push(nextListener);
561            fNextItemBitmask = 0;
562            break;
563        }
564        case B_JSON_ARRAY_START:
565        {
566{{#propertyarray}}{{#property.isarray}}            if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask) {
567                {{#property.items.isstring}}
568                StringList{{toplevelcppname}}JsonListener* nextListener = new StringList{{toplevelcppname}}JsonListener(fMainListener, this);
569                fTarget->Set{{property.cppname}}(nextListener->Target());
570                {{/property.items.isstring}}{{^property.items.isstring}}
571                {{property.items.cppname}}_List_Stacked{{toplevelcppname}}JsonListener* nextListener =
572                    new {{property.items.cppname}}_List_Stacked{{toplevelcppname}}JsonListener(fMainListener, this);
573                fTarget->Set{{property.cppname}}(nextListener->Target());
574                {{/property.items.isstring}}
575                Push(nextListener);
576                fNextItemBitmask = 0;
577                break;
578            }
579{{/property.isarray}}{{/propertyarray}}
580            // a general consumer that will clear any spurious data that was not
581            // part of the schema.
582            AbstractStacked{{toplevelcppname}}JsonListener* nextListener
583                = new GeneralArrayStacked{{toplevelcppname}}JsonListener(fMainListener, this);
584            Push(nextListener);
585            fNextItemBitmask = 0;
586            break;
587        }
588    }
589
590    return ErrorStatus() == B_OK;
591}
592
593
594{{cppname}}_List_Stacked{{toplevelcppname}}JsonListener::{{cppname}}_List_Stacked{{toplevelcppname}}JsonListener(
595    AbstractMain{{toplevelcppname}}JsonListener* mainListener,
596    AbstractStacked{{toplevelcppname}}JsonListener* parent)
597    :
598    AbstractStacked{{toplevelcppname}}JsonListener(mainListener, parent)
599{
600    fTarget = new BObjectList<{{cppname}}>();
601}
602
603
604{{cppname}}_List_Stacked{{toplevelcppname}}JsonListener::~{{cppname}}_List_Stacked{{toplevelcppname}}JsonListener()
605{
606}
607
608
609BObjectList<{{cppname}}>*
610{{cppname}}_List_Stacked{{toplevelcppname}}JsonListener::Target()
611{
612    return fTarget;
613}
614
615
616bool
617{{cppname}}_List_Stacked{{toplevelcppname}}JsonListener::Handle(const BJsonEvent& event)
618{
619    switch (event.EventType()) {
620        case B_JSON_ARRAY_END:
621        {
622            bool status = Pop() && (ErrorStatus() == B_OK);
623            delete this;
624            return status;
625        }
626        case B_JSON_OBJECT_START:
627        {
628            {{cppname}}_Stacked{{toplevelcppname}}JsonListener* nextListener =
629                new {{cppname}}_Stacked{{toplevelcppname}}JsonListener(fMainListener, this);
630            fTarget->AddItem(nextListener->Target());
631            Push(nextListener);
632            break;
633        }
634        default:
635            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
636                "illegal state - unexpected json event parsing an array of {{cppname}}");
637            break;
638    }
639
640    return ErrorStatus() == B_OK;
641}
642{{/allobjs}}
643
644ItemEmittingStacked{{toplevel.cppname}}JsonListener::ItemEmittingStacked{{toplevel.cppname}}JsonListener(
645    AbstractMain{{toplevel.cppname}}JsonListener* mainListener, AbstractStacked{{toplevel.cppname}}JsonListener* parent,
646    {{toplevel.cppname}}Listener* itemListener)
647:
648{{toplevel.cppname}}_Stacked{{toplevel.cppname}}JsonListener(mainListener, parent)
649{
650    fItemListener = itemListener;
651}
652
653
654ItemEmittingStacked{{toplevel.cppname}}JsonListener::~ItemEmittingStacked{{toplevel.cppname}}JsonListener()
655{
656}
657
658
659bool
660ItemEmittingStacked{{toplevel.cppname}}JsonListener::WillPop()
661{
662    bool result = fItemListener->Handle(fTarget);
663    delete fTarget;
664    fTarget = NULL;
665    return result;
666}
667
668
669BulkContainerStacked{{toplevel.cppname}}JsonListener::BulkContainerStacked{{toplevel.cppname}}JsonListener(
670    AbstractMain{{toplevel.cppname}}JsonListener* mainListener, AbstractStacked{{toplevel.cppname}}JsonListener* parent,
671    {{toplevel.cppname}}Listener* itemListener)
672:
673AbstractStacked{{toplevel.cppname}}JsonListener(mainListener, parent)
674{
675    fItemListener = itemListener;
676}
677
678
679BulkContainerStacked{{toplevel.cppname}}JsonListener::~BulkContainerStacked{{toplevel.cppname}}JsonListener()
680{
681}
682
683
684bool
685BulkContainerStacked{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
686{
687    switch (event.EventType()) {
688        case B_JSON_ARRAY_END:
689            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected end of array");
690            break;
691        case B_JSON_OBJECT_NAME:
692            fNextItemIsItems = (0 == strcmp(event.Content(), "items"));
693            break;
694        case B_JSON_OBJECT_START:
695            Push(new GeneralObjectStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
696            fNextItemIsItems = false;
697            break;
698        case B_JSON_ARRAY_START:
699            if (fNextItemIsItems)
700                Push(new BulkContainerItemsStacked{{toplevel.cppname}}JsonListener(fMainListener, this, fItemListener));
701            else
702                Push(new GeneralArrayStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
703            break;
704        case B_JSON_OBJECT_END:
705        {
706            bool status = Pop() && (ErrorStatus() == B_OK);
707            delete this;
708            return status;
709        }
710        default:
711                // ignore any other fields
712            fNextItemIsItems = false;
713            break;
714    }
715
716    return ErrorStatus() == B_OK;
717}
718
719
720BulkContainerItemsStacked{{toplevel.cppname}}JsonListener::BulkContainerItemsStacked{{toplevel.cppname}}JsonListener(
721    AbstractMain{{toplevel.cppname}}JsonListener* mainListener, AbstractStacked{{toplevel.cppname}}JsonListener* parent,
722    {{toplevel.cppname}}Listener* itemListener)
723:
724AbstractStacked{{toplevel.cppname}}JsonListener(mainListener, parent)
725{
726    fItemListener = itemListener;
727}
728
729
730BulkContainerItemsStacked{{toplevel.cppname}}JsonListener::~BulkContainerItemsStacked{{toplevel.cppname}}JsonListener()
731{
732}
733
734
735bool
736BulkContainerItemsStacked{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
737{
738    switch (event.EventType()) {
739        case B_JSON_OBJECT_START:
740            Push(new ItemEmittingStacked{{toplevel.cppname}}JsonListener(fMainListener, this, fItemListener));
741            break;
742        case B_JSON_ARRAY_END:
743        {
744            bool status = Pop() && (ErrorStatus() == B_OK);
745            delete this;
746            return status;
747        }
748        default:
749            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected json event");
750            break;
751    }
752
753    return ErrorStatus() == B_OK;
754}
755
756
757bool
758BulkContainerItemsStacked{{toplevel.cppname}}JsonListener::WillPop()
759{
760    fItemListener->Complete();
761    return true;
762}
763
764
765// #pragma mark - implementations for the main listeners
766
767
768AbstractMain{{toplevel.cppname}}JsonListener::AbstractMain{{toplevel.cppname}}JsonListener()
769{
770    fStackedListener = NULL;
771    fErrorStatus = B_OK;
772}
773
774
775AbstractMain{{toplevel.cppname}}JsonListener::~AbstractMain{{toplevel.cppname}}JsonListener()
776{
777}
778
779
780void
781AbstractMain{{toplevel.cppname}}JsonListener::HandleError(status_t status, int32 line, const char* message)
782{
783    if (message != NULL) {
784        fprintf(stderr, "an error has arisen processing json for '{{toplevel.cppname}}'; %s\\n", message);
785    } else {
786        fprintf(stderr, "an error has arisen processing json for '{{toplevel.cppname}}'\\n");
787    }
788    fErrorStatus = status;
789}
790
791
792void
793AbstractMain{{toplevel.cppname}}JsonListener::Complete()
794{
795}
796
797
798status_t
799AbstractMain{{toplevel.cppname}}JsonListener::ErrorStatus()
800{
801    return fErrorStatus;
802}
803
804void
805AbstractMain{{toplevel.cppname}}JsonListener::SetStackedListener(
806    AbstractStacked{{toplevel.cppname}}JsonListener* stackedListener)
807{
808    fStackedListener = stackedListener;
809}
810
811
812Single{{toplevel.cppname}}JsonListener::Single{{toplevel.cppname}}JsonListener()
813    :
814    AbstractMain{{toplevel.cppname}}JsonListener()
815{
816    fTarget = NULL;
817}
818
819
820Single{{toplevel.cppname}}JsonListener::~Single{{toplevel.cppname}}JsonListener()
821{
822}
823
824
825bool
826Single{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
827{
828    if (fErrorStatus != B_OK)
829       return false;
830
831    if (fStackedListener != NULL)
832        return fStackedListener->Handle(event);
833
834    switch (event.EventType()) {
835        case B_JSON_OBJECT_START:
836        {
837            {{toplevel.cppname}}_Stacked{{toplevel.cppname}}JsonListener* nextListener
838                = new {{toplevel.cppname}}_Stacked{{toplevel.cppname}}JsonListener(this, NULL);
839            fTarget = nextListener->Target();
840            SetStackedListener(nextListener);
841            break;
842        }
843        default:
844            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
845                "illegal state - unexpected json event parsing top level for {{toplevel.cppname}}");
846            break;
847    }
848
849    return ErrorStatus() == B_OK;
850}
851
852
853{{toplevel.cppname}}*
854Single{{toplevel.cppname}}JsonListener::Target()
855{
856    return fTarget;
857}
858
859
860BulkContainer{{toplevel.cppname}}JsonListener::BulkContainer{{toplevel.cppname}}JsonListener(
861    {{toplevel.cppname}}Listener* itemListener) : AbstractMain{{toplevel.cppname}}JsonListener()
862{
863    fItemListener = itemListener;
864}
865
866
867BulkContainer{{toplevel.cppname}}JsonListener::~BulkContainer{{toplevel.cppname}}JsonListener()
868{
869}
870
871
872bool
873BulkContainer{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
874{
875    if (fErrorStatus != B_OK)
876       return false;
877
878    if (fStackedListener != NULL)
879        return fStackedListener->Handle(event);
880
881    switch (event.EventType()) {
882        case B_JSON_OBJECT_START:
883        {
884            BulkContainerStacked{{toplevel.cppname}}JsonListener* nextListener =
885                new BulkContainerStacked{{toplevel.cppname}}JsonListener(this, NULL, fItemListener);
886            SetStackedListener(nextListener);
887            return true;
888            break;
889        }
890        default:
891            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
892                "illegal state - unexpected json event parsing top level for BulkContainerStacked{{toplevel.cppname}}JsonListener");
893            break;
894    }
895
896    return ErrorStatus() == B_OK;
897}
898"""
899
900
901def write_files(schema : dict[str, any], output_directory: str) -> None:
902    cpp_listener_name = schema["cppname"] + "JsonListener"
903    cpp_header_filename = os.path.join(output_directory, cpp_listener_name + '.h')
904    cpp_implementation_filename = os.path.join(output_directory, cpp_listener_name + '.cpp')
905
906    model = dict[string, any]()
907
908    model['toplevel'] = schema
909    model['allobjs'] = hdsjsonschemacommon.collect_all_objects(schema)
910
911    with open(cpp_header_filename, 'w') as cpp_h_file:
912        cpp_h_file.write(ustache.render(
913            HEADER_TEMPLATE,
914            model,
915            escape= lambda x: x))
916
917    with open(cpp_implementation_filename, 'w') as cpp_i_file:
918        cpp_i_file.write(ustache.render(
919            IMPLEMENTATION_TEMPLATE,
920            model,
921            escape= lambda x: x))
922
923
924def main():
925
926    parser = argparse.ArgumentParser(description='Convert JSON schema to Haiku C++ Parsers')
927    parser.add_argument('-i', '--inputfile', required=True, help='The input filename containing the JSON schema')
928    parser.add_argument('--outputdirectory', help='The output directory where the C++ files should be written')
929
930    args = parser.parse_args()
931
932    outputdirectory = args.outputdirectory
933
934    if not outputdirectory:
935        outputdirectory = '.'
936
937    with open(args.inputfile) as inputfile:
938        schema = json.load(inputfile)
939        hdsjsonschemacommon.augment_schema(schema)
940        write_files(schema, outputdirectory)
941
942
943if __name__ == "__main__":
944    main()