HaikuDepot: code-generation improvements
The HaikuDepot server's schemas are surfaced into HaikuDepot and then code-generated so that they can easily stay at parity with the server and any issues will be caught at build-time. Changes included; - improves the Jam build files' readability - changes the generated models to be immutable - changes the generated models to use `BReferenceable` - changes the creation of the models to use a builder pattern - changes string fields to use BString instead of BString* - outputs the model to a single file with ordered classes - makes corresponding changes to the listener for the model changes - changes the listener to use references instead of object pointers Purpose; - improve / tidy the as-built code generation system - prepare the system for potential use with REST APIs later - prepare the system for potential use for internal models Notable file modifications; - the file `HdsSchemaGenRules` has been renamed to `JsonSchemaGenRules` and edited - the file `hdsjsonschemacommon.py` has been renamed to `jsonschemacommon.py` and edited - the templates from `jsonschema2cppmodel.py` have been externalized into `modelheader.mustache` and `modelimplementation.mustache` - the templates from `jsonschema2cppparser.py` have been externalized into `parserheader.mustache` and `parserimplementation.mustache` Change-Id: I809efd2e40d9227939ab83f18f1deda27873f03f Reviewed-on: https://review.haiku-os.org/c/haiku/+/10612 Tested-by: Commit checker robot <[email protected]> Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
+119
-133
@@ -3,56 +3,59 @@ SubDir HAIKU_TOP src apps haikudepot ;
|
|||||||
# disable -Werror for this directory only
|
# disable -Werror for this directory only
|
||||||
SubDirC++Flags -Wno-error ;
|
SubDirC++Flags -Wno-error ;
|
||||||
|
|
||||||
include [ FDirName $(SUBDIR) build jam HdsSchemaGenRules ] ;
|
include [ FDirName $(SUBDIR) build jam JsonSchemaGenRules ] ;
|
||||||
|
|
||||||
UsePrivateHeaders interface net netservices package shared storage ;
|
UsePrivateHeaders interface net netservices package shared storage ;
|
||||||
UsePrivateHeaders support locale ;
|
UsePrivateHeaders support locale ;
|
||||||
|
|
||||||
HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY = $(TARGET_COMMON_DEBUG_LOCATE_TARGET) ;
|
HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY = $(TARGET_COMMON_DEBUG_LOCATE_TARGET) ;
|
||||||
|
|
||||||
|
# Generated sources JSON Schemas
|
||||||
|
|
||||||
|
local genServerDumpExportPkgSchema =
|
||||||
|
[ FDirName $(SUBDIR) server schema dumpexportpkg.json ] ;
|
||||||
|
local genServerDumpExportRepositorySchema =
|
||||||
|
[ FDirName $(SUBDIR) server schema dumpexportrepository.json ] ;
|
||||||
|
local genServerDumpExportReferenceSchema =
|
||||||
|
[ FDirName $(SUBDIR) server schema dumpexportreference.json ] ;
|
||||||
|
|
||||||
# During the build process, some sources are generated into directories. These
|
# During the build process, some sources are generated into directories. These
|
||||||
# are the directories.
|
# are the directories.
|
||||||
|
|
||||||
local dumpExportRepositoryBulkListenerTargetDirectory =
|
local genServerDumpExportRepositoryBulkListenerTargetDirectory =
|
||||||
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
|
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY) dumpexportrepositorybulklistener ] ;
|
||||||
dumpexportrepositorybulklistener ] ;
|
local genServerDumpExportPkgBulkListenerTargetDirectory =
|
||||||
local dumpExportPkgBulkListenerTargetDirectory =
|
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY) dumpexportpkgbulklistener ] ;
|
||||||
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
|
local genServerDumpExportReferenceListenerTargetDirectory =
|
||||||
dumpexportpkgbulklistener ] ;
|
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY) dumpexportreferencelistener ] ;
|
||||||
local dumpExportReferenceListenerTargetDirectory =
|
|
||||||
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
|
|
||||||
dumpexportreferencelistener ] ;
|
|
||||||
|
|
||||||
local dumpExportPkgModelTargetDirectory =
|
local genServerDumpExportPkgModelTargetDirectory =
|
||||||
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
|
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY) dumpexportpkgmodel ] ;
|
||||||
dumpexportpkgmodel ] ;
|
local genServerDumpExportRepositoryModelTargetDirectory =
|
||||||
local dumpExportRepositoryModelTargetDirectory =
|
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY) dumpexportrepositorymodel ] ;
|
||||||
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
|
local genServerDumpExportReferenceModelTargetDirectory =
|
||||||
dumpexportrepositorymodel ] ;
|
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY) dumpexportreferencemodel ] ;
|
||||||
local dumpExportReferenceModelTargetDirectory =
|
|
||||||
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
|
|
||||||
dumpexportreferencemodel ] ;
|
|
||||||
|
|
||||||
# During the build process, some sources are generated into a directory. In
|
# During the build process, some sources are generated into a directory. In
|
||||||
# order to maintain a timestamp on that generation process, a dummy file is
|
# order to maintain a timestamp on that generation process, a dummy file is
|
||||||
# used to signify the target of the generation. The leafname of this dummy file
|
# used to signify the target of the generation. The leafname of this dummy file
|
||||||
# is defined here.
|
# is defined here.
|
||||||
|
|
||||||
local dummyFile = "dummy.dat" ;
|
local genDummyFile = "dummy.dat" ;
|
||||||
|
|
||||||
local dumpExportRepositoryBulkListenerTargetFile =
|
local genServerDumpExportRepositoryBulkListenerTargetFile =
|
||||||
[ FDirName $(dumpExportRepositoryBulkListenerTargetDirectory) $(dummyFile) ] ;
|
[ FDirName $(genServerDumpExportRepositoryBulkListenerTargetDirectory) $(genDummyFile) ] ;
|
||||||
local dumpExportPkgBulkListenerTargetFile =
|
local genServerDumpExportPkgBulkListenerTargetFile =
|
||||||
[ FDirName $(dumpExportPkgBulkListenerTargetDirectory) $(dummyFile) ] ;
|
[ FDirName $(genServerDumpExportPkgBulkListenerTargetDirectory) $(genDummyFile) ] ;
|
||||||
local dumpExportReferenceListenerTargetFile =
|
local genServerDumpExportReferenceListenerTargetFile =
|
||||||
[ FDirName $(dumpExportReferenceListenerTargetDirectory) $(dummyFile) ] ;
|
[ FDirName $(genServerDumpExportReferenceListenerTargetDirectory) $(genDummyFile) ] ;
|
||||||
|
|
||||||
local dumpExportPkgModelTargetFile =
|
local genServerDumpExportPkgModelTargetFile =
|
||||||
[ FDirName $(dumpExportPkgModelTargetDirectory) $(dummyFile) ] ;
|
[ FDirName $(genServerDumpExportPkgModelTargetDirectory) $(genDummyFile) ] ;
|
||||||
local dumpExportRepositoryModelTargetFile =
|
local genServerDumpExportRepositoryModelTargetFile =
|
||||||
[ FDirName $(dumpExportRepositoryModelTargetDirectory) $(dummyFile) ] ;
|
[ FDirName $(genServerDumpExportRepositoryModelTargetDirectory) $(genDummyFile) ] ;
|
||||||
local dumpExportReferenceModelTargetFile =
|
local genServerDumpExportReferenceModelTargetFile =
|
||||||
[ FDirName $(dumpExportReferenceModelTargetDirectory) $(dummyFile) ] ;
|
[ FDirName $(genServerDumpExportReferenceModelTargetDirectory) $(genDummyFile) ] ;
|
||||||
|
|
||||||
# source directories
|
# source directories
|
||||||
local sourceDirs =
|
local sourceDirs =
|
||||||
@@ -77,13 +80,13 @@ for sourceDir in $(sourceDirs) {
|
|||||||
|
|
||||||
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src servers package ] ;
|
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src servers package ] ;
|
||||||
|
|
||||||
SEARCH_SOURCE += $(dumpExportRepositoryBulkListenerTargetDirectory) ;
|
SEARCH_SOURCE += $(genServerDumpExportRepositoryBulkListenerTargetDirectory) ;
|
||||||
SEARCH_SOURCE += $(dumpExportPkgBulkListenerTargetDirectory) ;
|
SEARCH_SOURCE += $(genServerDumpExportPkgBulkListenerTargetDirectory) ;
|
||||||
SEARCH_SOURCE += $(dumpExportReferenceListenerTargetDirectory) ;
|
SEARCH_SOURCE += $(genServerDumpExportReferenceListenerTargetDirectory) ;
|
||||||
|
|
||||||
SEARCH_SOURCE += $(dumpExportPkgModelTargetDirectory) ;
|
SEARCH_SOURCE += $(genServerDumpExportPkgModelTargetDirectory) ;
|
||||||
SEARCH_SOURCE += $(dumpExportRepositoryModelTargetDirectory) ;
|
SEARCH_SOURCE += $(genServerDumpExportRepositoryModelTargetDirectory) ;
|
||||||
SEARCH_SOURCE += $(dumpExportReferenceModelTargetDirectory) ;
|
SEARCH_SOURCE += $(genServerDumpExportReferenceModelTargetDirectory) ;
|
||||||
|
|
||||||
local textDocumentSources =
|
local textDocumentSources =
|
||||||
# edits_generic
|
# edits_generic
|
||||||
@@ -242,49 +245,30 @@ local applicationSources =
|
|||||||
ResultWindow.cpp
|
ResultWindow.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
local generatedPkgModelSourceFiles =
|
local genServerPkgModelSourceFiles =
|
||||||
DumpExportPkg.cpp
|
DumpExportPkgModel.cpp ;
|
||||||
DumpExportPkgCategory.cpp
|
local genServerRepositoryModelSourceFiles =
|
||||||
DumpExportPkgScreenshot.cpp
|
DumpExportRepositoryModel.cpp ;
|
||||||
DumpExportPkgVersion.cpp
|
local genServerReferenceModelSourceFiles =
|
||||||
;
|
DumpExportReferenceModel.cpp ;
|
||||||
|
|
||||||
local generatedRepositoryModelSourceFiles =
|
local genServerPkgParserSourceFiles =
|
||||||
DumpExportRepository.cpp
|
DumpExportPkgJsonListener.cpp ;
|
||||||
DumpExportRepositorySource.cpp
|
local genServerRepositoryParserSourceFiles =
|
||||||
DumpExportRepositorySourceMirror.cpp
|
DumpExportRepositoryJsonListener.cpp ;
|
||||||
;
|
local genServerReferenceParserSourceFiles =
|
||||||
|
DumpExportReferenceJsonListener.cpp ;
|
||||||
local generatedReferenceModelSourceFiles =
|
|
||||||
DumpExportReference.cpp
|
|
||||||
DumpExportReferenceNaturalLanguage.cpp
|
|
||||||
DumpExportReferencePkgCategory.cpp
|
|
||||||
DumpExportReferenceUserRatingStability.cpp
|
|
||||||
DumpExportReferenceCountry.cpp
|
|
||||||
;
|
|
||||||
|
|
||||||
local generatedPkgParserSourceFiles =
|
|
||||||
DumpExportPkgJsonListener.cpp
|
|
||||||
;
|
|
||||||
|
|
||||||
local generatedRepositoryParserSourceFiles =
|
|
||||||
DumpExportRepositoryJsonListener.cpp
|
|
||||||
;
|
|
||||||
|
|
||||||
local generatedReferenceParserSourceFiles =
|
|
||||||
DumpExportReferenceJsonListener.cpp
|
|
||||||
;
|
|
||||||
|
|
||||||
Application HaikuDepot
|
Application HaikuDepot
|
||||||
:
|
:
|
||||||
$(applicationSources)
|
$(applicationSources)
|
||||||
$(textDocumentSources)
|
$(textDocumentSources)
|
||||||
$(generatedPkgModelSourceFiles)
|
$(genServerPkgModelSourceFiles)
|
||||||
$(generatedRepositoryModelSourceFiles)
|
$(genServerRepositoryModelSourceFiles)
|
||||||
$(generatedReferenceModelSourceFiles)
|
$(genServerReferenceModelSourceFiles)
|
||||||
$(generatedPkgParserSourceFiles)
|
$(genServerPkgParserSourceFiles)
|
||||||
$(generatedRepositoryParserSourceFiles)
|
$(genServerRepositoryParserSourceFiles)
|
||||||
$(generatedReferenceParserSourceFiles)
|
$(genServerReferenceParserSourceFiles)
|
||||||
:
|
:
|
||||||
be package bnetapi translation libnetservices.a libcolumnlistview.a shared
|
be package bnetapi translation libnetservices.a libcolumnlistview.a shared
|
||||||
[ TargetLibstdc++ ] [ TargetLibsupc++ ] localestub
|
[ TargetLibstdc++ ] [ TargetLibsupc++ ] localestub
|
||||||
@@ -302,79 +286,81 @@ DoCatalogs HaikuDepot :
|
|||||||
# JSON schemas that are defined in the HaikuDepotServer system. See the
|
# JSON schemas that are defined in the HaikuDepotServer system. See the
|
||||||
# included Jam rules and actions for the definitions.
|
# included Jam rules and actions for the definitions.
|
||||||
|
|
||||||
HdsSchemaGenModel $(dumpExportPkgModelTargetFile)
|
JsonSchemaGenModel $(genServerDumpExportPkgModelTargetFile)
|
||||||
: dumpexportpkg.json : jsonschema2cppmodel.py ;
|
: $(genServerDumpExportPkgSchema) ;
|
||||||
|
JsonSchemaGenModel $(genServerDumpExportRepositoryModelTargetFile)
|
||||||
|
: $(genServerDumpExportRepositorySchema) ;
|
||||||
|
JsonSchemaGenModel $(genServerDumpExportReferenceModelTargetFile)
|
||||||
|
: $(genServerDumpExportReferenceSchema) ;
|
||||||
|
|
||||||
HdsSchemaGenModel $(dumpExportRepositoryModelTargetFile)
|
JsonSchemaGenParser $(genServerDumpExportRepositoryBulkListenerTargetFile)
|
||||||
: dumpexportrepository.json : jsonschema2cppmodel.py ;
|
: $(genServerDumpExportRepositorySchema) ;
|
||||||
|
JsonSchemaGenParser $(genServerDumpExportPkgBulkListenerTargetFile)
|
||||||
|
: $(genServerDumpExportPkgSchema) ;
|
||||||
|
JsonSchemaGenParser $(genServerDumpExportReferenceListenerTargetFile)
|
||||||
|
: $(genServerDumpExportReferenceSchema) ;
|
||||||
|
|
||||||
HdsSchemaGenModel $(dumpExportReferenceModelTargetFile)
|
JsonSchemaGenAppSrcDependsOnGeneration
|
||||||
: dumpexportreference.json : jsonschema2cppmodel.py ;
|
[ FGristFiles $(genServerPkgParserSourceFiles) ]
|
||||||
|
[ FGristFiles $(genServerPkgParserSourceFiles:S=.h) ]
|
||||||
|
: $(genServerDumpExportPkgBulkListenerTargetDirectory)
|
||||||
|
: $(genServerDumpExportPkgBulkListenerTargetFile)
|
||||||
|
;
|
||||||
|
|
||||||
HdsSchemaGenBulkParser $(dumpExportRepositoryBulkListenerTargetFile)
|
JsonSchemaGenAppSrcDependsOnGeneration
|
||||||
: dumpexportrepository.json : jsonschema2cppparser.py ;
|
[ FGristFiles $(genServerRepositoryParserSourceFiles) ]
|
||||||
|
[ FGristFiles $(genServerRepositoryParserSourceFiles:S=.h) ]
|
||||||
|
: $(genServerDumpExportRepositoryBulkListenerTargetDirectory)
|
||||||
|
: $(genServerDumpExportRepositoryBulkListenerTargetFile)
|
||||||
|
;
|
||||||
|
|
||||||
HdsSchemaGenBulkParser $(dumpExportPkgBulkListenerTargetFile)
|
JsonSchemaGenAppSrcDependsOnGeneration
|
||||||
: dumpexportpkg.json : jsonschema2cppparser.py ;
|
[ FGristFiles $(genServerReferenceParserSourceFiles) ]
|
||||||
|
[ FGristFiles $(genServerReferenceParserSourceFiles:S=.h) ]
|
||||||
|
: $(genServerDumpExportReferenceListenerTargetDirectory)
|
||||||
|
: $(genServerDumpExportReferenceListenerTargetFile)
|
||||||
|
;
|
||||||
|
|
||||||
HdsSchemaGenParser $(dumpExportReferenceListenerTargetFile)
|
JsonSchemaGenAppSrcDependsOnGeneration
|
||||||
: dumpexportreference.json : jsonschema2cppparser.py ;
|
[ FGristFiles $(genServerRepositoryModelSourceFiles) ]
|
||||||
|
[ FGristFiles $(genServerRepositoryModelSourceFiles:S=.h) ]
|
||||||
|
: $(genServerDumpExportRepositoryModelTargetDirectory)
|
||||||
|
: $(genServerDumpExportRepositoryModelTargetFile)
|
||||||
|
;
|
||||||
|
|
||||||
HdsSchemaGenAppSrcDependsOnGeneration
|
JsonSchemaGenAppSrcDependsOnGeneration
|
||||||
[ FGristFiles $(generatedPkgParserSourceFiles) ]
|
[ FGristFiles $(genServerPkgModelSourceFiles) ]
|
||||||
[ FGristFiles $(generatedPkgParserSourceFiles:S=.h) ]
|
[ FGristFiles $(genServerPkgModelSourceFiles:S=.h) ]
|
||||||
: $(dumpExportPkgBulkListenerTargetDirectory)
|
: $(genServerDumpExportPkgModelTargetDirectory)
|
||||||
: $(dumpExportPkgBulkListenerTargetFile) ;
|
: $(genServerDumpExportPkgModelTargetFile)
|
||||||
|
;
|
||||||
|
|
||||||
HdsSchemaGenAppSrcDependsOnGeneration
|
JsonSchemaGenAppSrcDependsOnGeneration
|
||||||
[ FGristFiles $(generatedRepositoryParserSourceFiles) ]
|
[ FGristFiles $(genServerReferenceModelSourceFiles) ]
|
||||||
[ FGristFiles $(generatedRepositoryParserSourceFiles:S=.h) ]
|
[ FGristFiles $(genServerReferenceModelSourceFiles:S=.h) ]
|
||||||
: $(dumpExportRepositoryBulkListenerTargetDirectory)
|
: $(genServerDumpExportReferenceModelTargetDirectory)
|
||||||
: $(dumpExportRepositoryBulkListenerTargetFile) ;
|
: $(genServerDumpExportReferenceModelTargetFile)
|
||||||
|
;
|
||||||
HdsSchemaGenAppSrcDependsOnGeneration
|
|
||||||
[ FGristFiles $(generatedReferenceParserSourceFiles) ]
|
|
||||||
[ FGristFiles $(generatedReferenceParserSourceFiles:S=.h) ]
|
|
||||||
: $(dumpExportReferenceListenerTargetDirectory)
|
|
||||||
: $(dumpExportReferenceListenerTargetFile) ;
|
|
||||||
|
|
||||||
HdsSchemaGenAppSrcDependsOnGeneration
|
|
||||||
[ FGristFiles $(generatedRepositoryModelSourceFiles) ]
|
|
||||||
[ FGristFiles $(generatedRepositoryModelSourceFiles:S=.h) ]
|
|
||||||
: $(dumpExportRepositoryModelTargetDirectory)
|
|
||||||
: $(dumpExportRepositoryModelTargetFile) ;
|
|
||||||
|
|
||||||
HdsSchemaGenAppSrcDependsOnGeneration
|
|
||||||
[ FGristFiles $(generatedPkgModelSourceFiles) ]
|
|
||||||
[ FGristFiles $(generatedPkgModelSourceFiles:S=.h) ]
|
|
||||||
: $(dumpExportPkgModelTargetDirectory)
|
|
||||||
: $(dumpExportPkgModelTargetFile) ;
|
|
||||||
|
|
||||||
HdsSchemaGenAppSrcDependsOnGeneration
|
|
||||||
[ FGristFiles $(generatedReferenceModelSourceFiles) ]
|
|
||||||
[ FGristFiles $(generatedReferenceModelSourceFiles:S=.h) ]
|
|
||||||
: $(dumpExportReferenceModelTargetDirectory)
|
|
||||||
: $(dumpExportReferenceModelTargetFile) ;
|
|
||||||
|
|
||||||
# This will ensure that if any of the generated files' header files change, then
|
# This will ensure that if any of the generated files' header files change, then
|
||||||
# the application should be re-built.
|
# the application should be re-built.
|
||||||
|
|
||||||
Includes [ FGristFiles $(applicationSources) ]
|
Includes [ FGristFiles $(applicationSources) ]
|
||||||
:
|
:
|
||||||
$(dumpExportRepositoryBulkListenerTargetDirectory)
|
$(genServerDumpExportRepositoryBulkListenerTargetDirectory)
|
||||||
$(dumpExportPkgBulkListenerTargetDirectory)
|
$(genServerDumpExportPkgBulkListenerTargetDirectory)
|
||||||
$(dumpExportReferenceListenerTargetDirectory)
|
$(genServerDumpExportReferenceListenerTargetDirectory)
|
||||||
$(dumpExportPkgModelTargetDirectory)
|
$(genServerDumpExportPkgModelTargetDirectory)
|
||||||
$(dumpExportRepositoryModelTargetDirectory)
|
$(genServerDumpExportRepositoryModelTargetDirectory)
|
||||||
$(dumpExportReferenceModelTargetDirectory)
|
$(genServerDumpExportReferenceModelTargetDirectory)
|
||||||
;
|
;
|
||||||
|
|
||||||
Depends [ FGristFiles $(applicationSources:S=.o) ]
|
Depends [ FGristFiles $(applicationSources:S=.o) ]
|
||||||
:
|
:
|
||||||
[ FGristFiles $(generatedPkgParserSourceFiles:S=.h) ]
|
[ FGristFiles $(genServerPkgParserSourceFiles:S=.h) ]
|
||||||
[ FGristFiles $(generatedPkgModelSourceFiles:S=.h) ]
|
[ FGristFiles $(genServerPkgModelSourceFiles:S=.h) ]
|
||||||
[ FGristFiles $(generatedRepositoryParserSourceFiles:S=.h) ]
|
[ FGristFiles $(genServerRepositoryParserSourceFiles:S=.h) ]
|
||||||
[ FGristFiles $(generatedRepositoryModelSourceFiles:S=.h) ]
|
[ FGristFiles $(genServerRepositoryModelSourceFiles:S=.h) ]
|
||||||
[ FGristFiles $(generatedReferenceParserSourceFiles:S=.h) ]
|
[ FGristFiles $(genServerReferenceParserSourceFiles:S=.h) ]
|
||||||
[ FGristFiles $(generatedReferenceModelSourceFiles:S=.h) ]
|
[ FGristFiles $(genServerReferenceModelSourceFiles:S=.h) ]
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -1,117 +0,0 @@
|
|||||||
# =====================================
|
|
||||||
# Copyright 2019-2023, Andrew Lindesay
|
|
||||||
# Distributed under the terms of the MIT License.
|
|
||||||
# =====================================
|
|
||||||
|
|
||||||
# HaikuDepotServer has a number of data-transfer-objects (DTO) that are defined
|
|
||||||
# by JSON schemas. The server uses these schemas to produce the objects at
|
|
||||||
# compile time. Likewise, the schema files also generate C++ side DTO model
|
|
||||||
# objects in the form of .cpp and .h files as well. This way the
|
|
||||||
# HaikuDepotServer server and HaikuDepot desktop application are able to
|
|
||||||
# communicate more 'safely'. The schema files still need to be copied from
|
|
||||||
# the server source to the Haiku source, but the generation process will ensure
|
|
||||||
# that the data-structures are consistent.
|
|
||||||
#
|
|
||||||
# The C++ side classes are generated with python scripts that are included in
|
|
||||||
# the Haiku source. These rules and actions take care of making sure that the
|
|
||||||
# python scripts are run when necessary to generate the C++ side classes. Note
|
|
||||||
# that there are two sorts of classes generated here; the model DTO objects and
|
|
||||||
# also the supporting classes that parse the DTO objects. The parsing classes
|
|
||||||
# are intended to be used with Haiku JSON parsing systems.
|
|
||||||
|
|
||||||
# pragma mark - Generic
|
|
||||||
|
|
||||||
actions HdsSchemaGenTouch
|
|
||||||
{
|
|
||||||
touch $(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
# pragma mark - Model Class Generation
|
|
||||||
|
|
||||||
# 1 : the dummy file in the class generation directory (target)
|
|
||||||
# 2 : the JSON schema file
|
|
||||||
# 3 : the Python script to use
|
|
||||||
|
|
||||||
rule HdsSchemaGenModel
|
|
||||||
{
|
|
||||||
SEARCH on $(2) = [ FDirName $(SUBDIR) server schema ] ;
|
|
||||||
SEARCH on $(3) = [ FDirName $(SUBDIR) build scripts ] ;
|
|
||||||
|
|
||||||
MkDir1 $(1:D) ;
|
|
||||||
CleanDir clean : $(1:D) ;
|
|
||||||
Depends $(1) : $(2) $(3) $(1:D) ;
|
|
||||||
HdsSchemaGenModel1 $(1) : $(2) $(3) $(1:D) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
actions HdsSchemaGenModel1
|
|
||||||
{
|
|
||||||
$(HOST_PYTHON) $(2[2]) -i $(2[1]) --outputdirectory $(2[3])
|
|
||||||
touch $(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
# pragma mark - Bulk Parsing Class Generation
|
|
||||||
|
|
||||||
# 1 : the dummy file in the class generation directory (target)
|
|
||||||
# 2 : the JSON schema file
|
|
||||||
# 3 : the Python script to use
|
|
||||||
|
|
||||||
rule HdsSchemaGenBulkParser
|
|
||||||
{
|
|
||||||
SEARCH on $(2) = [ FDirName $(SUBDIR) server schema ] ;
|
|
||||||
SEARCH on $(3) = [ FDirName $(SUBDIR) build scripts ] ;
|
|
||||||
|
|
||||||
MkDir1 $(1:D) ;
|
|
||||||
CleanDir clean : $(1:D) ;
|
|
||||||
Depends $(1) : $(2) $(3) $(1:D) ;
|
|
||||||
HdsSchemaGenBulkParser1 $(1) : $(2) $(3) $(1:D) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
actions HdsSchemaGenBulkParser1
|
|
||||||
{
|
|
||||||
$(HOST_PYTHON) $(2[2]) -i $(2[1]) --outputdirectory $(2[3])
|
|
||||||
touch $(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
# pragma mark - Non-Bulk Parsing Class Generation
|
|
||||||
|
|
||||||
# 1 : the dummy file in the class generation directory (target)
|
|
||||||
# 2 : the JSON schema file
|
|
||||||
# 3 : the Python script to use
|
|
||||||
|
|
||||||
rule HdsSchemaGenParser
|
|
||||||
{
|
|
||||||
SEARCH on $(2) = [ FDirName $(SUBDIR) server schema ] ;
|
|
||||||
SEARCH on $(3) = [ FDirName $(SUBDIR) build scripts ] ;
|
|
||||||
|
|
||||||
MkDir1 $(1:D) ;
|
|
||||||
CleanDir clean : $(1:D) ;
|
|
||||||
Depends $(1) : $(2) $(3) $(1:D) ;
|
|
||||||
HdsSchemaGenParser1 $(1) : $(2) $(3) $(1:D) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
actions HdsSchemaGenParser1
|
|
||||||
{
|
|
||||||
$(HOST_PYTHON) $(2[2]) -i $(2[1]) --outputdirectory $(2[3])
|
|
||||||
touch $(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
# pragma mark - Registering Generated Classes
|
|
||||||
|
|
||||||
# Because a number of .cpp and .h files will be generated from a single python
|
|
||||||
# script's run, it is necessary to introduce a dependency between the known
|
|
||||||
# output files and the target for a given python script run.
|
|
||||||
|
|
||||||
# 1 : generated files (.h and .cpp)
|
|
||||||
# 2 : target directory generated files will be placed in
|
|
||||||
# 3 : target that will generate the generated files (dummy file)
|
|
||||||
|
|
||||||
rule HdsSchemaGenAppSrcDependsOnGeneration {
|
|
||||||
MakeLocate $(1) : $(2) ;
|
|
||||||
MkDir1 $(2) ;
|
|
||||||
Depends $(1) : $(2) $(3) ;
|
|
||||||
Clean clean : $(1) ;
|
|
||||||
|
|
||||||
# just in case the dummy file ends up being newer than the generated
|
|
||||||
# sources, update the modified timestamp on the generated files.
|
|
||||||
HdsSchemaGenTouch $(1) ;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
# =====================================
|
||||||
|
# Copyright 2019-2026, Andrew Lindesay
|
||||||
|
# Distributed under the terms of the MIT License.
|
||||||
|
# =====================================
|
||||||
|
|
||||||
|
# HaikuDepotServer has a number of data-transfer-objects (DTO) that are defined
|
||||||
|
# by JSON schemas. The server uses these schemas to produce the objects at
|
||||||
|
# compile time. Likewise, the schema files also generate C++ side DTO model
|
||||||
|
# objects in the form of .cpp and .h files as well. This way the
|
||||||
|
# HaikuDepotServer server and HaikuDepot desktop application are able to
|
||||||
|
# communicate more 'safely'. The schema files still need to be copied from
|
||||||
|
# the server source to the Haiku source, but the generation process will ensure
|
||||||
|
# that the data-structures are consistent.
|
||||||
|
#
|
||||||
|
# The C++ side classes are generated with python scripts that are included in
|
||||||
|
# the Haiku source. These rules and actions take care of making sure that the
|
||||||
|
# python scripts are run when necessary to generate the C++ side classes. Note
|
||||||
|
# that there are two sorts of classes generated here; the model DTO objects and
|
||||||
|
# also the supporting classes that parse the DTO objects. The parsing classes
|
||||||
|
# are intended to be used with Haiku JSON parsing systems.
|
||||||
|
|
||||||
|
# =====================================
|
||||||
|
# pragma mark - Generic
|
||||||
|
|
||||||
|
actions JsonSchemaGenTouch
|
||||||
|
{
|
||||||
|
touch $(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Because a number of .cpp and .h files will be generated from a single python
|
||||||
|
# script's run, it is necessary to introduce a dependency between the known
|
||||||
|
# output files and the target for a given python script run.
|
||||||
|
|
||||||
|
# 1 : generated files (.h and .cpp)
|
||||||
|
# 2 : target directory generated files will be placed in
|
||||||
|
# 3 : target that will generate the generated files (dummy file)
|
||||||
|
|
||||||
|
rule JsonSchemaGenAppSrcDependsOnGeneration {
|
||||||
|
local generatedFiles = $(1) ;
|
||||||
|
local targetDirectory = $(2) ;
|
||||||
|
local dummy = $(3) ;
|
||||||
|
|
||||||
|
MakeLocate $(generatedFiles) : $(targetDirectory) ;
|
||||||
|
MkDir1 $(targetDirectory) ;
|
||||||
|
Depends $(generatedFiles) : $(targetDirectory) $(dummy) ;
|
||||||
|
Clean clean : $(generatedFiles) ;
|
||||||
|
|
||||||
|
# just in case the dummy file ends up being newer than the generated
|
||||||
|
# sources, update the modified timestamp on the generated files.
|
||||||
|
JsonSchemaGenTouch $(generatedFiles) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================
|
||||||
|
# pragma mark - Model Class Generation
|
||||||
|
|
||||||
|
# This section is responsible for generation of .h and .cpp files which
|
||||||
|
# represent the data model described in the supplied JSON schema file. These
|
||||||
|
# files do not include parsers.
|
||||||
|
#
|
||||||
|
# A dummy file is used as a dependency item which then has the two generated
|
||||||
|
# file as it's dependency. This approach avoids complications around the
|
||||||
|
# generated target being two files rather than one.
|
||||||
|
|
||||||
|
# 1 : the dummy file in the class generation directory (target)
|
||||||
|
# 2 : the JSON schema file
|
||||||
|
|
||||||
|
rule JsonSchemaGenModel
|
||||||
|
{
|
||||||
|
local dummy = $(1) ;
|
||||||
|
local schema = $(2) ;
|
||||||
|
|
||||||
|
local script = [ FDirName $(HAIKU_TOP) src apps haikudepot build scripts jsonschema2cppmodel.py ] ;
|
||||||
|
local commonScript = [ FDirName $(HAIKU_TOP) src apps haikudepot build scripts jsonschemacommon.py ] ;
|
||||||
|
local headerTemplate = [ FDirName $(HAIKU_TOP) src apps haikudepot build scripts template modelheader.mustache ] ;
|
||||||
|
local implementationTemplate = [ FDirName $(HAIKU_TOP) src apps haikudepot build scripts template modelimplementation.mustache ] ;
|
||||||
|
|
||||||
|
MkDir1 $(dummy:D) ;
|
||||||
|
CleanDir clean : $(dummy:D) ;
|
||||||
|
Depends $(dummy) : $(schema) $(script) $(commonScript) $(headerTemplate) $(implementationTemplate) $(dummy:D) ;
|
||||||
|
JsonSchemaGenModel1 $(dummy) : $(schema) $(script) $(dummy:D) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions JsonSchemaGenModel1
|
||||||
|
{
|
||||||
|
$(HOST_PYTHON) $(2[2]) --jsonschemafile $(2[1]) --outputdirectory $(2[3])
|
||||||
|
touch $(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================
|
||||||
|
# pragma mark - Parsing Class Generation
|
||||||
|
|
||||||
|
# This section is responsible for generation of .h and .cpp files which
|
||||||
|
# represent a listener that is intended for use with the `BJson` class to
|
||||||
|
# parse JSON data according to a supplied JSON Schema model. The C++
|
||||||
|
# representation of the schema as a model must be also generated with
|
||||||
|
# `JsonSchemaGenModel`.
|
||||||
|
#
|
||||||
|
# A dummy file is used as a dependency item which then has the two generated
|
||||||
|
# file as it's dependency. This approach avoids complications around the
|
||||||
|
# generated target being two files rather than one.
|
||||||
|
|
||||||
|
# 1 : the dummy file in the class generation directory (target)
|
||||||
|
# 2 : the JSON schema file
|
||||||
|
|
||||||
|
rule JsonSchemaGenParser
|
||||||
|
{
|
||||||
|
local dummy = $(1) ;
|
||||||
|
local schema = $(2) ;
|
||||||
|
|
||||||
|
local script = [ FDirName $(HAIKU_TOP) src apps haikudepot build scripts jsonschema2cppparser.py ] ;
|
||||||
|
local commonScript = [ FDirName $(HAIKU_TOP) src apps haikudepot build scripts jsonschemacommon.py ] ;
|
||||||
|
local headerTemplate = [ FDirName $(HAIKU_TOP) src apps haikudepot build scripts template modelheader.mustache ] ;
|
||||||
|
local implementationTemplate = [ FDirName $(HAIKU_TOP) src apps haikudepot build scripts template modelimplementation.mustache ] ;
|
||||||
|
|
||||||
|
MkDir1 $(dummy:D) ;
|
||||||
|
CleanDir clean : $(dummy:D) ;
|
||||||
|
Depends $(dummy) : $(schema) $(script) $(commonScript) $(headerTemplate) $(implementationTemplate) $(dummy:D) ;
|
||||||
|
JsonSchemaGenParser1 $(dummy) : $(schema) $(script) $(dummy:D) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions JsonSchemaGenParser1
|
||||||
|
{
|
||||||
|
$(HOST_PYTHON) $(2[2]) --jsonschemafile $(2[1]) --outputdirectory $(2[3])
|
||||||
|
touch $(1)
|
||||||
|
}
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
|
|
||||||
# =====================================
|
|
||||||
# Copyright 2017-2023, Andrew Lindesay
|
|
||||||
# Distributed under the terms of the MIT License.
|
|
||||||
# =====================================
|
|
||||||
|
|
||||||
# common material related to generation of schema-generated artifacts.
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
|
||||||
# The possible JSON types
|
|
||||||
JSON_TYPE_STRING = "string"
|
|
||||||
JSON_TYPE_OBJECT = "object"
|
|
||||||
JSON_TYPE_ARRAY = "array"
|
|
||||||
JSON_TYPE_BOOLEAN = "boolean"
|
|
||||||
JSON_TYPE_INTEGER = "integer"
|
|
||||||
JSON_TYPE_NUMBER = "number"
|
|
||||||
|
|
||||||
|
|
||||||
# The possible C++ types
|
|
||||||
CPP_TYPE_STRING = "BString"
|
|
||||||
CPP_TYPE_ARRAY = "BObjectList"
|
|
||||||
CPP_TYPE_BOOLEAN = "bool"
|
|
||||||
CPP_TYPE_INTEGER = "int64"
|
|
||||||
CPP_TYPE_NUMBER = "double"
|
|
||||||
|
|
||||||
|
|
||||||
# The possible C++ default values
|
|
||||||
CPP_DEFAULT_STRING = "NULL"
|
|
||||||
CPP_DEFAULT_OBJECT = "NULL"
|
|
||||||
CPP_DEFAULT_ARRAY = "NULL"
|
|
||||||
CPP_DEFAULT_BOOLEAN = "false"
|
|
||||||
CPP_DEFAULT_INTEGER = "0"
|
|
||||||
CPP_DEFAULT_NUMBER = "0.0"
|
|
||||||
|
|
||||||
|
|
||||||
def uniondicts(d1, d2):
|
|
||||||
d = dict(d1)
|
|
||||||
d.update(d2)
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def javatypetocppname(javaname):
|
|
||||||
return javaname[javaname.rindex('.')+1:]
|
|
||||||
|
|
||||||
|
|
||||||
def propnametocppname(propname):
|
|
||||||
return propname[0:1].upper() + propname[1:]
|
|
||||||
|
|
||||||
|
|
||||||
def propnametocppmembername(propname):
|
|
||||||
return "f" + propnametocppname(propname)
|
|
||||||
|
|
||||||
def propmetadatatocppdefaultvalue(propmetadata):
|
|
||||||
type = propmetadata['type']
|
|
||||||
|
|
||||||
if type == JSON_TYPE_STRING:
|
|
||||||
return CPP_DEFAULT_STRING
|
|
||||||
if type == JSON_TYPE_BOOLEAN:
|
|
||||||
return CPP_DEFAULT_BOOLEAN
|
|
||||||
if type == JSON_TYPE_INTEGER:
|
|
||||||
return CPP_DEFAULT_INTEGER
|
|
||||||
if type == JSON_TYPE_NUMBER:
|
|
||||||
return CPP_DEFAULT_NUMBER
|
|
||||||
if type == JSON_TYPE_OBJECT:
|
|
||||||
return CPP_DEFAULT_OBJECT
|
|
||||||
if type == JSON_TYPE_ARRAY:
|
|
||||||
return CPP_DEFAULT_ARRAY
|
|
||||||
|
|
||||||
raise Exception('unknown json-schema type [' + type + ']')
|
|
||||||
|
|
||||||
def propmetadatatocpptypename(propmetadata):
|
|
||||||
type = propmetadata['type']
|
|
||||||
|
|
||||||
if type == JSON_TYPE_STRING:
|
|
||||||
return CPP_TYPE_STRING
|
|
||||||
if type == JSON_TYPE_BOOLEAN:
|
|
||||||
return CPP_TYPE_BOOLEAN
|
|
||||||
if type == JSON_TYPE_INTEGER:
|
|
||||||
return CPP_TYPE_INTEGER
|
|
||||||
if type == JSON_TYPE_NUMBER:
|
|
||||||
return CPP_TYPE_NUMBER
|
|
||||||
if type == JSON_TYPE_OBJECT:
|
|
||||||
javatype = propmetadata['javaType']
|
|
||||||
|
|
||||||
if not javatype or 0 == len(javatype):
|
|
||||||
raise Exception('missing "javaType" field')
|
|
||||||
|
|
||||||
return javatypetocppname(javatype)
|
|
||||||
|
|
||||||
if type == JSON_TYPE_ARRAY:
|
|
||||||
itemsmetadata = propmetadata['items']
|
|
||||||
itemstype = itemsmetadata['type']
|
|
||||||
|
|
||||||
if not itemstype or 0 == len(itemstype):
|
|
||||||
raise Exception('missing "type" field')
|
|
||||||
|
|
||||||
if itemstype == JSON_TYPE_OBJECT:
|
|
||||||
itemsjavatype = itemsmetadata['javaType']
|
|
||||||
if not itemsjavatype or 0 == len(itemsjavatype):
|
|
||||||
raise Exception('missing "javaType" field')
|
|
||||||
return "%s<%s>" % (CPP_TYPE_ARRAY, javatypetocppname(itemsjavatype))
|
|
||||||
|
|
||||||
if itemstype == JSON_TYPE_STRING:
|
|
||||||
return "%s<%s>" % (CPP_TYPE_ARRAY, CPP_TYPE_STRING)
|
|
||||||
|
|
||||||
raise Exception('unsupported type [%s]' % itemstype)
|
|
||||||
|
|
||||||
raise Exception('unknown json-schema type [' + type + ']')
|
|
||||||
|
|
||||||
|
|
||||||
def propmetadatatypeisscalar(propmetadata):
|
|
||||||
type = propmetadata['type']
|
|
||||||
return type == JSON_TYPE_BOOLEAN or type == JSON_TYPE_INTEGER or type == JSON_TYPE_NUMBER
|
|
||||||
|
|
||||||
|
|
||||||
def writetopcomment(f, inputfilename, variant):
|
|
||||||
f.write((
|
|
||||||
'/*\n'
|
|
||||||
' * Generated %s Object\n'
|
|
||||||
' * source json-schema : %s\n'
|
|
||||||
' * generated at : %s\n'
|
|
||||||
' */\n'
|
|
||||||
) % (variant, inputfilename, datetime.datetime.now().isoformat()))
|
|
||||||
|
|
||||||
def collect_all_objects(schema: dict[str, any]) -> list[dict[str, any]]:
|
|
||||||
assembly = dict[str, dict[str, any]]()
|
|
||||||
|
|
||||||
def accumulate_all_objects(obj: dict[str, any]) -> None:
|
|
||||||
assembly[obj["cppname"]] = obj
|
|
||||||
|
|
||||||
for prop_name, prop in obj['properties'].items():
|
|
||||||
if JSON_TYPE_ARRAY == prop["type"]:
|
|
||||||
array_items = prop['items']
|
|
||||||
|
|
||||||
if JSON_TYPE_OBJECT == array_items["type"] and not array_items["cppname"] in assembly:
|
|
||||||
accumulate_all_objects(array_items)
|
|
||||||
|
|
||||||
if JSON_TYPE_OBJECT == prop["type"] and not prop["cppname"] in assembly:
|
|
||||||
accumulate_all_objects(prop)
|
|
||||||
|
|
||||||
accumulate_all_objects(schema)
|
|
||||||
result = list(assembly.values())
|
|
||||||
result.sort(key= lambda v: v["cppname"])
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def augment_schema(schema: dict[str, any]) -> None:
|
|
||||||
"""This function will take the data from the JSON schema and will overlay any
|
|
||||||
specific information required for rendering the templates.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def derive_cpp_classname(obj: dict[str, any]) -> str:
|
|
||||||
obj_type = obj['type']
|
|
||||||
|
|
||||||
if obj_type != JSON_TYPE_OBJECT:
|
|
||||||
raise Exception('expecting object, but was [' + obj_type + ']')
|
|
||||||
|
|
||||||
java_type = obj['javaType']
|
|
||||||
|
|
||||||
return javatypetocppname(java_type)
|
|
||||||
|
|
||||||
def augment_property(prop: dict[str, any]) -> None:
|
|
||||||
prop_type = prop['type']
|
|
||||||
|
|
||||||
prop["cpptype"] = propmetadatatocpptypename(prop)
|
|
||||||
|
|
||||||
is_scalar_type = propmetadatatypeisscalar(prop)
|
|
||||||
is_array_type = (prop_type == JSON_TYPE_ARRAY)
|
|
||||||
|
|
||||||
prop["iscppscalartype"] = is_scalar_type
|
|
||||||
prop["isarray"] = is_array_type
|
|
||||||
prop["isstring"] = JSON_TYPE_STRING == prop_type
|
|
||||||
prop["isboolean"] = JSON_TYPE_BOOLEAN == prop_type
|
|
||||||
prop["isnumber"] = JSON_TYPE_NUMBER == prop_type
|
|
||||||
prop["isinteger"] = JSON_TYPE_INTEGER == prop_type
|
|
||||||
prop["isobject"] = JSON_TYPE_OBJECT == prop_type
|
|
||||||
prop["iscppnonscalarnoncollectiontype"] = not is_scalar_type and not is_array_type
|
|
||||||
prop["toplevelcppname"] = top_level_cpp_name
|
|
||||||
prop["cppdefaultvalue"] = propmetadatatocppdefaultvalue(prop)
|
|
||||||
|
|
||||||
if not prop_type or 0 == len(prop_type):
|
|
||||||
raise Exception('missing "type" field')
|
|
||||||
|
|
||||||
if JSON_TYPE_ARRAY == prop_type:
|
|
||||||
array_items = prop['items']
|
|
||||||
array_items_type = array_items["type"]
|
|
||||||
|
|
||||||
if array_items_type == JSON_TYPE_OBJECT:
|
|
||||||
augment_object(array_items)
|
|
||||||
else:
|
|
||||||
augment_property(array_items)
|
|
||||||
|
|
||||||
if JSON_TYPE_OBJECT == prop_type:
|
|
||||||
augment_object(prop)
|
|
||||||
|
|
||||||
def augment_object(obj: dict[str, any]) -> None:
|
|
||||||
|
|
||||||
def collect_referenced_class_names() -> list[str]:
|
|
||||||
result = set()
|
|
||||||
properties = obj['properties'].items()
|
|
||||||
|
|
||||||
if len(properties) > 15:
|
|
||||||
raise RuntimeError("an object has more than 15 properties"
|
|
||||||
" which is not allowed")
|
|
||||||
|
|
||||||
for _, prop in obj['properties'].items():
|
|
||||||
if prop['type'] == JSON_TYPE_ARRAY:
|
|
||||||
array_items = prop['items']
|
|
||||||
if array_items['type'] == JSON_TYPE_OBJECT:
|
|
||||||
result.add(array_items['cppname'])
|
|
||||||
if prop['type'] == JSON_TYPE_OBJECT:
|
|
||||||
result.add(prop['cppname'])
|
|
||||||
|
|
||||||
result_ordered = list(result)
|
|
||||||
result_ordered.sort()
|
|
||||||
return result_ordered
|
|
||||||
|
|
||||||
def has_any_list_properties() -> bool:
|
|
||||||
for _, prop in obj['properties'].items():
|
|
||||||
if prop['type'] == JSON_TYPE_ARRAY:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
obj_cpp_classname = derive_cpp_classname(obj)
|
|
||||||
obj["cppname"] = obj_cpp_classname
|
|
||||||
obj["cppnameupper"] = obj_cpp_classname.upper()
|
|
||||||
obj["cpptype"] = obj_cpp_classname
|
|
||||||
obj["hasanylistproperties"] = has_any_list_properties()
|
|
||||||
|
|
||||||
# allows the object to know the top level object that contains it
|
|
||||||
obj["toplevelcppname"] = top_level_cpp_name
|
|
||||||
|
|
||||||
properties = obj['properties'].items()
|
|
||||||
|
|
||||||
for prop_name, prop in properties:
|
|
||||||
prop["cppname"] = propnametocppname(prop_name)
|
|
||||||
prop["cppmembername"] = propnametocppmembername(prop_name)
|
|
||||||
augment_property(prop)
|
|
||||||
|
|
||||||
# mustache is not able to iterate over a dictionary; it can only
|
|
||||||
# iterate over a list where each item in the list has some properties.
|
|
||||||
|
|
||||||
property_array = sorted(
|
|
||||||
[{
|
|
||||||
"name": k,
|
|
||||||
"property": v,
|
|
||||||
"cppobjectname": obj_cpp_classname
|
|
||||||
# ^ this is the name of the containing object
|
|
||||||
} for k,v in properties],
|
|
||||||
key= lambda item: item["name"]
|
|
||||||
)
|
|
||||||
|
|
||||||
for i in range(len(property_array)):
|
|
||||||
property_array[i]["cppbitmaskexpression"] = "(1 << %d)" % i
|
|
||||||
|
|
||||||
if 0 != len(property_array):
|
|
||||||
property_array[0]["isfirst"] = True
|
|
||||||
property_array[len(property_array) - 1]["islast"] = True
|
|
||||||
|
|
||||||
obj["propertyarray"] = property_array
|
|
||||||
obj["referencedclasscpptypes"] = collect_referenced_class_names()
|
|
||||||
|
|
||||||
top_level_cpp_name = derive_cpp_classname(schema)
|
|
||||||
augment_object(schema)
|
|
||||||
@@ -1,266 +1,76 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
# =====================================
|
# =====================================
|
||||||
# Copyright 2017-2025, Andrew Lindesay
|
# Copyright 2017-2026, Andrew Lindesay
|
||||||
# Distributed under the terms of the MIT License.
|
# Distributed under the terms of the MIT License.
|
||||||
# =====================================
|
# =====================================
|
||||||
|
|
||||||
# This simple tool will read a JSON schema and will then generate
|
"""\
|
||||||
# some model objects that can be used to hold the data-structure
|
This is a command line tool which is able to take a JSON Schema file as input
|
||||||
# in the C++ environment.
|
and will generate the `.h` and `.cpp` C++ source files for model classes that
|
||||||
|
represent the schema. The classes will appear in the output file in the correct
|
||||||
|
order according to their relationship in the schema.
|
||||||
|
"""
|
||||||
|
|
||||||
import json
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import json
|
||||||
import hdsjsonschemacommon
|
import pathlib
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import jsonschemacommon
|
||||||
import ustache
|
import ustache
|
||||||
|
|
||||||
|
|
||||||
HEADER_TEMPLATE = """
|
|
||||||
/*
|
|
||||||
* Generated Model Object for {{cppname}}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef GEN_JSON_SCHEMA_MODEL__{{cppnameupper}}_H
|
|
||||||
#define GEN_JSON_SCHEMA_MODEL__{{cppnameupper}}_H
|
|
||||||
|
|
||||||
#include <ObjectList.h>
|
|
||||||
#include <String.h>
|
|
||||||
|
|
||||||
{{#referencedclasscpptypes}}#include "{{.}}.h"
|
|
||||||
{{/referencedclasscpptypes}}
|
|
||||||
|
|
||||||
|
|
||||||
class {{cppname}} {
|
|
||||||
public:
|
|
||||||
{{cppname}}();
|
|
||||||
virtual ~{{cppname}}();
|
|
||||||
|
|
||||||
void Reset();
|
|
||||||
{{#propertyarray}}{{#property.iscppscalartype}}
|
|
||||||
{{property.cpptype}} {{property.cppname}}() const;
|
|
||||||
void Set{{property.cppname}}({{property.cpptype}} value);
|
|
||||||
void Set{{property.cppname}}Null();
|
|
||||||
bool {{property.cppname}}IsNull() const;
|
|
||||||
{{/property.iscppscalartype}}{{#property.isarray}}
|
|
||||||
void AddTo{{property.cppname}}({{property.items.cpptype}}* value);
|
|
||||||
void Set{{property.cppname}}({{property.cpptype}}* value);
|
|
||||||
int32 Count{{property.cppname}}() const;
|
|
||||||
{{property.items.cpptype}}* {{property.cppname}}ItemAt(int32 index) const;
|
|
||||||
bool {{property.cppname}}IsNull() const;
|
|
||||||
{{/property.isarray}}{{#property.iscppnonscalarnoncollectiontype}}
|
|
||||||
{{property.cpptype}}* {{property.cppname}}() const;
|
|
||||||
void Set{{property.cppname}}({{property.cpptype}}* value);
|
|
||||||
void Set{{property.cppname}}Null();
|
|
||||||
bool {{property.cppname}}IsNull() const;
|
|
||||||
{{/property.iscppnonscalarnoncollectiontype}}
|
|
||||||
{{/propertyarray}}
|
|
||||||
|
|
||||||
public:
|
|
||||||
{{#propertyarray}} static const uint16 k{{property.cppname}}Bitmask;
|
|
||||||
{{/propertyarray}}
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint16 fHasValueBitmask;
|
|
||||||
{{#propertyarray}} {{property.cpptype}}{{^property.iscppscalartype}}*{{/property.iscppscalartype}} {{property.cppmembername}};
|
|
||||||
{{/propertyarray}}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // GEN_JSON_SCHEMA_MODEL__{{cppnameupper}}_H
|
|
||||||
"""
|
|
||||||
|
|
||||||
IMPLEMENTATION_TEMPLATE = """
|
|
||||||
/*
|
|
||||||
* Generated Model Object for {{cppname}}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "{{cppname}}.h"
|
|
||||||
|
|
||||||
|
|
||||||
{{#propertyarray}}/*static*/ const uint16 {{cppname}}::k{{property.cppname}}Bitmask = {{cppbitmaskexpression}};
|
|
||||||
{{/propertyarray}}
|
|
||||||
|
|
||||||
|
|
||||||
{{cppname}}::{{cppname}}()
|
|
||||||
:
|
|
||||||
fHasValueBitmask(0),
|
|
||||||
{{#propertyarray}} {{property.cppmembername}}({{property.cppdefaultvalue}}){{^islast}},{{/islast}}
|
|
||||||
{{/propertyarray}}{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{{cppname}}::~{{cppname}}()
|
|
||||||
{
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
{{cppname}}::Reset()
|
|
||||||
{
|
|
||||||
{{#propertyarray}}{{#property.isstring}} delete {{property.cppmembername}};
|
|
||||||
{{/property.isstring}}{{#property.isobject}} delete {{property.cppmembername}};
|
|
||||||
{{/property.isobject}}{{#property.isarray}}
|
|
||||||
if ({{property.cppmembername}} != NULL) {
|
|
||||||
for (int i = {{property.cppmembername}}->CountItems() - 1; i >= 0; i--)
|
|
||||||
delete {{property.cppmembername}}->ItemAt(i);
|
|
||||||
delete {{property.cppmembername}};
|
|
||||||
}
|
|
||||||
{{/property.isarray}} {{property.cppmembername}} = {{property.cppdefaultvalue}};
|
|
||||||
{{/propertyarray}}
|
|
||||||
fHasValueBitmask = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
{{#propertyarray}}{{#property.iscppscalartype}}{{property.cpptype}}
|
|
||||||
{{cppobjectname}}::{{property.cppname}}() const
|
|
||||||
{
|
|
||||||
return {{property.cppmembername}};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
{{cppobjectname}}::Set{{property.cppname}}({{property.cpptype}} value)
|
|
||||||
{
|
|
||||||
fHasValueBitmask |= k{{property.cppname}}Bitmask;
|
|
||||||
{{property.cppmembername}} = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
{{cppobjectname}}::Set{{property.cppname}}Null()
|
|
||||||
{
|
|
||||||
fHasValueBitmask &= ~k{{property.cppname}}Bitmask;
|
|
||||||
{{property.cppmembername}} = {{property.cppdefaultvalue}};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
{{cppobjectname}}::{{property.cppname}}IsNull() const
|
|
||||||
{
|
|
||||||
return 0 == (fHasValueBitmask & k{{property.cppname}}Bitmask);
|
|
||||||
}
|
|
||||||
|
|
||||||
{{/property.iscppscalartype}}{{#property.isarray}}void
|
|
||||||
{{cppobjectname}}::AddTo{{property.cppname}}({{property.items.cpptype}}* value)
|
|
||||||
{
|
|
||||||
if ({{property.cppmembername}} == NULL)
|
|
||||||
{{property.cppmembername}} = new {{property.cpptype}}();
|
|
||||||
{{property.cppmembername}}->AddItem(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
{{cppobjectname}}::Set{{property.cppname}}({{property.cpptype}}* value)
|
|
||||||
{
|
|
||||||
if ({{property.cppmembername}} != NULL) {
|
|
||||||
delete {{property.cppmembername}};
|
|
||||||
}
|
|
||||||
{{property.cppmembername}} = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int32
|
|
||||||
{{cppobjectname}}::Count{{property.cppname}}() const
|
|
||||||
{
|
|
||||||
if ({{property.cppmembername}} == NULL)
|
|
||||||
return 0;
|
|
||||||
return {{property.cppmembername}}->CountItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{{property.items.cpptype}}*
|
|
||||||
{{cppobjectname}}::{{property.cppname}}ItemAt(int32 index) const
|
|
||||||
{
|
|
||||||
return {{property.cppmembername}}->ItemAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
{{cppobjectname}}::{{property.cppname}}IsNull() const
|
|
||||||
{
|
|
||||||
return {{property.cppmembername}} == NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
{{/property.isarray}}{{#property.iscppnonscalarnoncollectiontype}}{{property.cpptype}}*
|
|
||||||
{{cppobjectname}}::{{property.cppname}}() const
|
|
||||||
{
|
|
||||||
return {{property.cppmembername}};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
{{cppobjectname}}::Set{{property.cppname}}({{property.cpptype}}* value)
|
|
||||||
{
|
|
||||||
{{property.cppmembername}} = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
{{cppobjectname}}::Set{{property.cppname}}Null()
|
|
||||||
{
|
|
||||||
if (!{{property.cppname}}IsNull()) {
|
|
||||||
delete {{property.cppmembername}};
|
|
||||||
{{property.cppmembername}} = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
{{cppobjectname}}::{{property.cppname}}IsNull() const
|
|
||||||
{
|
|
||||||
return {{property.cppmembername}} == NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
{{/property.iscppnonscalarnoncollectiontype}}
|
|
||||||
{{/propertyarray}}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def write_models_for_schema(schema: dict[str, any], output_directory: str) -> None:
|
|
||||||
|
|
||||||
def write_model_object(obj: dict[str, any]) -> None:
|
|
||||||
cpp_name = obj["cppname"]
|
|
||||||
cpp_header_filename = os.path.join(output_directory, cpp_name + '.h')
|
|
||||||
cpp_implementation_filename = os.path.join(output_directory, cpp_name + '.cpp')
|
|
||||||
|
|
||||||
with open(cpp_header_filename, 'w') as cpp_h_file:
|
|
||||||
cpp_h_file.write(ustache.render(
|
|
||||||
HEADER_TEMPLATE,
|
|
||||||
obj,
|
|
||||||
escape= lambda x: x))
|
|
||||||
|
|
||||||
with open(cpp_implementation_filename, 'w') as cpp_i_file:
|
|
||||||
cpp_i_file.write(ustache.render(
|
|
||||||
IMPLEMENTATION_TEMPLATE,
|
|
||||||
obj,
|
|
||||||
escape= lambda x: x))
|
|
||||||
|
|
||||||
for obj in hdsjsonschemacommon.collect_all_objects(schema):
|
|
||||||
write_model_object(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Convert JSON schema to Haiku C++ Models')
|
description="Convert JSON schema to Haiku C++ Models")
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-i', '--inputfile',
|
"--jsonschemafile",
|
||||||
|
dest = "jsonschemafile",
|
||||||
required=True,
|
required=True,
|
||||||
help='The input filename containing the JSON schema')
|
help="The input filename containing the JSON schema")
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--outputdirectory',
|
"--outputdirectory",
|
||||||
help='The output directory where the C++ files should be written')
|
dest = "outputdirectory",
|
||||||
|
required=True,
|
||||||
|
help="The output filename where the C++ header and implementation file should be written")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
schema_path = Path(args.jsonschemafile).absolute()
|
||||||
|
|
||||||
output_directory = args.outputdirectory
|
if not schema_path.is_file():
|
||||||
|
raise RuntimeError(f"The input file [{schema_path}] does not exist")
|
||||||
|
|
||||||
if not output_directory:
|
output_directory_path = Path(args.outputdirectory).absolute()
|
||||||
output_directory = '.'
|
output_directory_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with open(args.inputfile) as inputfile:
|
with open(schema_path) as input_file:
|
||||||
schema = json.load(inputfile)
|
schema = json.load(input_file)
|
||||||
hdsjsonschemacommon.augment_schema(schema)
|
|
||||||
write_models_for_schema(schema, output_directory)
|
jsonschemacommon.augment_schema(schema)
|
||||||
|
object_schemas = jsonschemacommon.collect_all_objects(schema)
|
||||||
|
|
||||||
|
def output_to_path(output_path: pathlib.Path):
|
||||||
|
template_type = jsonschemacommon.path_to_extension_type(output_path)
|
||||||
|
template = jsonschemacommon.load_template("model", template_type)
|
||||||
|
template_model = {
|
||||||
|
"objects": object_schemas,
|
||||||
|
"outputfilename": output_path.name,
|
||||||
|
"outputheaderfile": output_path.stem + ".h",
|
||||||
|
"outputfilestemupper": output_path.stem.upper()
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(output_path, 'w') as out_file:
|
||||||
|
out_file.write(ustache.render(
|
||||||
|
template,
|
||||||
|
template_model,
|
||||||
|
escape= lambda x: x))
|
||||||
|
|
||||||
|
cpp_name = schema["cppname"]
|
||||||
|
output_to_path(output_directory_path / f"{cpp_name}Model.h")
|
||||||
|
output_to_path(output_directory_path / f"{cpp_name}Model.cpp")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,335 @@
|
|||||||
|
# =====================================
|
||||||
|
# Copyright 2017-2026, Andrew Lindesay
|
||||||
|
# Distributed under the terms of the MIT License.
|
||||||
|
# =====================================
|
||||||
|
|
||||||
|
"""\
|
||||||
|
This file contains classes and helper functions that are used in the generation
|
||||||
|
of `.h` and `.cpp` C++ files based on a JSON Schema. The schemas are intended
|
||||||
|
for use in a Java setting, but are adapted by this logic for use with C++ and
|
||||||
|
types that are typical for the Haiku environment.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import pathlib
|
||||||
|
from typing import Any
|
||||||
|
from enum import Enum, auto
|
||||||
|
|
||||||
|
# The possible JSON types
|
||||||
|
JSON_TYPE_STRING = "string"
|
||||||
|
JSON_TYPE_OBJECT = "object"
|
||||||
|
JSON_TYPE_ARRAY = "array"
|
||||||
|
JSON_TYPE_BOOLEAN = "boolean"
|
||||||
|
JSON_TYPE_INTEGER = "integer"
|
||||||
|
JSON_TYPE_NUMBER = "number"
|
||||||
|
|
||||||
|
|
||||||
|
# The possible C++ types
|
||||||
|
CPP_TYPE_STRING = "BString"
|
||||||
|
CPP_TYPE_ARRAY = "std::vector"
|
||||||
|
CPP_TYPE_BOOLEAN = "bool"
|
||||||
|
CPP_TYPE_INTEGER = "int64"
|
||||||
|
CPP_TYPE_NUMBER = "double"
|
||||||
|
|
||||||
|
|
||||||
|
# The possible C++ default values
|
||||||
|
CPP_DEFAULT_STRING = "\"\""
|
||||||
|
CPP_DEFAULT_BOOLEAN = "false"
|
||||||
|
CPP_DEFAULT_INTEGER = "0"
|
||||||
|
CPP_DEFAULT_NUMBER = "0.0"
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateType(Enum):
|
||||||
|
"""Type of template to load."""
|
||||||
|
HEADER = auto()
|
||||||
|
IMPLEMENTATION = auto()
|
||||||
|
|
||||||
|
|
||||||
|
class CppClassNode:
|
||||||
|
"""An object representing a C++ class for establishing a dependency graph."""
|
||||||
|
def __init__(self, cpp_name: str, schema: dict[str, Any], dependencies: set):
|
||||||
|
self.cpp_name = cpp_name
|
||||||
|
self.schema = schema
|
||||||
|
self.dependencies = dependencies
|
||||||
|
|
||||||
|
|
||||||
|
def java_type_to_cpp_name(java_name: str) -> str:
|
||||||
|
return java_name[java_name.rindex('.') + 1:]
|
||||||
|
|
||||||
|
|
||||||
|
def prop_name_to_cpp_name(prop_name: str) -> str:
|
||||||
|
return prop_name[0:1].upper() + prop_name[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def prop_name_to_cpp_member_name(prop_name: str) -> str:
|
||||||
|
return "f" + prop_name_to_cpp_name(prop_name)
|
||||||
|
|
||||||
|
|
||||||
|
def prop_metadata_to_cpp_default_value(prop_metadata: dict[str, Any]) -> str:
|
||||||
|
"""Converts the property data into a C++ default for the field.
|
||||||
|
|
||||||
|
This function will look at the metadata for the property and decide what a
|
||||||
|
generic default value for that type might be.
|
||||||
|
|
||||||
|
Returns: The function will return a string representation of the C++ value.
|
||||||
|
"""
|
||||||
|
type = prop_metadata['type']
|
||||||
|
|
||||||
|
if type == JSON_TYPE_STRING:
|
||||||
|
return CPP_DEFAULT_STRING
|
||||||
|
if type == JSON_TYPE_BOOLEAN:
|
||||||
|
return CPP_DEFAULT_BOOLEAN
|
||||||
|
if type == JSON_TYPE_INTEGER:
|
||||||
|
return CPP_DEFAULT_INTEGER
|
||||||
|
if type == JSON_TYPE_NUMBER:
|
||||||
|
return CPP_DEFAULT_NUMBER
|
||||||
|
if type == JSON_TYPE_OBJECT:
|
||||||
|
cpp_type = prop_metadata["cpptype"]
|
||||||
|
return f"{cpp_type}Ref()"
|
||||||
|
if type == JSON_TYPE_ARRAY:
|
||||||
|
items_cpptype = prop_metadata["items"]["cpptype"]
|
||||||
|
return f"{CPP_TYPE_ARRAY}<{items_cpptype}>()"
|
||||||
|
|
||||||
|
raise Exception(f"unknown json-schema type [{type}]")
|
||||||
|
|
||||||
|
def prop_metadata_to_cpp_type_name(prop_metadata: dict[str, Any]) -> str:
|
||||||
|
"""Introspects data metadata for a property and returns its type in C++"""
|
||||||
|
type_ = prop_metadata['type']
|
||||||
|
|
||||||
|
if type_ == JSON_TYPE_STRING:
|
||||||
|
return CPP_TYPE_STRING
|
||||||
|
if type_ == JSON_TYPE_BOOLEAN:
|
||||||
|
return CPP_TYPE_BOOLEAN
|
||||||
|
if type_ == JSON_TYPE_INTEGER:
|
||||||
|
return CPP_TYPE_INTEGER
|
||||||
|
if type_ == JSON_TYPE_NUMBER:
|
||||||
|
return CPP_TYPE_NUMBER
|
||||||
|
if type_ == JSON_TYPE_OBJECT:
|
||||||
|
java_type = prop_metadata['javaType']
|
||||||
|
|
||||||
|
if not java_type or 0 == len(java_type):
|
||||||
|
raise Exception('missing "javaType" field')
|
||||||
|
|
||||||
|
return java_type_to_cpp_name(java_type)
|
||||||
|
|
||||||
|
if type_ == JSON_TYPE_ARRAY:
|
||||||
|
items_cpptype = prop_metadata["items"]["cpptype"]
|
||||||
|
return f"{CPP_TYPE_ARRAY}<{items_cpptype}>"
|
||||||
|
|
||||||
|
raise Exception(f"unknown json-schema type [{type_}]")
|
||||||
|
|
||||||
|
|
||||||
|
def prop_metadata_type_is_scalar(prop_metadata: dict[str, Any]) -> bool:
|
||||||
|
type_ = prop_metadata['type']
|
||||||
|
return type_ == JSON_TYPE_BOOLEAN or type_ == JSON_TYPE_INTEGER or type_ == JSON_TYPE_NUMBER
|
||||||
|
|
||||||
|
|
||||||
|
def write_top_comment(f, input_filename: str, variant: str) -> None:
|
||||||
|
f.write((
|
||||||
|
'/*\n'
|
||||||
|
' * Generated %s Object\n'
|
||||||
|
' * source json-schema : %s\n'
|
||||||
|
' * generated at : %s\n'
|
||||||
|
' */\n'
|
||||||
|
) % (variant, input_filename, datetime.datetime.now().isoformat()))
|
||||||
|
|
||||||
|
|
||||||
|
def _to_cpp_class_dependency_tree(schema: dict[str, Any]) -> CppClassNode | None:
|
||||||
|
"""Builds up a dependency tree of to-be C++ classes
|
||||||
|
|
||||||
|
The output of this is a dependency tree of C++ classes from which it is
|
||||||
|
possible to get the order that the classes need to be written into the
|
||||||
|
header and implementation files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
all_nodes: list[CppClassNode] = []
|
||||||
|
|
||||||
|
def to_node(sub_schema: dict[str, Any]):
|
||||||
|
if sub_schema["type"] == JSON_TYPE_ARRAY:
|
||||||
|
return to_node(sub_schema["items"])
|
||||||
|
if sub_schema["type"] == JSON_TYPE_OBJECT:
|
||||||
|
cpp_name = sub_schema["cppname"]
|
||||||
|
|
||||||
|
existing_class = next((c for c in all_nodes if c.cpp_name == cpp_name), None)
|
||||||
|
if existing_class:
|
||||||
|
return existing_class
|
||||||
|
|
||||||
|
node = CppClassNode(cpp_name=cpp_name, schema=sub_schema, dependencies=set())
|
||||||
|
all_nodes.append(node)
|
||||||
|
|
||||||
|
dependencies = [to_node(property_schema) for property_schema in sub_schema["properties"].values()]
|
||||||
|
node.dependencies = sorted([d for d in dependencies if d], key = lambda d: d.cpp_name)
|
||||||
|
|
||||||
|
return node
|
||||||
|
return None
|
||||||
|
|
||||||
|
return to_node(schema)
|
||||||
|
|
||||||
|
|
||||||
|
def _to_object_sub_schemas_in_dependency_order(root_node: CppClassNode) -> list[dict[str, Any]]:
|
||||||
|
"""Returns a list of the schemas representing objects such that any dependencies appear first."""
|
||||||
|
|
||||||
|
result: list[dict[str, Any]] = []
|
||||||
|
|
||||||
|
def walk(node: CppClassNode):
|
||||||
|
for d in node.dependencies:
|
||||||
|
walk(d)
|
||||||
|
result.append(node.schema)
|
||||||
|
|
||||||
|
walk(root_node)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def collect_all_objects(schema: dict[str, Any]) -> list[dict[str, Any]]:
|
||||||
|
"""Walks the schema and extracts all definitions of objects.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of schema structure which defines the class. The list will be in
|
||||||
|
order so that latter classes are able to have dependency on earlier
|
||||||
|
classes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
root_node = _to_cpp_class_dependency_tree(schema)
|
||||||
|
|
||||||
|
if not root_node:
|
||||||
|
raise RuntimeError("it was not possible to extract a tree from the schema data")
|
||||||
|
|
||||||
|
return _to_object_sub_schemas_in_dependency_order(root_node)
|
||||||
|
|
||||||
|
|
||||||
|
def augment_schema(schema: dict[str, Any]) -> None:
|
||||||
|
"""This function will take the data from the JSON schema and will overlay any
|
||||||
|
specific information required for rendering the templates.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def augment_property_type(prop: dict[str, Any]) -> None:
|
||||||
|
prop_type = prop['type']
|
||||||
|
|
||||||
|
if not prop_type or 0 == len(prop_type):
|
||||||
|
raise Exception('missing "type" field')
|
||||||
|
|
||||||
|
# For the object type, a `BReference` is used as the member variable
|
||||||
|
# type but the actual wrapped type is still required for constructors.
|
||||||
|
|
||||||
|
if prop_type == JSON_TYPE_OBJECT:
|
||||||
|
prop["cpptyperaw"] = prop_metadata_to_cpp_type_name(prop)
|
||||||
|
prop["cpptype"] = prop_metadata_to_cpp_type_name(prop) + "Ref"
|
||||||
|
else:
|
||||||
|
prop["cpptype"] = prop_metadata_to_cpp_type_name(prop)
|
||||||
|
|
||||||
|
is_scalar_type = prop_metadata_type_is_scalar(prop)
|
||||||
|
is_array_type = (prop_type == JSON_TYPE_ARRAY)
|
||||||
|
|
||||||
|
prop["iscppscalartype"] = is_scalar_type
|
||||||
|
prop["isarray"] = is_array_type
|
||||||
|
prop["isstring"] = JSON_TYPE_STRING == prop_type
|
||||||
|
prop["isboolean"] = JSON_TYPE_BOOLEAN == prop_type
|
||||||
|
prop["isnumber"] = JSON_TYPE_NUMBER == prop_type
|
||||||
|
prop["isinteger"] = JSON_TYPE_INTEGER == prop_type
|
||||||
|
prop["isobject"] = JSON_TYPE_OBJECT == prop_type
|
||||||
|
prop["isobjectorarray"] = prop_type in [JSON_TYPE_OBJECT, JSON_TYPE_ARRAY]
|
||||||
|
prop["iscppnonscalarnoncollectiontype"] = not is_scalar_type and not is_array_type
|
||||||
|
|
||||||
|
def derive_cpp_classname(obj: dict[str, Any]) -> str:
|
||||||
|
obj_type = obj['type']
|
||||||
|
|
||||||
|
if obj_type != JSON_TYPE_OBJECT:
|
||||||
|
raise Exception('expecting object, but was [' + obj_type + ']')
|
||||||
|
|
||||||
|
java_type = obj['javaType']
|
||||||
|
|
||||||
|
return java_type_to_cpp_name(java_type)
|
||||||
|
|
||||||
|
def augment_property(prop: dict[str, Any]) -> None:
|
||||||
|
|
||||||
|
if JSON_TYPE_ARRAY == prop["type"]:
|
||||||
|
array_items = prop['items']
|
||||||
|
array_items_type = array_items["type"]
|
||||||
|
|
||||||
|
if array_items_type == JSON_TYPE_OBJECT:
|
||||||
|
augment_object(array_items)
|
||||||
|
elif array_items_type == JSON_TYPE_STRING:
|
||||||
|
augment_property(array_items)
|
||||||
|
else:
|
||||||
|
raise Exception(f"bad type for array items [{array_items_type}]")
|
||||||
|
|
||||||
|
augment_property_type(prop)
|
||||||
|
prop["cppdefaultvalue"] = prop_metadata_to_cpp_default_value(prop)
|
||||||
|
|
||||||
|
if JSON_TYPE_OBJECT == prop["type"]:
|
||||||
|
augment_object(prop)
|
||||||
|
|
||||||
|
def augment_object(obj: dict[str, Any]) -> None:
|
||||||
|
def has_any_list_properties() -> bool:
|
||||||
|
for _, prop in obj['properties'].items():
|
||||||
|
if prop['type'] == JSON_TYPE_ARRAY:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
augment_property_type(obj)
|
||||||
|
|
||||||
|
obj_cpp_classname = derive_cpp_classname(obj)
|
||||||
|
obj["cppname"] = obj_cpp_classname
|
||||||
|
obj["cppnameupper"] = obj_cpp_classname.upper()
|
||||||
|
obj["hasanylistproperties"] = has_any_list_properties()
|
||||||
|
|
||||||
|
# allows the object to know the top level object that contains it
|
||||||
|
obj["toplevelcppname"] = top_level_cpp_name
|
||||||
|
|
||||||
|
properties = obj['properties'].items()
|
||||||
|
|
||||||
|
for prop_name, prop in properties:
|
||||||
|
prop["cppname"] = prop_name_to_cpp_name(prop_name)
|
||||||
|
prop["cppmembername"] = prop_name_to_cpp_member_name(prop_name)
|
||||||
|
augment_property(prop)
|
||||||
|
|
||||||
|
# mustache is not able to iterate over a dictionary; it can only
|
||||||
|
# iterate over a list where each item in the list has some properties.
|
||||||
|
|
||||||
|
property_array = sorted(
|
||||||
|
[{
|
||||||
|
"name": k,
|
||||||
|
"property": v,
|
||||||
|
"cppobjectname": obj_cpp_classname
|
||||||
|
# ^ this is the name of the containing object
|
||||||
|
} for k,v in properties],
|
||||||
|
key= lambda item: item["name"]
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in range(len(property_array)):
|
||||||
|
property_array[i]["cppbitmaskexpression"] = "(1 << %d)" % i
|
||||||
|
|
||||||
|
if 0 != len(property_array):
|
||||||
|
property_array[0]["isfirst"] = True
|
||||||
|
property_array[len(property_array) - 1]["islast"] = True
|
||||||
|
|
||||||
|
obj["propertyarray"] = property_array
|
||||||
|
|
||||||
|
top_level_cpp_name = derive_cpp_classname(schema)
|
||||||
|
augment_object(schema)
|
||||||
|
|
||||||
|
|
||||||
|
def path_to_extension_type(file_path: pathlib.Path) -> TemplateType:
|
||||||
|
if ".h" == file_path.suffix:
|
||||||
|
return TemplateType.HEADER
|
||||||
|
if ".cpp" == file_path.suffix:
|
||||||
|
return TemplateType.IMPLEMENTATION
|
||||||
|
raise RuntimeError(f"unknown extension [{file_path.suffix}]")
|
||||||
|
|
||||||
|
|
||||||
|
def load_template(template_name: str, template_type: TemplateType) -> str:
|
||||||
|
"""Loads the template from the `./template` directory."""
|
||||||
|
|
||||||
|
def template_type_path_component() -> str:
|
||||||
|
if template_type == TemplateType.HEADER:
|
||||||
|
return "header"
|
||||||
|
if template_type == TemplateType.IMPLEMENTATION:
|
||||||
|
return "implementation"
|
||||||
|
raise RuntimeError(f"unknown template type [{str(template_type)}")
|
||||||
|
|
||||||
|
script_directory = pathlib.Path(__file__).parent.resolve()
|
||||||
|
template_path = script_directory / "template" / f"{template_name}{template_type_path_component()}.mustache"
|
||||||
|
|
||||||
|
with open(template_path, "r") as f:
|
||||||
|
return f.read()
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Generated Model Object for {{outputfilename}}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GEN_JSON_SCHEMA_MODEL__{{outputfilestemupper}}_H
|
||||||
|
#define GEN_JSON_SCHEMA_MODEL__{{outputfilestemupper}}_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <Archivable.h>
|
||||||
|
#include <Referenceable.h>
|
||||||
|
#include <String.h>{{#objects}}
|
||||||
|
|
||||||
|
|
||||||
|
class {{cppname}} : public BArchivable, public BReferenceable
|
||||||
|
{
|
||||||
|
friend class {{cppname}}Builder;
|
||||||
|
|
||||||
|
public:
|
||||||
|
{{cppname}}();
|
||||||
|
{{cppname}}(const BMessage* message);
|
||||||
|
virtual ~{{cppname}}();
|
||||||
|
|
||||||
|
bool operator==(const {{cppname}}& other) const;
|
||||||
|
bool operator!=(const {{cppname}}& other) const;
|
||||||
|
|
||||||
|
{{#propertyarray}}{{#property.isarray}}
|
||||||
|
const {{property.cpptype}} {{property.cppname}}() const;
|
||||||
|
int32 Count{{property.cppname}}() const;
|
||||||
|
const {{property.items.cpptype}}& {{property.cppname}}ItemAt(int32 index) const;
|
||||||
|
bool IsSet{{property.cppname}}() const;
|
||||||
|
{{/property.isarray}}{{^property.isarray}}
|
||||||
|
{{#property.iscppscalartype}} {{property.cpptype}} {{property.cppname}}() const;
|
||||||
|
{{/property.iscppscalartype}}{{^property.iscppscalartype}} const {{property.cpptype}}& {{property.cppname}}() const;
|
||||||
|
{{/property.iscppscalartype}}
|
||||||
|
bool IsSet{{property.cppname}}() const;
|
||||||
|
{{/property.isarray}}
|
||||||
|
{{/propertyarray}}
|
||||||
|
|
||||||
|
status_t Archive(BMessage* into, bool deep = true) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
{{#propertyarray}}{{#property.isarray}} void _Set{{property.cppname}}(const {{property.cpptype}}& value);
|
||||||
|
void _AddTo{{property.cppname}}(const {{property.items.cpptype}}& value);
|
||||||
|
void _Unset{{property.cppname}}();
|
||||||
|
{{/property.isarray}}{{^property.isarray}}
|
||||||
|
{{#property.iscppscalartype}} void _Set{{property.cppname}}({{property.cpptype}} value);
|
||||||
|
{{/property.iscppscalartype}}{{^property.iscppscalartype}} void _Set{{property.cppname}}(const {{property.cpptype}}& value);
|
||||||
|
{{/property.iscppscalartype}}
|
||||||
|
void _Unset{{property.cppname}}();
|
||||||
|
{{/property.isarray}}
|
||||||
|
{{/propertyarray}}
|
||||||
|
|
||||||
|
public:
|
||||||
|
{{#propertyarray}} static const uint16 k{{property.cppname}}Bitmask;
|
||||||
|
{{/propertyarray}}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16 fHasValueBitmask;
|
||||||
|
{{#propertyarray}} {{property.cpptype}} {{property.cppmembername}};
|
||||||
|
{{/propertyarray}}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef BReference<{{cppname}}> {{cppname}}Ref;
|
||||||
|
|
||||||
|
|
||||||
|
class {{cppname}}Builder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
{{cppname}}Builder();
|
||||||
|
{{cppname}}Builder(const {{cppname}}Ref& source);
|
||||||
|
{{cppname}}Builder(const {{cppname}}* source);
|
||||||
|
virtual ~{{cppname}}Builder();
|
||||||
|
|
||||||
|
{{cppname}}Builder& Clear();
|
||||||
|
{{cppname}}Ref BuildRef();
|
||||||
|
|
||||||
|
{{#propertyarray}}{{#property.isarray}}
|
||||||
|
{{cppname}}Builder& With{{property.cppname}}({{property.cpptype}} value);
|
||||||
|
{{cppname}}Builder& Without{{property.cppname}}();
|
||||||
|
{{cppname}}Builder& AddTo{{property.cppname}}({{property.items.cpptype}} value);
|
||||||
|
{{/property.isarray}}{{^property.isarray}}
|
||||||
|
{{cppname}}Builder& With{{property.cppname}}({{property.cpptype}} value);
|
||||||
|
{{cppname}}Builder& Without{{property.cppname}}();
|
||||||
|
{{/property.isarray}}
|
||||||
|
{{/propertyarray}}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _InitFromSource();
|
||||||
|
void _Init(const {{cppname}}* source);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16 fHasValueBitmask;
|
||||||
|
{{cppname}}Ref fSourceRef;
|
||||||
|
|
||||||
|
{{#propertyarray}} {{property.cpptype}} {{property.cppmembername}};
|
||||||
|
{{/propertyarray}}
|
||||||
|
};{{/objects}}
|
||||||
|
|
||||||
|
#endif // GEN_JSON_SCHEMA_MODEL__{{outputfilestemupper}}_H
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
/*
|
||||||
|
* Generated Model Object for {{outputfilename}}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "{{outputheaderfile}}"{{#objects}}
|
||||||
|
|
||||||
|
|
||||||
|
{{#propertyarray}}/*static*/ const uint16 {{cppname}}::k{{property.cppname}}Bitmask = {{cppbitmaskexpression}};
|
||||||
|
{{/propertyarray}}
|
||||||
|
|
||||||
|
|
||||||
|
{{cppname}}::{{cppname}}()
|
||||||
|
:
|
||||||
|
fHasValueBitmask(0),
|
||||||
|
{{#propertyarray}} {{property.cppmembername}}({{property.cppdefaultvalue}}){{^islast}},{{/islast}}
|
||||||
|
{{/propertyarray}}{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{cppname}}::{{cppname}}(const BMessage* from)
|
||||||
|
:
|
||||||
|
fHasValueBitmask(0)
|
||||||
|
{
|
||||||
|
{{#hasanylistproperties}} bool arrayIsSet;
|
||||||
|
BString arrayItemKey;
|
||||||
|
{{/hasanylistproperties}}
|
||||||
|
{{#propertyarray}}{{#property.isstring}} if (from->FindString("{{property.cppname}}", &{{property.cppmembername}}) == B_OK)
|
||||||
|
fHasValueBitmask |= k{{property.cppname}}Bitmask;
|
||||||
|
{{/property.isstring}}{{#property.isboolean}} if (from->FindBool("{{property.cppname}}", &{{property.cppmembername}}) == B_OK)
|
||||||
|
fHasValueBitmask |= k{{property.cppname}}Bitmask;
|
||||||
|
{{/property.isboolean}}{{#property.isnumber}} if (from->FindDouble("{{property.cppname}}", &{{property.cppmembername}}) == B_OK)
|
||||||
|
fHasValueBitmask |= k{{property.cppname}}Bitmask;
|
||||||
|
{{/property.isnumber}}{{#property.isinteger}} if (from->FindInt64("{{property.cppname}}", &{{property.cppmembername}}) == B_OK)
|
||||||
|
fHasValueBitmask |= k{{property.cppname}}Bitmask;
|
||||||
|
{{/property.isinteger}}{{#property.isobject}} {
|
||||||
|
BMessage objectMessage;
|
||||||
|
if (from->FindMessage("{{property.cppname}}", &objectMessage) == B_OK) {
|
||||||
|
{{property.cpptyperaw}}* object = new {{property.cpptyperaw}}(&objectMessage);
|
||||||
|
{{property.cppmembername}} = {{property.cpptyperaw}}(object, true);
|
||||||
|
fHasValueBitmask |= k{{property.cppname}}Bitmask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{/property.isobject}}{{#property.isarray}} if (from->FindBool("{{property.cppname}}_isSet", &arrayIsSet) == B_OK && arrayIsSet) {
|
||||||
|
status_t arrayItemFindResult = B_OK;
|
||||||
|
|
||||||
|
for (int32 i = 0; arrayItemFindResult == B_OK; i++) {
|
||||||
|
arrayItemKey.SetToFormat("{{property.cppname}}_%" B_PRId32, i);
|
||||||
|
{{#property.items.isstring}} BString arrayItemString;
|
||||||
|
arrayItemFindResult = from->FindString(arrayItemKey, &arrayItemString);
|
||||||
|
if (arrayItemFindResult == B_OK)
|
||||||
|
{{property.cppmembername}}.push_back(arrayItemString);
|
||||||
|
{{/property.items.isstring}}{{^property.items.isstring}} BMessage arrayItemMessage;
|
||||||
|
arrayItemFindResult = from->FindMessage(arrayItemKey, &arrayItemMessage);
|
||||||
|
if (arrayItemFindResult == B_OK) {
|
||||||
|
{{property.items.cpptyperaw}}* arrayItemObject = new {{property.items.cpptyperaw}}(&arrayItemMessage);
|
||||||
|
{{property.cppmembername}}.push_back({{property.items.cpptype}}(arrayItemObject, true));
|
||||||
|
}
|
||||||
|
{{/property.items.isstring}} }
|
||||||
|
}
|
||||||
|
{{/property.isarray}}{{/propertyarray}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{cppname}}::~{{cppname}}()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
{{cppname}}::operator==(const {{cppname}}& other) const
|
||||||
|
{
|
||||||
|
if (fHasValueBitmask != other.fHasValueBitmask)
|
||||||
|
return false;
|
||||||
|
{{#propertyarray}}{{#property.iscppscalartype}} if (0 != (fHasValueBitmask & k{{property.cppname}}Bitmask) && {{property.cppmembername}} != other.{{property.cppmembername}})
|
||||||
|
return false;
|
||||||
|
{{/property.iscppscalartype}}{{/propertyarray}}{{#propertyarray}}{{#property.isstring}} if (0 != (fHasValueBitmask & k{{property.cppname}}Bitmask) && {{property.cppmembername}} != other.{{property.cppmembername}})
|
||||||
|
return false;
|
||||||
|
{{/property.isstring}}{{/propertyarray}}{{#propertyarray}}{{#property.isobject}} if (0 != (fHasValueBitmask & k{{property.cppname}}Bitmask) && *({{property.cppmembername}}.Get()) != *(other.{{property.cppmembername}}.Get())
|
||||||
|
return false;
|
||||||
|
{{/property.isobject}}{{/propertyarray}}{{#propertyarray}}{{#property.isarray}} if (0 != (fHasValueBitmask & k{{property.cppname}}Bitmask)) {
|
||||||
|
if ({{property.cppmembername}}.size() != other.{{property.cppmembername}}.size())
|
||||||
|
return false;
|
||||||
|
for (int32 i = {{property.cppmembername}}.size() - 1; i >= 0; i--) {
|
||||||
|
{{#property.items.isobject}} if (*({{property.cppmembername}}[i].Get()) != *(other.{{property.cppmembername}}[i].Get()))
|
||||||
|
return false;
|
||||||
|
{{/property.items.isobject}}{{^property.items.isobject}} if ({{property.cppmembername}}[i] != other.{{property.cppmembername}}[i])
|
||||||
|
return false;
|
||||||
|
{{/property.items.isobject}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{/property.isarray}}{{/propertyarray}}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
{{cppname}}::operator!=(const {{cppname}}& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{#propertyarray}}void
|
||||||
|
{{#property.iscppscalartype}}{{cppname}}::_Set{{property.cppname}}({{property.cpptype}} value)
|
||||||
|
{{/property.iscppscalartype}}{{^property.iscppscalartype}}{{cppname}}::_Set{{property.cppname}}(const {{property.cpptype}}& value)
|
||||||
|
{{/property.iscppscalartype}}{
|
||||||
|
{{#property.isobject}} if (!{{property.cpptype}}.IsSet())
|
||||||
|
fHasValueBitmask &= ~k{{property.cppname}}Bitmask;
|
||||||
|
else
|
||||||
|
fHasValueBitmask |= k{{property.cppname}}Bitmask;
|
||||||
|
{{/property.isobject}}{{^property.isobject}} fHasValueBitmask |= k{{property.cppname}}Bitmask;
|
||||||
|
{{/property.isobject}} {{property.cppmembername}} = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
{{cppname}}::_Unset{{property.cppname}}()
|
||||||
|
{
|
||||||
|
fHasValueBitmask &= ~k{{property.cppname}}Bitmask;
|
||||||
|
{{property.cppmembername}} = {{property.cppdefaultvalue}};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
{{cppname}}::IsSet{{property.cppname}}() const
|
||||||
|
{
|
||||||
|
return 0 != (fHasValueBitmask & k{{property.cppname}}Bitmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{#property.isarray}}void
|
||||||
|
{{cppname}}::_AddTo{{property.cppname}}(const {{property.items.cpptype}}& value)
|
||||||
|
{
|
||||||
|
{{#property.items.isobject}} if (!value.IsSet()) {
|
||||||
|
debugger("attempt to inset an unset object into an array field [{{property.cppmembername}}] of [{{cppobjectname}}]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
{{/property.items.isobject}} fHasValueBitmask |= k{{property.cppname}}Bitmask;
|
||||||
|
{{property.cppmembername}}.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const {{property.cpptype}}
|
||||||
|
{{cppname}}::{{property.cppname}}() const
|
||||||
|
{
|
||||||
|
return {{property.cppmembername}};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
{{cppname}}::Count{{property.cppname}}() const
|
||||||
|
{
|
||||||
|
return {{property.cppmembername}}.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const {{property.items.cpptype}}&
|
||||||
|
{{cppname}}::{{property.cppname}}ItemAt(int32 index) const
|
||||||
|
{
|
||||||
|
return {{property.cppmembername}}[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{/property.isarray}}{{^property.isarray}}{{#property.iscppscalartype}}{{property.cpptype}}
|
||||||
|
{{cppname}}::{{property.cppname}}() const
|
||||||
|
{{/property.iscppscalartype}}{{^property.iscppscalartype}}const {{property.cpptype}}&
|
||||||
|
{{cppname}}::{{property.cppname}}() const
|
||||||
|
{{/property.iscppscalartype}}{
|
||||||
|
return {{property.cppmembername}};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{/property.isarray}}{{/propertyarray}}
|
||||||
|
status_t
|
||||||
|
{{cppname}}::Archive(BMessage* into, bool deep) const
|
||||||
|
{
|
||||||
|
{{#hasanylistproperties}} BString arrayItemKey;{{/hasanylistproperties}}
|
||||||
|
status_t result = B_OK;
|
||||||
|
|
||||||
|
{{#propertyarray}}{{#property.isstring}} if (result == B_OK && 0 != (fHasValueBitmask & k{{property.cppname}}Bitmask))
|
||||||
|
result = into->AddString("{{property.cppname}}", {{property.cppmembername}});
|
||||||
|
{{/property.isstring}}{{#property.isboolean}} if (result == B_OK && 0 != (fHasValueBitmask & k{{property.cppname}}Bitmask))
|
||||||
|
result = into->AddBool("{{property.cppname}}", {{property.cppmembername}});
|
||||||
|
{{/property.isboolean}}{{#property.isnumber}} if (result == B_OK && 0 != (fHasValueBitmask & k{{property.cppname}}Bitmask))
|
||||||
|
result = into->AddDouble("{{property.cppname}}", {{property.cppmembername}});
|
||||||
|
{{/property.isnumber}}{{#property.isinteger}} if (result == B_OK && 0 != (fHasValueBitmask & k{{property.cppname}}Bitmask))
|
||||||
|
result = into->AddInt64("{{property.cppname}}", {{property.cppmembername}});
|
||||||
|
{{/property.isinteger}}{{#property.isobject}} if (deep && result == B_OK && 0 != (fHasValueBitmask & k{{property.cppname}}Bitmask && {{property.cppmembername}}.IsSet())) {
|
||||||
|
BMessage objectMessage;
|
||||||
|
result = {{property.cppmembername}}->Archive(&objectMessage, deep);
|
||||||
|
if (result == B_OK)
|
||||||
|
result = into->AddMessage("{{property.cppname}}", &objectMessage);
|
||||||
|
}
|
||||||
|
{{/property.isobject}}{{#property.isarray}} if (deep && result == B_OK && 0 != (fHasValueBitmask & k{{property.cppname}}Bitmask)) {
|
||||||
|
for (int32 i = Count{{property.cppname}}() - 1; result == B_OK && i >= 0; i--) {
|
||||||
|
arrayItemKey.SetToFormat("%s_%" B_PRId32, "{{property.cppname}}", i);
|
||||||
|
{{#property.items.isstring}} result = into->AddString(arrayItemKey, {{property.cppname}}ItemAt(i));
|
||||||
|
{{/property.items.isstring}}{{#property.items.isobject}} BMessage arrayItemMessage;
|
||||||
|
result = {{property.cppname}}ItemAt(i)->Archive(&arrayItemMessage, deep);
|
||||||
|
if (result == B_OK)
|
||||||
|
result = into->AddMessage(arrayItemKey, &arrayItemMessage);
|
||||||
|
{{/property.items.isobject}} }
|
||||||
|
if (result == B_OK)
|
||||||
|
result = into->AddBool("{{property.cppname}}_isSet", true);
|
||||||
|
}
|
||||||
|
{{/property.isarray}}
|
||||||
|
{{/propertyarray}} return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{cppname}}Builder::{{cppname}}Builder()
|
||||||
|
:
|
||||||
|
fHasValueBitmask(0),
|
||||||
|
fSourceRef(),
|
||||||
|
{{#propertyarray}} {{property.cppmembername}}({{property.cppdefaultvalue}}){{^islast}},
|
||||||
|
{{/islast}}{{/propertyarray}}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{cppname}}Builder::{{cppname}}Builder(const {{cppname}}Ref& source)
|
||||||
|
:
|
||||||
|
fHasValueBitmask(0),
|
||||||
|
fSourceRef(source),
|
||||||
|
{{#propertyarray}} {{property.cppmembername}}({{property.cppdefaultvalue}}){{^islast}},
|
||||||
|
{{/islast}}{{/propertyarray}}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{cppname}}Builder::~{{cppname}}Builder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
{{cppname}}Builder::_InitFromSource()
|
||||||
|
{
|
||||||
|
if (fSourceRef.IsSet()) {
|
||||||
|
_Init(fSourceRef.Get());
|
||||||
|
fSourceRef.Unset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
{{cppname}}Builder::_Init(const {{cppname}}* source)
|
||||||
|
{
|
||||||
|
{{#propertyarray}} if (source->IsSet{{property.cppname}}()) {
|
||||||
|
fHasValueBitmask |= {{cppobjectname}}::k{{property.cppname}}Bitmask;
|
||||||
|
{{property.cppmembername}} = source->{{property.cppname}}();
|
||||||
|
}
|
||||||
|
{{/propertyarray}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{#propertyarray}}{{#property.isarray}}{{cppname}}Builder&
|
||||||
|
{{cppname}}Builder::AddTo{{property.cppname}}({{property.items.cpptype}} value)
|
||||||
|
{
|
||||||
|
{{#property.items.isstring}} _InitFromSource();
|
||||||
|
fHasValueBitmask |= {{cppobjectname}}::k{{property.cppname}}Bitmask;
|
||||||
|
{{property.cppmembername}}.push_back(value);
|
||||||
|
{{/property.items.isstring}}{{#property.items.isobject}} if (!value.IsSet()) {
|
||||||
|
debugger("value for [{{property.cppname}}] in class [{{cppname}}Builder}}] is unset");
|
||||||
|
} else {
|
||||||
|
_InitFromSource();
|
||||||
|
fHasValueBitmask |= {{cppobjectname}}::k{{property.cppname}}Bitmask;
|
||||||
|
{{property.cppmembername}}.push_back(value);
|
||||||
|
}
|
||||||
|
{{/property.items.isobject}}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
{{/property.isarray}}
|
||||||
|
|
||||||
|
{{cppname}}Builder&
|
||||||
|
{{cppname}}Builder::With{{property.cppname}}({{property.cpptype}} value)
|
||||||
|
{
|
||||||
|
{{#property.isobject}} if (!value->IsSet())
|
||||||
|
return Without{{property.cppname}}();
|
||||||
|
{{/property.isobject}} if (!fSourceRef.IsSet() || !fSourceRef->IsSet{{property.cppname}}() || fSourceRef->{{property.cppname}}() != value) {
|
||||||
|
_InitFromSource();
|
||||||
|
fHasValueBitmask |= {{cppobjectname}}::k{{property.cppname}}Bitmask;
|
||||||
|
{{property.cppmembername}} = value;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{cppname}}Builder&
|
||||||
|
{{cppname}}Builder::Without{{property.cppname}}()
|
||||||
|
{
|
||||||
|
if (!fSourceRef.IsSet() || fSourceRef->IsSet{{property.cppname}}()) {
|
||||||
|
_InitFromSource();
|
||||||
|
fHasValueBitmask &= ~{{cppobjectname}}::k{{property.cppname}}Bitmask;
|
||||||
|
{{property.cppmembername}} = {{property.cppdefaultvalue}};
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
{{/propertyarray}}
|
||||||
|
|
||||||
|
{{cppname}}Ref
|
||||||
|
{{cppname}}Builder::BuildRef()
|
||||||
|
{
|
||||||
|
if (fSourceRef.IsSet())
|
||||||
|
return fSourceRef;
|
||||||
|
|
||||||
|
{{cppname}}* result = new {{cppname}}();
|
||||||
|
|
||||||
|
{{#propertyarray}} if (0 != (fHasValueBitmask & {{cppobjectname}}::k{{property.cppname}}Bitmask))
|
||||||
|
result->_Set{{property.cppname}}({{property.cppmembername}});
|
||||||
|
{{/propertyarray}}
|
||||||
|
return {{cppname}}Ref(result, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Sets the builder as if it had no values set. Even if it was initialized
|
||||||
|
with an existing object, it will be reset with no values.
|
||||||
|
*/
|
||||||
|
{{cppname}}Builder&
|
||||||
|
{{cppname}}Builder::Clear()
|
||||||
|
{
|
||||||
|
fHasValueBitmask = 0;
|
||||||
|
fSourceRef.Unset();
|
||||||
|
return *this;
|
||||||
|
}{{/objects}}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Generated Listener Object for {{outputfilename}}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GEN_JSON_SCHEMA_PARSER__{{outputfilestemupper}}_H
|
||||||
|
#define GEN_JSON_SCHEMA_PARSER__{{outputfilestemupper}}_H
|
||||||
|
|
||||||
|
#include <JsonEventListener.h>
|
||||||
|
|
||||||
|
#include "{{toplevel.cppname}}Model.h"
|
||||||
|
|
||||||
|
|
||||||
|
class _AbstractStacked{{toplevel.cppname}}JsonListener;
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractMain{{toplevel.cppname}}JsonListener : public BJsonEventListener {
|
||||||
|
friend class _AbstractStacked{{toplevel.cppname}}JsonListener;
|
||||||
|
public:
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener();
|
||||||
|
virtual ~AbstractMain{{toplevel.cppname}}JsonListener();
|
||||||
|
|
||||||
|
void HandleError(status_t status, int32 line, const char* message);
|
||||||
|
void Complete();
|
||||||
|
status_t ErrorStatus();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void SetStackedListener(
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* listener);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
status_t fErrorStatus;
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* fStackedListener;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! Use this listener when you want to parse some JSON data that contains
|
||||||
|
just a single instance of {{toplevel.cppname}}.
|
||||||
|
*/
|
||||||
|
class Single{{toplevel.cppname}}JsonListener
|
||||||
|
: public AbstractMain{{toplevel.cppname}}JsonListener {
|
||||||
|
friend class AbstractStacked{{toplevel.cppname}}JsonListener;
|
||||||
|
public:
|
||||||
|
Single{{toplevel.cppname}}JsonListener();
|
||||||
|
virtual ~Single{{toplevel.cppname}}JsonListener();
|
||||||
|
|
||||||
|
bool Handle(const BJsonEvent& event);
|
||||||
|
{{toplevel.cppname}}Ref Result();
|
||||||
|
|
||||||
|
static bool WithResultCallback(void*, const {{toplevel.cppname}}Ref value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
{{toplevel.cppname}}Ref fResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! Concrete sub-classes of this class are able to respond to each
|
||||||
|
{{toplevel.cppname}} instance as
|
||||||
|
it is parsed from the bulk container. When the stream is
|
||||||
|
finished, the Complete() method is invoked.
|
||||||
|
*/
|
||||||
|
class {{toplevel.cppname}}Listener {
|
||||||
|
public:
|
||||||
|
virtual bool Handle({{toplevel.cppname}}Ref item) = 0;
|
||||||
|
virtual void Complete() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! Use this listener, together with an instance of a concrete
|
||||||
|
subclass of {{toplevel.cppname}}Listener
|
||||||
|
in order to parse the JSON data in a specific "bulk
|
||||||
|
container" format. Each time that an instance of
|
||||||
|
{{toplevel.cppname}}
|
||||||
|
is parsed, the instance item listener will be invoked.
|
||||||
|
*/
|
||||||
|
class BulkContainer{{toplevel.cppname}}JsonListener
|
||||||
|
: public AbstractMain{{toplevel.cppname}}JsonListener {
|
||||||
|
friend class AbstractStacked{{toplevel.cppname}}JsonListener;
|
||||||
|
public:
|
||||||
|
BulkContainer{{toplevel.cppname}}JsonListener(
|
||||||
|
{{toplevel.cppname}}Listener* itemListener);
|
||||||
|
~BulkContainer{{toplevel.cppname}}JsonListener();
|
||||||
|
|
||||||
|
bool Handle(const BJsonEvent& event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
{{toplevel.cppname}}Listener* fItemListener;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GEN_JSON_SCHEMA_PARSER__{{outputfilestemupper}}_H
|
||||||
@@ -0,0 +1,916 @@
|
|||||||
|
/*
|
||||||
|
* Generated Listener Object for {{outputfilename}}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "{{toplevel.cppname}}JsonListener.h"
|
||||||
|
|
||||||
|
#include <ObjectList.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// #pragma mark - private interfaces for the stacked listeners
|
||||||
|
|
||||||
|
|
||||||
|
/*! This class is the top level of the stacked listeners. The stack structure
|
||||||
|
is maintained in a linked list and sub-classes implement specific behaviors
|
||||||
|
depending where in the parse tree the stacked listener is working at.
|
||||||
|
*/
|
||||||
|
class _AbstractStacked{{toplevel.cppname}}JsonListener : public BJsonEventListener {
|
||||||
|
public:
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* parent);
|
||||||
|
~_AbstractStacked{{toplevel.cppname}}JsonListener();
|
||||||
|
|
||||||
|
void HandleError(status_t status, int32 line, const char* message);
|
||||||
|
void Complete();
|
||||||
|
|
||||||
|
status_t ErrorStatus();
|
||||||
|
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* Parent();
|
||||||
|
|
||||||
|
virtual bool WillPop();
|
||||||
|
|
||||||
|
virtual void Clear();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* fMainListener;
|
||||||
|
|
||||||
|
virtual bool Pop();
|
||||||
|
|
||||||
|
void Push(_AbstractStacked{{toplevel.cppname}}JsonListener* stackedListener);
|
||||||
|
|
||||||
|
private:
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* fParent;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! This implementation of the listener will consume data from the array and
|
||||||
|
discard it.
|
||||||
|
*/
|
||||||
|
class _GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener : public _AbstractStacked{{toplevel.cppname}}JsonListener {
|
||||||
|
public:
|
||||||
|
_GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* parent);
|
||||||
|
~_GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener();
|
||||||
|
|
||||||
|
bool Handle(const BJsonEvent& event);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class _GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener : public _AbstractStacked{{toplevel.cppname}}JsonListener {
|
||||||
|
public:
|
||||||
|
_GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* parent);
|
||||||
|
~_GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener();
|
||||||
|
|
||||||
|
bool Handle(const BJsonEvent& event);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! Sometimes attributes of objects are able to be arrays of strings. This
|
||||||
|
listener will parse and return the array of strings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class _StringListStacked{{toplevel.cppname}}JsonListener : public _AbstractStacked{{toplevel.cppname}}JsonListener {
|
||||||
|
public:
|
||||||
|
_StringListStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
bool (*callback)(void* context, const std::vector<BString>& values),
|
||||||
|
void* callbackContext,
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* parent);
|
||||||
|
~_StringListStacked{{toplevel.cppname}}JsonListener();
|
||||||
|
|
||||||
|
bool Handle(const BJsonEvent& event);
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool Pop();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<BString> fAccumulator;
|
||||||
|
bool (*fCallback)(void* context, const std::vector<BString>& values);
|
||||||
|
void* fCallbackContext;
|
||||||
|
};
|
||||||
|
{{#allobjs}}
|
||||||
|
|
||||||
|
class _{{cppname}}Stacked{{toplevelcppname}}JsonListener : public _AbstractStacked{{toplevelcppname}}JsonListener {
|
||||||
|
public:
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener(
|
||||||
|
bool (*callback)(void* context, const {{cppname}}Ref ref),
|
||||||
|
void* callbackContext,
|
||||||
|
AbstractMain{{toplevelcppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevelcppname}}JsonListener* parent);
|
||||||
|
~_{{cppname}}Stacked{{toplevelcppname}}JsonListener();
|
||||||
|
|
||||||
|
bool Handle(const BJsonEvent& event);
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
{{#propertyarray}}{{#property.isobject}} static bool With{{property.cppname}}Callback(void* context, const {{property.cpptype}} value);
|
||||||
|
{{/property.isobject}}{{#property.isarray}} static bool With{{property.cppname}}Callback(void* context, const {{property.cpptype}}& value);
|
||||||
|
{{/property.isarray}}{{/propertyarray}}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool Pop();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
{{cppname}}Builder fBuilder;
|
||||||
|
uint16 fNextItemBitmask;
|
||||||
|
bool (*fCallback)(void* context, const {{cppname}}Ref ref);
|
||||||
|
void* fCallbackContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
class _{{cppname}}ListStacked{{toplevelcppname}}JsonListener : public _AbstractStacked{{toplevelcppname}}JsonListener {
|
||||||
|
public:
|
||||||
|
_{{cppname}}ListStacked{{toplevelcppname}}JsonListener(
|
||||||
|
bool (*callback)(void* context, const std::vector<{{cppname}}Ref>& values),
|
||||||
|
void* callbackContext,
|
||||||
|
AbstractMain{{toplevelcppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevelcppname}}JsonListener* parent);
|
||||||
|
~_{{cppname}}ListStacked{{toplevelcppname}}JsonListener();
|
||||||
|
|
||||||
|
bool Handle(const BJsonEvent& event);
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
static bool AddItemCallback(void* target, const {{cppname}}Ref value);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool Pop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<{{cppname}}Ref> fAccumulator;
|
||||||
|
bool (*fCallback)(void* context, const std::vector<{{cppname}}Ref>& values);
|
||||||
|
void* fCallbackContext;
|
||||||
|
};
|
||||||
|
{{/allobjs}}
|
||||||
|
|
||||||
|
|
||||||
|
class _BulkContainerStacked{{toplevel.cppname}}JsonListener : public _AbstractStacked{{toplevel.cppname}}JsonListener {
|
||||||
|
public:
|
||||||
|
_BulkContainerStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* parent,
|
||||||
|
{{toplevel.cppname}}Listener* itemListener);
|
||||||
|
~_BulkContainerStacked{{toplevel.cppname}}JsonListener();
|
||||||
|
|
||||||
|
bool Handle(const BJsonEvent& event);
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool fNextItemIsItems;
|
||||||
|
{{toplevel.cppname}}Listener* fItemListener;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class _BulkContainerItemsStacked{{toplevel.cppname}}JsonListener : public _AbstractStacked{{toplevel.cppname}}JsonListener {
|
||||||
|
public:
|
||||||
|
_BulkContainerItemsStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* parent,
|
||||||
|
{{toplevel.cppname}}Listener* itemListener);
|
||||||
|
~_BulkContainerItemsStacked{{toplevel.cppname}}JsonListener();
|
||||||
|
|
||||||
|
bool Handle(const BJsonEvent& event);
|
||||||
|
bool WillPop();
|
||||||
|
|
||||||
|
static bool NotifyListenerCallback(void* context, const {{toplevel.cppname}}Ref value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
{{toplevel.cppname}}Listener* fItemListener;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - implementations for the stacked listeners
|
||||||
|
|
||||||
|
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::_AbstractStacked{{toplevel.cppname}}JsonListener (
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* parent)
|
||||||
|
{
|
||||||
|
fMainListener = mainListener;
|
||||||
|
fParent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::~_AbstractStacked{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::HandleError(status_t status, int32 line, const char* message)
|
||||||
|
{
|
||||||
|
fMainListener->HandleError(status, line, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::Complete()
|
||||||
|
{
|
||||||
|
fMainListener->Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::ErrorStatus()
|
||||||
|
{
|
||||||
|
return fMainListener->ErrorStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener*
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::Parent()
|
||||||
|
{
|
||||||
|
return fParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::Push(_AbstractStacked{{toplevel.cppname}}JsonListener* stackedListener)
|
||||||
|
{
|
||||||
|
fMainListener->SetStackedListener(stackedListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::WillPop()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::Pop()
|
||||||
|
{
|
||||||
|
bool result = WillPop();
|
||||||
|
fMainListener->SetStackedListener(fParent);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::Clear()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener::_GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* parent)
|
||||||
|
:
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener(mainListener, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
_GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener::~_GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
_GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
|
||||||
|
{
|
||||||
|
switch (event.EventType()) {
|
||||||
|
case B_JSON_OBJECT_NAME:
|
||||||
|
case B_JSON_NUMBER:
|
||||||
|
case B_JSON_STRING:
|
||||||
|
case B_JSON_TRUE:
|
||||||
|
case B_JSON_FALSE:
|
||||||
|
case B_JSON_NULL:
|
||||||
|
// ignore
|
||||||
|
break;
|
||||||
|
case B_JSON_OBJECT_START:
|
||||||
|
Push(new _GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
|
||||||
|
break;
|
||||||
|
case B_JSON_ARRAY_START:
|
||||||
|
Push(new _GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
|
||||||
|
break;
|
||||||
|
case B_JSON_ARRAY_END:
|
||||||
|
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected end of array");
|
||||||
|
break;
|
||||||
|
case B_JSON_OBJECT_END:
|
||||||
|
{
|
||||||
|
bool status = Pop() && (ErrorStatus() == B_OK);
|
||||||
|
delete this;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorStatus() == B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
_GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener::_GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* parent)
|
||||||
|
:
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener(mainListener, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
_GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener::~_GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
_GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
|
||||||
|
{
|
||||||
|
switch (event.EventType()) {
|
||||||
|
case B_JSON_OBJECT_NAME:
|
||||||
|
case B_JSON_NUMBER:
|
||||||
|
case B_JSON_STRING:
|
||||||
|
case B_JSON_TRUE:
|
||||||
|
case B_JSON_FALSE:
|
||||||
|
case B_JSON_NULL:
|
||||||
|
// ignore
|
||||||
|
break;
|
||||||
|
case B_JSON_OBJECT_START:
|
||||||
|
Push(new _GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
|
||||||
|
break;
|
||||||
|
case B_JSON_ARRAY_START:
|
||||||
|
Push(new _GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
|
||||||
|
break;
|
||||||
|
case B_JSON_OBJECT_END:
|
||||||
|
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected end of object");
|
||||||
|
break;
|
||||||
|
case B_JSON_ARRAY_END:
|
||||||
|
{
|
||||||
|
bool status = Pop() && (ErrorStatus() == B_OK);
|
||||||
|
delete this;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return ErrorStatus() == B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
_StringListStacked{{toplevel.cppname}}JsonListener::_StringListStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
bool (*callback)(void* context, const std::vector<BString>& values),
|
||||||
|
void* callbackContext,
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* parent)
|
||||||
|
:
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener(mainListener, parent),
|
||||||
|
fAccumulator(),
|
||||||
|
fCallback(callback),
|
||||||
|
fCallbackContext(callbackContext)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_StringListStacked{{toplevel.cppname}}JsonListener::~_StringListStacked{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_StringListStacked{{toplevel.cppname}}JsonListener::Pop()
|
||||||
|
{
|
||||||
|
bool callbackResult = fCallback(fCallbackContext, fAccumulator);
|
||||||
|
return _AbstractStacked{{toplevel.cppname}}JsonListener::Pop() && callbackResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_StringListStacked{{toplevel.cppname}}JsonListener::Clear()
|
||||||
|
{
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::Clear();
|
||||||
|
fAccumulator.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_StringListStacked{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
|
||||||
|
{
|
||||||
|
switch (event.EventType()) {
|
||||||
|
case B_JSON_ARRAY_END:
|
||||||
|
{
|
||||||
|
bool status = Pop() && (ErrorStatus() == B_OK);
|
||||||
|
delete this;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
case B_JSON_STRING:
|
||||||
|
{
|
||||||
|
fAccumulator.push_back(event.Content());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
|
||||||
|
"illegal state - unexpected json event parsing a string array");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorStatus() == B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
{{#allobjs}}
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener::_{{cppname}}Stacked{{toplevelcppname}}JsonListener(
|
||||||
|
bool (*callback)(void* context, const {{cppname}}Ref ref),
|
||||||
|
void* callbackContext,
|
||||||
|
AbstractMain{{toplevelcppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevelcppname}}JsonListener* parent)
|
||||||
|
:
|
||||||
|
_AbstractStacked{{toplevelcppname}}JsonListener(mainListener, parent),
|
||||||
|
fBuilder(),
|
||||||
|
fCallback(callback),
|
||||||
|
fCallbackContext(callbackContext)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener::~_{{cppname}}Stacked{{toplevelcppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener::Pop()
|
||||||
|
{
|
||||||
|
bool callbackResult = fCallback(fCallbackContext, fBuilder.BuildRef());
|
||||||
|
return _AbstractStacked{{toplevelcppname}}JsonListener::Pop() && callbackResult;
|
||||||
|
}
|
||||||
|
{{#propertyarray}}{{#property.isobject}}
|
||||||
|
/*static*/ bool
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener::With{{property.cppname}}Callback(void* context, const {{property.cpptype}} value)
|
||||||
|
{{/property.isobject}}{{#property.isarray}}/*static*/ bool
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener::With{{property.cppname}}Callback(void* context, const {{property.cpptype}}& value)
|
||||||
|
{{/property.isarray}}{{#property.isobjectorarray}}{
|
||||||
|
{{cppname}}Builder* builder = static_cast<{{cppname}}Builder*>(context);
|
||||||
|
builder->With{{property.cppname}}(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
{{/property.isobjectorarray}}{{/propertyarray}}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener::Clear()
|
||||||
|
{
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::Clear();
|
||||||
|
fBuilder.Clear();
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener::Handle(const BJsonEvent& event)
|
||||||
|
{
|
||||||
|
switch (event.EventType()) {
|
||||||
|
case B_JSON_ARRAY_END:
|
||||||
|
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected start of array");
|
||||||
|
break;
|
||||||
|
case B_JSON_OBJECT_NAME:
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
{{#propertyarray}} if (0 == strcmp(event.Content(), "{{name}}"))
|
||||||
|
fNextItemBitmask = {{cppobjectname}}::k{{property.cppname}}Bitmask;
|
||||||
|
{{/propertyarray}}
|
||||||
|
break;
|
||||||
|
case B_JSON_OBJECT_END:
|
||||||
|
{
|
||||||
|
bool status = Pop() && (ErrorStatus() == B_OK);
|
||||||
|
delete this;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
case B_JSON_STRING:
|
||||||
|
{
|
||||||
|
{{#propertyarray}}{{#property.isstring}} if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
|
||||||
|
fBuilder.With{{property.cppname}}(event.Content());
|
||||||
|
{{/property.isstring}}{{/propertyarray}}
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case B_JSON_TRUE:
|
||||||
|
{{#propertyarray}}{{#property.isboolean}} if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
|
||||||
|
fBuilder.With{{property.cppname}}(true);
|
||||||
|
{{/property.isboolean}}{{/propertyarray}}
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
break;
|
||||||
|
case B_JSON_FALSE:
|
||||||
|
{{#propertyarray}}{{#property.isboolean}} if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
|
||||||
|
fBuilder.With{{property.cppname}}(false);
|
||||||
|
{{/property.isboolean}}
|
||||||
|
{{/propertyarray}}
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
break;
|
||||||
|
case B_JSON_NULL:
|
||||||
|
{
|
||||||
|
{{#propertyarray}}{{^property.isarray}} if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
|
||||||
|
fBuilder.Without{{property.cppname}}();
|
||||||
|
{{/property.isarray}}{{/propertyarray}}
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case B_JSON_NUMBER:
|
||||||
|
{
|
||||||
|
{{#propertyarray}}{{#property.isinteger}} if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
|
||||||
|
fBuilder.With{{property.cppname}}(event.ContentInteger());
|
||||||
|
{{/property.isinteger}}
|
||||||
|
{{#property.isnumber}} if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask)
|
||||||
|
fBuilder.With{{property.cppname}}(event.ContentDouble());
|
||||||
|
{{/property.isnumber}}
|
||||||
|
{{/propertyarray}}
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case B_JSON_OBJECT_START:
|
||||||
|
{
|
||||||
|
{{#propertyarray}}{{#property.isobject}} if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask) {
|
||||||
|
_GeneralNoOpObjectStacked{{toplevelcppname}}JsonListener* nextListener
|
||||||
|
= new _GeneralNoOpObjectStacked{{toplevelcppname}}JsonListener(
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener::With{{property.cppname}},
|
||||||
|
&fBuilder,
|
||||||
|
fMainListener,
|
||||||
|
this);
|
||||||
|
Push(nextListener);
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{{/property.isobject}}{{/propertyarray}}
|
||||||
|
// a general consumer that will clear any spurious data that was not
|
||||||
|
// part of the schema. This is important for schema evolution.
|
||||||
|
_GeneralNoOpObjectStacked{{toplevelcppname}}JsonListener* nextListener
|
||||||
|
= new _GeneralNoOpObjectStacked{{toplevelcppname}}JsonListener(fMainListener, this);
|
||||||
|
Push(nextListener);
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case B_JSON_ARRAY_START:
|
||||||
|
{
|
||||||
|
{{#propertyarray}}{{#property.isarray}} if (fNextItemBitmask == {{cppobjectname}}::k{{property.cppname}}Bitmask) {
|
||||||
|
{{#property.items.isstring}}
|
||||||
|
_StringListStacked{{toplevelcppname}}JsonListener* nextListener = new _StringListStacked{{toplevelcppname}}JsonListener(
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener::With{{property.cppname}}Callback,
|
||||||
|
&fBuilder,
|
||||||
|
fMainListener,
|
||||||
|
this);
|
||||||
|
{{/property.items.isstring}}{{^property.items.isstring}}
|
||||||
|
_{{property.items.cppname}}ListStacked{{toplevelcppname}}JsonListener* nextListener =
|
||||||
|
new _{{property.items.cppname}}ListStacked{{toplevelcppname}}JsonListener(
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener::With{{property.cppname}}Callback,
|
||||||
|
&fBuilder,
|
||||||
|
fMainListener,
|
||||||
|
this);
|
||||||
|
{{/property.items.isstring}}
|
||||||
|
Push(nextListener);
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{{/property.isarray}}{{/propertyarray}}
|
||||||
|
// a general consumer that will clear any spurious data that was not
|
||||||
|
// part of the schema. This is important for schema evolution.
|
||||||
|
_AbstractStacked{{toplevelcppname}}JsonListener* nextListener
|
||||||
|
= new _GeneralNoOpArrayStacked{{toplevelcppname}}JsonListener(fMainListener, this);
|
||||||
|
Push(nextListener);
|
||||||
|
fNextItemBitmask = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorStatus() == B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_{{cppname}}ListStacked{{toplevelcppname}}JsonListener::_{{cppname}}ListStacked{{toplevelcppname}}JsonListener(
|
||||||
|
bool (*callback)(void* context, const std::vector<{{cppname}}Ref>& values),
|
||||||
|
void* callbackContext,
|
||||||
|
AbstractMain{{toplevelcppname}}JsonListener* mainListener,
|
||||||
|
_AbstractStacked{{toplevelcppname}}JsonListener* parent)
|
||||||
|
:
|
||||||
|
_AbstractStacked{{toplevelcppname}}JsonListener(mainListener, parent),
|
||||||
|
fAccumulator(),
|
||||||
|
fCallback(callback),
|
||||||
|
fCallbackContext(callbackContext)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_{{cppname}}ListStacked{{toplevelcppname}}JsonListener::~_{{cppname}}ListStacked{{toplevelcppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_{{cppname}}ListStacked{{toplevelcppname}}JsonListener::Pop()
|
||||||
|
{
|
||||||
|
bool callbackResult = fCallback(fCallbackContext, fAccumulator);
|
||||||
|
return _AbstractStacked{{toplevelcppname}}JsonListener::Pop() && callbackResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ bool
|
||||||
|
_{{cppname}}ListStacked{{toplevelcppname}}JsonListener::AddItemCallback(void* context, const {{cppname}}Ref value)
|
||||||
|
{
|
||||||
|
std::vector<{{cppname}}Ref>* accumulator = static_cast<std::vector<{{cppname}}Ref>*>(context);
|
||||||
|
accumulator->push_back(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_{{cppname}}ListStacked{{toplevelcppname}}JsonListener::Handle(const BJsonEvent& event)
|
||||||
|
{
|
||||||
|
switch (event.EventType()) {
|
||||||
|
case B_JSON_ARRAY_END:
|
||||||
|
{
|
||||||
|
bool status = Pop() && (ErrorStatus() == B_OK);
|
||||||
|
delete this;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
case B_JSON_OBJECT_START:
|
||||||
|
{
|
||||||
|
_{{cppname}}Stacked{{toplevelcppname}}JsonListener* nextListener =
|
||||||
|
new _{{cppname}}Stacked{{toplevelcppname}}JsonListener(
|
||||||
|
AddItemCallback,
|
||||||
|
&fAccumulator,
|
||||||
|
fMainListener,
|
||||||
|
this);
|
||||||
|
Push(nextListener);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
|
||||||
|
"illegal state - unexpected json event parsing an array of {{cppname}}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorStatus() == B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_{{cppname}}ListStacked{{toplevelcppname}}JsonListener::Clear()
|
||||||
|
{
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::Clear();
|
||||||
|
fAccumulator.clear();
|
||||||
|
}
|
||||||
|
{{/allobjs}}
|
||||||
|
|
||||||
|
_BulkContainerStacked{{toplevel.cppname}}JsonListener::_BulkContainerStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener, _AbstractStacked{{toplevel.cppname}}JsonListener* parent,
|
||||||
|
{{toplevel.cppname}}Listener* itemListener)
|
||||||
|
:
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener(mainListener, parent)
|
||||||
|
{
|
||||||
|
fItemListener = itemListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_BulkContainerStacked{{toplevel.cppname}}JsonListener::~_BulkContainerStacked{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_BulkContainerStacked{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
|
||||||
|
{
|
||||||
|
switch (event.EventType()) {
|
||||||
|
case B_JSON_ARRAY_END:
|
||||||
|
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected end of array");
|
||||||
|
break;
|
||||||
|
case B_JSON_OBJECT_NAME:
|
||||||
|
fNextItemIsItems = (0 == strcmp(event.Content(), "items"));
|
||||||
|
break;
|
||||||
|
case B_JSON_OBJECT_START:
|
||||||
|
Push(new _GeneralNoOpObjectStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
|
||||||
|
fNextItemIsItems = false;
|
||||||
|
break;
|
||||||
|
case B_JSON_ARRAY_START:
|
||||||
|
if (fNextItemIsItems)
|
||||||
|
Push(new _BulkContainerItemsStacked{{toplevel.cppname}}JsonListener(fMainListener, this, fItemListener));
|
||||||
|
else
|
||||||
|
Push(new _GeneralNoOpArrayStacked{{toplevel.cppname}}JsonListener(fMainListener, this));
|
||||||
|
break;
|
||||||
|
case B_JSON_OBJECT_END:
|
||||||
|
{
|
||||||
|
bool status = Pop() && (ErrorStatus() == B_OK);
|
||||||
|
delete this;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// ignore any other fields
|
||||||
|
fNextItemIsItems = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorStatus() == B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_BulkContainerStacked{{toplevel.cppname}}JsonListener::Clear()
|
||||||
|
{
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener::Clear();
|
||||||
|
fNextItemIsItems = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_BulkContainerItemsStacked{{toplevel.cppname}}JsonListener::_BulkContainerItemsStacked{{toplevel.cppname}}JsonListener(
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener* mainListener, _AbstractStacked{{toplevel.cppname}}JsonListener* parent,
|
||||||
|
{{toplevel.cppname}}Listener* itemListener)
|
||||||
|
:
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener(mainListener, parent),
|
||||||
|
fItemListener(itemListener)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_BulkContainerItemsStacked{{toplevel.cppname}}JsonListener::~_BulkContainerItemsStacked{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_BulkContainerItemsStacked{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
|
||||||
|
{
|
||||||
|
switch (event.EventType()) {
|
||||||
|
case B_JSON_OBJECT_START:
|
||||||
|
Push(new _{{toplevel.cppname}}Stacked{{toplevel.cppname}}JsonListener(
|
||||||
|
_BulkContainerItemsStacked{{toplevel.cppname}}JsonListener::NotifyListenerCallback,
|
||||||
|
fItemListener,
|
||||||
|
fMainListener,
|
||||||
|
this
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case B_JSON_ARRAY_END:
|
||||||
|
{
|
||||||
|
bool status = Pop() && (ErrorStatus() == B_OK);
|
||||||
|
delete this;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected json event");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorStatus() == B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ bool
|
||||||
|
_BulkContainerItemsStacked{{toplevel.cppname}}JsonListener::NotifyListenerCallback(void* context, const {{toplevel.cppname}}Ref value)
|
||||||
|
{
|
||||||
|
{{toplevel.cppname}}Listener* listener = static_cast<{{toplevel.cppname}}Listener*>(context);
|
||||||
|
return listener->Handle(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
_BulkContainerItemsStacked{{toplevel.cppname}}JsonListener::WillPop()
|
||||||
|
{
|
||||||
|
fItemListener->Complete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - implementations for the main listeners
|
||||||
|
|
||||||
|
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener::AbstractMain{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
fStackedListener = NULL;
|
||||||
|
fErrorStatus = B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener::~AbstractMain{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener::HandleError(status_t status, int32 line, const char* message)
|
||||||
|
{
|
||||||
|
if (message != NULL) {
|
||||||
|
fprintf(stderr, "an error has arisen processing json for '{{toplevel.cppname}}'; %s\\n", message);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "an error has arisen processing json for '{{toplevel.cppname}}'\\n");
|
||||||
|
}
|
||||||
|
fErrorStatus = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener::Complete()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener::ErrorStatus()
|
||||||
|
{
|
||||||
|
return fErrorStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener::SetStackedListener(
|
||||||
|
_AbstractStacked{{toplevel.cppname}}JsonListener* stackedListener)
|
||||||
|
{
|
||||||
|
fStackedListener = stackedListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Single{{toplevel.cppname}}JsonListener::Single{{toplevel.cppname}}JsonListener()
|
||||||
|
:
|
||||||
|
AbstractMain{{toplevel.cppname}}JsonListener(),
|
||||||
|
fResult()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Single{{toplevel.cppname}}JsonListener::~Single{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Single{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
|
||||||
|
{
|
||||||
|
if (fErrorStatus != B_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (fStackedListener != NULL)
|
||||||
|
return fStackedListener->Handle(event);
|
||||||
|
|
||||||
|
switch (event.EventType()) {
|
||||||
|
case B_JSON_OBJECT_START:
|
||||||
|
{
|
||||||
|
_{{toplevel.cppname}}Stacked{{toplevel.cppname}}JsonListener* nextListener
|
||||||
|
= new _{{toplevel.cppname}}Stacked{{toplevel.cppname}}JsonListener(
|
||||||
|
Single{{toplevel.cppname}}JsonListener::WithResultCallback,
|
||||||
|
&fResult,
|
||||||
|
this,
|
||||||
|
NULL);
|
||||||
|
SetStackedListener(nextListener);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
|
||||||
|
"illegal state - unexpected json event parsing top level for {{toplevel.cppname}}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorStatus() == B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ bool
|
||||||
|
Single{{toplevel.cppname}}JsonListener::WithResultCallback(void* context, const {{toplevel.cppname}}Ref value)
|
||||||
|
{
|
||||||
|
{{toplevel.cppname}}Ref* ref = static_cast<{{toplevel.cppname}}Ref*>(context);
|
||||||
|
if (value.IsSet())
|
||||||
|
ref->SetTo(value.Get(), false);
|
||||||
|
else
|
||||||
|
ref->Unset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{toplevel.cppname}}Ref
|
||||||
|
Single{{toplevel.cppname}}JsonListener::Result()
|
||||||
|
{
|
||||||
|
return fResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BulkContainer{{toplevel.cppname}}JsonListener::BulkContainer{{toplevel.cppname}}JsonListener(
|
||||||
|
{{toplevel.cppname}}Listener* itemListener) : AbstractMain{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
fItemListener = itemListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BulkContainer{{toplevel.cppname}}JsonListener::~BulkContainer{{toplevel.cppname}}JsonListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
BulkContainer{{toplevel.cppname}}JsonListener::Handle(const BJsonEvent& event)
|
||||||
|
{
|
||||||
|
if (fErrorStatus != B_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (fStackedListener != NULL)
|
||||||
|
return fStackedListener->Handle(event);
|
||||||
|
|
||||||
|
switch (event.EventType()) {
|
||||||
|
case B_JSON_OBJECT_START:
|
||||||
|
{
|
||||||
|
_BulkContainerStacked{{toplevel.cppname}}JsonListener* nextListener =
|
||||||
|
new _BulkContainerStacked{{toplevel.cppname}}JsonListener(this, NULL, fItemListener);
|
||||||
|
SetStackedListener(nextListener);
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
|
||||||
|
"illegal state - unexpected json event parsing top level for _BulkContainerStacked{{toplevel.cppname}}JsonListener");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorStatus() == B_OK;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018-2020, Andrew Lindesay <[email protected]>.
|
* Copyright 2018-2026, Andrew Lindesay <[email protected]>.
|
||||||
* All rights reserved. Distributed under the terms of the MIT License.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -132,6 +132,8 @@ LocalRepositoryUpdateProcess::_RunForRepositoryName(const BString& repoName,
|
|||||||
BPackageKit::BContext& context, BPackageKit::BPackageRoster& roster,
|
BPackageKit::BContext& context, BPackageKit::BPackageRoster& roster,
|
||||||
BPackageKit::BRepositoryCache* cache)
|
BPackageKit::BRepositoryCache* cache)
|
||||||
{
|
{
|
||||||
|
HDINFO("[%s] will update local repo [%s] cache", Name(), repoName.String());
|
||||||
|
|
||||||
status_t result = B_ERROR;
|
status_t result = B_ERROR;
|
||||||
BRepositoryConfig repoConfig;
|
BRepositoryConfig repoConfig;
|
||||||
result = roster.GetRepositoryConfig(repoName, &repoConfig);
|
result = roster.GetRepositoryConfig(repoName, &repoConfig);
|
||||||
@@ -140,8 +142,7 @@ LocalRepositoryUpdateProcess::_RunForRepositoryName(const BString& repoName,
|
|||||||
try {
|
try {
|
||||||
BRefreshRepositoryRequest refreshRequest(context, repoConfig);
|
BRefreshRepositoryRequest refreshRequest(context, repoConfig);
|
||||||
result = refreshRequest.Process();
|
result = refreshRequest.Process();
|
||||||
HDINFO("[%s] did update local repo [%s] cache", Name(),
|
HDINFO("[%s] did update local repo [%s] cache", Name(), repoName.String());
|
||||||
repoName.String());
|
|
||||||
result = B_OK;
|
result = B_OK;
|
||||||
} catch (BFatalErrorException& ex) {
|
} catch (BFatalErrorException& ex) {
|
||||||
_NotifyError(ex.Message(), ex.Details());
|
_NotifyError(ex.Message(), ex.Details());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017-2025, Andrew Lindesay <[email protected]>.
|
* Copyright 2017-2026, Andrew Lindesay <[email protected]>.
|
||||||
* All rights reserved. Distributed under the terms of the MIT License.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef SERVER_ICON_EXPORT_UPDATE_PROCESS_H
|
#ifndef SERVER_ICON_EXPORT_UPDATE_PROCESS_H
|
||||||
@@ -15,9 +15,6 @@
|
|||||||
#include "Model.h"
|
#include "Model.h"
|
||||||
|
|
||||||
|
|
||||||
class DumpExportPkg;
|
|
||||||
|
|
||||||
|
|
||||||
class ServerIconExportUpdateProcess : public AbstractSingleFileServerProcess {
|
class ServerIconExportUpdateProcess : public AbstractSingleFileServerProcess {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017-2025, Andrew Lindesay <[email protected]>.
|
* Copyright 2017-2026, Andrew Lindesay <[email protected]>.
|
||||||
* All rights reserved. Distributed under the terms of the MIT License.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -17,11 +17,8 @@
|
|||||||
#include <StopWatch.h>
|
#include <StopWatch.h>
|
||||||
#include <Url.h>
|
#include <Url.h>
|
||||||
|
|
||||||
#include "DumpExportPkg.h"
|
|
||||||
#include "DumpExportPkgCategory.h"
|
|
||||||
#include "DumpExportPkgJsonListener.h"
|
#include "DumpExportPkgJsonListener.h"
|
||||||
#include "DumpExportPkgScreenshot.h"
|
#include "DumpExportPkgModel.h"
|
||||||
#include "DumpExportPkgVersion.h"
|
|
||||||
#include "HaikuDepotConstants.h"
|
#include "HaikuDepotConstants.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "PackageUtils.h"
|
#include "PackageUtils.h"
|
||||||
@@ -49,7 +46,7 @@ public:
|
|||||||
PackageFillingPkgListener(Model *model, Stoppable* stoppable);
|
PackageFillingPkgListener(Model *model, Stoppable* stoppable);
|
||||||
virtual ~PackageFillingPkgListener();
|
virtual ~PackageFillingPkgListener();
|
||||||
|
|
||||||
virtual bool Handle(DumpExportPkg* item);
|
virtual bool Handle(DumpExportPkgRef item);
|
||||||
virtual void Complete();
|
virtual void Complete();
|
||||||
|
|
||||||
uint32 Count();
|
uint32 Count();
|
||||||
@@ -153,19 +150,19 @@ PackageFillingPkgListener::_CreateUpdatePackage(const PackageInfoRef& package,
|
|||||||
|
|
||||||
DumpExportPkgVersion* pkgVersion = pkg->PkgVersionsItemAt(0);
|
DumpExportPkgVersion* pkgVersion = pkg->PkgVersionsItemAt(0);
|
||||||
|
|
||||||
if (!pkgVersion->TitleIsNull())
|
if (pkgVersion->IsSetTitle())
|
||||||
localizedTextBuilder.WithTitle(*(pkgVersion->Title()));
|
localizedTextBuilder.WithTitle(pkgVersion->Title());
|
||||||
|
|
||||||
if (!pkgVersion->SummaryIsNull())
|
if (pkgVersion->IsSetSummary())
|
||||||
localizedTextBuilder.WithSummary(*(pkgVersion->Summary()));
|
localizedTextBuilder.WithSummary(pkgVersion->Summary());
|
||||||
|
|
||||||
if (!pkgVersion->DescriptionIsNull())
|
if (pkgVersion->IsSetDescription())
|
||||||
localizedTextBuilder.WithDescription(*(pkgVersion->Description()));
|
localizedTextBuilder.WithDescription(pkgVersion->Description());
|
||||||
|
|
||||||
if (!pkgVersion->PayloadLengthIsNull())
|
if (pkgVersion->IsSetPayloadLength())
|
||||||
localInfoBuilder.WithSize(static_cast<off_t>(pkgVersion->PayloadLength()));
|
localInfoBuilder.WithSize(static_cast<off_t>(pkgVersion->PayloadLength()));
|
||||||
|
|
||||||
if (!pkgVersion->CreateTimestampIsNull()) {
|
if (pkgVersion->IsSetCreateTimestamp()) {
|
||||||
PackageVersionRef versionRef = PackageUtils::Version(package);
|
PackageVersionRef versionRef = PackageUtils::Version(package);
|
||||||
|
|
||||||
if (versionRef.IsSet()) {
|
if (versionRef.IsSet()) {
|
||||||
@@ -181,7 +178,7 @@ PackageFillingPkgListener::_CreateUpdatePackage(const PackageInfoRef& package,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pkg->DerivedRatingIsNull()) {
|
if (pkg->IsSetDerivedRating()) {
|
||||||
// TODO; unify the naming here!
|
// TODO; unify the naming here!
|
||||||
userRatingBuilder.WithSummary(UserRatingSummaryBuilder()
|
userRatingBuilder.WithSummary(UserRatingSummaryBuilder()
|
||||||
.WithAverageRating(pkg->DerivedRating())
|
.WithAverageRating(pkg->DerivedRating())
|
||||||
@@ -192,22 +189,22 @@ PackageFillingPkgListener::_CreateUpdatePackage(const PackageInfoRef& package,
|
|||||||
int32 countPkgCategories = pkg->CountPkgCategories();
|
int32 countPkgCategories = pkg->CountPkgCategories();
|
||||||
|
|
||||||
for (i = 0; i < countPkgCategories; i++) {
|
for (i = 0; i < countPkgCategories; i++) {
|
||||||
BString* categoryCode = pkg->PkgCategoriesItemAt(i)->Code();
|
BString categoryCode = pkg->PkgCategoriesItemAt(i)->Code();
|
||||||
CategoryRef category = fCategories[*categoryCode];
|
CategoryRef category = fCategories[categoryCode];
|
||||||
|
|
||||||
if (!category.IsSet())
|
if (!category.IsSet())
|
||||||
HDERROR("unable to find the category for [%s]", categoryCode->String());
|
HDERROR("unable to find the category for [%s]", categoryCode.String());
|
||||||
else
|
else
|
||||||
classificationInfoBuilder.AddCategory(category);
|
classificationInfoBuilder.AddCategory(category);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pkg->ProminenceOrderingIsNull())
|
if (pkg->IsSetProminenceOrdering())
|
||||||
classificationInfoBuilder.WithProminence(static_cast<uint32>(pkg->ProminenceOrdering()));
|
classificationInfoBuilder.WithProminence(static_cast<uint32>(pkg->ProminenceOrdering()));
|
||||||
|
|
||||||
if (!pkg->IsDesktopIsNull())
|
if (pkg->IsSetIsDesktop())
|
||||||
classificationInfoBuilder.WithIsDesktop(pkg->IsDesktop());
|
classificationInfoBuilder.WithIsDesktop(pkg->IsDesktop());
|
||||||
|
|
||||||
if (!pkg->IsNativeDesktopIsNull())
|
if (pkg->IsSetIsNativeDesktop())
|
||||||
classificationInfoBuilder.WithIsNativeDesktop(pkg->IsNativeDesktop());
|
classificationInfoBuilder.WithIsNativeDesktop(pkg->IsNativeDesktop());
|
||||||
|
|
||||||
int32 countPkgScreenshots = pkg->CountPkgScreenshots();
|
int32 countPkgScreenshots = pkg->CountPkgScreenshots();
|
||||||
@@ -232,7 +229,7 @@ PackageFillingPkgListener::_CreateUpdatePackage(const PackageInfoRef& package,
|
|||||||
PackageFillingPkgListener::_CreateScreenshot(DumpExportPkgScreenshot* screenshot)
|
PackageFillingPkgListener::_CreateScreenshot(DumpExportPkgScreenshot* screenshot)
|
||||||
{
|
{
|
||||||
return ScreenshotInfoRef(
|
return ScreenshotInfoRef(
|
||||||
new ScreenshotInfo(*(screenshot->Code()), static_cast<int32>(screenshot->Width()),
|
new ScreenshotInfo(screenshot->Code(), static_cast<int32>(screenshot->Width()),
|
||||||
static_cast<int32>(screenshot->Height()), static_cast<int32>(screenshot->Length())),
|
static_cast<int32>(screenshot->Height()), static_cast<int32>(screenshot->Length())),
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
@@ -246,14 +243,14 @@ PackageFillingPkgListener::Count()
|
|||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PackageFillingPkgListener::Handle(DumpExportPkg* pkg)
|
PackageFillingPkgListener::Handle(DumpExportPkgRef pkg)
|
||||||
{
|
{
|
||||||
const BString packageName = *(pkg->Name());
|
const BString packageName = pkg->Name();
|
||||||
PackageInfoRef package = _PackageForName(packageName);
|
PackageInfoRef package = _PackageForName(packageName);
|
||||||
|
|
||||||
if (package.IsSet()) {
|
if (package.IsSet()) {
|
||||||
fUpdatedPackages.push_back(_CreateUpdatePackage(package, pkg));
|
fUpdatedPackages.push_back(_CreateUpdatePackage(package, pkg));
|
||||||
HDTRACE("did populate data for [%s]", pkg->Name()->String());
|
HDTRACE("did populate data for [%s]", packageName.String());
|
||||||
fCount++;
|
fCount++;
|
||||||
} else {
|
} else {
|
||||||
HDINFO("[PackageFillingPkgListener] unable to find the pkg [%s]", packageName.String());
|
HDINFO("[PackageFillingPkgListener] unable to find the pkg [%s]", packageName.String());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2025, Andrew Lindesay <[email protected]>.
|
* Copyright 2019-2026, Andrew Lindesay <[email protected]>.
|
||||||
* All rights reserved. Distributed under the terms of the MIT License.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -11,17 +11,12 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include <AutoDeleter.h>
|
#include <AutoDeleter.h>
|
||||||
#include <AutoLocker.h>
|
|
||||||
#include <Catalog.h>
|
#include <Catalog.h>
|
||||||
#include <FileIO.h>
|
#include <FileIO.h>
|
||||||
#include <Url.h>
|
#include <Url.h>
|
||||||
|
|
||||||
#include "DumpExportReference.h"
|
|
||||||
#include "DumpExportReferenceCountry.h"
|
|
||||||
#include "DumpExportReferenceJsonListener.h"
|
#include "DumpExportReferenceJsonListener.h"
|
||||||
#include "DumpExportReferenceNaturalLanguage.h"
|
#include "DumpExportReferenceModel.h"
|
||||||
#include "DumpExportReferencePkgCategory.h"
|
|
||||||
#include "DumpExportReferenceUserRatingStability.h"
|
|
||||||
#include "LocaleUtils.h"
|
#include "LocaleUtils.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "ServerSettings.h"
|
#include "ServerSettings.h"
|
||||||
@@ -80,6 +75,7 @@ status_t
|
|||||||
ServerReferenceDataUpdateProcess::ProcessLocalData()
|
ServerReferenceDataUpdateProcess::ProcessLocalData()
|
||||||
{
|
{
|
||||||
SingleDumpExportReferenceJsonListener* listener = new SingleDumpExportReferenceJsonListener();
|
SingleDumpExportReferenceJsonListener* listener = new SingleDumpExportReferenceJsonListener();
|
||||||
|
ObjectDeleter<SingleDumpExportReferenceJsonListener> listenerDeleter(listener);
|
||||||
|
|
||||||
BPath localPath;
|
BPath localPath;
|
||||||
status_t result = GetLocalPath(localPath);
|
status_t result = GetLocalPath(localPath);
|
||||||
@@ -97,12 +93,12 @@ ServerReferenceDataUpdateProcess::ProcessLocalData()
|
|||||||
if (result != B_OK)
|
if (result != B_OK)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
return _ProcessData(listener->Target());
|
return _ProcessData(listener->Result());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
ServerReferenceDataUpdateProcess::_ProcessData(DumpExportReference* data)
|
ServerReferenceDataUpdateProcess::_ProcessData(DumpExportReferenceRef data)
|
||||||
{
|
{
|
||||||
status_t result = B_OK;
|
status_t result = B_OK;
|
||||||
if (result == B_OK)
|
if (result == B_OK)
|
||||||
@@ -116,7 +112,7 @@ ServerReferenceDataUpdateProcess::_ProcessData(DumpExportReference* data)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
ServerReferenceDataUpdateProcess::_ProcessNaturalLanguages(DumpExportReference* data)
|
ServerReferenceDataUpdateProcess::_ProcessNaturalLanguages(DumpExportReferenceRef data)
|
||||||
{
|
{
|
||||||
HDINFO("[%s] will populate from %" B_PRId32 " possible natural languages", Name(),
|
HDINFO("[%s] will populate from %" B_PRId32 " possible natural languages", Name(),
|
||||||
data->CountNaturalLanguages());
|
data->CountNaturalLanguages());
|
||||||
@@ -126,7 +122,7 @@ ServerReferenceDataUpdateProcess::_ProcessNaturalLanguages(DumpExportReference*
|
|||||||
|
|
||||||
for (int32 i = data->CountNaturalLanguages() - 1; i > 0; i--) {
|
for (int32 i = data->CountNaturalLanguages() - 1; i > 0; i--) {
|
||||||
DumpExportReferenceNaturalLanguage* naturalLanguage = data->NaturalLanguagesItemAt(i);
|
DumpExportReferenceNaturalLanguage* naturalLanguage = data->NaturalLanguagesItemAt(i);
|
||||||
Language* language = new Language(*(naturalLanguage->Code()), *(naturalLanguage->Name()),
|
Language* language = new Language(naturalLanguage->Code(), naturalLanguage->Name(),
|
||||||
naturalLanguage->IsPopular());
|
naturalLanguage->IsPopular());
|
||||||
languages.push_back(LanguageRef(language, true));
|
languages.push_back(LanguageRef(language, true));
|
||||||
count++;
|
count++;
|
||||||
@@ -142,7 +138,7 @@ ServerReferenceDataUpdateProcess::_ProcessNaturalLanguages(DumpExportReference*
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
ServerReferenceDataUpdateProcess::_ProcessPkgCategories(DumpExportReference* data)
|
ServerReferenceDataUpdateProcess::_ProcessPkgCategories(DumpExportReferenceRef data)
|
||||||
{
|
{
|
||||||
HDINFO("[%s] will populate %" B_PRId32 " pkg categories", Name(), data->CountPkgCategories());
|
HDINFO("[%s] will populate %" B_PRId32 " pkg categories", Name(), data->CountPkgCategories());
|
||||||
|
|
||||||
@@ -151,7 +147,7 @@ ServerReferenceDataUpdateProcess::_ProcessPkgCategories(DumpExportReference* dat
|
|||||||
for (int32 i = 0; i < data->CountPkgCategories(); i++) {
|
for (int32 i = 0; i < data->CountPkgCategories(); i++) {
|
||||||
DumpExportReferencePkgCategory* pkgCategory = data->PkgCategoriesItemAt(i);
|
DumpExportReferencePkgCategory* pkgCategory = data->PkgCategoriesItemAt(i);
|
||||||
assembledCategories.push_back(
|
assembledCategories.push_back(
|
||||||
CategoryRef(new PackageCategory(*(pkgCategory->Code()), *(pkgCategory->Name())), true));
|
CategoryRef(new PackageCategory(pkgCategory->Code(), pkgCategory->Name()), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
fModel->SetCategories(assembledCategories);
|
fModel->SetCategories(assembledCategories);
|
||||||
@@ -161,7 +157,7 @@ ServerReferenceDataUpdateProcess::_ProcessPkgCategories(DumpExportReference* dat
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
ServerReferenceDataUpdateProcess::_ProcessRatingStabilities(DumpExportReference* data)
|
ServerReferenceDataUpdateProcess::_ProcessRatingStabilities(DumpExportReferenceRef data)
|
||||||
{
|
{
|
||||||
HDINFO("[%s] will populate %" B_PRId32 " rating stabilities", Name(),
|
HDINFO("[%s] will populate %" B_PRId32 " rating stabilities", Name(),
|
||||||
data->CountUserRatingStabilities());
|
data->CountUserRatingStabilities());
|
||||||
@@ -172,8 +168,8 @@ ServerReferenceDataUpdateProcess::_ProcessRatingStabilities(DumpExportReference*
|
|||||||
DumpExportReferenceUserRatingStability* ratingStability
|
DumpExportReferenceUserRatingStability* ratingStability
|
||||||
= data->UserRatingStabilitiesItemAt(i);
|
= data->UserRatingStabilitiesItemAt(i);
|
||||||
assembledRatingStabilities.push_back(
|
assembledRatingStabilities.push_back(
|
||||||
RatingStabilityRef(new RatingStability(*(ratingStability->Code()),
|
RatingStabilityRef(new RatingStability(ratingStability->Code(), ratingStability->Name(),
|
||||||
*(ratingStability->Name()), ratingStability->Ordering()),
|
ratingStability->Ordering()),
|
||||||
true));
|
true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2020, Andrew Lindesay <[email protected]>.
|
* Copyright 2019-2026, Andrew Lindesay <[email protected]>.
|
||||||
* All rights reserved. Distributed under the terms of the MIT License.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef SERVER_REFERENCE_DATA_UPDATE_PROCESS_H
|
#ifndef SERVER_REFERENCE_DATA_UPDATE_PROCESS_H
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <String.h>
|
#include <String.h>
|
||||||
#include <Url.h>
|
#include <Url.h>
|
||||||
|
|
||||||
|
#include "DumpExportReferenceModel.h"
|
||||||
#include "Model.h"
|
#include "Model.h"
|
||||||
#include "PackageInfo.h"
|
#include "PackageInfo.h"
|
||||||
|
|
||||||
@@ -20,7 +21,6 @@
|
|||||||
#undef B_TRANSLATION_CONTEXT
|
#undef B_TRANSLATION_CONTEXT
|
||||||
#define B_TRANSLATION_CONTEXT "ServerReferenceDataUpdateProcess"
|
#define B_TRANSLATION_CONTEXT "ServerReferenceDataUpdateProcess"
|
||||||
|
|
||||||
class DumpExportReference;
|
|
||||||
|
|
||||||
class ServerReferenceDataUpdateProcess : public AbstractSingleFileServerProcess
|
class ServerReferenceDataUpdateProcess : public AbstractSingleFileServerProcess
|
||||||
{
|
{
|
||||||
@@ -43,13 +43,10 @@ protected:
|
|||||||
status_t GetLocalPath(BPath& path) const;
|
status_t GetLocalPath(BPath& path) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
status_t _ProcessData(DumpExportReference* data);
|
status_t _ProcessData(DumpExportReferenceRef data);
|
||||||
status_t _ProcessNaturalLanguages(
|
status_t _ProcessNaturalLanguages(DumpExportReferenceRef data);
|
||||||
DumpExportReference* data);
|
status_t _ProcessPkgCategories(DumpExportReferenceRef data);
|
||||||
status_t _ProcessPkgCategories(
|
status_t _ProcessRatingStabilities(DumpExportReferenceRef data);
|
||||||
DumpExportReference* data);
|
|
||||||
status_t _ProcessRatingStabilities(
|
|
||||||
DumpExportReference* data);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Model* fModel;
|
Model* fModel;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017-2025, Andrew Lindesay <[email protected]>.
|
* Copyright 2017-2026, Andrew Lindesay <[email protected]>.
|
||||||
* All rights reserved. Distributed under the terms of the MIT License.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#include "ServerRepositoryDataUpdateProcess.h"
|
#include "ServerRepositoryDataUpdateProcess.h"
|
||||||
@@ -9,14 +9,12 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include <AutoDeleter.h>
|
#include <AutoDeleter.h>
|
||||||
#include <Autolock.h>
|
|
||||||
#include <Catalog.h>
|
#include <Catalog.h>
|
||||||
#include <FileIO.h>
|
#include <FileIO.h>
|
||||||
#include <Url.h>
|
#include <Url.h>
|
||||||
|
|
||||||
#include "DumpExportRepository.h"
|
|
||||||
#include "DumpExportRepositoryJsonListener.h"
|
#include "DumpExportRepositoryJsonListener.h"
|
||||||
#include "DumpExportRepositorySource.h"
|
#include "DumpExportRepositoryModel.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "PackageInfo.h"
|
#include "PackageInfo.h"
|
||||||
#include "ServerSettings.h"
|
#include "ServerSettings.h"
|
||||||
@@ -35,26 +33,22 @@
|
|||||||
|
|
||||||
class DepotMatchingRepositoryListener : public DumpExportRepositoryListener {
|
class DepotMatchingRepositoryListener : public DumpExportRepositoryListener {
|
||||||
public:
|
public:
|
||||||
DepotMatchingRepositoryListener(Model* model,
|
DepotMatchingRepositoryListener(Model* model, Stoppable* stoppable);
|
||||||
Stoppable* stoppable);
|
|
||||||
virtual ~DepotMatchingRepositoryListener();
|
virtual ~DepotMatchingRepositoryListener();
|
||||||
|
|
||||||
virtual bool Handle(DumpExportRepository* item);
|
virtual bool Handle(DumpExportRepositoryRef item);
|
||||||
void Handle(DumpExportRepository* repository,
|
void Handle(DumpExportRepositoryRef repository,
|
||||||
DumpExportRepositorySource*
|
DumpExportRepositorySourceRef repositorySource);
|
||||||
repositorySource);
|
|
||||||
void Handle(const BString& identifier,
|
void Handle(const BString& identifier,
|
||||||
DumpExportRepository* repository,
|
DumpExportRepositoryRef repository,
|
||||||
DumpExportRepositorySource*
|
DumpExportRepositorySourceRef repositorySource);
|
||||||
repositorySource);
|
|
||||||
virtual void Complete();
|
virtual void Complete();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _SetupRepositoryData(
|
void _SetupRepositoryData(
|
||||||
DepotInfoRef& depot,
|
DepotInfoRef& depot,
|
||||||
DumpExportRepository* repository,
|
DumpExportRepositoryRef repository,
|
||||||
DumpExportRepositorySource*
|
DumpExportRepositorySourceRef repositorySource);
|
||||||
repositorySource);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Model* fModel;
|
Model* fModel;
|
||||||
@@ -80,15 +74,15 @@ DepotMatchingRepositoryListener::~DepotMatchingRepositoryListener()
|
|||||||
|
|
||||||
void
|
void
|
||||||
DepotMatchingRepositoryListener::_SetupRepositoryData(DepotInfoRef& depot,
|
DepotMatchingRepositoryListener::_SetupRepositoryData(DepotInfoRef& depot,
|
||||||
DumpExportRepository* repository, DumpExportRepositorySource* repositorySource)
|
DumpExportRepositoryRef repository, DumpExportRepositorySourceRef repositorySource)
|
||||||
{
|
{
|
||||||
BString* repositoryCode = repository->Code();
|
BString repositoryCode = repository->Code();
|
||||||
BString* repositorySourceCode = repositorySource->Code();
|
BString repositorySourceCode = repositorySource->Code();
|
||||||
|
|
||||||
DepotInfoBuilder depotBuilder(depot);
|
DepotInfoBuilder depotBuilder(depot);
|
||||||
|
|
||||||
depotBuilder.WithWebAppRepositoryCode(*repositoryCode);
|
depotBuilder.WithWebAppRepositoryCode(repositoryCode);
|
||||||
depotBuilder.WithWebAppRepositorySourceCode(*repositorySourceCode);
|
depotBuilder.WithWebAppRepositorySourceCode(repositorySourceCode);
|
||||||
|
|
||||||
DepotInfoRef depotInfo = depotBuilder.BuildRef();
|
DepotInfoRef depotInfo = depotBuilder.BuildRef();
|
||||||
fDepots.push_back(depotInfo);
|
fDepots.push_back(depotInfo);
|
||||||
@@ -96,19 +90,19 @@ DepotMatchingRepositoryListener::_SetupRepositoryData(DepotInfoRef& depot,
|
|||||||
if (Logger::IsDebugEnabled()) {
|
if (Logger::IsDebugEnabled()) {
|
||||||
HDDEBUG("[DepotMatchingRepositoryListener] associated depot [%s] (%s) "
|
HDDEBUG("[DepotMatchingRepositoryListener] associated depot [%s] (%s) "
|
||||||
"with server repository source [%s] (%s)",
|
"with server repository source [%s] (%s)",
|
||||||
depot->Name().String(), depot->Identifier().String(), repositorySourceCode->String(),
|
depot->Name().String(), depot->Identifier().String(), repositorySourceCode.String(),
|
||||||
repositorySource->Identifier()->String());
|
repositorySource->Identifier().String());
|
||||||
} else {
|
} else {
|
||||||
HDINFO("[DepotMatchingRepositoryListener] associated depot [%s] with "
|
HDINFO("[DepotMatchingRepositoryListener] associated depot [%s] with "
|
||||||
"server repository source [%s]",
|
"server repository source [%s]",
|
||||||
depot->Name().String(), repositorySourceCode->String());
|
depot->Name().String(), repositorySourceCode.String());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
DepotMatchingRepositoryListener::Handle(const BString& identifier, DumpExportRepository* repository,
|
DepotMatchingRepositoryListener::Handle(const BString& identifier,
|
||||||
DumpExportRepositorySource* repositorySource)
|
DumpExportRepositoryRef repository, DumpExportRepositorySourceRef repositorySource)
|
||||||
{
|
{
|
||||||
if (!identifier.IsEmpty()) {
|
if (!identifier.IsEmpty()) {
|
||||||
DepotInfoRef depotForIdentifier = fModel->DepotForIdentifier(identifier);
|
DepotInfoRef depotForIdentifier = fModel->DepotForIdentifier(identifier);
|
||||||
@@ -120,22 +114,22 @@ DepotMatchingRepositoryListener::Handle(const BString& identifier, DumpExportRep
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository,
|
DepotMatchingRepositoryListener::Handle(DumpExportRepositoryRef repository,
|
||||||
DumpExportRepositorySource* repositorySource)
|
DumpExportRepositorySourceRef repositorySource)
|
||||||
{
|
{
|
||||||
if (!repositorySource->IdentifierIsNull())
|
if (repositorySource->IsSetIdentifier())
|
||||||
Handle(*(repositorySource->Identifier()), repository, repositorySource);
|
Handle(repositorySource->Identifier(), repository, repositorySource);
|
||||||
|
|
||||||
// there may be additional identifiers for the remote repository and
|
// there may be additional identifiers for the remote repository and
|
||||||
// these should also be taken into consideration.
|
// these should also be taken into consideration.
|
||||||
|
|
||||||
for (int32 i = 0; i < repositorySource->CountExtraIdentifiers(); i++)
|
for (int32 i = 0; i < repositorySource->CountExtraIdentifiers(); i++)
|
||||||
Handle(*(repositorySource->ExtraIdentifiersItemAt(i)), repository, repositorySource);
|
Handle(repositorySource->ExtraIdentifiersItemAt(i), repository, repositorySource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository)
|
DepotMatchingRepositoryListener::Handle(DumpExportRepositoryRef repository)
|
||||||
{
|
{
|
||||||
int32 i;
|
int32 i;
|
||||||
for (i = 0; i < repository->CountRepositorySources(); i++)
|
for (i = 0; i < repository->CountRepositorySources(); i++)
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017-2020, Andrew Lindesay <[email protected]>.
|
* Copyright 2017-2026, Andrew Lindesay <[email protected]>.
|
||||||
* All rights reserved. Distributed under the terms of the MIT License.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "DumpExportRepositorySource.h"
|
|
||||||
#include "DumpExportRepository.h"
|
|
||||||
#include "DumpExportRepositoryJsonListener.h"
|
#include "DumpExportRepositoryJsonListener.h"
|
||||||
#include "DumpExportRepositoryJsonListenerTest.h"
|
#include "DumpExportRepositoryJsonListenerTest.h"
|
||||||
|
#include "DumpExportRepositoryModel.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
@@ -124,7 +123,7 @@ public:
|
|||||||
TestBulkContainerItemListener();
|
TestBulkContainerItemListener();
|
||||||
virtual ~TestBulkContainerItemListener();
|
virtual ~TestBulkContainerItemListener();
|
||||||
|
|
||||||
bool Handle(DumpExportRepository* item);
|
bool Handle(DumpExportRepositoryRef item);
|
||||||
void Complete();
|
void Complete();
|
||||||
|
|
||||||
BString ConcatenatedCodes();
|
BString ConcatenatedCodes();
|
||||||
@@ -200,21 +199,15 @@ DumpExportRepositoryJsonListenerTest::TestSingle()
|
|||||||
BPrivate::BJson::Parse(inputData, listener);
|
BPrivate::BJson::Parse(inputData, listener);
|
||||||
// ----------------------
|
// ----------------------
|
||||||
|
|
||||||
DumpExportRepository *repository = listener->Target();
|
DumpExportRepositoryRef repository = listener->Result();
|
||||||
ObjectDeleter<DumpExportRepository> repositoryDeleter(repository);
|
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(B_OK, listener->ErrorStatus());
|
CPPUNIT_ASSERT_EQUAL(B_OK, listener->ErrorStatus());
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(
|
CPPUNIT_ASSERT_EQUAL(BString("haikuports"), repository->Code());
|
||||||
BString("haikuports"), *(repository->Code()));
|
CPPUNIT_ASSERT_EQUAL(BString("HaikuPorts"), repository->Name());
|
||||||
CPPUNIT_ASSERT_EQUAL(
|
CPPUNIT_ASSERT_EQUAL(BString("HaikuPorts is a centralized collection..."),
|
||||||
BString("HaikuPorts"), *(repository->Name()));
|
repository->Description());
|
||||||
CPPUNIT_ASSERT_EQUAL(
|
CPPUNIT_ASSERT_EQUAL(BString("https://example.com"), repository->InformationUrl());
|
||||||
BString("HaikuPorts is a centralized collection..."),
|
|
||||||
*(repository->Description()));
|
|
||||||
CPPUNIT_ASSERT_EQUAL(
|
|
||||||
BString("https://example.com"),
|
|
||||||
*(repository->InformationUrl()));
|
|
||||||
CPPUNIT_ASSERT_EQUAL(2, repository->CountRepositorySources());
|
CPPUNIT_ASSERT_EQUAL(2, repository->CountRepositorySources());
|
||||||
|
|
||||||
DumpExportRepositorySource *source0 =
|
DumpExportRepositorySource *source0 =
|
||||||
@@ -222,12 +215,12 @@ DumpExportRepositoryJsonListenerTest::TestSingle()
|
|||||||
DumpExportRepositorySource *source1 =
|
DumpExportRepositorySource *source1 =
|
||||||
repository->RepositorySourcesItemAt(1);
|
repository->RepositorySourcesItemAt(1);
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(BString("haikuports_x86_64"), *(source0->Code()));
|
CPPUNIT_ASSERT_EQUAL(BString("haikuports_x86_64"), source0->Code());
|
||||||
CPPUNIT_ASSERT_EQUAL(BString("haiku:hpkr:haikuports_x86_64"), *(source0->Identifier()));
|
CPPUNIT_ASSERT_EQUAL(BString("haiku:hpkr:haikuports_x86_64"), source0->Identifier());
|
||||||
CPPUNIT_ASSERT_EQUAL(BString("zing"), *(source0->ExtraIdentifiersItemAt(0)));
|
CPPUNIT_ASSERT_EQUAL(BString("zing"), source0->ExtraIdentifiersItemAt(0));
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(BString("haikuports_x86_gcc2"), *(source1->Code()));
|
CPPUNIT_ASSERT_EQUAL(BString("haikuports_x86_gcc2"), source1->Code());
|
||||||
CPPUNIT_ASSERT_EQUAL(BString("haiku:hpkr:haikuports_x86_gcc2"), *(source1->Identifier()));
|
CPPUNIT_ASSERT_EQUAL(BString("haiku:hpkr:haikuports_x86_gcc2"), source1->Identifier());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -267,21 +260,20 @@ TestBulkContainerItemListener::~TestBulkContainerItemListener()
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TestBulkContainerItemListener::Handle(DumpExportRepository* item)
|
TestBulkContainerItemListener::Handle(DumpExportRepositoryRef item)
|
||||||
{
|
{
|
||||||
int32 i;
|
int32 i;
|
||||||
|
|
||||||
if (!fConcatenatedCodes.IsEmpty())
|
if (!fConcatenatedCodes.IsEmpty())
|
||||||
fConcatenatedCodes.Append(" ");
|
fConcatenatedCodes.Append(" ");
|
||||||
|
|
||||||
fConcatenatedCodes.Append(item->Code()->String());
|
fConcatenatedCodes.Append(item->Code().String());
|
||||||
|
|
||||||
for (i = 0; i < item->CountRepositorySources(); i++) {
|
for (i = 0; i < item->CountRepositorySources(); i++) {
|
||||||
if (!fConcatenatedSourcesUrl.IsEmpty())
|
if (!fConcatenatedSourcesUrl.IsEmpty())
|
||||||
fConcatenatedSourcesUrl.Append(" ");
|
fConcatenatedSourcesUrl.Append(" ");
|
||||||
|
|
||||||
fConcatenatedSourcesUrl.Append(
|
fConcatenatedSourcesUrl.Append(item->RepositorySourcesItemAt(i)->Identifier().String());
|
||||||
item->RepositorySourcesItemAt(i)->Identifier()->String());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -0,0 +1,390 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026, Andrew Lindesay <[email protected]>.
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DumpExportRepositoryTest.h"
|
||||||
|
|
||||||
|
#include <cppunit/TestCaller.h>
|
||||||
|
#include <cppunit/TestSuite.h>
|
||||||
|
|
||||||
|
#include "DumpExportRepositoryModel.h"
|
||||||
|
|
||||||
|
|
||||||
|
DumpExportRepositoryTest::DumpExportRepositoryTest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DumpExportRepositoryTest::~DumpExportRepositoryTest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DumpExportRepositoryTest::TestEqualityTrue()
|
||||||
|
{
|
||||||
|
DumpExportRepositoryRef repositoryRef1 = _CreateAny();
|
||||||
|
DumpExportRepositoryRef repositoryRef2 = _CreateAny();
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
bool result = (*(repositoryRef1.Get()) == *(repositoryRef2.Get()));
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("==", true, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DumpExportRepositoryTest::TestEqualityFalse()
|
||||||
|
{
|
||||||
|
DumpExportRepositoryRef repositoryRef1 = _CreateAny();
|
||||||
|
DumpExportRepositoryRef repositoryRef2
|
||||||
|
= DumpExportRepositoryBuilder(repositoryRef1).WithCode("craters").BuildRef();
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
bool result = (*(repositoryRef1.Get()) != *(repositoryRef2.Get()));
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("!=", true, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! This test case checks what happens when data changes down the hierarchy of
|
||||||
|
objects. It should still see this as part of the equality.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
DumpExportRepositoryTest::TestEqualityTransientFalse()
|
||||||
|
{
|
||||||
|
DumpExportRepositoryRef repositoryRef1 = _CreateAny();
|
||||||
|
DumpExportRepositorySourceRef repositorySourceRef1 = repositoryRef1->RepositorySourcesItemAt(0);
|
||||||
|
|
||||||
|
DumpExportRepositoryRef repositoryRef2
|
||||||
|
= DumpExportRepositoryBuilder(repositoryRef1)
|
||||||
|
.WithoutRepositorySources()
|
||||||
|
.AddToRepositorySources(DumpExportRepositorySourceBuilder(repositorySourceRef1)
|
||||||
|
.WithCode("pigeon")
|
||||||
|
.BuildRef())
|
||||||
|
.BuildRef();
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
bool result = (*(repositoryRef1.Get()) == *(repositoryRef2.Get()));
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("R1 CountRepositorySources", 1,
|
||||||
|
repositoryRef1->CountRepositorySources());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("R2 CountRepositorySources", 1,
|
||||||
|
repositoryRef2->CountRepositorySources());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("==", false, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Check to make sure that the Clear method works. */
|
||||||
|
void
|
||||||
|
DumpExportRepositoryTest::TestBuilderClear()
|
||||||
|
{
|
||||||
|
DumpExportRepositoryBuilder builder
|
||||||
|
= DumpExportRepositoryBuilder().WithCode("oak").WithName("Oak Tree");
|
||||||
|
|
||||||
|
DumpExportRepositoryRef repositoryBeforeRef = builder.BuildRef();
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
builder.Clear();
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
DumpExportRepositoryRef repositoryAfterRef = builder.WithCode("birch").BuildRef();
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("CodeBefore", BString("oak"), repositoryBeforeRef->Code());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("NameBefore", BString("Oak Tree"), repositoryBeforeRef->Name());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("CodeAfter", BString("birch"), repositoryAfterRef->Code());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("IsSetNameAfter", false, repositoryAfterRef->IsSetName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Checks use of the builder without any prior object. */
|
||||||
|
void
|
||||||
|
DumpExportRepositoryTest::TestBuilderFromCold()
|
||||||
|
{
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
DumpExportRepositoryRef repositoryRef = _CreateAny();
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Code", BString("flamingo"), repositoryRef->Code());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("IsSetCode", true, repositoryRef->IsSetCode());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Description", BString("Flamingo is a bird"),
|
||||||
|
repositoryRef->Description());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Name", BString("Flamingo"), repositoryRef->Name());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("InformationUrl", BString("http://example.com"),
|
||||||
|
repositoryRef->InformationUrl());
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("SourcesCount", 1, repositoryRef->CountRepositorySources());
|
||||||
|
DumpExportRepositorySourceRef repositorySourceRef = repositoryRef->RepositorySourcesItemAt(0);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Sources0Code", BString("flamingo_x86_64"),
|
||||||
|
repositorySourceRef->Code());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Sources0Identifier", BString("repo:flamingo"),
|
||||||
|
repositorySourceRef->Identifier());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Sources0IsSetArchitectureCode", false,
|
||||||
|
repositorySourceRef->IsSetArchitectureCode());
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("CountRepositorySourceMirrors", 1,
|
||||||
|
repositorySourceRef->CountRepositorySourceMirrors());
|
||||||
|
DumpExportRepositorySourceMirrorRef repositorySourceMirrorRef
|
||||||
|
= repositorySourceRef->RepositorySourceMirrorsItemAt(0);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("CountryCode", BString("de"),
|
||||||
|
repositorySourceMirrorRef->CountryCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DumpExportRepositoryTest::TestBuilderFromExisting()
|
||||||
|
{
|
||||||
|
DumpExportRepositoryRef originRepositoryRef = _CreateAny();
|
||||||
|
DumpExportRepositoryBuilder builder
|
||||||
|
= DumpExportRepositoryBuilder(originRepositoryRef).WithCode("flamingo2");
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
DumpExportRepositoryRef repositoryRef = builder.BuildRef();
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Code", BString("flamingo2"), repositoryRef->Code());
|
||||||
|
// check that the changed field has come through
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Name", BString("Flamingo"), repositoryRef->Name());
|
||||||
|
// check that the unchanged field has come through
|
||||||
|
|
||||||
|
// check that unmodified subordinate objects are coming through
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("SourcesCount", 1, repositoryRef->CountRepositorySources());
|
||||||
|
DumpExportRepositorySourceRef repositorySourceRef = repositoryRef->RepositorySourcesItemAt(0);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Sources0Code", BString("flamingo_x86_64"),
|
||||||
|
repositorySourceRef->Code());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DumpExportRepositoryTest::TestBuilderFromExistingNoChange()
|
||||||
|
{
|
||||||
|
DumpExportRepositoryRef originRepositoryRef = _CreateAny();
|
||||||
|
DumpExportRepositoryBuilder builder = DumpExportRepositoryBuilder(originRepositoryRef);
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
DumpExportRepositoryRef repositoryRef = builder.BuildRef();
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Code", BString("flamingo"), repositoryRef->Code());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DumpExportRepositoryTest::TestFromMessage()
|
||||||
|
{
|
||||||
|
BMessage message = _CreateAnyMessage();
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
DumpExportRepositoryRef repository(new DumpExportRepository(&message), true);
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("R Code", BString("myrepo"), repository->Code());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("IsSetCode", true, repository->IsSetCode());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("IsSetInformationUrl", false, repository->IsSetInformationUrl());
|
||||||
|
// ^ specifically check that one of the missing fields is showing as missing.
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("CountRepositorySources", 1, repository->CountRepositorySources());
|
||||||
|
|
||||||
|
DumpExportRepositorySourceRef source0 = repository->RepositorySourcesItemAt(0);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("RS Code", BString("myrepo_x86_64"), source0->Code());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("IsSetCode", true, source0->IsSetCode());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("CountExtraIdentifiers", 2, source0->CountExtraIdentifiers());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("ExtraIdentifier_0", BString("extra_ident:myrepo_x86_64_0"),
|
||||||
|
source0->ExtraIdentifiersItemAt(0));
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("ExtraIdentifier_1", BString("extra_ident:myrepo_x86_64_1"),
|
||||||
|
source0->ExtraIdentifiersItemAt(1));
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("CountRepositorySourceMirrors", 1,
|
||||||
|
source0->CountRepositorySourceMirrors());
|
||||||
|
|
||||||
|
DumpExportRepositorySourceMirrorRef mirror0 = source0->RepositorySourceMirrorsItemAt(0);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("CountryCode", BString("nz"), mirror0->CountryCode());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("IsPrimary", true, mirror0->IsPrimary());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("IsSetIsPrimary", true, mirror0->IsSetIsPrimary());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DumpExportRepositoryTest::TestToMessage()
|
||||||
|
{
|
||||||
|
DumpExportRepositoryRef repositoryRef = _CreateAny();
|
||||||
|
BMessage repositoryMessage;
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
status_t result = repositoryRef->Archive(&repositoryMessage, true);
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Result", B_OK, result);
|
||||||
|
|
||||||
|
// quick checks as this could get quite laborious.
|
||||||
|
status_t status;
|
||||||
|
BString anyString;
|
||||||
|
bool anyBool;
|
||||||
|
|
||||||
|
status = repositoryMessage.FindString("Code", &anyString);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Code Status", B_OK, status);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Code", BString("flamingo"), anyString);
|
||||||
|
|
||||||
|
status = repositoryMessage.FindBool("RepositorySources_isSet", &anyBool);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("RepositorySources_isSet Status", B_OK, status);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("RepositorySources_isSet", true, anyBool);
|
||||||
|
|
||||||
|
BMessage repositorySource0Message;
|
||||||
|
status = repositoryMessage.FindMessage("RepositorySources_0", &repositorySource0Message);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("RepositorySources_0 Status", B_OK, status);
|
||||||
|
|
||||||
|
status = repositorySource0Message.FindString("Code", &anyString);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Code Status", B_OK, status);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Code", BString("flamingo_x86_64"), anyString);
|
||||||
|
|
||||||
|
status = repositorySource0Message.FindBool("ExtraIdentifiers_isSet", &anyBool);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("ExtraIdentifiers_isSet Status", B_OK, status);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("ExtraIdentifiers_isSet", true, anyBool);
|
||||||
|
|
||||||
|
status = repositorySource0Message.FindString("ExtraIdentifiers_0", &anyString);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("ExtraIdentifiers_0 Status", B_OK, status);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("ExtraIdentifiers_0", BString("repo:flamingo2"), anyString);
|
||||||
|
|
||||||
|
status = repositorySource0Message.FindBool("RepositorySourceMirrors_isSet", &anyBool);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("RepositorySourceMirrors_isSet Status", B_OK, status);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("RepositorySourceMirrors_isSet", true, anyBool);
|
||||||
|
|
||||||
|
BMessage repositorySourceMirror0Message;
|
||||||
|
status = repositorySource0Message.FindMessage("RepositorySourceMirrors_0",
|
||||||
|
&repositorySourceMirror0Message);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("RepositorySourceMirrors_0 Status", B_OK, status);
|
||||||
|
|
||||||
|
status = repositorySourceMirror0Message.FindString("CountryCode", &anyString);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("CountryCode Status", B_OK, status);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("CountryCode", BString("de"), anyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void
|
||||||
|
DumpExportRepositoryTest::AddTests(BTestSuite& parent)
|
||||||
|
{
|
||||||
|
CppUnit::TestSuite& suite = *new CppUnit::TestSuite("DumpExportRepositoryTest");
|
||||||
|
|
||||||
|
suite.addTest(new CppUnit::TestCaller<DumpExportRepositoryTest>(
|
||||||
|
"DumpExportRepositoryTest::TestEqualityTrue", &DumpExportRepositoryTest::TestEqualityTrue));
|
||||||
|
|
||||||
|
suite.addTest(new CppUnit::TestCaller<DumpExportRepositoryTest>(
|
||||||
|
"DumpExportRepositoryTest::TestEqualityFalse",
|
||||||
|
&DumpExportRepositoryTest::TestEqualityFalse));
|
||||||
|
|
||||||
|
suite.addTest(new CppUnit::TestCaller<DumpExportRepositoryTest>(
|
||||||
|
"DumpExportRepositoryTest::TestEqualityTransientFalse",
|
||||||
|
&DumpExportRepositoryTest::TestEqualityTransientFalse));
|
||||||
|
|
||||||
|
suite.addTest(new CppUnit::TestCaller<DumpExportRepositoryTest>(
|
||||||
|
"DumpExportRepositoryTest::TestBuilderFromCold",
|
||||||
|
&DumpExportRepositoryTest::TestBuilderFromCold));
|
||||||
|
|
||||||
|
suite.addTest(new CppUnit::TestCaller<DumpExportRepositoryTest>(
|
||||||
|
"DumpExportRepositoryTest::TestBuilderFromExisting",
|
||||||
|
&DumpExportRepositoryTest::TestBuilderFromExisting));
|
||||||
|
|
||||||
|
suite.addTest(new CppUnit::TestCaller<DumpExportRepositoryTest>(
|
||||||
|
"DumpExportRepositoryTest::TestBuilderFromExistingNoChange",
|
||||||
|
&DumpExportRepositoryTest::TestBuilderFromExistingNoChange));
|
||||||
|
|
||||||
|
suite.addTest(new CppUnit::TestCaller<DumpExportRepositoryTest>(
|
||||||
|
"DumpExportRepositoryTest::TestBuilderClear", &DumpExportRepositoryTest::TestBuilderClear));
|
||||||
|
|
||||||
|
suite.addTest(new CppUnit::TestCaller<DumpExportRepositoryTest>(
|
||||||
|
"DumpExportRepositoryTest::TestFromMessage", &DumpExportRepositoryTest::TestFromMessage));
|
||||||
|
|
||||||
|
suite.addTest(new CppUnit::TestCaller<DumpExportRepositoryTest>(
|
||||||
|
"DumpExportRepositoryTest::TestToMessage", &DumpExportRepositoryTest::TestToMessage));
|
||||||
|
|
||||||
|
parent.addTest("DumpExportRepositoryTest", &suite);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! This is a `BMessage` that is an archive of an `DumpExportRepository` object. */
|
||||||
|
BMessage
|
||||||
|
DumpExportRepositoryTest::_CreateAnyMessage()
|
||||||
|
{
|
||||||
|
BMessage repositorySourceMirrorMessage;
|
||||||
|
|
||||||
|
if (repositorySourceMirrorMessage.AddString("BaseUrl", "http://www.example.com/mirror_baseurl")
|
||||||
|
!= B_OK) {
|
||||||
|
CPPUNIT_FAIL("unable to add the key [BaseUrl]");
|
||||||
|
}
|
||||||
|
if (repositorySourceMirrorMessage.AddString("CountryCode", "nz") != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [CountryCode]");
|
||||||
|
if (repositorySourceMirrorMessage.AddString("Description", "New Zealand mirror") != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [Description]");
|
||||||
|
if (repositorySourceMirrorMessage.AddBool("IsPrimary", true) != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [IsPrimary]");
|
||||||
|
|
||||||
|
BMessage repositorySourceMessage;
|
||||||
|
|
||||||
|
if (repositorySourceMessage.AddString("ArchitectureCode", "x86_64") != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [ArchitectureCode]");
|
||||||
|
if (repositorySourceMessage.AddString("Code", "myrepo_x86_64") != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [Code]");
|
||||||
|
if (repositorySourceMessage.AddBool("ExtraIdentifiers_isSet", true) != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [ExtraIdentifiers_isSet]");
|
||||||
|
if (repositorySourceMessage.AddString("ExtraIdentifiers_0", "extra_ident:myrepo_x86_64_0")
|
||||||
|
!= B_OK) {
|
||||||
|
CPPUNIT_FAIL("unable to add the key [ExtraIdentifiers_0]");
|
||||||
|
}
|
||||||
|
if (repositorySourceMessage.AddString("ExtraIdentifiers_1", "extra_ident:myrepo_x86_64_1")
|
||||||
|
!= B_OK) {
|
||||||
|
CPPUNIT_FAIL("unable to add the key [ExtraIdentifiers_1]");
|
||||||
|
}
|
||||||
|
if (repositorySourceMessage.AddString("Identifier", "ident:myrepo_x86_64") != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [Identifier]");
|
||||||
|
if (repositorySourceMessage.AddString("RepoInfoUrl", "http://www.example.com/myrepo_x86_64")
|
||||||
|
!= B_OK) {
|
||||||
|
CPPUNIT_FAIL("unable to add the key [RepoInfoUrl]");
|
||||||
|
}
|
||||||
|
if (repositorySourceMessage.AddBool("RepositorySourceMirrors_isSet", true) != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [RepositorySourceMirrors_isSet]");
|
||||||
|
if (repositorySourceMessage.AddMessage("RepositorySourceMirrors_0",
|
||||||
|
&repositorySourceMirrorMessage)
|
||||||
|
!= B_OK) {
|
||||||
|
CPPUNIT_FAIL("unable to add the key [RepositorySourceMirrors_0]");
|
||||||
|
}
|
||||||
|
|
||||||
|
BMessage repositoryMessage;
|
||||||
|
|
||||||
|
if (repositoryMessage.AddString("Code", "myrepo") != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [Code]");
|
||||||
|
if (repositoryMessage.AddString("Description", "This is my repo") != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [Description]");
|
||||||
|
// note; missing the informational url intentionally
|
||||||
|
if (repositoryMessage.AddString("Name", "My Repo") != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [Name]");
|
||||||
|
if (repositoryMessage.AddBool("RepositorySources_isSet", true) != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [RepositorySources_isSet]");
|
||||||
|
if (repositoryMessage.AddMessage("RepositorySources_0", &repositorySourceMessage) != B_OK)
|
||||||
|
CPPUNIT_FAIL("unable to add the key [RepositorySources_0]");
|
||||||
|
|
||||||
|
return repositoryMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DumpExportRepositoryRef
|
||||||
|
DumpExportRepositoryTest::_CreateAny()
|
||||||
|
{
|
||||||
|
return DumpExportRepositoryBuilder()
|
||||||
|
.WithCode("flamingo")
|
||||||
|
.WithDescription("Flamingo is a bird")
|
||||||
|
.WithInformationUrl("http://example.com")
|
||||||
|
.WithName("Flamingo")
|
||||||
|
.AddToRepositorySources(DumpExportRepositorySourceBuilder()
|
||||||
|
.WithCode("flamingo_x86_64")
|
||||||
|
.WithIdentifier("repo:flamingo")
|
||||||
|
.AddToExtraIdentifiers("repo:flamingo2")
|
||||||
|
.AddToRepositorySourceMirrors(DumpExportRepositorySourceMirrorBuilder()
|
||||||
|
.WithCountryCode("de")
|
||||||
|
.WithBaseUrl("http://www.example.com/base_url")
|
||||||
|
.BuildRef())
|
||||||
|
.BuildRef())
|
||||||
|
.BuildRef();
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026, Andrew Lindesay <[email protected]>
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef DUMP_EXPORT_REPOSITORY_TEST_H
|
||||||
|
#define DUMP_EXPORT_REPOSITORY_TEST_H
|
||||||
|
|
||||||
|
#include <Message.h>
|
||||||
|
|
||||||
|
#include <TestCase.h>
|
||||||
|
#include <TestSuite.h>
|
||||||
|
|
||||||
|
#include "DumpExportRepositoryModel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*! Tests the model in insolation from the listener. */
|
||||||
|
class DumpExportRepositoryTest : public CppUnit::TestCase {
|
||||||
|
public:
|
||||||
|
DumpExportRepositoryTest();
|
||||||
|
virtual ~DumpExportRepositoryTest();
|
||||||
|
|
||||||
|
void TestEqualityTrue();
|
||||||
|
void TestEqualityFalse();
|
||||||
|
void TestEqualityTransientFalse();
|
||||||
|
|
||||||
|
void TestBuilderFromCold();
|
||||||
|
void TestBuilderFromExisting();
|
||||||
|
void TestBuilderFromExistingNoChange();
|
||||||
|
void TestBuilderClear();
|
||||||
|
|
||||||
|
void TestFromMessage();
|
||||||
|
void TestToMessage();
|
||||||
|
|
||||||
|
static void AddTests(BTestSuite& suite);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DumpExportRepositoryRef
|
||||||
|
_CreateAny();
|
||||||
|
BMessage _CreateAnyMessage();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // DUMP_EXPORT_REPOSITORY_TEST_H
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017-2024, Andrew Lindesay, [email protected]
|
* Copyright 2017-2026, Andrew Lindesay, [email protected]
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "DataIOUtilsTest.h"
|
#include "DataIOUtilsTest.h"
|
||||||
#include "DumpExportRepositoryJsonListenerTest.h"
|
#include "DumpExportRepositoryJsonListenerTest.h"
|
||||||
|
#include "DumpExportRepositoryTest.h"
|
||||||
#include "JwtTokenHelperTest.h"
|
#include "JwtTokenHelperTest.h"
|
||||||
#include "LocaleUtilsTest.h"
|
#include "LocaleUtilsTest.h"
|
||||||
#include "StandardMetaDataJsonEventListenerTest.h"
|
#include "StandardMetaDataJsonEventListenerTest.h"
|
||||||
@@ -27,7 +28,7 @@ getTestSuite()
|
|||||||
StandardMetaDataJsonEventListenerTest::AddTests(*suite);
|
StandardMetaDataJsonEventListenerTest::AddTests(*suite);
|
||||||
DataIOUtilsTest::AddTests(*suite);
|
DataIOUtilsTest::AddTests(*suite);
|
||||||
DumpExportRepositoryJsonListenerTest::AddTests(*suite);
|
DumpExportRepositoryJsonListenerTest::AddTests(*suite);
|
||||||
DumpExportRepositoryJsonListenerTest::AddTests(*suite);
|
DumpExportRepositoryTest::AddTests(*suite);
|
||||||
LocaleUtilsTest::AddTests(*suite);
|
LocaleUtilsTest::AddTests(*suite);
|
||||||
JwtTokenHelperTest::AddTests(*suite);
|
JwtTokenHelperTest::AddTests(*suite);
|
||||||
ValidationFailureTest::AddTests(*suite);
|
ValidationFailureTest::AddTests(*suite);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ SubDir HAIKU_TOP src tests apps haikudepot ;
|
|||||||
|
|
||||||
AddSubDirSupportedPlatforms libbe_test ;
|
AddSubDirSupportedPlatforms libbe_test ;
|
||||||
|
|
||||||
include [ FDirName $(HAIKU_TOP) src apps haikudepot build jam HdsSchemaGenRules ] ;
|
include [ FDirName $(HAIKU_TOP) src apps haikudepot build jam JsonSchemaGenRules ] ;
|
||||||
|
|
||||||
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot ] ;
|
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot ] ;
|
||||||
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot model ] ;
|
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot model ] ;
|
||||||
@@ -27,52 +27,50 @@ local sourceDir ;
|
|||||||
for sourceDir in $(sourceDirs) {
|
for sourceDir in $(sourceDirs) {
|
||||||
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src apps haikudepot $(sourceDir) ] ;
|
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src apps haikudepot $(sourceDir) ] ;
|
||||||
}
|
}
|
||||||
local dumpExportRepositoryBulkListenerTargetDirectory =
|
local genServerDumpExportRepositoryListenerTargetDirectory =
|
||||||
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
|
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY) dumpexportrepositorylistener ] ;
|
||||||
dumpexportrepositorybulklistener ] ;
|
local genServerDumpExportRepositoryModelTargetDirectory =
|
||||||
local dumpExportRepositoryModelTargetDirectory =
|
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY) dumpexportrepositorymodel ] ;
|
||||||
[ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY)
|
|
||||||
dumpexportrepositorymodel ] ;
|
|
||||||
SEARCH_SOURCE += $(dumpExportRepositoryBulkListenerTargetDirectory) ;
|
|
||||||
SEARCH_SOURCE += $(dumpExportRepositoryModelTargetDirectory) ;
|
|
||||||
|
|
||||||
local generatedRepositoryModelSourceFiles =
|
SEARCH_SOURCE += $(genServerDumpExportRepositoryListenerTargetDirectory) ;
|
||||||
DumpExportRepository.cpp
|
SEARCH_SOURCE += $(genServerDumpExportRepositoryModelTargetDirectory) ;
|
||||||
DumpExportRepositorySource.cpp
|
|
||||||
DumpExportRepositorySourceMirror.cpp
|
local genServerDumpExportRepositorySchema =
|
||||||
|
[ FDirName $(HAIKU_TOP) src apps haikudepot server schema dumpexportrepository.json ] ;
|
||||||
|
|
||||||
|
local genServerRepositoryModelSourceFiles =
|
||||||
|
DumpExportRepositoryModel.cpp
|
||||||
;
|
;
|
||||||
local generatedRepositoryParserSourceFiles =
|
local genServerRepositoryParserSourceFiles =
|
||||||
DumpExportRepositoryJsonListener.cpp
|
DumpExportRepositoryJsonListener.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
LOCATE on dumpexportrepository.json = [ FDirName $(HAIKU_TOP) src apps haikudepot server schema ] ;
|
local genDummyFile = "dummy.dat" ;
|
||||||
LOCATE on jsonschema2cppmodel.py = [ FDirName $(HAIKU_TOP) src apps haikudepot build scripts ] ;
|
|
||||||
LOCATE on jsonschema2cppparser.py = [ FDirName $(HAIKU_TOP) src apps haikudepot build scripts ] ;
|
|
||||||
|
|
||||||
local dummyFile = "dummy.dat" ;
|
local genServerDumpExportRepositoryListenerTargetFile =
|
||||||
|
[ FDirName $(genServerDumpExportRepositoryListenerTargetDirectory) $(genDummyFile) ] ;
|
||||||
|
local genServerDumpExportRepositoryModelTargetFile =
|
||||||
|
[ FDirName $(genServerDumpExportRepositoryModelTargetDirectory) $(genDummyFile) ] ;
|
||||||
|
|
||||||
local dumpExportRepositoryBulkListenerTargetFile =
|
JsonSchemaGenModel $(genServerDumpExportRepositoryModelTargetFile)
|
||||||
[ FDirName $(dumpExportRepositoryBulkListenerTargetDirectory) $(dummyFile) ] ;
|
: $(genServerDumpExportRepositorySchema) ;
|
||||||
local dumpExportRepositoryModelTargetFile =
|
|
||||||
[ FDirName $(dumpExportRepositoryModelTargetDirectory) $(dummyFile) ] ;
|
|
||||||
|
|
||||||
HdsSchemaGenModel $(dumpExportRepositoryModelTargetFile)
|
JsonSchemaGenParser $(genServerDumpExportRepositoryListenerTargetFile)
|
||||||
: dumpexportrepository.json : jsonschema2cppmodel.py ;
|
: $(genServerDumpExportRepositorySchema) ;
|
||||||
|
|
||||||
HdsSchemaGenBulkParser $(dumpExportRepositoryBulkListenerTargetFile)
|
JsonSchemaGenAppSrcDependsOnGeneration
|
||||||
: dumpexportrepository.json : jsonschema2cppparser.py ;
|
[ FGristFiles $(genServerRepositoryParserSourceFiles) ]
|
||||||
|
[ FGristFiles $(genServerRepositoryParserSourceFiles:S=.h) ]
|
||||||
|
: $(genServerDumpExportRepositoryListenerTargetDirectory)
|
||||||
|
: $(genServerDumpExportRepositoryListenerTargetFile)
|
||||||
|
;
|
||||||
|
|
||||||
HdsSchemaGenAppSrcDependsOnGeneration
|
JsonSchemaGenAppSrcDependsOnGeneration
|
||||||
[ FGristFiles $(generatedRepositoryParserSourceFiles) ]
|
[ FGristFiles $(genServerRepositoryModelSourceFiles) ]
|
||||||
[ FGristFiles $(generatedRepositoryParserSourceFiles:S=.h) ]
|
[ FGristFiles $(genServerRepositoryModelSourceFiles:S=.h) ]
|
||||||
: $(dumpExportRepositoryBulkListenerTargetDirectory)
|
: $(genServerDumpExportRepositoryModelTargetDirectory)
|
||||||
: $(dumpExportRepositoryBulkListenerTargetFile) ;
|
: $(genServerDumpExportRepositoryModelTargetFile)
|
||||||
|
;
|
||||||
HdsSchemaGenAppSrcDependsOnGeneration
|
|
||||||
[ FGristFiles $(generatedRepositoryModelSourceFiles) ]
|
|
||||||
[ FGristFiles $(generatedRepositoryModelSourceFiles:S=.h) ]
|
|
||||||
: $(dumpExportRepositoryModelTargetDirectory)
|
|
||||||
: $(dumpExportRepositoryModelTargetFile) ;
|
|
||||||
|
|
||||||
UnitTestLib haikudepottest.so :
|
UnitTestLib haikudepottest.so :
|
||||||
HaikuDepotTestAddon.cpp
|
HaikuDepotTestAddon.cpp
|
||||||
@@ -80,9 +78,10 @@ UnitTestLib haikudepottest.so :
|
|||||||
DataIOUtils.cpp
|
DataIOUtils.cpp
|
||||||
DataIOUtilsTest.cpp
|
DataIOUtilsTest.cpp
|
||||||
|
|
||||||
$(generatedRepositoryModelSourceFiles)
|
$(genServerRepositoryModelSourceFiles)
|
||||||
$(generatedRepositoryParserSourceFiles)
|
$(genServerRepositoryParserSourceFiles)
|
||||||
DumpExportRepositoryJsonListenerTest.cpp
|
DumpExportRepositoryJsonListenerTest.cpp
|
||||||
|
DumpExportRepositoryTest.cpp
|
||||||
|
|
||||||
JwtTokenHelper.cpp
|
JwtTokenHelper.cpp
|
||||||
JwtTokenHelperTest.cpp
|
JwtTokenHelperTest.cpp
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2024, Andrew Lindesay <[email protected]>.
|
* Copyright 2024-2026, Andrew Lindesay <[email protected]>.
|
||||||
* All rights reserved. Distributed under the terms of the MIT License.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ LocaleUtilsTest::TestLanguageIsBeforeFalseAfter()
|
|||||||
LanguageRef languageDeCh = LanguageRef(new Language("de_CH", "German (Swiss)", true), true);
|
LanguageRef languageDeCh = LanguageRef(new Language("de_CH", "German (Swiss)", true), true);
|
||||||
|
|
||||||
// ----------------------
|
// ----------------------
|
||||||
bool actual = languageZh < languageDeCh;
|
bool actual = (*(languageZh.Get()) < *(languageDeCh.Get()));
|
||||||
// ----------------------
|
// ----------------------
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(false, actual);
|
CPPUNIT_ASSERT_EQUAL(false, actual);
|
||||||
@@ -48,7 +48,7 @@ LocaleUtilsTest::TestLanguageIsBeforeFalseEqual()
|
|||||||
LanguageRef languageDeCh = LanguageRef(new Language("de_CH", "German (Swiss)", true), true);
|
LanguageRef languageDeCh = LanguageRef(new Language("de_CH", "German (Swiss)", true), true);
|
||||||
|
|
||||||
// ----------------------
|
// ----------------------
|
||||||
bool actual = languageDeCh < languageDeCh;
|
bool actual = (*(languageDeCh.Get()) < *(languageDeCh.Get()));
|
||||||
// ----------------------
|
// ----------------------
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(false, actual);
|
CPPUNIT_ASSERT_EQUAL(false, actual);
|
||||||
@@ -62,7 +62,7 @@ LocaleUtilsTest::TestLanguageIsBeforeTrueBefore()
|
|||||||
LanguageRef languageDeCh = LanguageRef(new Language("de_CH", "German (Swiss)", true), true);
|
LanguageRef languageDeCh = LanguageRef(new Language("de_CH", "German (Swiss)", true), true);
|
||||||
|
|
||||||
// ----------------------
|
// ----------------------
|
||||||
bool actual = languageDeCh < languageZh;
|
bool actual = (*(languageDeCh.Get()) < *(languageZh.Get()));
|
||||||
// ----------------------
|
// ----------------------
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(true, actual);
|
CPPUNIT_ASSERT_EQUAL(true, actual);
|
||||||
|
|||||||
Reference in New Issue
Block a user