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()