diff --git a/.gitignore b/.gitignore index 332d760..ef6a3cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ .idea +*.log .gradle build tmp classes out *.swp +pushLatest.bat +src/main/groovy/de/lisaplus/atlas/DummyGenerator.groovy +src/main/groovy/de/lisaplus/atlas/TestStarter.groovy diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ee4bc57 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +FROM mcr.microsoft.com/java/jdk:11u3-zulu-alpine as build + +RUN ["jlink", \ + "--compress=2", \ + "--strip-debug", \ + "--no-header-files", \ + "--no-man-pages", \ + "--module-path", "/usr/lib/jvm/zulu-11-azure-jdk_11.31.11-11.0.3-linux_musl_x64/jmods", \ + "--add-modules", "java.base,java.logging,java.desktop,java.naming,java.prefs,java.xml,java.scripting,java.sql", \ + "--output", "/custom_jre"] + +FROM alpine:latest +COPY --from=build /custom_jre /opt/jre/ + +RUN ln -s /opt/jre/bin/java /usr/bin/ + +ADD build/release /opt/jsonCodeGen/ + +ENTRYPOINT ["/opt/jsonCodeGen/jsonCodeGen.sh"] + +CMD ["--help"] \ No newline at end of file diff --git a/README.md b/README.md index 5e701ad..8826ec6 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A simple Groovy based program to do generation tasks from a JSON schema. ## Requirements * Java 8 -* Gradle v4.* +* Gradle v5.4* ## Unsupported JSON schema features * patternProperties - make no sense in model description @@ -52,8 +52,6 @@ A simple Groovy based program to do generation tasks from a JSON schema. ## Handle with gradle ### Using with gradle ```bash -# do a complete release to configured maven repository -gradle publish # builds a release with all dependencies # release is built in PROJECT_DIR/build/release @@ -80,6 +78,53 @@ gradle myRun -PmyArgs="-o,/tmp/test_beans,-m,src/test/resources/test_schemas/mul -gp,destFileNameExt=java,-gp,packageName=de.sw.atlas.test" ``` ### Usage of the release + +```bash +./jsonCodeGen.sh +Usage: de.lisaplus.atlas.DoCodeGen [options] + Options: + -at, --add-tag + add a text as tag to a specific type, f.e. -at User=unused + Default: [] + -b, --black-list + black listed type, multiple usage possible + Default: [] + -g, --generator + generator that are used with the model. This parameter can be used + multiple times + Default: [] + -gp, --generator-parameter + special parameter that are passed to template via maps + Default: [] + -h, --help + + * -m, --model + Path to JSON schema to parse + -o, --outputBase + Base directory for the output + -pmt, --print-main-types + don't do any code generation, simply loads the model and print the + main-types of it + Default: false + -pmta, --print-main-types-attrib + don't do any code generation, simply loads the model and print the + main-types of it + -pmti, --print-main-types-info + print with info header + Default: false + -pmts, --print-main-types-separator + separator to use for printing main types + -rt, --remove-tag + remove a tag from a specific type, f.e. -rt User=unused + Default: [] + -rta, --remove-tag-all + remove a tag from all model types, f.e. -rta rest + Default: [] + -w, --white-list + white listed type, multiple usage possible + Default: [] +``` + After you built a release with gradle or you download a release bundle you can start the program with the contained start script. If you start it with the help option you get a full description of the possible parameters @@ -107,3 +152,46 @@ basic design. ## Usage [see here](src/main/resources/docs/usage.md) + +## Releases +[see here](Releases.md) + +# Template Debugging +## General Remarks +From version 0.13.0 it is also possible to debug code from templates. This is +a common use case if the templates become more complicated. Unfortunately is it +not possible to debug code that is included direct into the template, but +with the '-gs' command line switch a external Groovy script can be injected into +the code generation templates. + +If the switch is used, then inside the template is a 'script' variable available. +This variable points to the injected script and allows to call functions that +are declared as clojure inside the generator-script. + +```Groovy +// example usage of a generator script defined function +${script.generatorScriptDefinedFunction(possibleParameter)} +``` + +```Groovy +// example function definition in a generator script +def generatorScriptDefinedFunction(def someString) { + return "Hello: $someString" +} +``` +## Code Examples +* [example additional generator script](./src/test/resources/templates/handling_helper.groovy) +* [example template that utilize the script](./src/test/resources/templates/handling.txt) +* [test that combines both](./src/test/groovy/de/lisaplus/atlas/codegen/test/MultiFileTemplates.groovy) +* [test in a shell script](./bin/debug_example.sh) + +## Steps to Debug template in IntelliJ +1. Open the jsonCodeGen project in IntelliJ +2. Open the generator library in the editor (example: `./src/test/resources/templates/handling_helper.groovy`) +3. Set a break point +4. Configure remote debugging in IntelliJ listen on port 8100 +5. Run `bin/debug_example.sh` from command line +6. Start remote debugging in IntelliJ +7. Break-Point should be triggered in IDE + +This approach can also be used for other projects. diff --git a/Releases.md b/Releases.md new file mode 100644 index 0000000..439f9be --- /dev/null +++ b/Releases.md @@ -0,0 +1,66 @@ +## 0.13.1 +- handle the right notation of external references: e.g. ./object_base.json#/definitions/ObjectBase + + (Attention, the wrong reference style before that version, is still working) +## 0.8.4 +- add additional generator parameters to improve plantuml view (ignoreAttribs,ignoreRefs,ignoreImplicitRefs, ignoreCompositions) + +## 0.8.3 +- ignoreTag, neededTag are handled now as lists for multi-file-generators +- rename 'pmta' command line switch to specify a required attribute for main types to 'mta' +- add 'tmt' command line switch to add automatically a 'mainType' tag to all main types + +## 0.8.2 +- needed fix in 'pmta' command line switch + +## 0.8.1 +- add to types schemaPath and schemaFileName attributes (to finally detect what are the interesting main types of a model) +- add a isMainType function to model type +- add command line switches pro print only main types + +## 0.8.0 +- command line switches for type black- and white-lists +- command line switch to add tags to types +- command line switch to remove a tag from types +- command line switch to remove a tag from all types +- enforce camel-case type names in models +- add guidTypeColor generator parameter for plantuml +- remove printTags generator parameter for plantuml +- add printTypeTags generator parameter for plantuml +- add printPropTags generator parameter for plantuml + +## 0.7.5 +- deep copy functions for types +- introduce pure array type to handle definitions where arrays contain only arrays, f.e. geoJson Polygons + +```json +... + "FeatureAreaGeometry": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + } + } + } + }, + "projection": { + "type": "string" + } + }, + "__tags": ["additional"] + }, +... +``` + +## 0.7.4 +- add attributes `selfReference` and `selfContainment` to model property type \ No newline at end of file diff --git a/bin/debug_example.sh b/bin/debug_example.sh new file mode 100755 index 0000000..b28e19b --- /dev/null +++ b/bin/debug_example.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Example script to illustrate the template debugging option of jsonCodeGen + +scriptPos=${0%/*} + +debugStarterScript=$scriptPos/../build/release/jsonCodeGenDebug.sh +if ! [ -f "$debugStarterScript" ]; then + echo "debug starter script not found: $debugStarterScript" + echo "Maybe release is not created. Try to create a release at first and re-run the script" + echo "gradle clean buildRelease" + exit 1 +fi + +$debugStarterScript -g "multifiles=$scriptPos/../src/test/resources/templates/handling.txt" \ + -gs "$scriptPos/../src/test/resources/templates/handling_helper.groovy" \ + -m "$scriptPos/../src/test/resources/test_schemas/ds/incident.json" \ + -o "$scriptPos/../tmp/handling" \ + -gp "packageName=de.debug" + + diff --git a/bin/uploadReleaseToNexus.sh b/bin/uploadReleaseToNexus.sh new file mode 100755 index 0000000..f94f979 --- /dev/null +++ b/bin/uploadReleaseToNexus.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +scriptPos=${0%/*} + +# retrieve the version from project file +version=`cat "$scriptPos/../build.gradle" | grep project.version | grep = | sed -e "s-.* '--" -e "s-'--"` +echo "version: $version" + +pushd "$scriptPos/.." > /dev/null +if ! gradle publish; then + echo "error while publish jars to nexus" + popd > /dev/null +fi +popd > /dev/null + +if [ -z "$NEXUS_USER" ]; then + echo "NEXUS_USER not defined" + exit 1 +fi + +if [ -z "$NEXUS_RAW_ARCHIVE" ]; then + echo "NEXUS_RAW_ARCHIVE not defined" + exit 1 +fi + +pushd "$scriptPos/.." > /dev/null +if ! gradle clean build; then + echo 'error while build' + exit 1 +fi + +if ! gradle buildRelease; then + echo 'error while build release' + exit 1 +fi + +cd build/release + + +releaseFile="jsonCodeGen_$version.tgz" + +if [ -f "$releaseFile" ]; then + rm -f "$releaseFile" +fi + +tar -czf "$releaseFile" * + +if ! curl -v --user "$NEXUS_USER" --upload-file "$releaseFile" \ + "$NEXUS_RAW_ARCHIVE/$releaseFile" +then + echo "error while upload release to nexus raw repo" +fi + +git tag "$version" + +popd > /dev/null + diff --git a/build.gradle b/build.gradle index 710cbd9..200f04e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,54 +1,33 @@ apply plugin: 'groovy' apply plugin: 'java' -/* apply plugin: 'maven' -*/ +apply plugin: 'java-library' apply plugin: 'maven-publish' -sourceCompatibility = 1.8 +sourceCompatibility = 8 def mainClass='de.lisaplus.atlas.DoCodeGen' project.group = 'de.lisaplus.tools' -project.version = '0.4' -def releaseArchiveName = "jsonCodeGen_${project.version}.zip" -/* - * the following attributes need to be declared in $HOME/.gradle/gradle.properties - mavenReleaseRepo=http://localhost:8081/content/repositories/releases - mavenSnapshotRepo=http://localhost:8081/content/repositories/snapshots - mavenUser=admin - mavenUserPwd=admin123 - mavenBaseRepo=http://localhost:8081/content/groups/public - **/ -publishing { - publications { - libraryJar(MavenPublication) { - from components.java - artifact zipRelease - } - } - repositories { - maven { - url "${project.version.endsWith('-SNAPSHOT') ? mavenSnapshotRepo : mavenReleaseRepo }" - credentials { - username = "${mavenUser}" - password = "${mavenUserPwd}" - } - } - } +project.version = '0.13.2' + + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allJava +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc.destinationDir } repositories { - mavenLocal() - maven { - url "${mavenBaseRepo}" - } mavenCentral() } artifacts { archives jar - archives file: new File("$releaseArchiveName"), name: 'jsonCodeGen', type: 'zip' } dependencies { @@ -56,7 +35,8 @@ dependencies { compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3' compile group: 'com.beust', name: 'jcommander', version: '1.69' compile 'org.apache.commons:commons-lang3:3.3' - compile 'org.codehaus.groovy:groovy-all:2.3.11' + compile 'org.codehaus.groovy:groovy-all:2.5.7' + compile group: 'org.apache.xmlbeans', name: 'xmlbeans', version: '3.0.0' testCompile group: 'junit', name: 'junit', version: '4.12' } @@ -70,26 +50,34 @@ task dependenciesToLibDir(type: Copy) { } task buildRelease (type: Copy) { - outputs.upToDateWhen { false } - into "${buildDir}/release/lib" - from "${buildDir}/libs" + println 'buildRelease start ...' + outputs.upToDateWhen { false } - copy { - from "${project.rootDir}/src/main/resources/bin" - into "${buildDir}/release" - } - copy { - from "${project.rootDir}/src/main/resources/conf" - into "${buildDir}/release/conf" - } + copy { + from "${project.rootDir}/src/main/resources/bin" + into "${buildDir}/release" + } + copy { + from "${project.rootDir}/src/main/resources/conf" + into "${buildDir}/release/conf" + } + + + into "${buildDir}/release/lib" + from "${buildDir}/libs" + + println 'buildRelease end ...' } task zipRelease (type: Zip) { - from "${buildDir}/release" - include '*' - include '*/*' - archiveName "$releaseArchiveName" - destinationDir(file("${buildDir}")) + doLast { + println 'zipRelease' + from "${buildDir}/release" + include '*' + include '*/*' + archiveName "$releaseArchiveName" + destinationDir(file("${buildDir}")) + } } task myRun (type: JavaExec, dependsOn: classes){ @@ -107,7 +95,6 @@ task myRun (type: JavaExec, dependsOn: classes){ dependenciesToLibDir.dependsOn jar buildRelease.dependsOn dependenciesToLibDir - configurations { integrationTestCompile.extendsFrom testCompile integrationTestRuntime.extendsFrom testRuntime @@ -139,40 +126,29 @@ task integrationTest(type: Test) { outputs.upToDateWhen { false } } -task copyReleaseToDockerImage (type: Copy) { - outputs.upToDateWhen { false } - into "docker/image/release" - from "${buildDir}/release" -} - -task cleanImageSources (type: Delete) { - delete 'docker/image/release' -} - task buildDockerImage { outputs.upToDateWhen { false } - def imageName=['bash','docker/bin/image_conf.sh'].execute().text.trim() - println "create image: $imageName ..." - def cmd = ['docker', 'build', '-t', imageName,'docker/image'] - def sout = new StringBuilder(), serr = new StringBuilder() - def proc = cmd.execute() - proc.consumeProcessOutput(sout, serr) - proc.waitForOrKill(10000) - if (serr.size()>0) { - println "err> $serr" - } - else { - println "... created" + doLast { + def imageName=['bash','docker/bin/image_conf.sh'].execute().text.trim() + println "create image: $imageName ..." + def cmd = ['docker', 'build', '-t', imageName,'.'] + def sout = new StringBuilder(), serr = new StringBuilder() + def proc = cmd.execute() + proc.consumeProcessOutput(sout, serr) + proc.waitForOrKill(20000) + if (serr.size()>0) { + println "err> $serr" + } + else { + println "... created" + } } } -buildRelease.dependsOn build -cleanImageSources.dependsOn buildRelease -copyReleaseToDockerImage.dependsOn cleanImageSources -buildDockerImage.dependsOn copyReleaseToDockerImage +buildRelease.dependsOn assemble +buildDockerImage.dependsOn buildRelease check.dependsOn integrationTest integrationTest.mustRunAfter test buildRelease.dependsOn test zipRelease.dependsOn buildRelease - diff --git a/docker/bin/image_conf.sh b/docker/bin/image_conf.sh index 334787c..2d7c3ba 100755 --- a/docker/bin/image_conf.sh +++ b/docker/bin/image_conf.sh @@ -1,5 +1,9 @@ imageBase=schlothauer/jsoncodegen -imageTag=0.4.1 +imageTag=0.11.3 + +if [ -f build.gradle ]; then + imageTag=`cat build.gradle | grep project.version | awk '{ print $3 }' | sed -e "s-'--g"` +fi imageName="$imageBase:$imageTag" diff --git a/docker/image/release/jsonCodeGen.sh b/docker/image/release/jsonCodeGen.sh index f661d42..5ae39ad 100755 --- a/docker/image/release/jsonCodeGen.sh +++ b/docker/image/release/jsonCodeGen.sh @@ -26,6 +26,15 @@ if [ ! -x "$JAVACMD" ] ; then exit 1 fi +case $OSTYPE in + "cygwin"|"msys"|"win32") + pathSep=";" + ;; + *) + pathSep=":" + ;; +esac + args= for arg in "$@"; do @@ -40,4 +49,13 @@ else LOGDIR="$JSONCODEGEN_HOME/conf" fi -$JAVACMD -cp "$JSONCODEGEN_LIB_DIR/*" "-Dlogback.configurationFile=$scriptPos/conf/logback.xml\" de.lisaplus.atlas.DoCodeGen $args \ No newline at end of file +# preset for defining Java classes for DateType and DateTimeType, see JavaTypeConvert.groovy +# Possible values are legacy,310.local,310.offset and 310.zoned +preset=310.local + +if [ -z "$ADDITIONAL_TEMPLATE_DIR" ]; then + "$JAVACMD" -cp "$JSONCODEGEN_LIB_DIR/*" "-Dlogback.configurationFile=$scriptPos/conf/logback.xml" "-Ddate.type.preset=$preset" de.lisaplus.atlas.DoCodeGen $args +else + # an additional direcotry with custom templates is give - needed for sub templates + "$JAVACMD" -cp "$JSONCODEGEN_LIB_DIR/*$pathSep$ADDITIONAL_TEMPLATE_DIR" "-Dlogback.configurationFile=$scriptPos/conf/logback.xml" "-Ddate.type.preset=$preset" de.lisaplus.atlas.DoCodeGen $args +fi diff --git a/docker/image/release/lib/jsonCodeGen-0.11.3.jar b/docker/image/release/lib/jsonCodeGen-0.11.3.jar new file mode 100644 index 0000000..f7e819a Binary files /dev/null and b/docker/image/release/lib/jsonCodeGen-0.11.3.jar differ diff --git a/docker/image/release/lib/jsonCodeGen-0.4.jar b/docker/image/release/lib/jsonCodeGen-0.4.jar deleted file mode 100644 index 3339909..0000000 Binary files a/docker/image/release/lib/jsonCodeGen-0.4.jar and /dev/null differ diff --git a/docker/image/release/lib/stax-api-1.0.1.jar b/docker/image/release/lib/stax-api-1.0.1.jar new file mode 100644 index 0000000..d9a1665 Binary files /dev/null and b/docker/image/release/lib/stax-api-1.0.1.jar differ diff --git a/docker/image/release/lib/xmlbeans-3.0.0.jar b/docker/image/release/lib/xmlbeans-3.0.0.jar new file mode 100644 index 0000000..a6eeb2d Binary files /dev/null and b/docker/image/release/lib/xmlbeans-3.0.0.jar differ diff --git a/docs/main_structure.puml b/docs/main_structure.puml index 66bb555..06b2a66 100644 --- a/docs/main_structure.puml +++ b/docs/main_structure.puml @@ -78,14 +78,17 @@ class Property { } BaseType <|-- BooleanType +BaseType <|-- ByteType BaseType <|-- MinMaxType BaseType <|-- ComplexType MinMaxType <|-- DateType MinMaxType <|-- DateTimeType MinMaxType <|-- IntType +MinMaxType <|-- LongType MinMaxType <|-- NumberType BaseType <|-- RefType BaseType <|-- StringType +BaseType <|-- ArrayType BaseType <|-- UnsupportedType Property o-- BaseType diff --git a/junctionModel.puml b/junctionModel.puml new file mode 100644 index 0000000..77e7b00 --- /dev/null +++ b/junctionModel.puml @@ -0,0 +1,1093 @@ + +@startuml +skinparam roundcorner 10 +skinparam class { + BackgroundColor #FFFFFF + ArrowColor #000000 + BorderColor #000000 + BorderColor<> #777777 + BackgroundColor<> #EEEEEE + BackgroundColor<> #e4ffd4 + FontName Courier + FontSize 12 +} + +skinparam note { + BackgroundColor #dedede + BorderColor #000000 + FontSize 10 +} + +skinparam classAttribute { + FontName Courier + FontSize 12 +} + +class JunctionLocationStreetsItem <> << ( ,#ff0000) >> { + -classification: string + .. + -entryId: string/uuid + .. + -main: boolean + .. + -name: string + .. + -position: number +} + +class JunctionLocation <> << ( ,#ff00ff) >> { + -city: string + .. + -country: string + .. + -county: string + .. + -district: string + .. + -streets: JunctionLocationStreetsItem[] +} + +class Junction <> << ( ,#ff0000) >> { + -guid: string/uuid + .. + -location: JunctionLocation + .. + -objectBaseId: string/uuid + .. + -startupDate: string/date + .. + -state: ListEntry + .. + -tenantId: string/uuid + .. + -type: ListEntry +} + +note top of Junction +Junction is +a anchor point +of interest +of application +level +end note +class JunctionComment <> << ( ,#007f00) >> { + -date: string/date-time + .. + -text: string + .. + -user: string + .. + -guid: string/uuid + .. + -junctionId: string/uuid + .. + -tenantId: string/uuid +} + +note top of JunctionComment +Junction is +a anchor point +of interest +of application +level +end note +class JunctionContact <> << ( ,#00007f) >> { + -address: Address + .. + -type: ListEntry + .. + -guid: string/uuid + .. + -junctionId: string/uuid + .. + -tenantId: string/uuid +} + +note top of JunctionContact +Junction is +a anchor point +of interest +of application +level +end note +class JunctionDocument <> << ( ,#ff00ff) >> { + -date: string + .. + -mimeType: string + .. + -name: string + .. + -originalName: string + .. + -previewUrl: string + .. + -smallPreviewUrl: string + .. + -thumbUrl: string + .. + -type: ListEntry + .. + -url: string + .. + -user: string + .. + -guid: string/uuid + .. + -junctionId: string/uuid + .. + -tenantId: string/uuid +} + +note top of JunctionDocument +Junction is +a anchor point +of interest +of application +level +end note +class JunctionJoined <> << ( ,#0000ff) >> { + -guid: string/uuid + .. + -location: JunctionLocation + .. + -objectBaseId: string/uuid + .. + -startupDate: string/date + .. + -state: ListEntry + .. + -tenantId: string/uuid + .. + -type: ListEntry + .. + -objectBase: ObjectBase +} + +note top of JunctionJoined +Junction with +joined ObjectBase +end note +class JunctionNumber <> << ( ,#007f00) >> { + -guid: string/uuid + .. + -junctionId: string/uuid + .. + -number: string + .. + -tenantId: string/uuid + .. + -type: ListEntry +} + +note top of JunctionNumber +Junction is +a anchor point +of interest +of application +level +end note +class JunctionNumberType <> << ( ,#7f007f) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of JunctionNumberType +Technical identifier +for that junction +end note +class JunctionState <> << ( ,#ff6100) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of JunctionState +state of junction +end note +class JunctionType <> << ( ,#00007f) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of JunctionType +types of junctions +end note +class GeoArea << ( ,#007f00) >> { + -points: GeoPoint[] + .. + -projection: string +} + +note top of GeoArea +Area geo-type +end note +class GeoPoint << ( ,#ff6100) >> { + -lon: number + .. + -lat: number + .. + -projection: string +} + +note top of GeoPoint +Single point +geo-type +end note +class ObjectBase <> << ( ,#007f00) >> { + -gis: ObjectBaseGis + .. + -guid: string/uuid + .. + -name: string + .. + -number: string + .. + -objectGroup: ListEntry + .. + -regions: string/uuid[] + .. + -tags: ListEntry[] + .. + -tenantId: string/uuid +} + +note top of ObjectBase +Object is a +anchor point +of interest +of application +level, that's +the minimal +information +about that +end note +class GeoMultiline << ( ,#ff0000) >> { + -points: GeoPoint[] + .. + -projection: string +} + +note top of GeoMultiline +Multiline geo-type +end note +class ListEntry << ( ,#0000ff) >> { + -refId: string + .. + -text: string +} + +note top of ListEntry +type for entries +that have references +to look-up +tables - as +an more dynamic +alternative +to enums +end note +class Region <> << ( ,#00007f) >> { + -active: boolean + .. + -comment: string + .. + -created: string/date-time + .. + -group: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -tenantId: string/uuid +} + +note top of Region +A region is +some kind of +a logical group +that could +be set for +some entries. +It allows 'vertical' +restrictions +of object visibility +end note +class Tenant <> << ( ,#7f007f) >> { + -active: boolean + .. + -description: string + .. + -guid: string/uuid + .. + -name: string +} + +note top of Tenant +Tenant of a +running application. +end note +class Tag <> << ( ,#5f005f) >> { + -active: boolean + .. + -color: string + .. + -comment: string + .. + -created: string/date-time + .. + -group: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -tenantId: string/uuid +} + +note top of Tag +A tag is some +kind of a global +keyword that +could be set +for some entries +end note +class ObjectBaseGis <> << ( ,#9b0000) >> { + -area: GeoArea + .. + -center: GeoPoint + .. + -route: GeoMultiline +} + +class ObjectGroup <> << ( ,#9b0000) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of ObjectGroup +Groups of objects +(f.e. intersections, +INES nets, +public transport +route, ...) +end note +class Comment << ( ,#0000ff) >> { + -date: string/date-time + .. + -text: string + .. + -user: string +} + +note top of Comment +A comment for +an data entry +end note +class Contact << ( ,#ff6100) >> { + -address: Address + .. + -type: ListEntry +} + +note top of Contact +contact to +a specific +entity +end note +class Address << ( ,#007f00) >> { + -city: string + .. + -contact: ContactData + .. + -country: string + .. + -department: string + .. + -extra: string + .. + -name: string + .. + -persons: AddressPerson[] + .. + -postalCode: string + .. + -street: string + .. + -type: ListEntry + .. + -web: string +} + +class ContactData << ( ,#9b0000) >> { + -email: string[] + .. + -fax: string + .. + -phone: string[] +} + +note top of ContactData +contact data +for a person +or address +end note +class Person << ( ,#ff00ff) >> { + -active: boolean + .. + -addressId: string/uuid + .. + -contact: ContactData + .. + -firstName: string + .. + -name: string + .. + -title: string +} + +note top of Person +simple person +definition +end note +class AddressPerson << ( ,#5f005f) >> { + -active: boolean + .. + -addressId: string/uuid + .. + -contact: ContactData + .. + -firstName: string + .. + -name: string + .. + -title: string + .. + -entryId: string/uuid +} + +note top of AddressPerson +a person that +is linked to +an address +end note +class AddressType <> << ( ,#ff0000) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of AddressType +type of a document +end note +class ContactType <> << ( ,#ff6100) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of ContactType +type of a contact +end note +class Document << ( ,#9b0000) >> { + -date: string + .. + -mimeType: string + .. + -name: string + .. + -originalName: string + .. + -previewUrl: string + .. + -smallPreviewUrl: string + .. + -thumbUrl: string + .. + -type: ListEntry + .. + -url: string + .. + -user: string +} + +note top of Document +description +of a contained +document +end note +class DocumentType <> << ( ,#ff0000) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of DocumentType +type of a document +end note +class Incident <> << ( ,#0000ff) >> { + -description: string + .. + -guid: string/uuid + .. + -number: IncidentNumber + .. + -objectBaseId: string/uuid + .. + -opMessages: string/uuid[] + .. + -rawData: IncidentRawData + .. + -references: string/uuid[] + .. + -states: IncidentState[] + .. + -tags: ListEntry[] + .. + -tenantId: string/uuid + .. + -type: ListEntry + .. + -urgency: integer +} + +note top of Incident +A detected +failure state +that needs +some actions +to solve it +end note +class IncidentComment <> << ( ,#ff6100) >> { + -date: string/date-time + .. + -text: string + .. + -user: string + .. + -guid: string/uuid + .. + -incidentId: string/uuid + .. + -tenantId: string/uuid +} + +note top of IncidentComment +Comment entries +to incidents +end note +class IncidentDocument <> << ( ,#ff0000) >> { + -date: string + .. + -mimeType: string + .. + -name: string + .. + -originalName: string + .. + -previewUrl: string + .. + -smallPreviewUrl: string + .. + -thumbUrl: string + .. + -type: ListEntry + .. + -url: string + .. + -user: string + .. + -guid: string/uuid + .. + -incidentId: string/uuid + .. + -tenantId: string/uuid +} + +note top of IncidentDocument +documents linked +to incident +entries +end note +class IncidentJoined <> << ( ,#ff00ff) >> { + -description: string + .. + -guid: string/uuid + .. + -number: IncidentNumber + .. + -objectBaseId: string/uuid + .. + -opMessages: string/uuid[] + .. + -rawData: IncidentRawData + .. + -references: string/uuid[] + .. + -states: IncidentState[] + .. + -tags: ListEntry[] + .. + -tenantId: string/uuid + .. + -type: ListEntry + .. + -urgency: integer + .. + -objectBase: ObjectBase +} + +note top of IncidentJoined +Incident with +joined ObjectBase +end note +class IncidentNumber << ( ,#0000ff) >> { + -external: string + .. + -internal: string +} + +class IncidentRawData << ( ,#007f00) >> { + -data: string + .. + -mimeType: string + .. + -type: string +} + +class IncidentState << ( ,#7f007f) >> { + -assignTo: string + .. + -endComment: string + .. + -endDate: string/date-time + .. + -endedBy: string + .. + -entryId: string/uuid + .. + -startComment: string + .. + -startDate: string/date-time + .. + -startedBy: string + .. + -stateType: ListEntry +} + +class IncidentStateType <> << ( ,#ff6100) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -incidentTypeId: string/uuid[] + .. + -locked: boolean + .. + -name: string + .. + -order: boolean + .. + -progress: string + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of IncidentStateType +occurs, entered, +resolved, ... +end note +class IncidentTag <> << ( ,#00007f) >> { + -active: boolean + .. + -color: string + .. + -comment: string + .. + -created: string/date-time + .. + -group: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -tenantId: string/uuid +} + +note top of IncidentTag +key-word for +additional +incident tagging +end note +class IncidentType <> << ( ,#007f00) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -objectGroupIds: string/uuid[] + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of IncidentType +types of incidents +end note +class OpMessage <> << ( ,#ff6100) >> { + -creationTime: string/date-time + .. + -eventTimeEnd: string/date-time + .. + -eventTimeStart: string/date-time + .. + -guid: string/uuid + .. + -level: string + .. + -msgCode: integer + .. + -msgResStr: string + .. + -msgText: string + .. + -objectBaseId: string/uuid + .. + -rawData: OpMessageRawData + .. + -references: string/uuid[] + .. + -sender: ListEntry + .. + -tags: ListEntry[] + .. + -tenantId: string/uuid + .. + -type: ListEntry +} + +note top of OpMessage +General system +operation message +end note +class OpMessageJoined <> << ( ,#00007f) >> { + -creationTime: string/date-time + .. + -eventTimeEnd: string/date-time + .. + -eventTimeStart: string/date-time + .. + -guid: string/uuid + .. + -level: string + .. + -msgCode: integer + .. + -msgResStr: string + .. + -msgText: string + .. + -objectBaseId: string/uuid + .. + -rawData: OpMessageRawData + .. + -references: string/uuid[] + .. + -sender: ListEntry + .. + -tags: ListEntry[] + .. + -tenantId: string/uuid + .. + -type: ListEntry + .. + -objectBase: ObjectBase +} + +note top of OpMessageJoined +OpMessage with +joined ObjectBase +end note +class OpMessageRawData << ( ,#007f00) >> { + -data: string + .. + -mimeType: string + .. + -type: string +} + +note top of OpMessageRawData +container for +additional, +app-specific +content +end note +class OpMessageSender <> << ( ,#9b0000) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of OpMessageSender +Who sends the +message +end note +class OpMessageTag <> << ( ,#5f005f) >> { + -active: boolean + .. + -color: string + .. + -comment: string + .. + -created: string/date-time + .. + -group: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -tenantId: string/uuid +} + +note top of OpMessageTag +Tag to group +messages +end note +class OpMessageType <> << ( ,#ff0000) >> { + -active: boolean + .. + -comment: string + .. + -guid: string/uuid + .. + -locked: boolean + .. + -name: string + .. + -resStr: string + .. + -tenantId: string/uuid +} + +note top of OpMessageType +types of message +end note +hide methods +JunctionLocation *-- "many" JunctionLocationStreetsItem #ff0000 +Junction *-- JunctionLocation #ff00ff + Junction .. ObjectBase #007f00 +Junction *-- ListEntry #0000ff + Junction .. JunctionState #ff6100 + Junction .. Tenant #7f007f + Junction .. JunctionType #00007f + JunctionComment .. Junction #ff0000 + JunctionComment .. Tenant #7f007f +JunctionContact *-- Address #007f00 +JunctionContact *-- ListEntry #0000ff + JunctionContact .. ContactType #ff6100 + JunctionContact .. Junction #ff0000 + JunctionContact .. Tenant #7f007f +JunctionDocument *-- ListEntry #0000ff + JunctionDocument .. DocumentType #ff0000 + JunctionDocument .. Junction #ff0000 + JunctionDocument .. Tenant #7f007f +JunctionJoined *-- JunctionLocation #ff00ff + JunctionJoined .. ObjectBase #007f00 +JunctionJoined *-- ListEntry #0000ff + JunctionJoined .. JunctionState #ff6100 + JunctionJoined .. Tenant #7f007f + JunctionJoined .. JunctionType #00007f +JunctionJoined *-- ObjectBase #007f00 + JunctionNumber .. Junction #ff0000 + JunctionNumber .. Tenant #7f007f +JunctionNumber *-- ListEntry #0000ff + JunctionNumber .. JunctionNumberType #7f007f + JunctionNumberType .. Tenant #7f007f + JunctionState .. Tenant #7f007f + JunctionType .. Tenant #7f007f +GeoArea *-- "many" GeoPoint #ff6100 +ObjectBase *-- ObjectBaseGis #9b0000 +ObjectBase *-- ListEntry #0000ff + ObjectBase .. ObjectGroup #9b0000 + ObjectBase .. "many" Region #00007f + ObjectBase .. "many" Tag #5f005f + ObjectBase .. Tenant #7f007f +GeoMultiline *-- "many" GeoPoint #ff6100 + Region .. Tenant #7f007f + Tag .. Tenant #7f007f +ObjectBaseGis *-- GeoArea #007f00 +ObjectBaseGis *-- GeoPoint #ff6100 +ObjectBaseGis *-- GeoMultiline #ff0000 + ObjectGroup .. Tenant #7f007f +Contact *-- Address #007f00 +Contact *-- ListEntry #0000ff + Contact .. ContactType #ff6100 +Address *-- ContactData #9b0000 +Address *-- "many" AddressPerson #5f005f +Address *-- ListEntry #0000ff + Address .. AddressType #ff0000 +Person *-- ContactData #9b0000 +AddressPerson *-- ContactData #9b0000 + AddressType .. Tenant #7f007f + ContactType .. Tenant #7f007f +Document *-- ListEntry #0000ff + Document .. DocumentType #ff0000 + DocumentType .. Tenant #7f007f +Incident *-- IncidentNumber #0000ff + Incident .. ObjectBase #0000ff + Incident .. "many" OpMessage #ff00ff +Incident *-- IncidentRawData #007f00 + Incident .. "many" Incident #0000ff +Incident *-- "many" IncidentState #7f007f +Incident *-- "many" ListEntry #007f00 + Incident .. "many" IncidentTag #00007f + Incident .. Tenant #5f005f + Incident .. IncidentType #007f00 + IncidentComment .. Incident #0000ff + IncidentComment .. Tenant #5f005f +IncidentDocument *-- ListEntry #007f00 + IncidentDocument .. DocumentType #5f005f + IncidentDocument .. Incident #0000ff + IncidentDocument .. Tenant #5f005f +IncidentJoined *-- IncidentNumber #0000ff + IncidentJoined .. ObjectBase #0000ff + IncidentJoined .. "many" OpMessage #ff00ff +IncidentJoined *-- IncidentRawData #007f00 + IncidentJoined .. "many" Incident #0000ff +IncidentJoined *-- "many" IncidentState #7f007f +IncidentJoined *-- "many" ListEntry #007f00 + IncidentJoined .. "many" IncidentTag #00007f + IncidentJoined .. Tenant #5f005f + IncidentJoined .. IncidentType #007f00 +IncidentJoined *-- ObjectBase #0000ff +IncidentState *-- ListEntry #007f00 + IncidentState .. IncidentStateType #ff6100 + IncidentStateType .. "many" IncidentType #007f00 + IncidentStateType .. Tenant #5f005f + IncidentTag .. Tenant #5f005f + IncidentType .. "many" ObjectGroup #007f00 + IncidentType .. Tenant #5f005f + OpMessage .. ObjectBase #0000ff +OpMessage *-- OpMessageRawData #007f00 + OpMessage .. "many" OpMessage #ff6100 +OpMessage *-- ListEntry #007f00 + OpMessage .. OpMessageSender #9b0000 + OpMessage .. "many" OpMessageTag #5f005f + OpMessage .. Tenant #5f005f + OpMessage .. OpMessageType #ff0000 + OpMessageJoined .. ObjectBase #0000ff +OpMessageJoined *-- OpMessageRawData #007f00 + OpMessageJoined .. "many" OpMessage #ff6100 +OpMessageJoined *-- ListEntry #007f00 + OpMessageJoined .. OpMessageSender #9b0000 + OpMessageJoined .. "many" OpMessageTag #5f005f + OpMessageJoined .. Tenant #5f005f + OpMessageJoined .. OpMessageType #ff0000 +OpMessageJoined *-- ObjectBase #0000ff + OpMessageSender .. Tenant #5f005f + OpMessageTag .. Tenant #5f005f + OpMessageType .. Tenant #5f005f +footer powered by plantuml, created with jsonCodeGen +@enduml diff --git a/junctionModel2.puml b/junctionModel2.puml new file mode 100644 index 0000000..859e2e5 --- /dev/null +++ b/junctionModel2.puml @@ -0,0 +1,444 @@ + +@startuml +skinparam roundcorner 10 +skinparam class { + BackgroundColor #FFFFFF + ArrowColor #000000 + BorderColor #000000 + BorderColor<> #777777 + BackgroundColor<> #EEEEEE + BackgroundColor<> #e4ffd4 + FontName Courier + FontSize 12 +} + +skinparam note { + BackgroundColor #dedede + BorderColor #000000 + FontSize 10 +} + +skinparam classAttribute { + FontName Courier + FontSize 12 +} + +class JunctionLocationStreetsItem <> << ( ,#ff0000) >> { +} + +class JunctionLocation <> << ( ,#ff00ff) >> { +} + +class Junction <> << ( ,#ff0000) >> { +} + +note top of Junction +Junction is +a anchor point +of interest +of application +level +end note +class JunctionComment <> << ( ,#007f00) >> { +} + +note top of JunctionComment +Junction is +a anchor point +of interest +of application +level +end note +class JunctionContact <> << ( ,#00007f) >> { +} + +note top of JunctionContact +Junction is +a anchor point +of interest +of application +level +end note +class JunctionDocument <> << ( ,#ff00ff) >> { +} + +note top of JunctionDocument +Junction is +a anchor point +of interest +of application +level +end note +class JunctionJoined <> << ( ,#0000ff) >> { +} + +note top of JunctionJoined +Junction with +joined ObjectBase +end note +class JunctionNumber <> << ( ,#007f00) >> { +} + +note top of JunctionNumber +Junction is +a anchor point +of interest +of application +level +end note +class JunctionNumberType <> << ( ,#7f007f) >> { +} + +note top of JunctionNumberType +Technical identifier +for that junction +end note +class JunctionState <> << ( ,#ff6100) >> { +} + +note top of JunctionState +state of junction +end note +class JunctionType <> << ( ,#00007f) >> { +} + +note top of JunctionType +types of junctions +end note +class GeoArea << ( ,#007f00) >> { +} + +note top of GeoArea +Area geo-type +end note +class GeoPoint << ( ,#ff6100) >> { +} + +note top of GeoPoint +Single point +geo-type +end note +class ObjectBase <> << ( ,#007f00) >> { +} + +note top of ObjectBase +Object is a +anchor point +of interest +of application +level, that's +the minimal +information +about that +end note +class GeoMultiline << ( ,#ff0000) >> { +} + +note top of GeoMultiline +Multiline geo-type +end note +class ListEntry << ( ,#0000ff) >> { +} + +note top of ListEntry +type for entries +that have references +to look-up +tables - as +an more dynamic +alternative +to enums +end note +class Region <> << ( ,#00007f) >> { +} + +note top of Region +A region is +some kind of +a logical group +that could +be set for +some entries. +It allows 'vertical' +restrictions +of object visibility +end note +class Tenant <> << ( ,#7f007f) >> { +} + +note top of Tenant +Tenant of a +running application. +end note +class Tag <> << ( ,#5f005f) >> { +} + +note top of Tag +A tag is some +kind of a global +keyword that +could be set +for some entries +end note +class ObjectBaseGis <> << ( ,#9b0000) >> { +} + +class ObjectGroup <> << ( ,#9b0000) >> { +} + +note top of ObjectGroup +Groups of objects +(f.e. intersections, +INES nets, +public transport +route, ...) +end note +class Comment << ( ,#0000ff) >> { +} + +note top of Comment +A comment for +an data entry +end note +class Contact << ( ,#ff6100) >> { +} + +note top of Contact +contact to +a specific +entity +end note +class Address << ( ,#007f00) >> { +} + +class ContactData << ( ,#9b0000) >> { +} + +note top of ContactData +contact data +for a person +or address +end note +class Person << ( ,#ff00ff) >> { +} + +note top of Person +simple person +definition +end note +class AddressPerson << ( ,#5f005f) >> { +} + +note top of AddressPerson +a person that +is linked to +an address +end note +class AddressType <> << ( ,#ff0000) >> { +} + +note top of AddressType +type of a document +end note +class ContactType <> << ( ,#ff6100) >> { +} + +note top of ContactType +type of a contact +end note +class Document << ( ,#9b0000) >> { +} + +note top of Document +description +of a contained +document +end note +class DocumentType <> << ( ,#ff0000) >> { +} + +note top of DocumentType +type of a document +end note +class Incident <> << ( ,#0000ff) >> { +} + +note top of Incident +A detected +failure state +that needs +some actions +to solve it +end note +class IncidentComment <> << ( ,#ff6100) >> { +} + +note top of IncidentComment +Comment entries +to incidents +end note +class IncidentDocument <> << ( ,#ff0000) >> { +} + +note top of IncidentDocument +documents linked +to incident +entries +end note +class IncidentJoined <> << ( ,#ff00ff) >> { +} + +note top of IncidentJoined +Incident with +joined ObjectBase +end note +class IncidentNumber << ( ,#0000ff) >> { +} + +class IncidentRawData << ( ,#007f00) >> { +} + +class IncidentState << ( ,#7f007f) >> { +} + +class IncidentStateType <> << ( ,#ff6100) >> { +} + +note top of IncidentStateType +occurs, entered, +resolved, ... +end note +class IncidentTag <> << ( ,#00007f) >> { +} + +note top of IncidentTag +key-word for +additional +incident tagging +end note +class IncidentType <> << ( ,#007f00) >> { +} + +note top of IncidentType +types of incidents +end note +class OpMessage <> << ( ,#ff6100) >> { +} + +note top of OpMessage +General system +operation message +end note +class OpMessageJoined <> << ( ,#00007f) >> { +} + +note top of OpMessageJoined +OpMessage with +joined ObjectBase +end note +class OpMessageRawData << ( ,#007f00) >> { +} + +note top of OpMessageRawData +container for +additional, +app-specific +content +end note +class OpMessageSender <> << ( ,#9b0000) >> { +} + +note top of OpMessageSender +Who sends the +message +end note +class OpMessageTag <> << ( ,#5f005f) >> { +} + +note top of OpMessageTag +Tag to group +messages +end note +class OpMessageType <> << ( ,#ff0000) >> { +} + +note top of OpMessageType +types of message +end note +hide methods +hide attributes + Junction .. ObjectBase #007f00 + Junction .. JunctionState #ff6100 + Junction .. Tenant #7f007f + Junction .. JunctionType #00007f + JunctionComment .. Junction #ff0000 + JunctionComment .. Tenant #7f007f + JunctionContact .. ContactType #ff6100 + JunctionContact .. Junction #ff0000 + JunctionContact .. Tenant #7f007f + JunctionDocument .. DocumentType #ff0000 + JunctionDocument .. Junction #ff0000 + JunctionDocument .. Tenant #7f007f + JunctionJoined .. ObjectBase #007f00 + JunctionJoined .. JunctionState #ff6100 + JunctionJoined .. Tenant #7f007f + JunctionJoined .. JunctionType #00007f + JunctionNumber .. Junction #ff0000 + JunctionNumber .. Tenant #7f007f + JunctionNumber .. JunctionNumberType #7f007f + JunctionNumberType .. Tenant #7f007f + JunctionState .. Tenant #7f007f + JunctionType .. Tenant #7f007f + ObjectBase .. ObjectGroup #9b0000 + ObjectBase .. "many" Region #00007f + ObjectBase .. "many" Tag #5f005f + ObjectBase .. Tenant #7f007f + Region .. Tenant #7f007f + Tag .. Tenant #7f007f + ObjectGroup .. Tenant #7f007f + Contact .. ContactType #ff6100 + Address .. AddressType #ff0000 + AddressType .. Tenant #7f007f + ContactType .. Tenant #7f007f + Document .. DocumentType #ff0000 + DocumentType .. Tenant #7f007f + Incident .. ObjectBase #0000ff + Incident .. "many" OpMessage #ff00ff + Incident .. "many" Incident #0000ff + Incident .. "many" IncidentTag #00007f + Incident .. Tenant #5f005f + Incident .. IncidentType #007f00 + IncidentComment .. Incident #0000ff + IncidentComment .. Tenant #5f005f + IncidentDocument .. DocumentType #5f005f + IncidentDocument .. Incident #0000ff + IncidentDocument .. Tenant #5f005f + IncidentJoined .. ObjectBase #0000ff + IncidentJoined .. "many" OpMessage #ff00ff + IncidentJoined .. "many" Incident #0000ff + IncidentJoined .. "many" IncidentTag #00007f + IncidentJoined .. Tenant #5f005f + IncidentJoined .. IncidentType #007f00 + IncidentState .. IncidentStateType #ff6100 + IncidentStateType .. "many" IncidentType #007f00 + IncidentStateType .. Tenant #5f005f + IncidentTag .. Tenant #5f005f + IncidentType .. "many" ObjectGroup #007f00 + IncidentType .. Tenant #5f005f + OpMessage .. ObjectBase #0000ff + OpMessage .. "many" OpMessage #ff6100 + OpMessage .. OpMessageSender #9b0000 + OpMessage .. "many" OpMessageTag #5f005f + OpMessage .. Tenant #5f005f + OpMessage .. OpMessageType #ff0000 + OpMessageJoined .. ObjectBase #0000ff + OpMessageJoined .. "many" OpMessage #ff6100 + OpMessageJoined .. OpMessageSender #9b0000 + OpMessageJoined .. "many" OpMessageTag #5f005f + OpMessageJoined .. Tenant #5f005f + OpMessageJoined .. OpMessageType #ff0000 + OpMessageSender .. Tenant #5f005f + OpMessageTag .. Tenant #5f005f + OpMessageType .. Tenant #5f005f +footer powered by plantuml, created with jsonCodeGen +@enduml diff --git a/src/main/groovy/de/lisaplus/atlas/DoCodeGen.groovy b/src/main/groovy/de/lisaplus/atlas/DoCodeGen.groovy index 2c60b54..d03fabe 100644 --- a/src/main/groovy/de/lisaplus/atlas/DoCodeGen.groovy +++ b/src/main/groovy/de/lisaplus/atlas/DoCodeGen.groovy @@ -4,6 +4,7 @@ import com.beust.jcommander.JCommander import com.beust.jcommander.Parameter import com.beust.jcommander.ParameterException import de.lisaplus.atlas.builder.JsonSchemaBuilder +import de.lisaplus.atlas.builder.XSDBuilder import de.lisaplus.atlas.codegen.TemplateType import de.lisaplus.atlas.codegen.external.ExtMultiFileGenarator import de.lisaplus.atlas.codegen.external.ExtSingleFileGenarator @@ -12,11 +13,13 @@ import de.lisaplus.atlas.codegen.java.JavaInterfaceGenerator import de.lisaplus.atlas.codegen.java.JavaInterfacedBeanGenerator import de.lisaplus.atlas.codegen.java.JavaInterfacedGenericDerivedBeanGenerator import de.lisaplus.atlas.codegen.java.JavaGenericDerivedBeanGenerator +import de.lisaplus.atlas.codegen.java.MongoBeanGenerator import de.lisaplus.atlas.codegen.meta.HistModelGenerator import de.lisaplus.atlas.codegen.meta.JsonSchemaGenerator import de.lisaplus.atlas.codegen.meta.PlantUmlGenerator import de.lisaplus.atlas.codegen.meta.SwaggerGenerator import de.lisaplus.atlas.codegen.meta.SwaggerGeneratorExt +import de.lisaplus.atlas.codegen.meta.XsdGenerator import de.lisaplus.atlas.interf.IExternalCodeGen import de.lisaplus.atlas.interf.IModelBuilder import de.lisaplus.atlas.model.Model @@ -28,21 +31,122 @@ import org.slf4j.LoggerFactory * Created by eiko on 30.05.17. */ class DoCodeGen { + /** + * model file + */ @Parameter(names = [ '-m', '--model' ], description = "Path to JSON schema to parse", required = true) - String model + List models=[] - @Parameter(names = [ '-o', '--outputBase' ], description = "Base directory for the output", required = true) + /** + * Base directory for the output + */ + @Parameter(names = [ '-o', '--outputBase' ], description = "Base directory for the output") String outputBaseDir + /** + * Generator to use + */ @Parameter(names = ['-g', '--generator'], description = "generator that are used with the model. This parameter can be used multiple times") List generators = [] + + /** + * Generator to use + */ + @Parameter(names = ['-gs', '--generator-scripts'], description = "additional script that should be passed to the used templates") + String generatorScript + + /** + * Generator parameter + */ @Parameter(names = ['-gp', '--generator-parameter'], description = "special parameter that are passed to template via maps") List generator_parameters = [] + /** + * Type white list + */ + @Parameter(names = ['-w', '--white-list'], description = "white listed type, multiple usage possible") + List whiteListed = [] + + /** + * Type black list + */ + @Parameter(names = ['-b', '--black-list'], description = "black listed type, multiple usage possible") + List blackListed = [] + + /** + * Print help + */ @Parameter(names = ['-h','--help'], help = true) boolean help = false + /** + * Simply print main types + */ + @Parameter(names = ['-pmt','--print-main-types'], description = "don't do any code generation, simply loads the model and print the main-types of it") + boolean printMainTypes = false + + @Parameter(names = ['-pmts','--print-main-types-separator'], description = "separator to use for printing main types") + String printMainTypesSeparator + + @Parameter(names = ['-pmti','--print-main-types-info'], description = "print with info header") + boolean printMainTypesInfo = false + + /** + * an required type property to include into main types + */ + @Parameter(names = ['-mta','--main-types-attrib'], description = "specify a needed attribute to be a maintype, used in addition to the schema location") + String mainTypeAttrib + + /** + * an required type property to include into main types + */ + @Parameter(names = ['-tmt','--tag-main-types'], description = "if this flag is set all maintypes will be extended with a 'mainType' tag") + boolean tagMainTypes + + /** + * List of type-name tag-text tuple, The tags will be merged after initialization with the object tree + */ + @Parameter(names = ['-at', '--add-tag'], description = "add a text as tag to a specific type, f.e. -at User=unused") + List typeAddTagList = [] + + /** + * List of type-name tag-text tuple, after initialization the tags will be removed for the given types in the object tree + */ + @Parameter(names = ['-rt', '--remove-tag'], description = "remove a tag from a specific type, f.e. -rt User=unused") + List typeRemoveTagList = [] + + /** + * List of tags-text tuple, after initialization the tags will be removed for the given types in the object tree + */ + @Parameter(names = ['-rta', '--remove-tag-all'], description = "remove a tag from all model types, f.e. -rta rest") + List typeRemoveTagAllList = [] + + + /** + * List of tags-text tuple, after initialization the tags will be removed for the given types in the object tree + */ + @Parameter(names = ['-rta2', '--remove-tag-all-if-not-main'], description = "remove a tag from all model types that are no main types, f.e. -rta rest") + List typeRemoveTagAllList2 = [] + + /** + * an required type property to include into main types + */ + @Parameter(names = ['-rta2a','--remove-tag-all-if-not-main-attrib'], description = "don't do any code generation, simply loads the model and print the main-types of it") + String mainTypeAttrib2 + + /** + * if set enum types insteed of strings will be used + */ + @Parameter(names = ['-cet','--create-enum-types'], description = "if set the model is built with enum types") + boolean createEnumTypes = false + + + /** + * The datamodel parsed by the builder. It is public accessible for tests + */ + Model dataModel + static void main(String ... args) { DoCodeGen doCodeGen = new DoCodeGen() try { @@ -64,29 +168,50 @@ class DoCodeGen { } void run() { - log.info("model=${model}") + log.info("model=${models}") log.info("outPutBase=${outputBaseDir}") - def modelFile = new File (model) - if (!modelFile.isFile()) { - log.error("path to model file doesn't point to a file: ${model}") - System.exit(1) - } - else { - log.info("use model file: ${model}") - } + models.each { model -> + def modelFile = model instanceof File ? model : new File (model) + def modelName = modelFile.getName() + if (!modelFile.isFile()) { + log.error("path to model file doesn't point to a file: ${model}") + System.exit(1) + } + else { + log.info("use model file: ${model}") + } + IModelBuilder builder = modelName.toLowerCase().endsWith('.json') ? new JsonSchemaBuilder() : + modelName.toLowerCase().endsWith('.xsd') ? new XSDBuilder() : null + if (builder==null) { + log.error("unknown file type, currently only jscon schema and xsd are supported: ${model}") + System.exit(1) + } + if (builder instanceof JsonSchemaBuilder) { + ((JsonSchemaBuilder)builder).createEnumTypes = createEnumTypes + } + Model tmpModel = builder.buildModel(modelFile) + printMainTypesIfNeeded(tmpModel,modelFile.getName()) + adjustTagsForModel(tmpModel,modelFile.getName()) + if (!dataModel) { + dataModel = tmpModel + } + else { + mergeIntoDataModel(dataModel,tmpModel) + } + } + sortTypesAndProperties(dataModel) prepareOutputBaseDir(outputBaseDir) - IModelBuilder builder = new JsonSchemaBuilder() - Model dataModel = builder.buildModel(modelFile) // convert extra generator parameter to a map Map extraParameters = getMapFromGeneratorParams(generator_parameters) + extraParameters['blackListed']=blackListed + extraParameters['whiteListed']=whiteListed if (generators==null || generators.isEmpty()) { log.warn('no generators configured - skip') } else { - // TODO start CodeGen generators.each { generatorName -> def trennerIndex = generatorName.indexOf('=') def templateName = trennerIndex!=-1 && trennerIndex < generatorName.length() - 1? generatorName.substring(trennerIndex+1) : null @@ -94,14 +219,54 @@ class DoCodeGen { def pureGeneratorName = trennerIndex!=-1? generatorName.substring(0,trennerIndex) : generatorName // later linked generators needs to contain package seperator if (pureGeneratorName.indexOf('.')!=-1) { - // a later linked generator - useCustomGenerator(pureGeneratorName,templateName,dataModel,extraParameters,outputBaseDir) + // a later linked generator - not implemented yet + useCustomGenerator(pureGeneratorName,templateName,dataModel,extraParameters,outputBaseDir,generatorScript) } else { // a build in generator - useBuiltInGenerator(pureGeneratorName,templateName,dataModel,extraParameters,outputBaseDir) + useBuiltInGenerator(pureGeneratorName,templateName,dataModel,extraParameters,outputBaseDir,generatorScript) + } + } + } + } + + static void mergeIntoDataModel(Model mainModel,Model newModel) { + newModel.types.each { type -> + if (!mainModel.types.find { existingType -> existingType.name==type.name && + existingType.schemaFileName==type.schemaFileName }) { + mainModel.types.add(type) + } + } + } + + static void sortTypesAndProperties(Model model) { + model.types.sort{ a,b -> a.name<=>b.name} + model.types.each { type -> + type.properties.sort{ a,b -> a.name<=>b.name } + } + } + + void printMainTypesIfNeeded(Model dataModel, String fileName) { + if (printMainTypes) { + def mfn = fileName + String separator = printMainTypesSeparator ? printMainTypesSeparator : ' ' + def attrib = mainTypeAttrib ? " - needed attrib: $mainTypeAttrib" : '' + def info = "mainTypes [$mfn$attrib]:" + def outStr = printMainTypesInfo ? info : '' + dataModel.types.each { type -> + if (type.isMainType(mfn)) { + if ((!mainTypeAttrib) || (type.hasPropertyWithName(mainTypeAttrib))) { + if (outStr=='') { + outStr = "${type.name}" + } + else { + outStr = "${outStr}${separator}${type.name}" + } + } } } + println (outStr) + System.exit(0) } } @@ -148,11 +313,11 @@ class DoCodeGen { } } - static void useCustomGenerator(String generatorName, String templateName, Model dataModel, Map extraParameters,String outputBaseDir) { + static void useCustomGenerator(String generatorName, String templateName, Model dataModel, Map extraParameters,String outputBaseDir,String generatorScript) { // TODO } - static void useBuiltInGenerator(String generatorName, String templateName, Model dataModel, Map extraParameters,String outputBaseDir) { + static void useBuiltInGenerator(String generatorName, String templateName, Model dataModel, Map extraParameters,String outputBaseDir,String generatorScript) { switch (generatorName) { case 'java_interfaces': JavaInterfaceGenerator generator = new JavaInterfaceGenerator() @@ -164,6 +329,11 @@ class DoCodeGen { generator.initTemplate() generator.doCodeGen(dataModel,outputBaseDir,extraParameters) break + case 'mongo_beans': + MongoBeanGenerator generator = new MongoBeanGenerator() + generator.initTemplate() + generator.doCodeGen(dataModel,outputBaseDir,extraParameters) + break case 'java_generic_derived_beans': JavaGenericDerivedBeanGenerator generator = new JavaGenericDerivedBeanGenerator() generator.initTemplate() @@ -210,11 +380,16 @@ class DoCodeGen { generator.initTemplate() generator.doCodeGen(dataModel,outputBaseDir,extraParameters) break + case 'xsd': + XsdGenerator generator = new XsdGenerator() + generator.initTemplate() + generator.doCodeGen(dataModel,outputBaseDir,extraParameters) + break case 'multifiles': - generateMultiFiles(generatorName,templateName,dataModel,extraParameters,outputBaseDir) + generateMultiFiles(generatorName,templateName,dataModel,extraParameters,outputBaseDir,generatorScript) break case 'singlefile': - generateSingleFile(generatorName,templateName,dataModel,extraParameters,outputBaseDir) + generateSingleFile(generatorName,templateName,dataModel,extraParameters,outputBaseDir,generatorScript) break default: def errorMsg = "unknown built in generator: ${generatorName}" @@ -223,9 +398,10 @@ class DoCodeGen { } } - static void generateMultiFiles(String generatorName,String templateName,Model dataModel, Map extraParameters,String outputBaseDir) { + static void generateMultiFiles(String generatorName,String templateName,Model dataModel, Map extraParameters,String outputBaseDir,String generatorScript) { checkForTemplate(generatorName,templateName) IExternalCodeGen generator = new ExtMultiFileGenarator() + generator.setGeneratorScript(generatorScript) if (isTemplateFile(templateName)) { generator.initTemplateFromFile(templateName, TemplateType.GString) } @@ -235,9 +411,10 @@ class DoCodeGen { generator.doCodeGen(dataModel,outputBaseDir,extraParameters) } - static void generateSingleFile(String generatorName,String templateName, Model dataModel, Map extraParameters,String outputBaseDir) { + static void generateSingleFile(String generatorName,String templateName, Model dataModel, Map extraParameters,String outputBaseDir,String generatorScript) { checkForTemplate(generatorName,templateName) IExternalCodeGen generator = new ExtSingleFileGenarator() + generator.setGeneratorScript(generatorScript) if (isTemplateFile(templateName)) { generator.initTemplateFromFile(templateName, TemplateType.GString) } @@ -272,6 +449,91 @@ class DoCodeGen { print (usageFile.getText()) } + private void adjustTagsForModel(Model dataModel, String modelFileName) { + Map> typeAddTagMap = mapFromConfig(typeAddTagList) + Map> typeRemoveTagMap = mapFromConfig(typeRemoveTagList) + // remove all tags + dataModel.types.each { type -> + boolean isMainType = type.isMainType(modelFileName) && ((!mainTypeAttrib) || type.hasPropertyWithName(mainTypeAttrib)) + if(tagMainTypes && isMainType) { + type.tags.add('mainType') + } + // remove all tags + typeRemoveTagAllList.each { tag -> + def tagList = tag.indexOf(',')!=-1 ? tag.split(',') : tag.split(':') + tagList.each { t -> + if (type.tags.contains(t)) { + type.tags.remove(t) + } + } + } + // remove tags from not main types + typeRemoveTagAllList2.each { tag -> + def tagList = tag.indexOf(',')!=-1 ? tag.split(',') : tag.split(':') + if ((!isMainType) && ((!mainTypeAttrib2) || (type.hasPropertyWithName(mainTypeAttrib2)))) { + tagList.each { t -> + if (type.tags.contains(t)) { + type.tags.remove(t) + } + } + } + } + // remove undesired tags + List tagsToRemove = typeRemoveTagMap[type.name] + if (tagsToRemove) { + // remove tags + tagsToRemove.each { tag -> + type.tags.remove(tag) + } + } + // add new tags + List tagsToAdd = typeAddTagMap[type.name] + if (tagsToAdd) { + // add tags + tagsToAdd.each { tag -> + if (!type.tags.contains(tag)) { + type.tags.add(tag) + } + } + } + } + } + + private Map> mapFromConfig(List config) { + Map> ret = [:] + if (!config) return ret + config.each { typeTagStr -> + def typeTagArray = typeTagStr.split('=') + if (typeTagArray.length!=2) { + println "[mapFromConfig] - wrong type/tag-tuple: $typeTagStr" + return + } + def typeName = typeTagArray[0].trim() + def typeNameList = typeName.indexOf(',')!=-1 ? typeName.split(',') : typeName.split(':') + def tag = typeTagArray[1].trim() + def tagList = tag.indexOf(',')!=-1 ? tag.split(',') : tag.split(':') + typeNameList.each { name -> + List alreadyExistingValues = ret[name] + if (alreadyExistingValues) { + tagList.each { t -> + if (!alreadyExistingValues.contains(t)) { + alreadyExistingValues.add(t) + } + } + } + else { + List values = [] + tagList.each { t -> + if (!values.contains(t)) { + values.add(t) + } + } + ret[name] = values + } + } + } + return ret + } private static final Logger log=LoggerFactory.getLogger(DoCodeGen.class) } diff --git a/src/main/groovy/de/lisaplus/atlas/JavaFileLoader.groovy b/src/main/groovy/de/lisaplus/atlas/JavaFileLoader.groovy new file mode 100644 index 0000000..0491bd8 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/JavaFileLoader.groovy @@ -0,0 +1,109 @@ +package de.lisaplus.atlas + +import com.beust.jcommander.JCommander +import com.beust.jcommander.Parameter +import com.beust.jcommander.ParameterException +import de.lisaplus.atlas.builder.JsonSchemaBuilder +import de.lisaplus.atlas.codegen.TemplateType +import de.lisaplus.atlas.codegen.external.ExtMultiFileGenarator +import de.lisaplus.atlas.codegen.external.ExtSingleFileGenarator +import de.lisaplus.atlas.codegen.java.* +import de.lisaplus.atlas.codegen.meta.* +import de.lisaplus.atlas.interf.IExternalCodeGen +import de.lisaplus.atlas.interf.IModelBuilder +import de.lisaplus.atlas.model.Model +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +import java.lang.reflect.Method + +/** + * Main class to start the code generation + * Created by eiko on 30.05.17. + */ +class JavaFileLoader { + @Parameter(names = [ '-t', '--template' ], description = "comma separated list of classes to load", required = true) + String template + + @Parameter(names = ['-p', '--path'], description = "path to load the Java files") + List paths = [] + + @Parameter(names = ['-tp', '--template-parameter'], description = "special parameter that are passed to template via maps") + List template_parameters = [] + + @Parameter(names = ['-c', '--class'], description = "class to load") + List classes = [] + + @Parameter(names = ['-h','--help'], help = true) + boolean help = false + + static void main(String ... args) { + JavaFileLoader doCodeGen = new JavaFileLoader() + try { + JCommander jCommander = JCommander.newBuilder() + .addObject(doCodeGen) + .build() + jCommander.setProgramName(doCodeGen.getClass().typeName) + jCommander.parse(args) + if (doCodeGen.help) { + doCodeGen.printHelp() + jCommander.usage() + return + } + doCodeGen.run() + } + catch(ParameterException e) { + e.usage() + } + } + + void run() { + final GroovyClassLoader classLoader = new GroovyClassLoader() + classLoader.addClasspath('/home/eiko/prog/gitg_swarco/lisa-junction-server/tmp/server/target/junction-server-1.0.0.jar') +/* + paths.forEach { path -> + classLoader.addClasspath(path) + } +*/ +/* + classes.forEach { className -> + Class c = classLoader.loadClass(className) + Method[] methods = c.getMethods() + } + */ + Class c = classLoader.loadClass('BOOT-INF.classes.io.swagger.Swagger2SpringBoot$ExitException') + Method[] methods = c.getMethods() + } + + + /** + * splits extra generator parameter to Map values + * expected entries looks like this: packageBase=de.sw.atlas + * @param generator_parameters + * @return + */ + static Map getMapFromGeneratorParams(List generator_parameters) { + Map map = [:] + if (generator_parameters==null) { + return map + } + generator_parameters.each { param -> + String[] splittedParam = param.split('=') + if (splittedParam.length!=2) { + log.warn("extra generator parameter has wrong format and will be ignored: ${param}") + } + else { + map.put(splittedParam[0].trim(),splittedParam[1].trim()) + } + } + return map + } + + void printHelp() { + InputStream usageFile = this.class.getClassLoader().getResourceAsStream('docs/usage.md') + print (usageFile.getText()) + } + + + private static final Logger log=LoggerFactory.getLogger(JavaFileLoader.class) +} diff --git a/src/main/groovy/de/lisaplus/atlas/builder/JsonSchemaBuilder.groovy b/src/main/groovy/de/lisaplus/atlas/builder/JsonSchemaBuilder.groovy index 3d3fe76..ddd9920 100644 --- a/src/main/groovy/de/lisaplus/atlas/builder/JsonSchemaBuilder.groovy +++ b/src/main/groovy/de/lisaplus/atlas/builder/JsonSchemaBuilder.groovy @@ -3,15 +3,19 @@ package de.lisaplus.atlas.builder import de.lisaplus.atlas.codegen.helper.java.TypeToColor import de.lisaplus.atlas.interf.IModelBuilder import de.lisaplus.atlas.model.AggregationType +import de.lisaplus.atlas.model.ArrayType import de.lisaplus.atlas.model.BaseType import de.lisaplus.atlas.model.BooleanType +import de.lisaplus.atlas.model.ByteType import de.lisaplus.atlas.model.ComplexType import de.lisaplus.atlas.model.DateTimeType import de.lisaplus.atlas.model.DateType import de.lisaplus.atlas.model.DummyType +import de.lisaplus.atlas.model.EnumType import de.lisaplus.atlas.model.ExternalType import de.lisaplus.atlas.model.InnerType import de.lisaplus.atlas.model.IntType +import de.lisaplus.atlas.model.LongType import de.lisaplus.atlas.model.Model import de.lisaplus.atlas.model.NumberType import de.lisaplus.atlas.model.Property @@ -34,6 +38,8 @@ import static de.lisaplus.atlas.builder.helper.BuildHelper.makeCamelCase * Created by eiko on 01.06.17. */ class JsonSchemaBuilder implements IModelBuilder { + def createEnumTypes = false + /** * Container for all created types helps - makes reference handling easier */ @@ -61,7 +67,7 @@ class JsonSchemaBuilder implements IModelBuilder { Model model = initModel(objectModel) if (objectModel['definitions']) { // multi type schema - loadSchemaTypes(objectModel,currentSchemaPath,model) + loadSchemaTypes(objectModel,modelFile.getName(),currentSchemaPath,model) } if (objectModel['allOf']) { // single type schema @@ -77,7 +83,6 @@ class JsonSchemaBuilder implements IModelBuilder { log.error(errorMsg) throw new Exception(errorMsg) } - return model } @@ -104,16 +109,18 @@ class JsonSchemaBuilder implements IModelBuilder { typeName = string2Name(typeName) Type newType = new Type() newType.name = typeName + newType.schemaPath = currentSchemaPath + newType.schemaFileName = modelFileName newType.description = strFromMap(objectModel,'description') if (objectModel.allOf) { // TODO - add allof Properties objectModel.allOf.each { allOfElem -> if (allOfElem.properties) { - newType.properties.addAll(getProperties(model,allOfElem,typeName,currentSchemaPath)) + newType.properties.addAll(getProperties(model,allOfElem,typeName,modelFileName,currentSchemaPath)) } else { if (allOfElem.'$ref') { - RefType tmp = initRefType(allOfElem.'$ref',currentSchemaPath) + RefType tmp = initRefType(allOfElem.'$ref',modelFileName,currentSchemaPath) newType.properties.addAll(tmp.type.properties) newType.baseTypes.add(tmp.type.name) } @@ -121,13 +128,19 @@ class JsonSchemaBuilder implements IModelBuilder { } } else { - newType.properties = getProperties(model,objectModel,typeName,currentSchemaPath) + newType.properties = getProperties(model,objectModel,typeName,modelFileName,currentSchemaPath) + } + if (objectModel.'__tags') { + newType.tags=objectModel.'__tags' + } + if (objectModel.'__version') { + newType.version = objectModel.'__version' } + // TODO initialize extra stuff addNewType(newType,model) addExternalTypesToModel(model) - model.initRefOwnerForTypes() - model.checkModelForErrors() + model.postProcess() return model } @@ -147,27 +160,26 @@ class JsonSchemaBuilder implements IModelBuilder { } } - -// private Model modelFromMultiTypeSchema(def objectModel,String currentSchemaPath) { - private Model loadSchemaTypes(def objectModel,String currentSchemaPath,Model model) { -// Model model = initModel(objectModel) + private Model loadSchemaTypes(def objectModel, String schemaFileName,String currentSchemaPath,Model model) { if (model==null) model = initModel(objectModel) objectModel.definitions.each { typeObj -> def typeName = string2Name(typeObj.key) Type newType = new Type() newType.name = typeName + newType.schemaPath = currentSchemaPath + newType.schemaFileName = schemaFileName newType.description = strFromMap(typeObj.value,'description') newType.properties = [] if (typeObj.value.allOf) { // TODO - add allof Properties typeObj.value.allOf.each { allOfElem -> if (allOfElem.properties) { - newType.properties.addAll(getProperties(model,allOfElem,typeName,currentSchemaPath)) + newType.properties.addAll(getProperties(model,allOfElem,typeName,schemaFileName,currentSchemaPath)) } else { if (allOfElem.'$ref') { - RefType tmp = initRefType(allOfElem.'$ref',currentSchemaPath) + RefType tmp = initRefType(allOfElem.'$ref',schemaFileName,currentSchemaPath) newType.properties.addAll(tmp.type.properties) newType.baseTypes.add(tmp.type.name) } @@ -176,22 +188,24 @@ class JsonSchemaBuilder implements IModelBuilder { } else if (typeObj.value.'$ref') { // this type refers to an external definition - RefType tmp = initRefType(typeObj.value.'$ref',currentSchemaPath) + RefType tmp = initRefType(typeObj.value.'$ref', schemaFileName, currentSchemaPath) newType.properties.addAll(tmp.type.properties) newType.baseTypes.add(tmp.type.name) } else { - newType.properties = getProperties(model,typeObj.value,typeName,currentSchemaPath) + newType.properties = getProperties(model,typeObj.value,typeName,schemaFileName,currentSchemaPath) } if (typeObj.value.'__tags') { newType.tags=typeObj.value.'__tags' } + if (typeObj.value.'__version') { + newType.version = typeObj.value.'__version' + } // TODO initialize extra stuff addNewType(newType,model) } addExternalTypesToModel(model) - model.initRefOwnerForTypes() - model.checkModelForErrors() + model.postProcess() return model } @@ -226,22 +240,38 @@ class JsonSchemaBuilder implements IModelBuilder { model.types.add(newType) } - private List getProperties(Model model,def propertyParent,def parentName,String currentSchemaPath) { + private List getProperties(Model model,def propertyParent,def parentName,String schemaFileName, String currentSchemaPath) { List propList = [] propertyParent.properties.each { propObj -> - propList.add(creeateSimpleProperty(model, parentName,currentSchemaPath,propObj)) + propList.add(creeateSimpleProperty(model, parentName,schemaFileName,currentSchemaPath,propObj)) + } + // Dirty hack for enabling the usage of mongoDB Index2d: + // For performing filtering on geo coordinates in Documents of the mongoDB (e.g. operation geoWithin), the Json + // document containing the coordinates must have the longitude/X coordinate as first property and the latitude/Y + // coordinate as second property (property names are irrelevant!). + // But as JsonSlurper reorders the properties in alphabetic sequence, we can not force the necessary + // longitude / latitude sequence by defining it in the model json. + // Therefore this code tests for the existence of the properties lon and lat. If both are present, + // then the properties are reordered so that lon and lat come first! + Property lonProp = propList.find {prop -> prop.name == 'lon'} + Property latProp = propList.find {prop -> prop.name == 'lat'} + if (lonProp && latProp) { + propList.remove(lonProp) + propList.remove(latProp) + propList.add(0, latProp) + propList.add(0, lonProp) } return propList } - private Property creeateSimpleProperty (Model model, def parentName,String currentSchemaPath,def propObj) { + private Property creeateSimpleProperty (Model model, def parentName,String schemaFileName,String currentSchemaPath, def propObj) { def newProp = new Property() newProp.name = string2Name(propObj.key, false) newProp.description = propObj.value['description'] String key = makeCamelCase(propObj.key) - newProp.type = getPropertyType(model, propObj.value, parentName + string2Name(key), currentSchemaPath) + newProp.type = getPropertyType(model, propObj.value, parentName + string2Name(key),schemaFileName, currentSchemaPath) if (newProp.type instanceof RefType) { - if (propObj.key.toLowerCase().endsWith('_id')) { + if (propObj.key.toLowerCase().endsWith('_id') || propObj.key.toLowerCase().endsWith('Id')) { // per convention newProp.aggregationType = AggregationType.aggregation } else { /** @@ -257,19 +287,19 @@ class JsonSchemaBuilder implements IModelBuilder { newProp.aggregationType = AggregationType.composition } } - // implizit refs for normal types and array types differ + // implicit refs for normal types and array types differ if (newProp.type.isArray) { if (propObj.value.items.'__ref') { - newProp.implicitRef = initRefType(propObj.value.items.'__ref', currentSchemaPath) + newProp.implicitRef = initRefType(propObj.value.items.'__ref', schemaFileName, currentSchemaPath) } } else { if (propObj.value.'__ref') { - newProp.implicitRef = initRefType(propObj.value.'__ref', currentSchemaPath) + newProp.implicitRef = initRefType(propObj.value.'__ref', schemaFileName, currentSchemaPath) } } if (propObj.value.'__ref') { - newProp.implicitRef = initRefType(propObj.value.'__ref', currentSchemaPath) + newProp.implicitRef = initRefType(propObj.value.'__ref', schemaFileName, currentSchemaPath) } if (propObj.value.'__tags') { newProp.tags=propObj.value.'__tags' @@ -277,10 +307,10 @@ class JsonSchemaBuilder implements IModelBuilder { return newProp } - private BaseType getPropertyType(Model model,def propObjMap,def innerTypeBaseName,String currentSchemaPath) { + private BaseType getPropertyType(Model model,def propObjMap,def innerTypeBaseName, String schemaFileName,String currentSchemaPath) { if (propObjMap.'$ref') { // reference to an external type - return initRefType(propObjMap.'$ref',currentSchemaPath) + return initRefType(propObjMap.'$ref', schemaFileName,currentSchemaPath) } else if (! propObjMap.type) { def errorMsg = "property object w/o any type: ${propObjMap}" @@ -288,11 +318,11 @@ class JsonSchemaBuilder implements IModelBuilder { throw new Exception(errorMsg) } else { - return getBaseTypeFromString(model,currentSchemaPath,propObjMap,innerTypeBaseName) + return getBaseTypeFromString(model,schemaFileName,currentSchemaPath,propObjMap,innerTypeBaseName) } } - private RefType initRefType(def refStr,String currentSchemaPath) { + private RefType initRefType(def refStr,String schemaFileName,String currentSchemaPath) { if (!refStr) { def errorMsg = "undefined refStr, so cancel init reference type" log.error(errorMsg) @@ -307,6 +337,10 @@ class JsonSchemaBuilder implements IModelBuilder { if (refStr.startsWith(localDefStrBase)) { def schemaTypeName = refStr.substring(localDefStrBase.length()) Type t = getLocalRefType(schemaTypeName) + if (!t.schemaPath) { + t.schemaPath = currentSchemaPath + t.schemaFileName = schemaFileName + } if (t instanceof DummyType) { // the needed type isn't already in the model created. later a update to the // right references is needed @@ -321,6 +355,10 @@ class JsonSchemaBuilder implements IModelBuilder { // "$ref": "definitions.json#/address" // "$ref": "http: //json-schema.org/geo" - HTTP not supported (eiko) Type t = getExternalRefType(refStr,currentSchemaPath) + if (!t.schemaPath) { + t.schemaPath = currentSchemaPath + t.schemaFileName = schemaFileName + } refType.type=t refType.typeName=t.name } @@ -348,10 +386,15 @@ class JsonSchemaBuilder implements IModelBuilder { throw new Exception("loaded model doesn't contain types") } def desiredName = refStr.substring(indexOfTrenner+EXT_REF_TRENNER.length()).toLowerCase() + if (desiredName.startsWith('definitions/')) { + desiredName = desiredName.substring('definitions/'.length()) + } Type extT2 = null tmpModel.types.each { type -> if ((type.name!=null) && (type.name.toLowerCase()==desiredName)) { extT2 = type + extT2.schemaPath = currentSchemaPath + extT2.schemaFileName = fileName } } if (extT2==null) { @@ -378,6 +421,10 @@ class JsonSchemaBuilder implements IModelBuilder { Type tmpT = tmpModel.types.find { ! (it instanceof InnerType) } + if (!tmpT.schemaPath) { + tmpT.schemaPath = currentSchemaPath + tmpT.schemaFileName = fileName + } extT.refStr = refStr extT.initFromType(tmpT) // can be removed, because it's identical to the early init call (*1) @@ -429,7 +476,45 @@ class JsonSchemaBuilder implements IModelBuilder { } - private ComplexType initComplexType(Model model,def propertiesParent,def baseTypeName, String currentSchemaPath) { + private EnumType initEnumType(Model model,def propertiesParent,def baseTypeName, String schemaFileName, String currentSchemaPath) { + if (!propertiesParent) { + def errorMsg = "undefined properties map, so cancel init complex type" + log.error(errorMsg) + throw new Exception(errorMsg) + } + String enumTypeName = propertiesParent."__enumName" ? propertiesParent."__enumName" : baseTypeName + + def allowedValues = propertiesParent."enum" + + def alreadyCreated = createdTypes[enumTypeName] + if (alreadyCreated) { + // check if the values are the same as already defined + if (!(alreadyCreated instanceof EnumType)) { + def errorMsg = "expect an enum type but there already exists an non-enum type with the same name: $enumTypeName" + log.error(errorMsg) + throw new Exception(errorMsg) + } + EnumType alreadyCreatedEnumType = (EnumType) alreadyCreated + if (alreadyCreatedEnumType.allowedValues != allowedValues) { + def errorMsg = "expect an enum type but there already exists an non-enum type with the same name: $enumTypeName" + log.error(errorMsg) + throw new Exception(errorMsg) + } + return alreadyCreated + } + + EnumType newType = new EnumType() + newType.name = enumTypeName + newType.schemaPath = currentSchemaPath + newType.schemaFileName = schemaFileName + newType.allowedValues = allowedValues + TypeToColor.setColor(newType) + createdTypes[newType.name] = newType + model.types.add(newType) + return newType + } + + private ComplexType initComplexType(Model model,def propertiesParent,def baseTypeName, String schemaFileName, String currentSchemaPath) { if (!propertiesParent) { def errorMsg = "undefined properties map, so cancel init complex type" log.error(errorMsg) @@ -438,13 +523,15 @@ class JsonSchemaBuilder implements IModelBuilder { ComplexType complexType = new ComplexType() Type newType = new InnerType() newType.name = baseTypeName - newType.properties = getProperties(model,propertiesParent,baseTypeName,currentSchemaPath) + newType.schemaPath = currentSchemaPath + newType.schemaFileName = schemaFileName + newType.properties = getProperties(model,propertiesParent,baseTypeName,schemaFileName,currentSchemaPath) complexType.type = newType addNewType(newType,model) return complexType } - private BaseType getBaseTypeFromString(Model model,String currentSchemaPath,def propObjMap, def innerTypeBaseName, def isArrayAllowed=true) { + private BaseType getBaseTypeFromString(Model model,String schemaFileName, String currentSchemaPath,def propObjMap, def innerTypeBaseName, def isArrayAllowed=true) { switch (propObjMap.type) { case 'string': if (propObjMap.format && propObjMap.format.toLowerCase()=="uuid") { @@ -460,10 +547,30 @@ class JsonSchemaBuilder implements IModelBuilder { else if (propObjMap.format && propObjMap.format.toLowerCase()=="date") { return new DateType() } - else - return new StringType() + else { + if (createEnumTypes && propObjMap.enum) { + EnumType enumType = initEnumType(model,propObjMap,innerTypeBaseName,schemaFileName,currentSchemaPath) + RefType ret = new RefType() + ret.type=enumType + ret.typeName=enumType.name + return ret + } + else { + return new StringType() + } + } case 'integer': - return new IntType() + if (propObjMap.format && propObjMap.format.toLowerCase()=="int64") { + return new LongType() + } + else if (propObjMap.format && propObjMap.format.toLowerCase()=="int32") { + return new IntType() + } + else if (propObjMap.format && propObjMap.format.toLowerCase()=="byte") { + return new ByteType() + } + else + return new IntType() case 'number': return new NumberType() case 'boolean': @@ -474,23 +581,46 @@ class JsonSchemaBuilder implements IModelBuilder { return new UnsupportedType() } else - return initComplexType(model,propObjMap,innerTypeBaseName,currentSchemaPath) + return initComplexType(model,propObjMap,innerTypeBaseName,schemaFileName,currentSchemaPath) case 'array': + /* if (!isArrayAllowed) { - def errorMsg = "detect not allowed sub array type" + def errorMsg = "detect not allowed sub array type - use array types instead" log.error(errorMsg) throw new Exception(errorMsg) } + */ if (propObjMap.items.type) { - BaseType ret = getBaseTypeFromString(model,currentSchemaPath,propObjMap.items,innerTypeBaseName+'Item',false) - ret.isArray = true - if (propObjMap.'__tags') { - ret.type.tags=propObjMap.'__tags' + // test - eiko + // BaseType ret = getBaseTypeFromString(model,currentSchemaPath,propObjMap.items,innerTypeBaseName+'Item',false) + if (propObjMap.items.type=='array') { + BaseType tmp = getBaseTypeFromString(model,schemaFileName,currentSchemaPath,propObjMap.items,innerTypeBaseName+'Item') + ArrayType ret = new ArrayType() + ret.isArray = true + ret.baseType = tmp + return ret + } + else { + BaseType ret = getBaseTypeFromString(model,schemaFileName,currentSchemaPath,propObjMap.items,innerTypeBaseName+'Item') + ret.isArray = true + // the attrib has an _t_tags entry ... + if (propObjMap.'__tags') { + if (! (ret instanceof UUIDType)) { + // ... so set it also for complex inner types + ret.type.tags=propObjMap.'__tags' + } + } + if (propObjMap.'__version') { + if (! (ret instanceof UUIDType)) { + // ... so set it also for complex inner types + ret.type.version=propObjMap.'__version' + } + } + return ret } - return ret } else if (propObjMap.items['$ref']) { - BaseType ret = initRefType(propObjMap.items['$ref'],currentSchemaPath) + BaseType ret = initRefType(propObjMap.items['$ref'],schemaFileName,currentSchemaPath) ret.isArray = true return ret } @@ -553,6 +683,9 @@ class JsonSchemaBuilder implements IModelBuilder { else if (objectModel.version) { model.version = objectModel.version } + else if (objectModel.__version) { + model.version = objectModel.__version + } return model } diff --git a/src/main/groovy/de/lisaplus/atlas/builder/XSDBuilder.groovy b/src/main/groovy/de/lisaplus/atlas/builder/XSDBuilder.groovy new file mode 100644 index 0000000..ae76778 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/builder/XSDBuilder.groovy @@ -0,0 +1,390 @@ +package de.lisaplus.atlas.builder + +import de.lisaplus.atlas.codegen.helper.java.TypeToColor +import de.lisaplus.atlas.interf.IModelBuilder +import de.lisaplus.atlas.model.AggregationType +import de.lisaplus.atlas.model.BaseType +import de.lisaplus.atlas.model.BooleanType +import de.lisaplus.atlas.model.ByteType +import de.lisaplus.atlas.model.ComplexType +import de.lisaplus.atlas.model.DateTimeType +import de.lisaplus.atlas.model.DateType +import de.lisaplus.atlas.model.DummyType +import de.lisaplus.atlas.model.InnerType +import de.lisaplus.atlas.model.IntType +import de.lisaplus.atlas.model.LongType +import de.lisaplus.atlas.model.Model +import de.lisaplus.atlas.model.NumberType +import de.lisaplus.atlas.model.Property +import de.lisaplus.atlas.model.RefType +import de.lisaplus.atlas.model.StringType +import de.lisaplus.atlas.model.Type +import de.lisaplus.atlas.model.UnsupportedType +import org.apache.xmlbeans.SchemaAnnotation +import org.apache.xmlbeans.SchemaGlobalElement +import org.apache.xmlbeans.SchemaLocalElement +import org.apache.xmlbeans.SchemaParticle +import org.apache.xmlbeans.SchemaProperty +import org.apache.xmlbeans.SchemaType +import org.apache.xmlbeans.SchemaTypeSystem +import org.apache.xmlbeans.XmlBeans +import org.apache.xmlbeans.XmlObject + + +// TODO - implement detection of lists!!!!! +// https://stackoverflow.com/questions/2293873/how-do-i-define-an-array-of-custom-types-in-wsdl +class XSDBuilder implements IModelBuilder { + /** + * Container for all created types helps - makes reference handling easier + */ + def createdTypes=[:] // typeName: TypeObj + def restrictionTypes=[:] // typeName: PropertyType + def globalTypes // to avoid late additional parameter + + @Override + Model buildModel(File modelFile) { + XmlObject object = XmlObject.Factory.parse(modelFile) + List objectList = [] + objectList.add(object) + SchemaTypeSystem sts = XmlBeans.compileXsd((XmlObject[])objectList.toArray(), XmlBeans.getBuiltinTypeSystem(), null); + globalTypes = sts.globalTypes() + + Model model = new Model() + // two interations needed because to replace pure restriction types they must be known + collectPureRestrictionTypes(model,globalTypes) + collectNormalTypes(model,globalTypes) + if (!model.types) { + collectTypesFromGlobalElements(model,sts.globalElements()) + } + model.postProcess() + + return model + } + + void collectTypesFromGlobalElements(Model model, def globalElements) { + globalElements.each { SchemaGlobalElement globalElement -> + def type = globalElement.getType() + handleNormalType(type,model,globalElement.name.localPart) + } + } + + String navigateParticleToGetDocumentation(SchemaParticle p, Property prop) { + if (p==null) return + if (p.getParticleType()==SchemaParticle.ELEMENT) { + if (((SchemaLocalElement)p).name.localPart==prop.name) { + SchemaAnnotation annotation = ((SchemaLocalElement) p).getAnnotation(); + if (annotation != null) { + def userInfos = annotation.userInformation + def docuTxt = null + for (XmlObject userInfo : userInfos) { + if (userInfo.getDomNode().localName == 'documentation') { + if (docuTxt != null) { + docuTxt += ' ' + } else { + docuTxt = '' + } + docuTxt += userInfo.getDomNode().getFirstChild().getNodeValue().replaceAll('\\W+', ' ').trim() + } + } + prop.description = docuTxt + } + } + } + else { + SchemaParticle[] children = p.getParticleChildren(); + if (children!=null) { + for (int i = 0; i < children.length; i++) + navigateParticleToGetDocumentation(children[i],prop); + } + } + /* + switch (p.getParticleType()) + { + case SchemaParticle.ALL: + case SchemaParticle.CHOICE: + case SchemaParticle.SEQUENCE: + // These are "container" particles, so iterate over their children + SchemaParticle[] children = p.getParticleChildren(); + for (int i = 0; i < children.length; i++) + navigateParticleToGetDocumentation(children[i],prop); + break; + case SchemaParticle.ELEMENT: + if (((SchemaLocalElement)p).name.localPart==prop.name) { + SchemaAnnotation annotation = ((SchemaLocalElement) p).getAnnotation(); + if (annotation != null) { + def userInfos = annotation.userInformation + def docuTxt = null + for (XmlObject userInfo : userInfos) { + if (userInfo.getDomNode().localName == 'documentation') { + if (docuTxt != null) { + docuTxt += ' ' + } else { + docuTxt = '' + } + docuTxt += userInfo.getDomNode().getFirstChild().getNodeValue().replaceAll('\\W+', ' ').trim() + } + } + prop.description = docuTxt + } + } + + } + */ + } + + private void collectPureRestrictionTypes(Model model, def globalTypes) { + for (SchemaType type: globalTypes) { + if (type.contentType==SchemaType.DT_NOT_DERIVED) { + handlePureRestrictionType(type,model) + } + } + } + + private void collectNormalTypes(Model model, def globalTypes) { + for (SchemaType type: globalTypes) { + if (!(type.contentType==SchemaType.DT_NOT_DERIVED)) { + handleNormalType(type,model,null) + } + } + } + + private void handleNormalType(SchemaType type, Model model, String desiredName = null) { + Type newType = desiredName==null ? new Type() : new InnerType() + newType.name = desiredName==null ? xsdToName(type.getName().localPart) : desiredName + if (model.types.contains(newType)) { + return + } + newType.description = getDescription(type) + newType.baseTypes.add (xsdToName(type.baseType.getName().localPart)) + + def baseTypeName = xsdToName(type.getBaseType().getName().localPart) + def extendsStr='' + if (baseTypeName && baseTypeName!='anyType') { + newType.baseTypes.add(baseTypeName) + extendsStr = " extends $baseTypeName" + } + println "new type: ${newType.name}$extendsStr" + addProperties(type,model,newType) + addNewType(newType,model) + //println "added type: ${newType.name}, contentType: ${type.contentType}" + } + + + private void addProperties(SchemaType type, Model model, Type newType) { + def properties = type.getProperties() + for (SchemaProperty prop: properties) { + // know currently no way to retrieve the documentation of properties + def propName = xsdToName(prop.getName().localPart) + def propTypeNameObj = prop.getType().name + def desiredName = null + if (prop.getType().contentType==3 && propTypeNameObj==null) { + // Complex inner type + println 'FOUND COMPLEX INNER TYPE :D ... have to add it' + desiredName = newType.name + propName.substring(0,1).toUpperCase() + propName.substring(1) + println '-- intermediate type creation - start --' + handleNormalType(prop.getType(), model, desiredName) + println '-- intermediate type creation - end --' + + } + def propTypeName = propTypeNameObj != null ? xsdToName(propTypeNameObj.localPart) : desiredName!=null ? desiredName : '???' + Property newProp = new Property() + newProp.name = propName + // TODO this implementation isn't perfect because it parse every time the same contentModel :-/ + // ... and it doesn't cover xsd attributes + navigateParticleToGetDocumentation (type.getContentModel(),newProp) + if (propName=='posList') { + propName = propName + } + if (prop.getType().contentType==3) { + // complex type + if (propName.endsWith('_id') || propName.endsWith('Id')) { // per convention + newProp.aggregationType = AggregationType.composition + } + else { + newProp.aggregationType = AggregationType.aggregation + } + newProp.type = new ComplexType() + if (propTypeNameObj==null) { + // complex inner type + newProp.type.type = createdTypes[desiredName] // have to be resolved + } + else { + // complex type + Type t = createdTypes[propTypeName] + if (t==null) { + // complex type that is currently not created ... handled temporary with dummy type + t = new DummyType() + ((DummyType)t).referencesToChange.add(newProp.type) + createdTypes[propTypeName] = t + } + else if (t instanceof DummyType ) { + ((DummyType)t).referencesToChange.add(newProp.type) + } + newProp.type.type = t + } + } + else { + typeStrToType(propTypeName,newProp,prop) + newProp.type.originalType = propTypeName + } + if (prop.maxOccurs==null || prop.maxOccurs>1) { + newProp.type.isArray = true + } + if (prop.isAttribute()) { + def attributeModel = type.getAttributeModel() + def attributes = attributeModel.getAttributes() + for (def attrib: attributes) { + if (attrib.name.localPart==newProp.name) { + newProp.description = getDescription(attrib) + break + } + } + } + println " ${newProp.name}: ${newProp.type.name()} (${prop.getType().contentType}) isArray: ${newProp.type.isArray}" + newType.properties.add(newProp) + } + } + + private BaseType typeForString(String s) { + switch(s) { + case 'token': + // it is basically a string + return new StringType() + case 'string': + return new StringType() + case 'boolean': + return new BooleanType() + case 'byte': + return new ByteType() + case 'long': + return new LongType() + case 'int': + return new IntType() + case 'unsignedLong': + return new LongType() + case 'unsignedInt': + return new IntType() + case 'positiveInteger': + return new IntType() + case 'unsignedShort': + return new IntType() + case 'integer': + return new IntType() + case 'decimal': + return new NumberType() + case 'float': + return new NumberType() + case 'double': + return new NumberType() + case 'dateTime': + return new DateTimeType() + case 'date': + return new DateType() + case 'time': + return new StringType() + case 'NCName': + return new StringType() + case 'NMTOKEN': + return new StringType() + case 'gMonthDay': + return new StringType() + default: + return null + } + } + + private void typeStrToType(String typeName, def newProp, def prop) { + newProp.type = typeForString(typeName) + if (!newProp.type) { + if (restrictionTypes[typeName] != null) { + newProp.type = restrictionTypes[typeName] + } + else if (createdTypes[typeName] != null) { + // type is currently in the model + newProp.type = new RefType() + def alreadyCreated = createdTypes[typeName] + if (alreadyCreated instanceof DummyType) { + ((DummyType)alreadyCreated).referencesToChange.add(newProp.type) + } + newProp.type.type = alreadyCreated + } + else if (globalTypes.find{ it.getName().localPart==typeName }) { + newProp.type = new RefType() + Type t = new DummyType() + ((DummyType)t).referencesToChange.add(newProp.type) + createdTypes[typeName] = t + newProp.type.type = t + } + else { + // try to look into the XMLBeans implementations to find some XSD specifics + if (prop!=null) { + def type = prop.getType() + if (type.isSimpleType() && type.getBaseType()) { + def simpleTypeName = type.getBaseType().name.localPart + newProp.type = typeForString(simpleTypeName) + } + } + if (!newProp.type) { + newProp.type = new UnsupportedType() + println "FOUND UNSUPPORTED TYPE: $typeName" + } + } + } + } + + private void handlePureRestrictionType(SchemaType type, Model model) { + // per convention -> restriction type with name guid == UUIDType + def baseName = xsdToName(type.getBaseType().name.localPart) + def typeName = xsdToName(type.getName().localPart) + def t = typeForString(baseName) + if (t!=null) { + t.originalType = baseName + restrictionTypes[typeName] = t + } + } + + private String getDescription(elem) { + def docuTxt = null + def annotation = elem.getAnnotation() + if (annotation!=null) { + def userInfos = annotation.userInformation + for (XmlObject userInfo: userInfos ) { + if (userInfo.getDomNode().localName=='documentation') { + if (docuTxt!=null) { + docuTxt+=' ' + } + else { + docuTxt='' + } + def firstChild = userInfo.getDomNode().getFirstChild() + if (firstChild!=null) { + docuTxt += firstChild.getNodeValue().replaceAll('\\W+',' ').trim() + } + } + } + } + return docuTxt + } + + private void addNewType(Type newType, def model) { + def typeName = newType.name + def alreadyCreated = createdTypes[typeName] + if (alreadyCreated) { + if (alreadyCreated instanceof DummyType) { + // handle forward usage of types in declarations ... references need to be updated + alreadyCreated.referencesToChange.each { refType -> + refType.type = newType + } + createdTypes[typeName] = newType + } + } + TypeToColor.setColor(newType) + createdTypes[newType.name] = newType + model.types.add(newType) + } + + private String xsdToName(String localPart) { + return localPart.replaceAll('-','_') + } + +} diff --git a/src/main/groovy/de/lisaplus/atlas/builder/helper/BuildHelper.groovy b/src/main/groovy/de/lisaplus/atlas/builder/helper/BuildHelper.groovy index 7887af7..0adbf14 100644 --- a/src/main/groovy/de/lisaplus/atlas/builder/helper/BuildHelper.groovy +++ b/src/main/groovy/de/lisaplus/atlas/builder/helper/BuildHelper.groovy @@ -13,10 +13,11 @@ class BuildHelper { static String string2Name(String s,boolean firstUpper=true) { def ret = s.replaceAll('[^a-zA-Z0-9]','_') if (firstUpper) { - return ret.substring(0,1).toUpperCase()+ret.substring(1) + return ret.substring(0,1).toUpperCase()+makeCamelCase(ret.substring(1)) + } + else { + return ret.substring(0,1).toLowerCase()+makeCamelCase(ret.substring(1)) } - else - return ret.substring(0,1).toLowerCase()+ret.substring(1) } static String makeCamelCase(String s) { diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/GeneratorBase.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/GeneratorBase.groovy index 6752bd7..dd4e2ee 100644 --- a/src/main/groovy/de/lisaplus/atlas/codegen/GeneratorBase.groovy +++ b/src/main/groovy/de/lisaplus/atlas/codegen/GeneratorBase.groovy @@ -1,36 +1,45 @@ package de.lisaplus.atlas.codegen import de.lisaplus.atlas.DoCodeGen -import de.lisaplus.atlas.codegen.helper.java.JavaTypeConvert -import de.lisaplus.atlas.codegen.helper.java.JsonTypeConvert -import de.lisaplus.atlas.codegen.helper.java.SwaggerTypeConvert -import de.lisaplus.atlas.model.ComplexType -import de.lisaplus.atlas.model.InnerType import de.lisaplus.atlas.model.Model -import de.lisaplus.atlas.model.Property -import de.lisaplus.atlas.model.RefType import de.lisaplus.atlas.model.Type import groovy.text.GStringTemplateEngine import groovy.text.Template import groovy.text.TemplateEngine -import groovy.text.XmlTemplateEngine import groovy.text.markup.MarkupTemplateEngine -import org.codehaus.groovy.runtime.StringBufferWriter import org.slf4j.Logger -/** - * it ignores the XML template engine because there is no XML input - */ -enum TemplateType { - GString, - Markup -} /** * Created by eiko on 05.06.17. */ -abstract class GeneratorBase { +abstract class GeneratorBase extends TypeStringManipulation { Template template + Map extraParams + + String generatorScript + + /** + * initialize additional scripts that should be passed to the used templates + * @param generatorScripts + */ + void setGeneratorScript (String generatorScript) { + this.generatorScript = generatorScript + } + + protected void initGeneratorScriptForTemplate (Map data) { + if (this.generatorScript) { + File scriptFile = new File(this.generatorScript) + if (scriptFile.isFile()) { + GroovyShell shell = new GroovyShell() + def script = shell.parse(scriptFile) + data.script = script + } + else { + throw new Exception("can't find passed script file: ${this.generatorScript}") + } + } + } static void createDir(String dirName) { DoCodeGen.prepareOutputBaseDir(dirName) @@ -144,240 +153,47 @@ abstract class GeneratorBase { } /** - * methon create a map object and initialize it with some basic stuff - * @param model + * method create a map object and initialize it with some basic string manipulation stuff + * needed for working with the types and their properties in the templates. * @return */ - Map createTemplateDataMap(Model model) { - return [ - model:model, - DOLLAR:'$', - toLowerCase: toLowerCase, - toUpperCase: toUpperCase, - firstLowerCase: firstLowerCase, - firstUpperCase: firstUpperCase, - lowerCamelCase: firstLowerCamelCase, - upperCamelCase: firstUpperCamelCase, - isInnerType: isInnerType, - isPropComplexType: isPropComplexType, - typeToJava: JavaTypeConvert.convert, - typeToSwagger: SwaggerTypeConvert.convert, - typeToJson: JsonTypeConvert.convert, - typeToMeta: JsonTypeConvert.meta, - typeFormatToSwagger: SwaggerTypeConvert.format, - typeFormatToJson: JsonTypeConvert.format, - renderInnerTemplate: renderInnerTemplate, - breakTxt: breakTxt, - containsTag: containsTag, - missingTag: missingTag, - containsPropName: containsPropName, - missingPropName: missingPropName, - propsContainsTag: propsContainsTag - ] - } - - def isInnerType = { type -> - return type && (type instanceof InnerType ) - } - - def isPropComplexType = { prop -> - return prop && prop.type && (prop.type instanceof ComplexType || prop.type instanceof RefType) - } - - def containsTag = { obj, tag -> - if (! tag ) return false - if (! ((obj instanceof Type) || (obj instanceof Property))) { - return false - } - if (!obj.tags) { - return false - } - return obj.tags.contains(tag) - } - - def containsPropName = { type, propName -> - if (! type ) return false - if (! propName ) return false - if (! (type instanceof Type)) return false - return type.properties.findIndexOf{ - it.name==propName - } != -1 - } - - def missingPropName = { type, propName -> - if (! type ) return false - if (! propName ) return false - if (! (type instanceof Type)) return false - return type.properties.findIndexOf{ - it.name==propName - } == -1 - } - - def missingTag = { obj, tag -> - if (! tag ) return false - if (! ((obj instanceof Type) || (obj instanceof Property))) { - return false - } - if (!obj.tags) { - return false - } - return ! obj.tags.contains(tag) - } - - def propsContainsTag = { type, name -> - if (! type ) return false - if (! name ) return false - if (! (type instanceof Type)) { - return false - } - def result = type.properties.find { it.tags.contains(name) } - if (result) { - return true - } - else { - return false - } - } - - def breakTxt = { String txtToBreak,int charPerLine,String breakText='\n' -> - if (!txtToBreak) return EMPTY - StringBuilder sb = new StringBuilder() - int txtLen = txtToBreak.length() - int aktPos = 0 - while (aktPos < txtLen) { - if ((aktPos + charPerLine) >= txtLen) { - // The rest of the word is smaller than the desired char count per line - sb.append(txtToBreak.substring(aktPos)) - break; - } else { - sb.append(txtToBreak.substring(aktPos, aktPos + charPerLine)) - aktPos += charPerLine - if (txtToBreak.substring(aktPos, aktPos + 1) == ' ') { - sb.append(breakText) - aktPos++ - } else { - for (aktPos; aktPos < txtLen; aktPos++) { - String subStr = txtToBreak.substring(aktPos, aktPos + 1) - if (subStr == ' ') { - sb.append(breakText) - aktPos++ - break - } else { - sb.append(subStr) - } - } - } - } - } - return sb.toString() + /* + Map getClosures() { + return new TypeStringManipulation().getClosures() } + */ - def toLowerCase = { str -> - return str==null ? EMPTY : str.toLowerCase() - } - - def toUpperCase = { str -> - return str==null ? EMPTY : str.toUpperCase() + /** + * method create a map object and initialize it with some basic stuff + * @param model + * @return + */ + Map createTemplateDataMap(Model model) { + Map map = getClosures() + map.model = model + map.renderInnerTemplate = renderInnerTemplate + return map } def renderInnerTemplate = { templateResource,actObj,indent -> def test = actObj.toString() def innerTemplate = createTemplateFromResource(templateResource,TemplateType.GString) - def data = [ - actObj: actObj, - indent: indent, - printIndent: printIndent, - DOLLAR:'$', - toLowerCase: toLowerCase, - toUpperCase: toUpperCase, - firstLowerCase: firstLowerCase, - firstUpperCase: firstUpperCase, - lowerCamelCase: firstLowerCamelCase, - upperCamelCase: firstUpperCamelCase, - isInnerType: isInnerType, - typeToJava: JavaTypeConvert.convert, - typeToSwagger: SwaggerTypeConvert.convert, - typeToJson: JsonTypeConvert.convert, - typeToMeta: JsonTypeConvert.meta, - typeFormatToSwagger: SwaggerTypeConvert.format, - typeFormatToJson: JsonTypeConvert.format, - renderInnerTemplate: renderInnerTemplate, - breakTxt: breakTxt - ] - - return innerTemplate.make(data) - } - - def printIndent = { indent -> - def ret = '' - for (def i=0;i - if (!str) return EMPTY - def first = str.substring(0,1) - first = first.toLowerCase() - if (str.length()>1) { - def rest = str.substring(1) - return first + rest - } - else { - return first - } - } - - def firstUpperCase = { str -> - if (!str) return EMPTY - def first = str.substring(0,1) - first = first.toUpperCase() - if (str.length()>1) { - def rest = str.substring(1) - return first + rest + def data = getClosures() + data.actObj = actObj + data.indent = indent + initGeneratorScriptForTemplate(data) + data.renderInnerTemplate = renderInnerTemplate + if (this.extraParams) { + data.extraParam = this.extraParams } else { - return first + data.extraParam = [:] } + return innerTemplate.make(data) } - def firstUpperCamelCase = { str -> - if (!str) return EMPTY - def firstUpper = firstUpperCase(str) - return convertAllUnderLinesToCamelCase(firstUpper) - } - - def firstLowerCamelCase = { str -> - if (!str) return EMPTY - def firstLower = firstLowerCase(str) - return convertAllUnderLinesToCamelCase(firstLower) - } - - def convertAllUnderLinesToCamelCase = { String str -> - if (!str) return EMPTY - def i_ = str.indexOf('_') - while (i_!=-1) { - def stopLen = str.length()-1 - if (i_ extraParameters,Type currentType=null) abstract String getDestDir(Model dataModel, String outputBasePath, Map extraParameters,Type currentType=null) - abstract Logger getLogger(); + abstract Logger getLogger() } diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/MultiFileGenarator.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/MultiFileGenarator.groovy index 121e8e5..ffdf4fc 100644 --- a/src/main/groovy/de/lisaplus/atlas/codegen/MultiFileGenarator.groovy +++ b/src/main/groovy/de/lisaplus/atlas/codegen/MultiFileGenarator.groovy @@ -24,6 +24,7 @@ abstract class MultiFileGenarator extends GeneratorBase implements ICodeGen { throw new Exception(errorMsg) } def data = createTemplateDataMap(model) + this.extraParams = extraParams if (extraParams) { data.extraParam = extraParams } @@ -31,10 +32,20 @@ abstract class MultiFileGenarator extends GeneratorBase implements ICodeGen { data.extraParam = [:] } + initGeneratorScriptForTemplate(data) + + def blackListed=data.extraParam['blackListed'] + def whiteListed=data.extraParam['whiteListed'] + def shouldRemoveEmptyLines = extraParams['removeEmptyLines'] def neededAttrib = extraParams['containsAttrib'] def missingAttrib = extraParams['missingAttrib'] + def neededTag = extraParams['neededTag'] + def neededTagList = splitValueToArray(neededTag) + + def ignoredTag = extraParams['ignoreTag'] + def ignoredTagList = splitValueToArray(ignoredTag) model.types*.each { type -> boolean handleNeeded = neededAttrib ? type.properties.find { prop -> return prop.name==neededAttrib @@ -43,7 +54,31 @@ abstract class MultiFileGenarator extends GeneratorBase implements ICodeGen { return prop.name==missingAttrib } == null : true - if (handleNeeded && handleMissing) { + boolean handleType=true; + if (whiteListed && (!whiteListed.contains(type.name))) { + handleType = false + println "ingnored by white-list: ${type.name}" + } + else if (blackListed && blackListed.contains(type.name)) { + handleType = false + println "ingnored by black-list: ${type.name}" + } + + boolean handleTag = ignoredTagList ? type.tags.find { tag -> + return ignoredTagList.contains(tag) + } == null : true + + if (handleTag && neededTagList) { + boolean allTagsFound=true; + neededTagList.each { needed -> + if (!type.tags.contains(needed)) { + allTagsFound = false + } + } + handleTag = allTagsFound + } + + if (handleType && handleNeeded && handleMissing && handleTag) { data.put('currentType', type) def ergebnis = template.make(data) def destFileName = getDestFileName(model, extraParams, type) @@ -57,4 +92,9 @@ abstract class MultiFileGenarator extends GeneratorBase implements ICodeGen { } } } + + private List splitValueToArray(String value) { + if (!value) return [] + return value.indexOf(',')!=-1 ? value.split(',') : value.split(':') + } } diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/SingleFileGenarator.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/SingleFileGenarator.groovy index c452f4c..635078e 100644 --- a/src/main/groovy/de/lisaplus/atlas/codegen/SingleFileGenarator.groovy +++ b/src/main/groovy/de/lisaplus/atlas/codegen/SingleFileGenarator.groovy @@ -2,6 +2,7 @@ package de.lisaplus.atlas.codegen import de.lisaplus.atlas.interf.ICodeGen import de.lisaplus.atlas.model.Model +import de.lisaplus.atlas.model.Type import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -24,13 +25,16 @@ abstract class SingleFileGenarator extends GeneratorBase implements ICodeGen { } def data = createTemplateDataMap(model) + this.extraParams = extraParams if (extraParams) { data.extraParam = extraParams } else { data.extraParam = [:] } + initGeneratorScriptForTemplate(data) + removeUnneededTypes(data,extraParams) def ergebnis = template.make(data) def destFileName = getDestFileName(model,extraParams) @@ -43,4 +47,60 @@ abstract class SingleFileGenarator extends GeneratorBase implements ICodeGen { File file=new File("${destDir}/${destFileName}") file.write( resultString ) } + + private void removeUnneededTypes (Map data, Map extraParams) { + def blackListed=data.extraParam['blackListed'] + def whiteListed=data.extraParam['whiteListed'] + + def neededAttrib = extraParams['containsAttrib'] + def missingAttrib = extraParams['missingAttrib'] + def neededTag = extraParams['neededTag'] + def neededTagList = splitValueToArray(neededTag) + + def ignoredTag = extraParams['ignoreTag'] + def ignoredTagList = splitValueToArray(ignoredTag) + List neededTypes = [] + data.model.types*.each { type -> + boolean handleNeeded = neededAttrib ? type.properties.find { prop -> + return prop.name==neededAttrib + } != null : true + boolean handleMissing = missingAttrib ? type.properties.find { prop -> + return prop.name==missingAttrib + } == null : true + + boolean handleType=true; + if (whiteListed && (!whiteListed.contains(type.name))) { + handleType = false + println "ingnored by white-list: ${type.name}" + } + else if (blackListed && blackListed.contains(type.name)) { + handleType = false + println "ingnored by black-list: ${type.name}" + } + + boolean handleTag = ignoredTagList ? type.tags.find { tag -> + return ignoredTagList.contains(tag) + } == null : true + + if (handleTag && neededTagList) { + boolean allTagsFound=true; + neededTagList.each { needed -> + if (!type.tags.contains(needed)) { + allTagsFound = false + } + } + handleTag = allTagsFound + } + + if (handleType && handleNeeded && handleMissing && handleTag) { + neededTypes.add(type) + } + } + data.model.types = neededTypes + } + + private List splitValueToArray(String value) { + if (!value) return [] + return value.indexOf(',')!=-1 ? value.split(',') : value.split(':') + } } diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/TemplateType.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/TemplateType.groovy new file mode 100644 index 0000000..0c63661 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/codegen/TemplateType.groovy @@ -0,0 +1,10 @@ +package de.lisaplus.atlas.codegen + +/** + * it ignores the XML template engine because there is no XML input + */ +enum TemplateType { + GString, + Markup +} + diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/TypeStringManipulation.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/TypeStringManipulation.groovy new file mode 100644 index 0000000..1c95549 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/codegen/TypeStringManipulation.groovy @@ -0,0 +1,289 @@ +package de.lisaplus.atlas.codegen + +import de.lisaplus.atlas.codegen.helper.java.DotnetTypeConvert +import de.lisaplus.atlas.codegen.helper.java.JavaTypeConvert +import de.lisaplus.atlas.codegen.helper.java.JsonTypeConvert +import de.lisaplus.atlas.codegen.helper.java.SwaggerTypeConvert +import de.lisaplus.atlas.model.ComplexType +import de.lisaplus.atlas.model.InnerType +import de.lisaplus.atlas.model.Property +import de.lisaplus.atlas.model.RefType +import de.lisaplus.atlas.model.Type + +class TypeStringManipulation { + + /** + * method create a map object and initialize it with some basic string manipulation stuff + * needed for working with the types and their properties in the templates. + * @return + */ + Map getClosures() { + return [ + DOLLAR:'$', + printIndent: printIndent, + toLowerCase: toLowerCase, + toUpperCase: toUpperCase, + firstLowerCase: firstLowerCase, + firstUpperCase: firstUpperCase, + lowerCamelCase: firstLowerCamelCase, + upperCamelCase: firstUpperCamelCase, + firstUpperCamelCase: firstUpperCamelCase, + firstLowerCamelCase: firstLowerCamelCase, + isInnerType: isInnerType, + isPropComplexType: isPropComplexType, + typeToJava: JavaTypeConvert.convert, + typeToDotnet: DotnetTypeConvert.convert, + typeToJavaForceSingle: JavaTypeConvert.convertForceSingle, + typeToSwagger: SwaggerTypeConvert.convert, + typeToJson: JsonTypeConvert.convert, + typeToMeta: JsonTypeConvert.meta, + typeFormatToSwagger: SwaggerTypeConvert.format, + typeFormatToJson: JsonTypeConvert.format, + breakTxt: breakTxt, + containsTag: containsTag, + missingTag: missingTag, + containsPropName: containsPropName, + missingPropName: missingPropName, + propsContainsTag: propsContainsTag, + filterProps: filterProps, + filterPropsPerform: filterPropsPerform, + printLines: printLines, + copyType: copyType + ] + } + + def isInnerType = { type -> + return type && (type instanceof InnerType ) + } + + def isPropComplexType = { prop -> + return prop && prop.type && (prop.type instanceof ComplexType || prop.type instanceof RefType) + } + + def containsTag = { obj, tag -> + if (! tag ) return false + if (! ((obj instanceof Type) || (obj instanceof Property))) { + return false + } + if (!obj.tags) { + return false + } + return obj.tags.contains(tag) + } + + def containsPropName = { type, propName -> + if (! type ) return false + if (! propName ) return false + if (! (type instanceof Type)) return false + return type.properties.findIndexOf{ + it.name==propName + } != -1 + } + + def missingPropName = { type, propName -> + if (! type ) return false + if (! propName ) return false + if (! (type instanceof Type)) return false + return type.properties.findIndexOf{ + it.name==propName + } == -1 + } + + def missingTag = { obj, tag -> + if (! tag ) return false + if (! ((obj instanceof Type) || (obj instanceof Property))) { + return false + } + if (!obj.tags) { + return false + } + return ! obj.tags.contains(tag) + } + + def propsContainsTag = { type, name -> + if (! type ) return false + if (! name ) return false + if (! (type instanceof Type)) { + return false + } + def result = type.properties.find { it.tags.contains(name) } + if (result) { + return true + } + else { + return false + } + } + + def breakTxt = { String txtToBreak,int charPerLine,String breakText='\n' -> + if (!txtToBreak) return EMPTY + StringBuilder sb = new StringBuilder() + int txtLen = txtToBreak.length() + int aktPos = 0 + while (aktPos < txtLen) { + if ((aktPos + charPerLine) >= txtLen) { + // The rest of the word is smaller than the desired char count per line + sb.append(txtToBreak.substring(aktPos)) + break + } else { + sb.append(txtToBreak.substring(aktPos, aktPos + charPerLine)) + aktPos += charPerLine + if (txtToBreak.substring(aktPos, aktPos + 1) == ' ') { + sb.append(breakText) + aktPos++ + } else { + for (aktPos; aktPos < txtLen; aktPos++) { + String subStr = txtToBreak.substring(aktPos, aktPos + 1) + if (subStr == ' ') { + sb.append(breakText) + aktPos++ + break + } else { + sb.append(subStr) + } + } + } + } + } + return sb.toString() + } + + def printIndent = { indent -> + def ret = '' + for (def i=0;i + return str==null ? EMPTY : str.toLowerCase() + } + + def toUpperCase = { str -> + return str==null ? EMPTY : str.toUpperCase() + } + + def firstLowerCase = { str -> + if (!str) return EMPTY + def first = str.substring(0,1) + first = first.toLowerCase() + if (str.length()>1) { + def rest = str.substring(1) + return first + rest + } + else { + return first + } + } + + def firstUpperCase = { str -> + if (!str) return EMPTY + def first = str.substring(0,1) + first = first.toUpperCase() + if (str.length()>1) { + def rest = str.substring(1) + return first + rest + } + else { + return first + } + } + + def firstUpperCamelCase = { str -> + if (!str) return EMPTY + def firstUpper = firstUpperCase(str) + return convertAllUnderLinesToCamelCase(firstUpper) + } + + def firstLowerCamelCase = { str -> + if (!str) return EMPTY + def firstLower = firstLowerCase(str) + return convertAllUnderLinesToCamelCase(firstLower) + } + + def convertAllUnderLinesToCamelCase = { String str -> + if (!str) return EMPTY + def i_ = str.indexOf('_') + while (i_!=-1) { + def stopLen = str.length()-1 + if (i_> filterProps = { Type type, Map params -> + def filterProps = { type, params -> + List props = type.properties + if (params.filterCls!= null) props = props.findAll { params.get('filterCls') } + if (params.name != null) props = props.findAll { prop -> prop.name == params.name } + if (params.namePattern != null) props = props.findAll { prop -> prop.name =~ params.namePattern } + if (params.complex != null) props = props.findAll { prop -> prop.isComplexType() == params.complex } + if (params.refComplex != null) props = props.findAll { prop -> + // println "refComplex: param=${params.refComplex} propName=${prop.name} propValue=${prop.isRefTypeOrComplexType()}" + prop.isRefTypeOrComplexType() == params.refComplex } + if (params.array != null) props = props.findAll { prop -> prop.type.isArray == params.array } + if (params.join != null) props = props.findAll { prop -> + // def evaled = prop.hasTag('join') == params.join + // println "join: param=${params.join} propName=${prop.name} propValue=${prop.hasTag('join')} evaled=${evaled}" + prop.hasTag('join') == params.join } + if (params.aggregation != null) props = props.findAll { prop -> prop.isAggregation() == params.aggregation } + if (params.implRefIsRef != null) props = props.findAll { prop -> prop.implicitRefIsRefType() == params.implRefIsRef } + if (params.implRefIsComp != null) props = props.findAll { prop -> prop.implicitRefIsComplexType() == params.implRefIsComp } + if (params.typeName!= null) props = props.findAll { prop -> prop.type.NAME == params.typeName } + if (params.typeNameNot!= null) props = props.findAll { prop -> prop.type.NAME != params.typeNameNot } + // Alternative: use pattern, e.g. typeNamePattern:'^(?!DATE$)' to get all types with names other than 'DATE' + if (params.typeNamePattern!= null) props = props.findAll { prop -> prop.type.NAME =~ params.typeNamePattern } + if (params.hasTag != null) props = props.findAll { prop -> prop.hasTag(params.hasTag) } + if (params.withoutTag != null) props = props.findAll { prop -> !prop.hasTag(params.withoutTag) } + if (params.prepLookup) props = props.findAll { prop -> + prop.hasTag('prepLookup') == params.prepLookup } + if (params.selfRef != null) props = props.findAll { prop -> prop.selfReference == params.selfRef } + return props + } + + // Closure> filterPropsPerform = {Type type, Map params, Closure> toLines -> + def filterPropsPerform = { type, params, toLines -> + def props = filterProps.call(type, params) + def lines = [] + if (props.size() > 0 && params.comment != null) { + lines += params.comment + } + // props.each { prop -> lines = lines + toLines.call(prop) } + props.each { prop -> lines += toLines.call(prop) } + if (props.size() > 0 && true == params.newLine) { + lines += '' + } + return lines + } + + // Vararg definition is mandatory, call example: printLines3.call( [level:2, by: '.-'], new StringWriter(), list, list2 ) + // Closure printLines = { Writer out, Map params, List... lists -> + def printLines = { out, params,List... lists -> + def level = params.level?:0 + def by = params.by?:' ' + def prefix = '' + level.times { prefix += by} + lists.flatten().each { line -> out << "${prefix}${line}\n" } + } + + /** + * Returns a copy of the provided type. + * @param type the type to copy + */ + def copyType = { type -> + return Type.copyOf(type, [:]) + } + + private final static String EMPTY='' +} diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/external/ExtMultiFileGenarator.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/external/ExtMultiFileGenarator.groovy index b11d660..72640ad 100644 --- a/src/main/groovy/de/lisaplus/atlas/codegen/external/ExtMultiFileGenarator.groovy +++ b/src/main/groovy/de/lisaplus/atlas/codegen/external/ExtMultiFileGenarator.groovy @@ -28,15 +28,13 @@ class ExtMultiFileGenarator extends MultiFileGenarator implements IExternalCodeG @Override String getDestFileName(Model dataModel, Map extraParameters,Type currentType=null) { def destFileNameExt = extraParameters['destFileNameExt'] - def destFileNamePre = extraParameters['destFileNamePre'] + def destFileNamePre = extraParameters['destFileNamePre'] ?: '' + def destFileNamePost = extraParameters['destFileNamePost'] ?: '' def destFileCamelCaseName = extraParameters['destFileCamelCaseName'] - def destName = destFileCamelCaseName ? firstUpperCamelCase(currentType.name) : firstUpperCase(currentType.name) + def destName = destFileNamePre + ( destFileCamelCaseName ? firstUpperCamelCase(currentType.name) : firstUpperCase(currentType.name) ) + destFileNamePost if (destFileNameExt) { destName = destName + "." + destFileNameExt } - if (destFileNamePre) { - destName = destFileNamePre + destName; - } return destName } diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/DotnetTypeConvert.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/DotnetTypeConvert.groovy new file mode 100644 index 0000000..7726d23 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/DotnetTypeConvert.groovy @@ -0,0 +1,100 @@ +package de.lisaplus.atlas.codegen.helper.java + +import de.lisaplus.atlas.model.* + +/** + * Converts meta model types to Swagger used types + * Created by eiko on 11.06.17. + */ +class DotnetTypeConvert { + static def convert = { type -> + if (! type instanceof BaseType) { + return BaseType.WRONG_TYPE+type + } + switch(type.name()) { + case IntType.NAME: + return type.isArray? 'List' : 'int' + case LongType.NAME: + return type.isArray? 'List' : 'long' + case NumberType.NAME: + return type.isArray? 'List' : 'double' + case StringType.NAME: + return type.isArray? 'List' : 'string' + case UUIDType.NAME: + return type.isArray? 'List' : 'Guid' + case BooleanType.NAME: + return type.isArray? 'List' : 'bool' + case ByteType.NAME: + return type.isArray? 'List' : 'byte' + case DateType.NAME: + return type.isArray? 'List' : 'DateTime' + case DateTimeType.NAME: + return type.isArray? 'List' : 'DateTime' + case RefType.NAME: + return type.type.name + case ArrayType.NAME: + return List + case ComplexType.NAME: // ? + return type.type.name + case UnsupportedType.NAME: + return "unsupported" + default: + return "???" + } + } + + static def meta = { type -> + if (! type instanceof BaseType) { + return BaseType.WRONG_TYPE+type + } + switch(type.name()) { + case IntType.NAME: + return 'integer' + case LongType.NAME: + return 'long' + case NumberType.NAME: + return 'number' + case StringType.NAME: + return 'string' + case UUIDType.NAME: + return 'string/uuid' + case BooleanType.NAME: + return 'boolean' + case ByteType.NAME: + return 'byte' + case DateType.NAME: + return 'string/date' + case DateTimeType.NAME: + return 'string/date-time' + case RefType.NAME: + return type.type.name + case ComplexType.NAME: // ? + return type.type.name + case UnsupportedType.NAME: + return "unsupported" + default: + return "???" + } + } + + static def format = { type -> + switch(type.name()) { + case IntType.NAME: + return 'int64' + case LongType.NAME: + return 'int64' + case NumberType.NAME: + return 'double' + case DateType.NAME: + return 'date' + case DateTimeType.NAME: + return 'date-time' + case UUIDType.NAME: + return 'uuid' + case UnsupportedType.NAME: + return BaseType.UNSUPPORTED_TYPE+type + default: + return '' + } + } +} diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/JavaTypeConvert.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/JavaTypeConvert.groovy index 672c354..7e22ec8 100644 --- a/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/JavaTypeConvert.groovy +++ b/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/JavaTypeConvert.groovy @@ -1,24 +1,44 @@ package de.lisaplus.atlas.codegen.helper.java import de.lisaplus.atlas.codegen.GeneratorBase -import de.lisaplus.atlas.model.BaseType -import de.lisaplus.atlas.model.BooleanType -import de.lisaplus.atlas.model.ComplexType -import de.lisaplus.atlas.model.DateTimeType -import de.lisaplus.atlas.model.DateType -import de.lisaplus.atlas.model.IntType -import de.lisaplus.atlas.model.NumberType -import de.lisaplus.atlas.model.RefType -import de.lisaplus.atlas.model.StringType -import de.lisaplus.atlas.model.UUIDType -import de.lisaplus.atlas.model.VoidType -import de.lisaplus.atlas.model.UnsupportedType +import de.lisaplus.atlas.model.* /** * Converts meta model types to Java types * Created by eiko on 11.06.17. */ class JavaTypeConvert { + + enum DateTypePreset { + /** No JSR-310 Date and Time classes, just plain old java.util.Date */ + LEGACY('java.util.Date', 'java.util.Date'), + /** Format natural to java-mongo-driver 3.7.1 or later */ + JSR_310_LOCAL('java.time.LocalDate', 'java.time.LocalDateTime'), + /** Format that preserves the time offset, natural to Jackson */ + JSR_310_OFFSET('java.time.LocalDate', 'java.time.OffsetDateTime'), + /** Format that preserved the time zone and preferres ZoneId */ + JSR_310_ZONED('java.time.LocalDate', 'java.time.ZonedDateTime'); + + final String dateClass + final String dateTimeClass + + private DateTypePreset(dateClass, dateTimeClass) { + this.dateClass = dateClass + this.dateTimeClass = dateTimeClass + } + } + + static DateTypePreset preset = DateTypePreset.LEGACY + + static { + switch (System.getProperty('date.type.preset', 'legacy').toLowerCase()) { + case 'legacy': preset = DateTypePreset.LEGACY; break + case '310.local': preset = DateTypePreset.JSR_310_LOCAL; break + case '310.offset': preset = DateTypePreset.JSR_310_OFFSET; break + case '310.zoned': preset = DateTypePreset.JSR_310_ZONED; break + } + } + static def convert = { type,prefix = '' -> if (! type instanceof BaseType) { return BaseType.WRONG_TYPE+type @@ -26,28 +46,128 @@ class JavaTypeConvert { switch(type.name()) { case IntType.NAME: return type.isArray? 'java.util.List' : 'Integer' + case LongType.NAME: + return type.isArray? 'java.util.List' : 'Long' case NumberType.NAME: return type.isArray? 'java.util.List' : 'Double' case StringType.NAME: return type.isArray? 'java.util.List' : 'String' case UUIDType.NAME: - return type.isArray? 'java.util.List' : 'String' + return type.isArray? 'java.util.List' : 'UUID' case BooleanType.NAME: return type.isArray? 'java.util.List' : 'Boolean' + case ByteType.NAME: + return type.isArray? 'java.util.List' : 'Byte' case DateType.NAME: - return type.isArray? 'java.util.List' : 'java.util.Date' + return type.isArray? "java.util.List<${preset.dateClass}>" : "${preset.dateClass}" case DateTimeType.NAME: - return type.isArray? 'java.util.List' : 'java.util.Date' + return type.isArray? "java.util.List<${preset.dateTimeClass}>" : "${preset.dateTimeClass}" case RefType.NAME: - return type.isArray? "java.util.List<${prefix}${type.type.name}>" : "${prefix}${type.type.name}" + return type.isArray? "java.util.List<${prefix}${firstUpperCamelCase(type.type.name)}>" : "${prefix}${firstUpperCamelCase(type.type.name)}" case ComplexType.NAME: - return type.isArray? "java.util.List<${prefix}${type.type.name}>" : "${prefix}${type.type.name}" + return type.isArray? "java.util.List<${prefix}${firstUpperCamelCase(type.type.name)}>" : "${prefix}${firstUpperCamelCase(type.type.name)}" + case ArrayType.NAME: + String base = "java.util.List<%s>"; + BaseType aktType = type; + String ret = base; + String baseTypeStr = convert.call (type.baseType) + return String.format(base,baseTypeStr) + /* + while (aktType instanceof ArrayType) { + if (aktType.baseType instanceof ArrayType ) { + ret = String.format(ret,base) + } + else { + ret = String.format(ret,"${prefix}${firstUpperCamelCase(aktType.baseType.type.name)}") + } + aktType = aktType.baseType + } + return ret + */ case UnsupportedType.NAME: - return BaseType.UNSUPPORTED_TYPE+type + return "Object /* FIXME unsupported type: ${type.originalType} */" +// return BaseType.UNSUPPORTED_TYPE+type case VoidType.NAME: return 'void' default: return "${BaseType.UNKNOWN_TYPE}\n$type\n${type.name()}" } } + + static def convertForceSingle = { type,prefix = '' -> + if (! type instanceof BaseType) { + return BaseType.WRONG_TYPE+type + } + switch(type.name()) { + case IntType.NAME: + return 'Integer' + case LongType.NAME: + return 'Long' + case NumberType.NAME: + return 'Double' + case StringType.NAME: + return 'String' + case UUIDType.NAME: + return 'UUID' + case BooleanType.NAME: + return 'Boolean' + case ByteType.NAME: + return 'Byte' + case DateType.NAME: + return "${preset.dateClass}" + case DateTimeType.NAME: + return "${preset.dateTimeClass}" + case RefType.NAME: + case ComplexType.NAME: + return "${prefix}${firstUpperCamelCase(type.type.name)}" + case UnsupportedType.NAME: + return BaseType.UNSUPPORTED_TYPE+type + case VoidType.NAME: + return 'void' + default: + return "${BaseType.UNKNOWN_TYPE}\n$type\n${type.name()}" + } + } + + private static firstUpperCase (String str) { + if (!str) return '' + def first = str.substring(0,1) + first = first.toUpperCase() + if (str.length()>1) { + def rest = str.substring(1) + return first + rest + } + else { + return first + } + } + + private static String firstUpperCamelCase(String str) { + if (!str) return '' + def firstUpper = firstUpperCase(str) + return convertAllUnderLinesToCamelCase(firstUpper) + } + + private static String convertAllUnderLinesToCamelCase(String str) { + if (!str) return '' + def i_ = str.indexOf('_') + while (i_!=-1) { + def stopLen = str.length()-1 + if (i_ switch(type.name()) { case IntType.NAME: + return 'int32' + case LongType.NAME: return 'int64' + case ByteType.NAME: + return 'byte' case NumberType.NAME: return 'double' case DateType.NAME: diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/SwaggerTypeConvert.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/SwaggerTypeConvert.groovy index f5c305e..f14a53f 100644 --- a/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/SwaggerTypeConvert.groovy +++ b/src/main/groovy/de/lisaplus/atlas/codegen/helper/java/SwaggerTypeConvert.groovy @@ -14,6 +14,8 @@ class SwaggerTypeConvert { switch(type.name()) { case IntType.NAME: return 'integer' + case LongType.NAME: + return 'integer' case NumberType.NAME: return 'number' case StringType.NAME: @@ -22,6 +24,8 @@ class SwaggerTypeConvert { return 'string' case BooleanType.NAME: return 'boolean' + case ByteType.NAME: + return 'integer' case DateType.NAME: return 'string' case DateTimeType.NAME: @@ -40,6 +44,8 @@ class SwaggerTypeConvert { static def format = { type -> switch(type.name()) { case IntType.NAME: + return 'int32' + case LongType.NAME: return 'int64' case NumberType.NAME: return 'double' diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/java/JavaGeneratorBase.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/java/JavaGeneratorBase.groovy index a018632..6318b36 100644 --- a/src/main/groovy/de/lisaplus/atlas/codegen/java/JavaGeneratorBase.groovy +++ b/src/main/groovy/de/lisaplus/atlas/codegen/java/JavaGeneratorBase.groovy @@ -13,14 +13,15 @@ import org.slf4j.LoggerFactory abstract class JavaGeneratorBase extends MultiFileGenarator { @Override String getDestFileName(Model dataModel, Map extraParameters, Type currentType=null) { - String fileNameBase = firstUpperCase(currentType.name) + String fileNameBase = firstUpperCamelCase(currentType.name) return "${fileNameBase}.java" } @Override String getDestDir(Model dataModel, String outputBasePath, Map extraParameters, Type currentType=null) { String destDirBase = extraParameters.outputDirExt ? outputBasePath + File.separator + extraParameters.outputDirExt : outputBasePath - String packageStr = extraParameters.packageName ? extraParameters.packageName.replaceAll('\\.',File.separator) : '' + // Force linux/unix file separator, JVM can handle that on all plattforms! + String packageStr = extraParameters.packageName ? extraParameters.packageName.replaceAll('\\.','/') : '' String destDirStr = "${destDirBase}${File.separator}${packageStr}" File destDir = new File(destDirStr) if (!destDir.exists()) destDir.mkdirs() diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/java/MongoBeanGenerator.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/java/MongoBeanGenerator.groovy new file mode 100644 index 0000000..8e1f100 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/codegen/java/MongoBeanGenerator.groovy @@ -0,0 +1,20 @@ +package de.lisaplus.atlas.codegen.java + +import de.lisaplus.atlas.codegen.TemplateType +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +/** + * Created by eiko on 05.06.17. + */ +class MongoBeanGenerator extends JavaGeneratorBase { + private static final Logger log=LoggerFactory.getLogger(MongoBeanGenerator.class) + + void initTemplate() { + template = createTemplateFromResource('templates/java/mongo_bean.txt',TemplateType.GString) + } + + Logger getLogger() { + return log + } +} diff --git a/src/main/groovy/de/lisaplus/atlas/codegen/meta/XsdGenerator.groovy b/src/main/groovy/de/lisaplus/atlas/codegen/meta/XsdGenerator.groovy new file mode 100644 index 0000000..48d0dd4 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/codegen/meta/XsdGenerator.groovy @@ -0,0 +1,45 @@ +package de.lisaplus.atlas.codegen.meta + +import de.lisaplus.atlas.codegen.SingleFileGenarator +import de.lisaplus.atlas.codegen.TemplateType +import de.lisaplus.atlas.model.Model +import de.lisaplus.atlas.model.Type +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +import static de.lisaplus.atlas.builder.helper.BuildHelper.string2Name + +/** + * Created by eiko on 05.06.17. + */ +class XsdGenerator extends SingleFileGenarator { + private static final Logger log=LoggerFactory.getLogger(XsdGenerator.class) + + void initTemplate() { + template = createTemplateFromResource('templates/meta/xsd.txt',TemplateType.GString) + } + + @Override + String getDestFileName(Model dataModel, Map extraParameters, Type currentType=null) { + if (extraParameters.destFileName) { + return extraParameters.destFileName + } + else { + return "${string2Name(dataModel.title,false)}.xsd" + } + } + + @Override + String getDestDir(Model dataModel, String outputBasePath, Map extraParameters,Type currentType=null) { + if (extraParameters.outputDirExt) { + return outputBasePath + "/" + extraParameters.outputDirExt + } + else { + return outputBasePath + } + } + + Logger getLogger() { + return log + } +} diff --git a/src/main/groovy/de/lisaplus/atlas/interf/IExternalCodeGen.groovy b/src/main/groovy/de/lisaplus/atlas/interf/IExternalCodeGen.groovy index 61833b6..1eaf9e7 100644 --- a/src/main/groovy/de/lisaplus/atlas/interf/IExternalCodeGen.groovy +++ b/src/main/groovy/de/lisaplus/atlas/interf/IExternalCodeGen.groovy @@ -7,6 +7,12 @@ import de.lisaplus.atlas.model.Model * Created by eiko on 02.06.17. */ interface IExternalCodeGen extends ICodeGen { + /** + * initialize additional scripts that should be passed to the used templates + * @param generatorScripts + */ + void setGeneratorScript (String generatorScript) + /** * initialize the template file for the generator * @param templateFile diff --git a/src/main/groovy/de/lisaplus/atlas/model/AggregationType.groovy b/src/main/groovy/de/lisaplus/atlas/model/AggregationType.groovy new file mode 100644 index 0000000..8c3528a --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/model/AggregationType.groovy @@ -0,0 +1,9 @@ +package de.lisaplus.atlas.model +/** + * Type used in meta model + * Created by eiko on 01.06.17. + */ +enum AggregationType { + aggregation, composition +} + diff --git a/src/main/groovy/de/lisaplus/atlas/model/ArrayType.groovy b/src/main/groovy/de/lisaplus/atlas/model/ArrayType.groovy new file mode 100644 index 0000000..1aef64f --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/model/ArrayType.groovy @@ -0,0 +1,13 @@ +package de.lisaplus.atlas.model +/** + * Type used in meta model + * Created by eiko on 01.06.17. + */ +class ArrayType extends BaseType { + BaseType baseType + String name () { + return NAME + } + final static NAME='ARRAY' +} + diff --git a/src/main/groovy/de/lisaplus/atlas/model/BaseType.groovy b/src/main/groovy/de/lisaplus/atlas/model/BaseType.groovy index 36646ac..dce8d97 100644 --- a/src/main/groovy/de/lisaplus/atlas/model/BaseType.groovy +++ b/src/main/groovy/de/lisaplus/atlas/model/BaseType.groovy @@ -1,4 +1,5 @@ package de.lisaplus.atlas.model + /** * Type used in meta model * Created by eiko on 01.06.17. @@ -9,7 +10,105 @@ abstract class BaseType { final static String UNKNOWN_TYPE = 'CODEGEN-ERROR: Unknown type: ' final static String UNSUPPORTED_TYPE = 'CODEGEN-ERROR: Unsupported type: ' + /** + * This attribute is only used in case of XSD imports to avoid the loose of additional type information, f.e. double, float + */ + String originalType=null + abstract String name() + + /** + * Reuse existing copies or create new ones if they are missing. If the object is immutable, then the object itself + * is being returned. + * @param type The type to process, not null! + * @param typeCopies The types, which were already copied (mapping of type name to the copy of the corresponding Type) + * @return The copy of the BaseType + */ + static T copyOf(T type, Map typeCopies) { + Objects.requireNonNull(type) + switch(type) { + // handle immutable by just returning them + case BooleanType: + BooleanType copyB = new BooleanType() + copyB.isArray = type.isArray + return copyB + case ByteType: + ByteType copyB = new ByteType() + copyB.isArray = type.isArray + return copyB + case VoidType: + VoidType copyV = new VoidType() + copyV.isArray = type.isArray + return copyV + case UnsupportedType: + UnsupportedType copyU = new UnsupportedType() + copyU.isArray = type.isArray + return copyU + case UUIDType: + UUIDType copyUU = new UUIDType() + copyUU.isArray = type.isArray + return copyUU + case DateType: + DateType copyD = new DateType() + copyD.isArray = type.isArray + return copyD + case DateTimeType: + DateTimeType copyDT = new DateTimeType() + copyDT.isArray = type.isArray + return copyDT + case StringType: + StringType copyS = new StringType() + copyS.isArray = type.isArray + // Assume immutable! + copyS.minLength = type.minLength + copyS.maxLength = type.maxLength + copyS.pattern = type.pattern + return copyS + case IntType: + IntType copyI = new IntType() + copyI.isArray = type.isArray + // Assume immutable! + copyI.min = type.min + copyI.max = type.max + copyI.exclusiveMin = type.exclusiveMin + copyI.exclusiveMax = type.exclusiveMax + return copyI + case LongType: + LongType copyLong = new LongType() + copyLong.isArray = type.isArray + // Assume immutable! + copyLong.min = type.min + copyLong.max = type.max + copyLong.exclusiveMin = type.exclusiveMin + copyLong.exclusiveMax = type.exclusiveMax + return copyLong + case NumberType: + NumberType copyN = new NumberType() + copyN.isArray = type.isArray + // Assume immutable! + copyN.min = type.min + copyN.max = type.max + copyN.exclusiveMin = type.exclusiveMin + copyN.exclusiveMax = type.exclusiveMax + return copyN + case RefType: + RefType copyR = new RefType() + copyR.isArray = type.isArray + // Assume immutable! + copyR.typeName = type.typeName + // if (type.type != null) println "RefType triggers copy of type ${type.type.name}" + copyR.type = type.type == null ? null : Type.copyOf(type.type, typeCopies) + return copyR + case ComplexType: + ComplexType copyC = new ComplexType() + copyC.isArray = type.isArray + // if (type.type != null) println "ComplexType triggers copy of type ${type.type.name}" + copyC.type = type.type == null ? null : Type.copyOf(type.type, typeCopies) + return copyC + default: + throw new RuntimeException("Add handling for type ${type.class}") + } + } } class VoidType extends BaseType { @@ -19,13 +118,6 @@ class VoidType extends BaseType { final static NAME='VOID' } -abstract class MinMaxType extends BaseType { - def max - def exclusiveMax - def min - def exclusiveMin -} - /** * this type is choosen in too complex schemas * for instance: use of patternPoperties @@ -37,20 +129,6 @@ class UnsupportedType extends BaseType { final static NAME='UNSUPPORTED' } -class IntType extends MinMaxType { - String name () { - return NAME - } - final static NAME='INT' -} - -class NumberType extends MinMaxType { - String name () { - return NAME - } - final static NAME='NUMBER' -} - class StringType extends BaseType { def maxLength def minLength @@ -69,16 +147,6 @@ class UUIDType extends BaseType { final static NAME='UUID' } -class RefType extends BaseType { - def typeName - Type type - - String name () { - return NAME - } - final static NAME='REF' -} - class BooleanType extends BaseType { String name () { return NAME @@ -86,24 +154,10 @@ class BooleanType extends BaseType { final static NAME='BOOLEAN' } -class DateType extends MinMaxType { +class ByteType extends BaseType { String name () { return NAME } - final static NAME='DATE' + final static NAME='BYTE' } -class DateTimeType extends MinMaxType { - String name () { - return NAME - } - final static NAME='DATETIME' -} - -class ComplexType extends BaseType { - Type type - String name () { - return NAME - } - final static NAME='COMPLEX' -} diff --git a/src/main/groovy/de/lisaplus/atlas/model/ComplexType.groovy b/src/main/groovy/de/lisaplus/atlas/model/ComplexType.groovy new file mode 100644 index 0000000..4f7f663 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/model/ComplexType.groovy @@ -0,0 +1,13 @@ +package de.lisaplus.atlas.model +/** + * Type used in meta model + * Created by eiko on 01.06.17. + */ +class ComplexType extends BaseType { + Type type + String name () { + return NAME + } + final static NAME='COMPLEX' +} + diff --git a/src/main/groovy/de/lisaplus/atlas/model/DateTimeType.groovy b/src/main/groovy/de/lisaplus/atlas/model/DateTimeType.groovy new file mode 100644 index 0000000..79b13f5 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/model/DateTimeType.groovy @@ -0,0 +1,12 @@ +package de.lisaplus.atlas.model +/** + * Type used in meta model + * Created by eiko on 01.06.17. + */ +class DateTimeType extends MinMaxType { + String name () { + return NAME + } + final static NAME='DATETIME' +} + diff --git a/src/main/groovy/de/lisaplus/atlas/model/DateType.groovy b/src/main/groovy/de/lisaplus/atlas/model/DateType.groovy new file mode 100644 index 0000000..65741d2 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/model/DateType.groovy @@ -0,0 +1,12 @@ +package de.lisaplus.atlas.model +/** + * Type used in meta model + * Created by eiko on 01.06.17. + */ +class DateType extends MinMaxType { + String name () { + return NAME + } + final static NAME='DATE' +} + diff --git a/src/main/groovy/de/lisaplus/atlas/model/InnerType.groovy b/src/main/groovy/de/lisaplus/atlas/model/InnerType.groovy new file mode 100644 index 0000000..8a4ae34 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/model/InnerType.groovy @@ -0,0 +1,15 @@ +package de.lisaplus.atlas.model + +/** + * this type is for inner declarations of complex types + */ +class InnerType extends Type { + + InnerType() { + super() + } + + InnerType(InnerType type) { + super() + } +} diff --git a/src/main/groovy/de/lisaplus/atlas/model/MinMaxType.groovy b/src/main/groovy/de/lisaplus/atlas/model/MinMaxType.groovy new file mode 100644 index 0000000..1aa86dd --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/model/MinMaxType.groovy @@ -0,0 +1,32 @@ +package de.lisaplus.atlas.model; +/** + * Type used in meta model + * Created by eiko on 01.06.17. + */ +abstract class MinMaxType extends BaseType { + def max + def exclusiveMax + def min + def exclusiveMin +} + +class IntType extends MinMaxType { + String name () { + return NAME + } + final static NAME='INT' +} + +class LongType extends MinMaxType { + String name () { + return NAME + } + final static NAME='LONG' +} + +class NumberType extends MinMaxType { + String name () { + return NAME + } + final static NAME='NUMBER' +} diff --git a/src/main/groovy/de/lisaplus/atlas/model/Model.groovy b/src/main/groovy/de/lisaplus/atlas/model/Model.groovy index 4b7efc0..4706b74 100644 --- a/src/main/groovy/de/lisaplus/atlas/model/Model.groovy +++ b/src/main/groovy/de/lisaplus/atlas/model/Model.groovy @@ -42,7 +42,7 @@ class Model { types.findAll { it.name!=t.name && (!(it instanceof InnerType) )}. each { currentType -> currentType.properties.find { - it.type instanceof RefType && it.type.type.name==t.name }.each { + it.type instanceof RefType && it.type.type && it.type.type.name==t.name }.each { if (!t.refOwner.contains(currentType)) { t.refOwner.add(currentType) } @@ -52,166 +52,77 @@ class Model { } /** - * checks whether the model has some errors + * looking for properties that contain objects of the same type as the property host + * @param model */ - void checkModelForErrors() { - // TODO + void setPropertySelfContainmentFlag() { + types.each { type -> + type.properties.each { prop -> + if (prop.isRefTypeOrComplexType() && type.class==prop.type.type) { + prop.selfContainment = true + } + } + } } -} - -class Type { - /** - * name for that type. - * Genarally build form JSON schema. For single type schemas first normalized content of title field is used. If - * ther is not title entry file name is used for type name. - * In multi type schemas key unter definitions section is used as type name - */ - String name - - /** - * for Stephan :) - */ - String color='#000000' - - /** - * List of properties, type of PropertyType - */ - List properties=[] - def description - - /** - * List of required properties, String list with property names - */ - List requiredProps=[]; - - /** - * List of inheritance base types - currently only used for nice plantuml diagramms - * Inheritance is not real implemented. attributes of a base Type are simply copied to the - * derived Type. But sometime the usage of the term inheritance simplify communication - */ - List baseTypes=[] - - /** - * since when is the type part of the model - */ - int sinceVersion - /** - * types that reference this type - */ - List refOwner=[] - - /** - * array of free definable strings to add keywords to types and attributes. - * This keywords can be used to select or deselect types or attributes while code generation + * looking for properties that are implicit reference on objects of the same type as the property host + * @param model */ - List tags=[] - - String toString() { - return ToStringBuilder.reflectionToString(this); - } - - void initFromType (Type t) { - this.name = t.name - this.properties = t.properties - this.description = t.description - this.requiredProps = t.requiredProps - this.sinceVersion = t.sinceVersion - } - - boolean equals(Object b) { - if (! b instanceof Type) return false - return name==b.name - } - - boolean isInnerType() { - return this instanceof InnerType + void setPropertySelfReferenceFlag() { + types.each { type -> + type.properties.each { prop -> + if (prop.type instanceof UUIDType && prop.implicitRef && prop.implicitRef.type && prop.implicitRef.type.class==type.class) { + prop.selfReference = true + } + } + } } -} - -/** - * This type is used to handle the use of schema types before they are declared in a schema. - * This could happen with references - */ -class DummyType extends Type { - /** - * List of RefType objects. After the real Type is created, it's needed to set the right references - */ - def referencesToChange=[] -} - -/** - * this type is for inner declarations of complex types - */ -class InnerType extends Type { -} - -/** - * this type describes an external type - */ -class ExternalType extends Type { - String refStr -} - -enum AggregationType { - aggregation, composition -} -class Property { - def description - def name - def format - - /** - * For entries that points to a global uuid, this flag enable the explizit relation to connected type - */ - BaseType implicitRef - /** - * marks the property how it should implemented, as reference or as object - */ - AggregationType aggregationType=AggregationType.composition /** - * Type of property field, covers also if the property is an array + * final process step after loading of the model */ - BaseType type - /** - * since when is the type part of the model - */ - int sinceVersion + void postProcess() { + initRefOwnerForTypes() + setPropertySelfContainmentFlag() + setPropertySelfReferenceFlag() + checkModelForErrors() + } /** - * array of free definable strings to add keywords to types and attributes. - * This keywords can be used to select or deselect types or attributes while code generation + * checks whether the model has some errors */ - List tags=[] - - - String toString() { - return ToStringBuilder.reflectionToString(this); - } - - boolean isRefTypeOrComplexType() { - return type && ( type instanceof RefType || type instanceof ComplexType ) - } - - boolean isRefType() { - return type && ( type instanceof RefType) - } - - boolean isComplexType() { - return type && ( type instanceof ComplexType ) - } - - boolean implicitRefIsRefType() { - return implicitRef && ( implicitRef instanceof RefType) + void checkModelForErrors() { + // TODO } - boolean implicitRefIsComplexType() { - return implicitRef && ( implicitRef instanceof ComplexType ) - } + void markOnlyBaseTypes() { + def baseTypeNames = [] + // collect baseType names + types.findAll { type -> return type.baseTypes }.each { type -> + type.baseTypes.each { baseTypeName -> + if (!baseTypeNames.contains(baseTypeName)) { + baseTypeNames.add(baseTypeName) + } + } + } + // collect all baseType-names that are also property types + def foundBaseTypeNames=[] + types.findAll { type -> return ! baseTypeNames.contains(type.name) }.each { type -> + // all types their name is no base type name + type.properties.findAll { prop -> return prop.isRefType() }.each { prop -> + if (baseTypeNames.contains(prop.type.type.name)) { + foundBaseTypeNames.add(prop.type.type.name) + } + } + } - boolean isAggregation() { - return aggregationType==AggregationType.aggregation + // mark all types that are not additional property types + types.findAll { type -> return baseTypeNames.contains(type.name) }.each { type -> + if (!foundBaseTypeNames.contains(type.name)) { +// type.onlyBaseType = true + types.remove(type) + } + } } } \ No newline at end of file diff --git a/src/main/groovy/de/lisaplus/atlas/model/Property.groovy b/src/main/groovy/de/lisaplus/atlas/model/Property.groovy new file mode 100644 index 0000000..dcb5791 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/model/Property.groovy @@ -0,0 +1,121 @@ +package de.lisaplus.atlas.model + +import org.apache.commons.lang3.builder.ToStringBuilder + +class Property { + def description + def name + def format + + /** + * For entries that points to a global uuid, this flag enable the explicit relation to connected type + */ + BaseType implicitRef + /** + * marks the property how it should implemented, as reference or as object + */ + AggregationType aggregationType=AggregationType.composition + /** + * Type of property field, covers also if the property is an array + */ + BaseType type + /** + * since when is the type part of the model + */ + int sinceVersion + + /** true if prop contains object of same type as property host */ + boolean selfContainment=false + + /** true if prop is implicit reference of same type as property host */ + boolean selfReference=false + + /** + * array of free definable strings to add keywords to types and attributes. + * This keywords can be used to select or deselect types or attributes while code generation + */ + List tags=[] + + /** + * Initializes the fields of this Property to equal to that of the source + * @param source The object to copy from + * @param typeCopies The types, which were already copied (mapping of type name to the copy of the corresponding Type) + */ + void initCopy(Property source, Map typeCopies) { + // Assume Strings / immutable + description = source.description + name = source.name + format = source.format + if (source.implicitRef == null) { + implicitRef = null + } else { + // reuse existing copies or create new ones if they are missing + implicitRef = BaseType.copyOf(source.type, typeCopies) + } + aggregationType = source.aggregationType + type = source.type == null ? null : BaseType.copyOf(source.type, typeCopies) + + sinceVersion = source.sinceVersion + selfContainment = source.selfContainment + selfReference = source.selfReference + tags = source.tags==null ? null : new ArrayList<>(source.tags) + } + + String toString() { + return ToStringBuilder.reflectionToString(this) + } + + boolean isRefTypeOrComplexType() { + boolean b = type && ( type instanceof RefType || type instanceof ComplexType) + if (!b) return false + if (type.type instanceof MinMaxType) return false + if (type.type instanceof BooleanType) return false + if (type.type instanceof ByteType) return false + if (type.type instanceof StringType) return false + if (type.type instanceof UnsupportedType) return false + if (type.type instanceof UUIDType) return false + if (type.type instanceof VoidType) return false + return true + } + + boolean isRefType() { + return type && ( type instanceof RefType) + } + + boolean isArrayType() { + return type && ( type instanceof ArrayType || ( type instanceof RefType && type.isArray )) + //return type && ( type instanceof ArrayType) + } + + boolean isComplexType() { + return type && ( type instanceof ComplexType ) + } + + boolean implicitRefIsRefType() { + return implicitRef && ( implicitRef instanceof RefType) + } + + boolean implicitRefIsComplexType() { + return implicitRef && ( implicitRef instanceof ComplexType ) + } + + boolean isAggregation() { + return aggregationType==AggregationType.aggregation + } + + boolean hasTag(String tag) { + return tags && tags.contains(tag) + } + + /** + * The default value defined in the tags, or null, if none was defined. + * The default value tag defaultTrue is returned as True! + */ + String getDefaultValue() { + def value = null + if (tags) { + value = tags.find {it =~ 'default.*'}?.drop(7) + } + return value + } +} diff --git a/src/main/groovy/de/lisaplus/atlas/model/RefType.groovy b/src/main/groovy/de/lisaplus/atlas/model/RefType.groovy new file mode 100644 index 0000000..025217a --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/model/RefType.groovy @@ -0,0 +1,15 @@ +package de.lisaplus.atlas.model +/** + * Type used in meta model + * Created by eiko on 01.06.17. + */ +class RefType extends BaseType { + def typeName + Type type + + String name () { + return NAME + } + final static NAME='REF' +} + diff --git a/src/main/groovy/de/lisaplus/atlas/model/Type.groovy b/src/main/groovy/de/lisaplus/atlas/model/Type.groovy new file mode 100644 index 0000000..3bd0a75 --- /dev/null +++ b/src/main/groovy/de/lisaplus/atlas/model/Type.groovy @@ -0,0 +1,246 @@ +package de.lisaplus.atlas.model + +import org.apache.commons.lang3.builder.ToStringBuilder + +/** + * Type used in meta model + * Created by eiko on 01.06.17. + */ +class Type { + /** + * name for that type. + * Genarally build form JSON schema. For single type schemas first normalized content of title field is used. If + * ther is not title entry file name is used for type name. + * In multi type schemas key unter definitions section is used as type name + */ + String name + + /** + * for Stephan :) + */ + String color='#000000' + + /** + * path where the schema for that type was located + */ + String schemaPath + + /** + * path where the schema for that type was located + */ + private String schemaFileName + + /** + * List of properties, type of PropertyType + */ + List properties=[] + def description + + /** + * List of required properties, String list with property names + */ + List requiredProps=[] + + /** + * List of inheritance base types - currently only used for nice plantuml diagramms + * Inheritance is not real implemented. attributes of a base Type are simply copied to the + * derived Type. But sometime the usage of the term inheritance simplify communication + */ + List baseTypes=[] + + /** + * since when is the type part of the model + */ + int sinceVersion + + /** + * types that reference this type + */ + List refOwner=[] + + /** + * true if the current type is only used as base type + */ + boolean onlyBaseType=false + + /** + * array of free definable strings to add keywords to types and attributes. + * This keywords can be used to select or deselect types or attributes while code generation + */ + List tags=[] + + /** + * What is the version of a complex or reference type. + * A "__version" entry in the type is used to populate this + */ + int version=0 + + Type() {} + + /** + * Initializes the fields of this Type to equal to that of the source + * @param source The object to copy from + * @param typeCopies The types, which were already copied (mapping of type name to corresponding Type object) + */ + void initCopy(Type source, Map typeCopies) { + this.name = source.name + this.color = source.color + this.tags = source.tags == null ? null : new ArrayList<>(source.tags) + def propCopy = { Property pSource -> + if (pSource == null) + return null + Property copy = new Property() + copy.initCopy(pSource, typeCopies) + return copy + } + this.properties = source.properties == null ? null : source.properties.collect { p -> /*println "type=${source.name} selfRef=${p.selfReference} prop=${p.name}"; return*/ propCopy.call(p) } + // Assumes immutable! + this.description = source.description + this.requiredProps = source.requiredProps == null ? null : new ArrayList<>(source.requiredProps) + this.baseTypes = source.baseTypes == null ? null : new ArrayList<>(source.baseTypes) + this.sinceVersion = source.sinceVersion + this.refOwner = source.refOwner == null ? null : source.refOwner.collect { owner -> Type.copyOf(owner, typeCopies)} + this.onlyBaseType = source.onlyBaseType + this.tags = source.tags== null ? null : new ArrayList<>(source.tags) + } + + String toString() { + return ToStringBuilder.reflectionToString(this); + } + + void initFromType (Type t) { + // TODO Check whether incomplete initialization is necessary / intended! + this.name = t.name + this.version = t.version + this.tags = t.tags + this.schemaPath = t.schemaPath + this.schemaFileName = t.schemaFileName + this.properties = t.properties + this.description = t.description + this.requiredProps = t.requiredProps + this.sinceVersion = t.sinceVersion + } + + /** + * + * @param model string of the model file name: f.e. junction, junction.json + */ + boolean isMainType(String model) { + if (model==null || this.schemaFileName==null || (this instanceof InnerType)) return false + String mStr = model.indexOf('.')!=-1 ? model.substring(0,model.lastIndexOf('.')) : model + return (!this.innerType) && this.schemaFileName==mStr + } + + boolean equals(Object b) { + if (! b instanceof Type) return false + return name==b.name + } + + boolean isInnerType() { + return this instanceof InnerType + } + + boolean hasTag(String tag) { + return tags && tags.contains(tag) + } + + boolean hasPropertyWithTag(String tag) { + if (!tags) return false + if (!properties) return false + return properties.find { prop -> + return prop.tags && prop.tags.contains(tag) + } != null + } + + boolean hasPropertyWithName(String name) { + if (!properties) return false + return properties.find { prop -> + return name == prop.name + } != null + } + + /** + * Reuse existing copies or create new ones if they are missing + * @param type The type to copy + * @param typeCopies The types, which were already copied (mapping of type name to the copy of the corresponding Type) + * @return The copy of the type + */ + static T copyOf(T type, Map typeCopies) { + Objects.requireNonNull(type) + T copy = (T) typeCopies.get(type.name) + if (copy == null) { + // println "Copying ${type.name}" + switch (type) { + case DummyType: + copy = DummyType() + break + case ExternalType: + copy = new ExternalType() + break + case InnerType: + copy = new InnerType() + break + case Type: + copy = new Type() + break + default: + throw new RuntimeException("Add handling for type ${type.class}") + } + typeCopies.put(type.name, copy) + copy.initCopy(type, typeCopies) + } + return copy + } + + String getSchemaFileName() { + return schemaFileName + } + + void setSchemaFileName(String schemaFileName) { + this.schemaFileName = schemaFileName + if (this.schemaFileName!=null) { + this.schemaFileName = this.schemaFileName.replaceAll('\\.json','') + this.schemaFileName = this.schemaFileName.replaceAll('\\.xsd','') + this.schemaFileName = this.schemaFileName.replaceAll('\\.','') + this.schemaFileName = this.schemaFileName.replaceAll('/','') + } + } +} + +/** + * This type is used to handle the use of schema types before they are declared in a schema. + * This could happen with references + */ +class DummyType extends Type { + /** + * List of BaseType or RefType objects. After the real Type is created, it's needed to set the right references + */ + List referencesToChange=[] + + @Override + void initCopy(Type source, Map typeCopies) { + super.initCopy(source, typeCopies) + referencesToChange = source.referencesToChange == null ? null : source.referencesToChange.collect { ref -> BaseType.copyOf(ref, typeCopies) } + } +} + +/** + * this type describes an external type + */ +class ExternalType extends Type { + String refStr + + @Override + void initCopy(Type source, Map typeCopies) { + super.initCopy(source, typeCopies) + this.refStr = source.refStr + } +} + +/** + * type that implements enums + */ +class EnumType extends Type { + // defined values for that enum + String[] allowedValues=[] +} diff --git a/src/main/resources/bin/jsonCodeGen.sh b/src/main/resources/bin/jsonCodeGen.sh index f661d42..5ae39ad 100755 --- a/src/main/resources/bin/jsonCodeGen.sh +++ b/src/main/resources/bin/jsonCodeGen.sh @@ -26,6 +26,15 @@ if [ ! -x "$JAVACMD" ] ; then exit 1 fi +case $OSTYPE in + "cygwin"|"msys"|"win32") + pathSep=";" + ;; + *) + pathSep=":" + ;; +esac + args= for arg in "$@"; do @@ -40,4 +49,13 @@ else LOGDIR="$JSONCODEGEN_HOME/conf" fi -$JAVACMD -cp "$JSONCODEGEN_LIB_DIR/*" "-Dlogback.configurationFile=$scriptPos/conf/logback.xml\" de.lisaplus.atlas.DoCodeGen $args \ No newline at end of file +# preset for defining Java classes for DateType and DateTimeType, see JavaTypeConvert.groovy +# Possible values are legacy,310.local,310.offset and 310.zoned +preset=310.local + +if [ -z "$ADDITIONAL_TEMPLATE_DIR" ]; then + "$JAVACMD" -cp "$JSONCODEGEN_LIB_DIR/*" "-Dlogback.configurationFile=$scriptPos/conf/logback.xml" "-Ddate.type.preset=$preset" de.lisaplus.atlas.DoCodeGen $args +else + # an additional direcotry with custom templates is give - needed for sub templates + "$JAVACMD" -cp "$JSONCODEGEN_LIB_DIR/*$pathSep$ADDITIONAL_TEMPLATE_DIR" "-Dlogback.configurationFile=$scriptPos/conf/logback.xml" "-Ddate.type.preset=$preset" de.lisaplus.atlas.DoCodeGen $args +fi diff --git a/src/main/resources/bin/jsonCodeGenDebug.sh b/src/main/resources/bin/jsonCodeGenDebug.sh new file mode 100755 index 0000000..d2af8f0 --- /dev/null +++ b/src/main/resources/bin/jsonCodeGenDebug.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +scriptPos=${0%/*} + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + JAVACMD="$JAVA_HOME/jre/sh/java" + elif [ -x "$JAVA_HOME/jre/bin/java" ] ; then + JAVACMD="$JAVA_HOME/jre/bin/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD=`which java 2> /dev/null ` + if [ -z "$JAVACMD" ] ; then + JAVACMD=java + fi + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." + echo " We cannot execute $JAVACMD" + exit 1 +fi + +case $OSTYPE in + "cygwin"|"msys"|"win32") + pathSep=";" + ;; + *) + pathSep=":" + ;; +esac + +args= +for arg in "$@"; +do + args="$args \"$arg\"" +done + +if [ -d $scriptPos/lib ]; then + JSONCODEGEN_LIB_DIR=$scriptPos/lib + LOGDIR=$scriptPos/conf +else + JSONCODEGEN_LIB_DIR="$JSONCODEGEN_HOME/lib" + LOGDIR="$JSONCODEGEN_HOME/conf" +fi + +# preset for defining Java classes for DateType and DateTimeType, see JavaTypeConvert.groovy +# Possible values are legacy,310.local,310.offset and 310.zoned +preset=310.local + +if [ -z "$ADDITIONAL_TEMPLATE_DIR" ]; then + "$JAVACMD" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8100 -cp "$JSONCODEGEN_LIB_DIR/*" "-Dlogback.configurationFile=$scriptPos/conf/logback.xml" "-Ddate.type.preset=$preset" de.lisaplus.atlas.DoCodeGen $args +else + # an additional direcotry with custom templates is give - needed for sub templates + "$JAVACMD" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8100 -cp "$JSONCODEGEN_LIB_DIR/*$pathSep$ADDITIONAL_TEMPLATE_DIR" "-Dlogback.configurationFile=$scriptPos/conf/logback.xml" "-Ddate.type.preset=$preset" de.lisaplus.atlas.DoCodeGen $args +fi diff --git a/src/main/resources/docs/usage.md b/src/main/resources/docs/usage.md index b518595..ae37c0d 100644 --- a/src/main/resources/docs/usage.md +++ b/src/main/resources/docs/usage.md @@ -26,7 +26,7 @@ de.lisaplus.atlas.DoCodeGen -o ./test/mein_output -m ./test/mein_modell.json -g ``` ```bash # creates a json schema from test schema - only for test cases -build/release/jsonCodeGen.sh -o /tmp -m src/test/resources/test_schemas/multiType.json \ + build/release/jsonCodeGen.sh -o /tmp -m src/test/resources/test_schemas/multiType.json \ -g singlefile=src/main/resources/templates/meta/json_schema.txt \ -gp destFileName=test.json @@ -45,7 +45,8 @@ build/release/jsonCodeGen.sh -o /tmp/test_beans -m src/test/resources/test_schem - removeEmptyLines - if set, then empty lines will be removed before file is written - containsAttrib - name of an attribute that is needed for the generation - missingAttrib - name of an attribute that the type doesn't contain for generation - + - versionConst - set with any value will add a version const into the bean + * java_interfaces - creates a set of Java interfaces from the model - packageName - what is the package for the generated beans (!) - removeEmptyLines - if set, then empty lines will be removed before file is written @@ -93,12 +94,17 @@ build/release/jsonCodeGen.sh -o /tmp/test_beans -m src/test/resources/test_schem - destFileName - name of the generated file - removeEmptyLines - if set, then empty lines will be removed before file is written +### PlantUML + - printTags - if set to true then plantuml prints also tags to the diagrams + - guidTypeColor - if given that color is used as background for the guid-types, f.e. -gp guidTypeColor=adadad + ### Base generators * multifiles - creates multiple files from model and extra given template - includeExtTypes - also use external type to generate files - outputDirExt - a needed subpath below the given outputBasePath - removeEmptyLines - if set, then empty lines will be removed before file is written - destFileNamePre - standard prefix for the created file names + - destFileNamePost - standard suffix for the created file names - destFileNameExt - file extension for the generated files * singlefile - creates single file from model and extra given template diff --git a/src/main/resources/templates/java/bean.txt b/src/main/resources/templates/java/bean.txt index b0a1b4b..cec599e 100644 --- a/src/main/resources/templates/java/bean.txt +++ b/src/main/resources/templates/java/bean.txt @@ -1,23 +1,73 @@ -package ${extraParam.packageName}; +<% +boolean versionConst = extraParam.versionConst + +def memberVisibility = extraParam.memberVisibility && extraParam.memberVisibility.toLowerCase()=='protected' ? 'protected' : 'private' +def hasUuid = !currentType.properties.findAll { it.type.name() == 'UUID'}.isEmpty() +def importStr = hasUuid ? '\nimport java.util.UUID;\n' : '' + +%>package ${extraParam.packageName}; +${importStr} +// This class is generated by the jsonCodeGen bean template (bean.txt), changes have to implement there <% if ( currentType.description ) { %> /** * ${currentType.description} */ -<% } %> -public class ${ firstUpperCase.call(currentType.name) } { - <% currentType.properties.each { prop -> %> - <% if (prop.description) { %> +<% } +if (currentType instanceof de.lisaplus.atlas.model.EnumType) { +%>public enum ${ upperCamelCase.call(currentType.name) } { + <% boolean first=true; currentType.allowedValues.each { value -> + if (first) {%> + ${value} + <% first=false} else { %> + ,${value} + <% } } %> +} +<% } else { +%>public class ${ upperCamelCase.call(currentType.name) } { + <% if (versionConst) { + %> /** - * ${prop.description} + * Resulting from the model description */ - <% } %> - private ${ typeToJava.call(prop.type) } ${prop.name}; + public final static int TYPE_VERSION=${currentType.version}; + /** + * Version information for a concrete object. It can differ from model type version f.e. in + * case of deserialization from message broker content + */ + private Integer _objectVersion=${currentType.version}; + + public void set_objectVersion(Integer version) { + this._objectVersion = version; + } + + public Integer get_objectVersion() { + return this._objectVersion; + } + + <% } %> + <% currentType.properties.each { prop -> %> + <% if (prop.description) { + %>/** + * ${prop.description} + */ + <% } + if (!prop.type.isArray) { + %>${memberVisibility} ${ typeToJava.call(prop.type) } ${prop.name}; + <% } else { + %>${memberVisibility} ${ typeToJava.call(prop.type) } ${prop.name} = new java.util.ArrayList<>(); + <% } + %> public ${ typeToJava.call(prop.type) } get${ firstUpperCase.call(prop.name) } () { return this.${prop.name}; } public void set${ firstUpperCase.call(prop.name) } (${ typeToJava.call(prop.type) } ${prop.name}) { - this.${prop.name} = ${prop.name}; + <% if (!prop.type.isArray) { + %>this.${prop.name} = ${prop.name}; + <% } else { + %>this.${prop.name} = ${prop.name}!=null ? ${prop.name} : new java.util.ArrayList<>(); + <% } + %> } <% } %> @@ -25,17 +75,27 @@ public class ${ firstUpperCase.call(currentType.name) } { @Override public boolean equals(Object obj) { if (obj==null) return false; - if ( ! (obj instanceof ${ firstUpperCase.call(currentType.name) })) return false; + if ( ! (obj instanceof ${ upperCamelCase.call(currentType.name) })) return false; - ${ firstUpperCase.call(currentType.name) } _typeInst = (${ firstUpperCase.call(currentType.name) }) obj; - <% currentType.properties.each { prop -> %> + ${ upperCamelCase.call(currentType.name) } _typeInst = (${ upperCamelCase.call(currentType.name) }) obj; + // handling of non-array-types + <% currentType.properties.findAll{ prop -> ! prop.type.isArray }.each { prop -> %> ${ typeToJava.call(prop.type) } _${prop.name} = _typeInst.get${ firstUpperCase.call(prop.name) } (); if (this.${prop.name} == null && _${prop.name} != null) return false; if (this.${prop.name} != null) { if (!this.${prop.name}.equals(_${prop.name})) return false; } + <% } %> + // handling of array-types + <% currentType.properties.findAll{ prop -> prop.type.isArray }.each { prop -> %> + ${ typeToJava.call(prop.type) } _${prop.name} = _typeInst.get${ firstUpperCase.call(prop.name) } (); + if (this.${prop.name} == null && (_${prop.name} != null && _${prop.name}.size()>0)) return false; + if (this.${prop.name} != null && this.${prop.name}.size()>0) { + if (!this.${prop.name}.equals(_${prop.name})) return false; + } <% } %> return true; } -} \ No newline at end of file +} +<% } %> \ No newline at end of file diff --git a/src/main/resources/templates/java/generic_derived_bean.txt b/src/main/resources/templates/java/generic_derived_bean.txt index 8d851e0..08fbd4b 100644 --- a/src/main/resources/templates/java/generic_derived_bean.txt +++ b/src/main/resources/templates/java/generic_derived_bean.txt @@ -1,39 +1,59 @@ package ${extraParam.packageName}; +// This class is generated by the jsonCodeGen bean template (generic_derived_bean.txt), changes have to implement there <% if ( currentType.description ) { %> /** * ${currentType.description} */ <% } %> -public class ${ firstUpperCase.call(currentType.name) } extends ${extraParam.genericClass} <${ firstUpperCase.call(currentType.name) }> { +public class ${ upperCamelCase.call(currentType.name) } extends ${extraParam.genericClass} <${ upperCamelCase.call(currentType.name) }> { <% currentType.properties.each { prop -> %> - <% if (prop.description) { %> - /** + <% if (prop.description) { + %>/** * ${prop.description} */ - <% } %> - private ${ typeToJava.call(prop.type) } ${prop.name}; - + <% } + if (!prop.type.isArray) { + %>${memberVisibility} ${ typeToJava.call(prop.type) } ${prop.name}; + <% } else { + %>${memberVisibility} ${ typeToJava.call(prop.type) } ${prop.name} = new java.util.ArrayList<>(); + <% } + %> public ${ typeToJava.call(prop.type) } get${ firstUpperCase.call(prop.name) } () { return this.${prop.name}; } public void set${ firstUpperCase.call(prop.name) } (${ typeToJava.call(prop.type) } ${prop.name}) { - this.${prop.name} = ${prop.name}; + <% if (!prop.type.isArray) { + %>this.${prop.name} = ${prop.name}; + <% } else { + %>this.${prop.name} = ${prop.name}!=null ? ${prop.name} : new java.util.ArrayList<>(); + <% } + %> } <% } %> + @Override public boolean equals(Object obj) { if (obj==null) return false; - if ( ! (obj instanceof ${ firstUpperCase.call(currentType.name) })) return false; + if ( ! (obj instanceof ${ upperCamelCase.call(currentType.name) })) return false; - ${ firstUpperCase.call(currentType.name) } _typeInst = (${ firstUpperCase.call(currentType.name) }) obj; - <% currentType.properties.each { prop -> %> + ${ upperCamelCase.call(currentType.name) } _typeInst = (${ upperCamelCase.call(currentType.name) }) obj; + // handling of non-array-types + <% currentType.properties.findAll{ prop -> ! prop.type.isArray }.each { prop -> %> ${ typeToJava.call(prop.type) } _${prop.name} = _typeInst.get${ firstUpperCase.call(prop.name) } (); if (this.${prop.name} == null && _${prop.name} != null) return false; if (this.${prop.name} != null) { if (!this.${prop.name}.equals(_${prop.name})) return false; } + <% } %> + // handling of array-types + <% currentType.properties.findAll{ prop -> prop.type.isArray }.each { prop -> %> + ${ typeToJava.call(prop.type) } _${prop.name} = _typeInst.get${ firstUpperCase.call(prop.name) } (); + if (this.${prop.name} == null && (_${prop.name} != null && _${prop.name}.size()>0)) return false; + if (this.${prop.name} != null && this.${prop.name}.size()>0) { + if (!this.${prop.name}.equals(_${prop.name})) return false; + } <% } %> return true; } diff --git a/src/main/resources/templates/java/interfaced_bean.txt b/src/main/resources/templates/java/interfaced_bean.txt index 0a5e630..d22dad8 100644 --- a/src/main/resources/templates/java/interfaced_bean.txt +++ b/src/main/resources/templates/java/interfaced_bean.txt @@ -10,7 +10,7 @@ import ${extraParam.interfacePackageName}.*; * ${currentType.description} */ <% } %> -public class ${ firstUpperCase.call(currentType.name) } implements I${ firstUpperCase.call(currentType.name) } { +public class ${ upperCamelCase.call(currentType.name) } implements I${ firstUpperCase.call(currentType.name) } { <% currentType.properties.each { prop -> %> <% if (prop.description) { %> /** diff --git a/src/main/resources/templates/java/interfaced_generic_derived_bean.txt b/src/main/resources/templates/java/interfaced_generic_derived_bean.txt index b6e32b3..f8dd790 100644 --- a/src/main/resources/templates/java/interfaced_generic_derived_bean.txt +++ b/src/main/resources/templates/java/interfaced_generic_derived_bean.txt @@ -10,7 +10,7 @@ import ${extraParam.interfacePackageName}.*; * ${currentType.description} */ <% } %> -public class ${ firstUpperCase.call(currentType.name) } extends ${extraParam.genericClass} <${ firstUpperCase.call(currentType.name) }> +public class ${ upperCamelCase.call(currentType.name) } extends ${extraParam.genericClass} <${ upperCamelCase.call(currentType.name) }> implements I${ firstUpperCase.call(currentType.name) } { <% currentType.properties.each { prop -> %> <% if (prop.description) { %> @@ -30,9 +30,9 @@ public class ${ firstUpperCase.call(currentType.name) } extends ${extraParam.gen @Override public boolean equals(Object obj) { if (obj==null) return false; - if ( ! (obj instanceof ${ firstUpperCase.call(currentType.name) })) return false; + if ( ! (obj instanceof ${ upperCamelCase.call(currentType.name) })) return false; - ${ firstUpperCase.call(currentType.name) } _typeInst = (${ firstUpperCase.call(currentType.name) }) obj; + ${ upperCamelCase.call(currentType.name) } _typeInst = (${ upperCamelCase.call(currentType.name) }) obj; <% currentType.properties.each { prop -> %> ${ typeToJava.call(prop.type) } _${prop.name} = _typeInst.get${ firstUpperCase.call(prop.name) } (); if (this.${prop.name} == null && _${prop.name} != null) return false; diff --git a/src/main/resources/templates/java/mongo_bean.txt b/src/main/resources/templates/java/mongo_bean.txt new file mode 100644 index 0000000..d9cba9b --- /dev/null +++ b/src/main/resources/templates/java/mongo_bean.txt @@ -0,0 +1,114 @@ +<% def isJoinedType = currentType.properties.find { prop -> return prop.hasTag('join') && prop.implicitRefIsRefType() } !=null %> + +package ${extraParam.packageName}; + +import de.lisaplus.util.mongodb.DbFactory; +import org.bson.Document; +import java.util.ArrayList; +<% if (currentType.tags.contains('mongodb')) { %> +import de.lisaplus.util.mongodb.SerializationException; +import de.lisaplus.util.mongodb.ObjNotFoundException; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Projections; +import com.mongodb.client.MongoCursor; +import org.bson.conversions.Bson; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.AggregateIterable; +import com.mongodb.client.model.Aggregates; +import com.mongodb.client.model.Filters; +import org.bson.types.*; +import java.util.Arrays; +import java.util.UUID; +<% } %> + +<% if ( currentType.description ) { %> +/** + * ${currentType.description} + */ +<% } %> +public class ${ upperCamelCase.call(currentType.name) } { + <% currentType.properties.each { prop -> %> + <% if (prop.description) { %> + /** + * ${prop.description} + */ + <% } %> + private ${ typeToJava.call(prop.type) } ${prop.name}; + public ${ typeToJava.call(prop.type) } get${ firstUpperCase.call(prop.name) } () { return this.${prop.name}; } + public void set${ firstUpperCase.call(prop.name) } (${ typeToJava.call(prop.type) } ${prop.name}) { + this.${prop.name} = ${prop.name}; + } + + <% } %> + + @Override + public boolean equals(Object obj) { + if (obj==null) return false; + if ( ! (obj instanceof ${ upperCamelCase.call(currentType.name) })) return false; + + ${ upperCamelCase.call(currentType.name) } _typeInst = (${ upperCamelCase.call(currentType.name) }) obj; + <% currentType.properties.each { prop -> %> + ${ typeToJava.call(prop.type) } _${prop.name} = _typeInst.get${ firstUpperCase.call(prop.name) } (); + if (this.${prop.name} == null && _${prop.name} != null) return false; + if (this.${prop.name} != null) { + if (!this.${prop.name}.equals(_${prop.name})) return false; + } + <% } %> + return true; + } + + <% if (!currentType.tags.contains('mongodb')) { %> + public static ${ upperCamelCase.call(currentType.name) } createFromDocument(Document document) { + if (document==null) return null; + ${ upperCamelCase.call(currentType.name) } newObj = new ${ upperCamelCase.call(currentType.name) }(); + // handle base types + <% currentType.properties.findAll {prop -> return ! prop.isRefTypeOrComplexType() }.each { prop -> %> + newObj.${prop.name} = (${ typeToJava.call(prop.type) }) document.get(${toUpperCase.call(prop.name)}); + <% } %> + // handle complex types + <% currentType.properties.findAll {prop -> return prop.isRefTypeOrComplexType() }.each { prop -> %> + <% if (prop.type.isArray) { %> + newObj.${prop.name} = ${ prop.type.type.name }.createArrayFromDocument((java.util.List)document.get(${toUpperCase.call(prop.name)})); + <% } else { %> + newObj.${prop.name} = ${ typeToJava.call(prop.type) }.createFromDocument((Document)document.get(${toUpperCase.call(prop.name)})); + <% } %> + <% } %> + return newObj; + } + <% } %> + + public static java.util.List<${ upperCamelCase.call(currentType.name) }> createArrayFromDocument(java.util.List documentList) { + if (documentList==null) return null; + ArrayList<${ upperCamelCase.call(currentType.name) }> ret = new ArrayList<>(); + for (Document document: documentList) { + ${ upperCamelCase.call(currentType.name) } newObj = createFromDocument(document); + if (newObj!=null) { + ret.add(newObj); + } + } + return ret; + } + + + <% if (currentType.tags.contains('mongodb')) { %> + private ObjectId objectId; + public ObjectId getObjectId() { return this.objectId; } + public final static String COLLECTION_NAME="${ upperCamelCase.call(currentType.name) }"; + <% if (isJoinedType) { %> + ${renderInnerTemplate.call('templates/java/sub/mongo_bean_joined_crud.txt',currentType,4)} + <% } else { %> + ${renderInnerTemplate.call('templates/java/sub/mongo_bean_normal_crud.txt',currentType,4)} + <% } %> + + public static MongoCollection getCollection() throws SerializationException { + MongoDatabase db = DbFactory.getInst().getDB(); + return db.getCollection(COLLECTION_NAME); + } + <% } %> + + <% currentType.properties.each { prop -> %> + public final static String ${toUpperCase.call(prop.name)} = "${lowerCamelCase.call(prop.name)}"; + <% } %> +} \ No newline at end of file diff --git a/src/main/resources/templates/java/sub/mongo_bean_joined_crud.txt b/src/main/resources/templates/java/sub/mongo_bean_joined_crud.txt new file mode 100644 index 0000000..e5ddca5 --- /dev/null +++ b/src/main/resources/templates/java/sub/mongo_bean_joined_crud.txt @@ -0,0 +1,179 @@ +//// prints the CRUD operations for mongo beans that should be joinded with other collections + + public static ${ upperCamelCase.call(actObj.name) } createFromDocument(Document document) { + ${ upperCamelCase.call(actObj.name) } newObj = new ${ upperCamelCase.call(actObj.name) }(); + // not joined attribs - start + // handle base types + <% actObj.properties.findAll { prop -> return ! prop.hasTag('join') }.findAll {prop -> return ! prop.isRefTypeOrComplexType() }.each { prop -> %> + newObj.${prop.name} = (${ typeToJava.call(prop.type) }) document.get(${toUpperCase.call(prop.name)}); + <% } %> + // handle complex types + <% actObj.properties.findAll {prop -> return prop.isRefTypeOrComplexType() }.each { prop -> %> + <% if (prop.type.isArray) { %> + newObj.${prop.name} = ${ prop.type.type.name }.createArrayFromDocument((java.util.List)document.get(${toUpperCase.call(prop.name)})); + <% } else { %> + newObj.${prop.name} = ${ typeToJava.call(prop.type) }.createFromDocument((Document)document.get(${toUpperCase.call(prop.name)})); + <% } %> + <% } %> + // not joined attribs - end + // joined attribs - start - joins can't be arrays! + <% actObj.properties.findAll { prop -> return prop.hasTag('join') }.each { prop -> %> + newObj.${lowerCamelCase.call(prop.implicitRef.type.name)} = ${ prop.implicitRef.type.name }.createFromDocument((Document)document.get("${lowerCamelCase.call(prop.implicitRef.type.name)}")); + if (null!=newObj.${lowerCamelCase.call(prop.implicitRef.type.name)}) { + newObj.${lowerCamelCase.call(prop.implicitRef.type.name)}JoinId = newObj.${lowerCamelCase.call(prop.implicitRef.type.name)}.getObjectId(); + newObj.${lowerCamelCase.call(prop.name)} = newObj.${lowerCamelCase.call(prop.implicitRef.type.name)}.getGuid(); + } + <% } %> + // joined attribs - end + return newObj; + } + + <% actObj.properties.findAll { prop -> return prop.hasTag('join') && prop.implicitRefIsRefType() }.each { prop -> %> + + private ${ upperCamelCase.call(prop.implicitRef.type.name) } ${ lowerCamelCase.call(prop.implicitRef.type.name) }; + public ${ upperCamelCase.call(prop.implicitRef.type.name) } get${ firstUpperCase.call(prop.implicitRef.type.name) }() { return ${ lowerCamelCase.call(prop.implicitRef.type.name) }; } + + + private ObjectId ${ lowerCamelCase.call(prop.implicitRef.type.name) }JoinId; + <% } %> + + // byId + public static ${ upperCamelCase.call(actObj.name) } byId (String guid) throws SerializationException { + MongoCollection collection = getCollection(); + java.util.List aggregations = new ArrayList<>(); + <% actObj.properties.find { prop -> return prop.hasTag('join') && prop.implicitRefIsRefType() }.each { prop -> %> + aggregations.add(Aggregates.match(Filters.eq(${ toUpperCase.call(prop.name) }, guid))); + aggregations.add(Aggregates.lookup(${ upperCamelCase.call(prop.implicitRef.type.name) }.COLLECTION_NAME,${ toUpperCase.call(prop.implicitRef.type.name) }_JOINID,"_id","${ lowerCamelCase.call(prop.implicitRef.type.name) }")); + aggregations.add(Aggregates.unwind("$DOLLAR${ firstLowerCase.call(prop.implicitRef.type.name) }")); + <% } %> + AggregateIterable iterable = collection.aggregate( + aggregations + ); + Object o = iterable.first(); + if (o==null) throw new ObjNotFoundException(guid); + if (! (o instanceof Document )) { + // TODO logging + throw new SerializationException(String.format("Wrong result type, expect Document but got: %s",o.getClass().getName())); + } + + Document document = (Document) o; + ${ upperCamelCase.call(actObj.name) } newObj = createFromDocument(document); + newObj.objectId = (ObjectId) document.get("_id"); + return newObj; + } + + /** + * return a list of elements + * @return + * @throws SerializationException + */ + public static java.util.List<${ upperCamelCase.call(actObj.name) }> list () throws SerializationException { + return list(-1,-1); + } + + /** + * return a list of elements + * @param skip an offset how many values should be skipped, values <1 cause the ignore the skipping + * @param limit how many set shall be returned + * @return + * @throws SerializationException + */ + public static java.util.List<${ upperCamelCase.call(actObj.name) }> list (int skip, int limit) throws SerializationException { + MongoCollection collection = getCollection(); + java.util.List aggregations = new ArrayList<>(); + + <% actObj.properties.find { prop -> return prop.hasTag('join') && prop.implicitRefIsRefType() }.each { prop -> %> + aggregations.add(Aggregates.lookup(${ upperCamelCase.call(prop.implicitRef.type.name) }.COLLECTION_NAME,${ toUpperCase.call(prop.implicitRef.type.name) }_JOINID,"_id","${ lowerCamelCase.call(prop.implicitRef.type.name) }")); + aggregations.add(Aggregates.unwind("$DOLLAR${ firstLowerCase.call(prop.implicitRef.type.name) }")); + <% } %> + + if (skip>0) { + aggregations.add(Aggregates.skip(skip)); + } + + if (limit>0) { + aggregations.add(Aggregates.limit(limit)); + } + +// projection example +// aggregations.add(Aggregates.project(new BasicDBObject("junctionBase.tags", false))); + + AggregateIterable iterable = collection.aggregate( + aggregations + ); + ArrayList<${ upperCamelCase.call(actObj.name) }> list = new ArrayList<>(); + MongoCursor iterator = iterable.iterator(); + while (iterator.hasNext()) { + ${ upperCamelCase.call(actObj.name) } newObj = createFromDocument(iterator.next()); + if (newObj!=null) { + list.add(newObj); + } + } + return list; + } + + <% actObj.properties.findAll { prop -> return prop.hasTag('join') && prop.implicitRefIsRefType() }.each { prop -> %> + + private void insert${ firstUpperCase.call(prop.implicitRef.type.name) } () throws SerializationException { + String guid = ${prop.name}!=null ? ${prop.name} : UUID.randomUUID().toString(); + ${ lowerCamelCase.call(prop.implicitRef.type.name) }.setGuid(guid); + ${ lowerCamelCase.call(prop.implicitRef.type.name) }.insert(); + ${ lowerCamelCase.call(prop.implicitRef.type.name) }JoinId = ${ lowerCamelCase.call(prop.implicitRef.type.name) }.getObjectId(); + ${ prop.name } = ${ lowerCamelCase.call(prop.implicitRef.type.name) }.getGuid(); + } + + private void insertAndInit${ firstUpperCase.call(prop.implicitRef.type.name) } () throws SerializationException { + String guid = ${ lowerCamelCase.call(prop.implicitRef.type.name) }.getGuid(); + if (guid==null) { + guid = ${ prop.name }; + } + if (guid!=null) { + try { + ${ upperCamelCase.call(prop.implicitRef.type.name) } x = ${ upperCamelCase.call(prop.implicitRef.type.name) }.byId(guid); + ${ lowerCamelCase.call(prop.implicitRef.type.name) }JoinId = ${ lowerCamelCase.call(prop.implicitRef.type.name) }.getObjectId(); + ${ prop.name } = ${ lowerCamelCase.call(prop.implicitRef.type.name) }.getGuid(); + } + catch (ObjNotFoundException e) { + insertJunctionBase (); + } + } + else { + insert${ firstUpperCase.call(prop.implicitRef.type.name) } (); + } + } + <% } %> + + + // insert + public void insert () throws SerializationException { + <% actObj.properties.findAll { prop -> return prop.hasTag('join') && prop.implicitRefIsRefType() }.each { prop -> %> + insertAndInit${ firstUpperCase.call(prop.implicitRef.type.name) }(); + <% } %> + + MongoCollection collection = getCollection(); + Document document = new Document(); + <% actObj.properties.each { prop -> %> + document.put(${toUpperCase.call(prop.name)}, ${prop.name}); + <% } %> + + <% actObj.properties.findAll { prop -> return prop.hasTag('join') && prop.implicitRefIsRefType() }.each { prop -> %> + document.put(${ toUpperCase.call(prop.implicitRef.type.name) }_JOINID, ${ lowerCamelCase.call(prop.implicitRef.type.name) }JoinId); + <% } %> + + collection.insertOne(document); + objectId = (ObjectId)document.get( "_id" ); + } + + // update + public void update () { + + } + + // delete + public void delete () { + + } + + <% actObj.properties.findAll { prop -> return prop.hasTag('join') && prop.implicitRefIsRefType() }.each { prop -> %> + public final static String ${ toUpperCase.call(prop.implicitRef.type.name) }_JOINID = "${ lowerCamelCase.call(prop.implicitRef.type.name) }JoinId"; + <% } %> diff --git a/src/main/resources/templates/java/sub/mongo_bean_normal_crud.txt b/src/main/resources/templates/java/sub/mongo_bean_normal_crud.txt new file mode 100644 index 0000000..73ae1e4 --- /dev/null +++ b/src/main/resources/templates/java/sub/mongo_bean_normal_crud.txt @@ -0,0 +1,106 @@ +//// prints the CRUD operations for normal mongo beans + public static ${ upperCamelCase.call(actObj.name) } createFromDocument(Document document) { + if (document==null) return null; + ${ upperCamelCase.call(actObj.name) } newObj = new ${ upperCamelCase.call(actObj.name) }(); + // handle base types + <% actObj.properties.findAll {prop -> return ! prop.isRefTypeOrComplexType() }.each { prop -> %> + newObj.${prop.name} = (${ typeToJava.call(prop.type) }) document.get(${toUpperCase.call(prop.name)}); + <% } %> + // handle complex types + <% actObj.properties.findAll {prop -> return prop.isRefTypeOrComplexType() }.each { prop -> %> + <% if (prop.type.isArray) { %> + newObj.${prop.name} = ${ typeToJava.call(prop.type) }.createArrayFromDocument((Document)document.get(${toUpperCase.call(prop.name)})); + <% } else { %> + newObj.${prop.name} = ${ typeToJava.call(prop.type) }.createFromDocument((Document)document.get(${toUpperCase.call(prop.name)})); + <% } %> + <% } %> + newObj.objectId = (ObjectId) document.get("_id"); + return newObj; + } + + + // byId + public static ${ upperCamelCase.call(actObj.name) } byId (String guid) throws SerializationException { + MongoCollection collection = getCollection(); + java.util.List aggregations = new ArrayList<>(); + aggregations.add(Aggregates.match(Filters.eq("guid", guid))); + AggregateIterable iterable = collection.aggregate( + aggregations + ); + Object o = iterable.first(); + if (o==null) throw new ObjNotFoundException(guid); + if (! (o instanceof Document )) { + // TODO logging + throw new SerializationException(String.format("Wrong result type, expect Document but got: %s",o.getClass().getName())); + } + + Document document = (Document) o; + ${ upperCamelCase.call(actObj.name) } newObj = createFromDocument(document); + return newObj; + } + + /** + * return a list of elements + * @return + * @throws SerializationException + */ + public static java.util.List<${ upperCamelCase.call(actObj.name) }> list () throws SerializationException { + return list (-1,-1); + } + + /** + * return a list of elements + * @param skip an offset how many values should be skipped, values <1 cause the ignore the skipping + * @param limit how many set shall be returned + * @return + * @throws SerializationException + */ + public static java.util.List<${ upperCamelCase.call(actObj.name) }> list (int skip, int limit) throws SerializationException { + MongoCollection collection = getCollection(); + java.util.List aggregations = new ArrayList<>(); + if (skip>0) { + aggregations.add(Aggregates.skip(skip)); + } + if (limit>0) { + aggregations.add(Aggregates.limit(limit)); + } + AggregateIterable iterable = collection.aggregate( + aggregations + ); + ArrayList<${ upperCamelCase.call(actObj.name) }> list = new ArrayList<>(); + MongoCursor iterator = iterable.iterator(); + while (iterator.hasNext()) { + ${ upperCamelCase.call(actObj.name) } newObj = createFromDocument(iterator.next()); + if (newObj!=null) { + list.add(newObj); + } + } + return list; + } + + // insert + public void insert () throws SerializationException { + MongoCollection collection = getCollection(); + String guid = getGuid(); + if (guid==null) { + guid = UUID.randomUUID().toString(); + setGuid(guid); + } + Document document = new Document(); + <% actObj.properties.each { prop -> %> + document.put(${toUpperCase.call(prop.name)}, ${prop.name}); + <% } %> + collection.insertOne(document); + objectId = (ObjectId)document.get( "_id" ); + } + + // update + public void update () { + + } + + // delete + public void delete () { + + } + diff --git a/src/main/resources/templates/meta/hist_model_schema.txt b/src/main/resources/templates/meta/hist_model_schema.txt index 8b8125b..cd38748 100644 --- a/src/main/resources/templates/meta/hist_model_schema.txt +++ b/src/main/resources/templates/meta/hist_model_schema.txt @@ -25,7 +25,7 @@ <% model.types.each { type -> %> <% if (! type.isInnerType()) { %> //// Only not inner types need to be handled , - "${toLowerCase.call(type.name)}": { + "${type.name}": { <% if (type.description) { %> "description": "${type.description}", <% } %> @@ -45,7 +45,7 @@ "type": "array", "description": "array with the historical values of that entry", "items": { - "${DOLLAR}ref": "#/definitions/${toLowerCase.call(type.name)}" + "${DOLLAR}ref": "#/definitions/${type.name}" } }, "meta_entry": { @@ -58,8 +58,8 @@ <% } %> <% } %> }, - "type": "object", + "type": "object" <% if ( model.version ) { %> - "version": "${model.version}" + ,"version": "${model.version}" <% } %> } \ No newline at end of file diff --git a/src/main/resources/templates/meta/json_schema.txt b/src/main/resources/templates/meta/json_schema.txt index 7bff409..cb03777 100644 --- a/src/main/resources/templates/meta/json_schema.txt +++ b/src/main/resources/templates/meta/json_schema.txt @@ -9,7 +9,7 @@ <% if (firstType) { firstType=false; } else { %> , <% } %> - "${toLowerCase.call(type.name)}": { + "${type.name}": { <% if (type.description) { %> "description": "${type.description}", <% } %> @@ -25,8 +25,8 @@ } <% } %> }, - "type": "object", + "type": "object" <% if ( model.version ) { %> - "version": "${model.version}" + ,"version": "${model.version}" <% } %> } \ No newline at end of file diff --git a/src/main/resources/templates/meta/plantuml.txt b/src/main/resources/templates/meta/plantuml.txt index e0f7d5e..a41e159 100644 --- a/src/main/resources/templates/meta/plantuml.txt +++ b/src/main/resources/templates/meta/plantuml.txt @@ -1,4 +1,14 @@ <% +def ignoreAttribs = extraParam.ignoreAttribs=='true' ? true : false +def ignoreRefs = extraParam.ignoreRefs=='true' ? true : false +def ignoreImplicitRefs = extraParam.ignoreImplicitRefs=='true' ? true : false +def ignoreCompositions = extraParam.ignoreCompositions=='true' ? true : false +def printTypeTagsValue = extraParam.printTypeTags=='false' ? false : true +def printPropTagsValue = extraParam.printPropTags=='true' ? true : false +def ignoreUnRefTypes = extraParam.ignoreUnRefTypes=='true' ? true : false + +def guidTypeColor = extraParam.guidTypeColor ? extraParam.guidTypeColor : 'e4ffd4' +def printJoins = extraParam.printJoins def printStereoType = { type -> if ( type instanceof de.lisaplus.atlas.model.InnerType ) { return '<>' @@ -12,12 +22,78 @@ def printStereoType = { type -> else return '' } + +def printPropTags = { prop -> + if (printPropTagsValue && prop.tags) { + return "<&tag> ${prop.tags}" + } + else + return '' +} + +def printTypeTags = { type -> + if (printTypeTagsValue && type.tags) { + def s = null + type.tags.each { tag -> + if (s!=null) { + s+=',\\n' + s+=tag + } + else + s=tag + } + return " <${s}>" + } + else + return '' +} + + +def typesToIgnore = [] + +extraParam.blackListed.each { typeName -> + typesToIgnore.add(typeName) +} + +if (ignoreUnRefTypes) { + // remove unreferenced type from the diagram + def linkedTypes=[] + + model.types.findAll { type -> + ! typesToIgnore.contains(type.name) + }.each { type -> + type.properties.each { prop -> + def propType + if (prop.isRefTypeOrComplexType()) { + propType = prop.type.type.name + } + else if (prop.implicitRef) { + propType = prop.implicitRef.type.name + } + if (!linkedTypes.contains(propType)) { + linkedTypes.add(propType) + } + } + if (type.refOwner && !linkedTypes.contains(type.name)) { + linkedTypes.add(type.name) + } + } + model.types.each { type -> + if (!linkedTypes.contains(type.name) && !typesToIgnore.contains(type.name)) { + typesToIgnore.add(type.name) + } + } +} + %> <% if (extraParam.markdown) { %> ```plantuml <% } else { %> @startuml <% } %> + +skinparam roundcorner 10 + skinparam class { BackgroundColor #FFFFFF ArrowColor #000000 @@ -26,7 +102,7 @@ skinparam class { BorderColor<> #777777 BackgroundColor<> #EEEEEE - BackgroundColor<> #e4ffd4 + BackgroundColor<> #${guidTypeColor} FontName Courier FontSize 12 @@ -44,17 +120,24 @@ skinparam classAttribute { FontSize 12 } -<% model.types.each { type -> %> -class ${firstUpperCase.call(type.name)} ${ printStereoType(type) } << ( ,${type.color}) >> { - <% type.properties.each { prop -> +<% model.types.findAll { type -> + ! typesToIgnore.contains(type.name) +}.each { type -> %> +class ${firstUpperCase.call(type.name)} ${printTypeTags(type) }${ printStereoType(type) } << ( ,${type.color}) >> { + <% if (!ignoreAttribs) { + def i=0; type.properties.each { prop -> def arrayStr = prop.type.isArray ? '[]' : '' - %> + i++ + if (i>1) { %> + .. + <% } %> + ${printPropTags.call(prop)} <% if (prop.isRefTypeOrComplexType()) { %> -${prop.name}: ${typeToMeta.call(prop.type)}${arrayStr} <% } else { %> -${prop.name}: ${typeToMeta.call(prop.type)}${arrayStr} <% } %> - <% } %> + <% } } %> } <% if (type.description) { %> note top of ${firstUpperCase.call(type.name)} @@ -63,28 +146,32 @@ end note <% } %> <% } %> hide methods +<% if ( ignoreAttribs ) { +%>hide attributes +<% } %> -<% model.types.each { type -> %> +<% model.types.findAll { type -> + ! typesToIgnore.contains(type.name) +}.each { type -> %> <% def linkedTypes=[] %> - <% type.properties.each { prop -> %> - <% if (prop.isRefTypeOrComplexType()) { %> + <% type.properties.each { prop -> %> + <% if (prop.isRefTypeOrComplexType() && (!ignoreRefs) && (!typesToIgnore.contains(prop.type.type.name))) { %> <% def testStr="${type.name}-${prop.type.type.name}"%> <% if(!linkedTypes.contains(testStr)) { %> <% if (prop.isAggregation()) { %> ${firstUpperCase.call(type.name)} o-- <% if(prop.type.isArray) { %>"many"<% } %> ${ firstUpperCase.call(prop.type.type.name) } ${ prop.type.type.color } - <% } else { %> + <% } else { if (!ignoreCompositions) { %> ${firstUpperCase.call(type.name)} *-- <% if(prop.type.isArray) { %>"many"<% } %> ${ firstUpperCase.call(prop.type.type.name) } ${ prop.type.type.color } - <% } %> + <% } } %> <% linkedTypes.add(testStr) %> <% } %> - <% } else if (prop.implicitRef) { %> + <% } else if (printJoins && prop.implicitRef && prop.hasTag('join')) { %> + ${firstUpperCase.call(type.name)} -- ${ firstUpperCase.call(prop.implicitRef.type.name) } ${ prop.implicitRef.type.color }: join + <% } + if (prop.implicitRef && (!ignoreImplicitRefs) && (!typesToIgnore.contains(prop.implicitRef.type.name))) { %> ${firstUpperCase.call(type.name)} .. <% if(prop.type.isArray) { %>"many"<% } %> ${ firstUpperCase.call(prop.implicitRef.type.name) } ${ prop.implicitRef.type.color } <% } %> <% } %> - <% type.baseTypes.each { baseType -> %> - ${firstUpperCase.call(type.name)} --|> ${firstUpperCase.call(baseType)} - <% } %> - <% } %> footer powered by plantuml, created with jsonCodeGen <% if (extraParam.markdown) { %> diff --git a/src/main/resources/templates/meta/plantuml_java.txt b/src/main/resources/templates/meta/plantuml_java.txt index 4478e2c..4605693 100644 --- a/src/main/resources/templates/meta/plantuml_java.txt +++ b/src/main/resources/templates/meta/plantuml_java.txt @@ -1,4 +1,14 @@ <% +def ignoreAttribs = extraParam.ignoreAttribs=='true' ? true : false +def ignoreRefs = extraParam.ignoreRefs=='true' ? true : false +def ignoreImplicitRefs = extraParam.ignoreImplicitRefs=='true' ? true : false +def ignoreCompositions = extraParam.ignoreCompositions=='true' ? true : false +def printTypeTagsValue = extraParam.printTypeTags=='false' ? false : true +def printPropTagsValue = extraParam.printPropTags=='true' ? true : false +def ignoreUnRefTypes = extraParam.ignoreUnRefTypes=='false' ? false : true + +def guidTypeColor = extraParam.guidTypeColor ? extraParam.guidTypeColor : 'e4ffd4' +def printJoins = extraParam.printJoins def printStereoType = { type -> if ( type instanceof de.lisaplus.atlas.model.InnerType ) { return '<>' @@ -12,12 +22,76 @@ def printStereoType = { type -> else return '' } + +def printPropTags = { prop -> + if (printPropTagsValue && prop.tags) { + return "<&tag> ${prop.tags}" + } + else + return '' + } + +def printTypeTags = { type -> + if (printTypeTagsValue && type.tags) { + def s = null + type.tags.each { tag -> + if (s!=null) { + s+=',\\n' + s+=tag + } + else + s=tag + } + return " <${s}>" + } + else + return '' +} + +def typesToIgnore = [] + +extraParam.blackListed.each { typeName -> + typesToIgnore.add(typeName) +} + +if (ignoreUnRefTypes) { + // remove unreferenced type from the diagram + def linkedTypes=[] + model.types.findAll { type -> + ! typesToIgnore.contains(type.name) + }.each { type -> + type.properties.each { prop -> + def propType + if (prop.isRefTypeOrComplexType()) { + propType = prop.type.type.name + } + else if (prop.implicitRef) { + propType = prop.implicitRef.type.name + } + if (!linkedTypes.contains(propType)) { + linkedTypes.add(propType) + } + } + if (type.refOwner && !linkedTypes.contains(type.name)) { + linkedTypes.add(type.name) + } + } + model.types.each { type -> + if (!linkedType.contains(type.name) && !typesToIgnore.contains(type.name)) { + typesToIgnore.add(type.name) + } + } +} + %> <% if (extraParam.markdown) { %> ```plantuml <% } else { %> @startuml <% } %> + +skinparam roundcorner 10 + skinparam class { BackgroundColor #FFFFFF ArrowColor #000000 @@ -26,7 +100,7 @@ skinparam class { BorderColor<> #777777 BackgroundColor<> #EEEEEE - BackgroundColor<> #e4ffd4 + BackgroundColor<> #${guidTypeColor} FontName Courier FontSize 12 @@ -44,16 +118,23 @@ skinparam classAttribute { FontSize 12 } -<% model.types.each { type -> %> -class ${firstUpperCase.call(type.name)} ${ printStereoType(type) } << ( ,${type.color}) >> { - <% type.properties.each { prop -> - def arrayStr = prop.type.isArray ? '[]' : '' %> - <% if (prop.isRefTypeOrComplexType()) { %> +<% model.types.findAll { type -> + ! typesToIgnore.contains(type.name) +}.each { type -> %> +class ${firstUpperCase.call(type.name)} ${printTypeTags(type) }${ printStereoType(type) } << ( ,${type.color}) >> { + <% if (!ignoreAttribs) { + def i=0; type.properties.each { prop -> + def arrayStr = prop.type.isArray ? '[]' : '' + i++ + if (i>1) { %> + .. + <% } %> + ${printPropTags.call(prop)} -${prop.name}: ${typeToJava.call(prop.type)}${arrayStr} <% } else { %> -${prop.name}: ${typeToJava.call(prop.type)}${arrayStr} <% } %> - <% } %> + <% } } %> } <% if (type.description) { %> note top of ${firstUpperCase.call(type.name)} @@ -62,28 +143,30 @@ end note <% } %> <% } %> hide methods +<% if ( ignoreAttribs ) { +%>hide attributes +<% } %> -<% model.types.each { type -> %> +<% model.types.findAll { type -> + ! typesToIgnore.contains(type.name) +}.each { type -> %> <% def linkedTypes=[] %> <% type.properties.each { prop -> %> - <% if (prop.isRefTypeOrComplexType()) { %> + <% if (prop.isRefTypeOrComplexType() && (!ignoreRefs) && (!typesToIgnore.contains(prop.type.type.name))) { %> <% def testStr="${type.name}-${prop.type.type.name}"%> <% if(!linkedTypes.contains(testStr)) { %> <% if (prop.isAggregation()) { %> ${firstUpperCase.call(type.name)} o-- <% if(prop.type.isArray) { %>"many"<% } %> ${ firstUpperCase.call(prop.type.type.name) } ${ prop.type.type.color } - <% } else { %> + <% } else { if (!ignoreCompositions) { %> ${firstUpperCase.call(type.name)} *-- <% if(prop.type.isArray) { %>"many"<% } %> ${ firstUpperCase.call(prop.type.type.name) } ${ prop.type.type.color } - <% } %> + <% } } %> <% linkedTypes.add(testStr) %> <% } %> - <% } else if (prop.implicitRef) { %> + <% } + if (prop.implicitRef && (!ignoreImplicitRefs) && (!typesToIgnore.contains(prop.implicitRef.type.name))) { %> ${firstUpperCase.call(type.name)} .. <% if(prop.type.isArray) { %>"many"<% } %> ${ firstUpperCase.call(prop.implicitRef.type.name) } ${ prop.implicitRef.type.color } <% } %> <% } %> - <% type.baseTypes.each { baseType -> %> - ${firstUpperCase.call(type.name)} --|> ${firstUpperCase.call(baseType)} - <% } %> - <% } %> footer powered by plantuml, created with jsonCodeGen <% if (extraParam.markdown) { %> diff --git a/src/main/resources/templates/meta/sub/json_schema_attribs.txt b/src/main/resources/templates/meta/sub/json_schema_attribs.txt index 37c7403..0445a21 100644 --- a/src/main/resources/templates/meta/sub/json_schema_attribs.txt +++ b/src/main/resources/templates/meta/sub/json_schema_attribs.txt @@ -1,6 +1,6 @@ //define INDENT=${printIndent.call(indent)} //// write one property entry to the resulting json schema -"${toLowerCase.call(actObj.name)}": { +"${actObj.name}": { <% if (actObj.description) { %> ${printIndent.call(indent)} "description": "${actObj.description}", <% } %> @@ -9,7 +9,7 @@ ${printIndent.call(indent)} "description": "${actObj.description}", INDENT "type": "array", INDENT "items": { <% if (actObj.isRefType()) { %> -INDENT "${DOLLAR}ref": "#/definitions/${toLowerCase.call(actObj.type.type.name)}" +INDENT "${DOLLAR}ref": "#/definitions/${actObj.type.type.name}" <% } else if (actObj.isComplexType() ) { %> INDENT "type": "object", @@ -31,9 +31,9 @@ INDENT "type": "${typeToJson.call(actObj.type)}" <% } %> <% if (actObj.implicitRef) { %> <% if (actObj.implicitRefIsRefType()) { %> -INDENT ,"ref": "#/definitions/${toLowerCase.call(actObj.implicitRef.type.name)}" +INDENT ,"ref": "#/definitions/${actObj.implicitRef.type.name}" <% } else if (actObj.implicitRefIsComplexType() ) { %> -INDENT ,"ref": "#/definitions/${toLowerCase.call(actObj.implicitRef.name)}" +INDENT ,"ref": "#/definitions/${actObj.implicitRef.name}" <% }%> <% } %> <% } %> @@ -41,7 +41,7 @@ INDENT } <% } else { %> //// the property is no arraytype <% if (actObj.isRefType()) { %> -INDENT "${DOLLAR}ref": "#/definitions/${toLowerCase.call(actObj.type.type.name)}" +INDENT "${DOLLAR}ref": "#/definitions/${actObj.type.type.name}" <% } else if (actObj.isComplexType() ) { %> INDENT "type": "object", INDENT "properties": { @@ -63,9 +63,9 @@ INDENT "type": "${typeToJson.call(actObj.type)}" //// implizit to another type <% if (actObj.implicitRef) { %> <% if (actObj.implicitRefIsRefType()) { %> -INDENT ,"ref": "#/definitions/${toLowerCase.call(actObj.implicitRef.type.name)}" +INDENT ,"ref": "#/definitions/${actObj.implicitRef.type.name}" <% } else if (actObj.implicitRefIsComplexType() ) { %> -INDENT ,"ref": "#/definitions/${toLowerCase.call(actObj.implicitRef.name)}" +INDENT ,"ref": "#/definitions/${actObj.implicitRef.name}" <% }%> <% } %> <% } %> diff --git a/src/main/resources/templates/meta/xsd.txt b/src/main/resources/templates/meta/xsd.txt new file mode 100644 index 0000000..8bb8010 --- /dev/null +++ b/src/main/resources/templates/meta/xsd.txt @@ -0,0 +1,99 @@ +<% +def targetNameSpace = extraParam.targetNamespace ? extraParam.targetNamespace : 'http://please.change.org/use/targetNamespace/generator/param/1.0' + +def printOccurs = { propType -> + if (propType.isArray) { + return ' minOccurs="0" maxOccurs="unbounded"' + } + else { + return '' + } +} + +def typeConvert = { type -> + if (! type instanceof de.lisaplus.atlas.model.BaseType) { + return BaseType.WRONG_TYPE+type + } + switch(type.name()) { + case de.lisaplus.atlas.model.IntType.NAME: + return 'xsd:integer' + case de.lisaplus.atlas.model.LongType.NAME: + return 'xsd:long' + case de.lisaplus.atlas.model.NumberType.NAME: + return 'xsd:double' + case de.lisaplus.atlas.model.StringType.NAME: + return 'xsd:string' + case de.lisaplus.atlas.model.UUIDType.NAME: + return 'Guid' + case de.lisaplus.atlas.model.BooleanType.NAME: + return 'xsd:boolean' + case de.lisaplus.atlas.model.ByteType.NAME: + return 'xsd:byte' + case de.lisaplus.atlas.model.DateType.NAME: + return 'xsd:date' + case de.lisaplus.atlas.model.DateTimeType.NAME: + return 'xsd:dateTime' + case de.lisaplus.atlas.model.RefType.NAME: + return type.type.name + case de.lisaplus.atlas.model.ComplexType.NAME: // ? + return type.type.name + case de.lisaplus.atlas.model.UnsupportedType.NAME: + return "Unsupported" + default: + return "???" + } +} + +def hasUnsupportedType = model.types.find { type -> + type.properties.find { prop -> + prop.type instanceof de.lisaplus.atlas.model.UnsupportedType + } != null +} + +def hasGuidType = model.types.find { type -> + type.properties.find { prop -> + prop.type instanceof de.lisaplus.atlas.model.UUIDType + } != null +} + +%> + + <% if (hasUnsupportedType) { + %> + + + <% } %> + <% if (hasGuidType) { + %> + + + + + <% } %> + + <% model.types.each { type -> + %> + <% if (type.description) { + %> + ${type.description} + + <% } %> + + <% type.properties.each { prop -> + %> + <% if (prop.description) { + %> + ${prop.description} + + <% } %> + + <% } %> + + + + <% } %> + \ No newline at end of file diff --git a/src/test/groovy/de/lisaplus/atlas/Model.groovy b/src/test/groovy/de/lisaplus/atlas/Model.groovy index 33e11ae..a86c404 100644 --- a/src/test/groovy/de/lisaplus/atlas/Model.groovy +++ b/src/test/groovy/de/lisaplus/atlas/Model.groovy @@ -30,7 +30,7 @@ class Model { assertEquals(0,type.refOwner.size()) } assertTrue(found) - typeName='App_module' + typeName='AppModule' found=false model.types.find { it.name==typeName }.each { type -> found=true @@ -52,7 +52,7 @@ class Model { assertEquals(0,type.refOwner.size()) } assertTrue(found) - typeName='User_log' + typeName='UserLog' found=false model.types.find { it.name==typeName }.each { type -> found=true diff --git a/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/ArrayTests.groovy b/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/ArrayTests.groovy new file mode 100644 index 0000000..4042fcf --- /dev/null +++ b/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/ArrayTests.groovy @@ -0,0 +1,74 @@ +package de.lisaplus.atlas.builder.test.jsonschema + +import de.lisaplus.atlas.ModelTestHelper +import de.lisaplus.atlas.builder.JsonSchemaBuilder +import de.lisaplus.atlas.codegen.test.base.FileHelper +import de.lisaplus.atlas.model.Property +import de.lisaplus.atlas.model.StringType +import org.junit.Test + +import static org.junit.Assert.* + +/** + * Created by eiko on 02.06.17. + */ +class ArrayTests { + @Test + void test_initModel() { + def modelFile = new File('src/test/resources/test_schemas/ds/array_test_simple.json') + assertTrue(modelFile.isFile()) + def builder = new JsonSchemaBuilder() + def model = builder.buildModel(modelFile) + assertEquals(model.description,'Test user model') + assertEquals('User model',model.title) + assertEquals(4,model.types.size()) + } + + @Test + void test_initModelWithEnums() { + def modelFile = new File('src/test/resources/test_schemas/ds/array_test_simple.json') + assertTrue(modelFile.isFile()) + def builder = new JsonSchemaBuilder() + builder.createEnumTypes = true + def model = builder.buildModel(modelFile) + assertEquals(model.description,'Test user model') + assertEquals('User model',model.title) + assertEquals(6,model.types.size()) + } + + @Test + void testContainsAttrib() { + def destDir = 'tmp/array_tests' + FileHelper.removeDirectoryIfExists(destDir) + def modelFile = new File('src/test/resources/test_schemas/ds/array_test_simple.json') + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = [modelFile] + doCodeGen.generators.add('java_beans') + doCodeGen.outputBaseDir = destDir + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.generator_parameters.add('packageName=de.test2.jsoncodegen.impl') + doCodeGen.run() +/* + assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/Role.java').exists()) + assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/User.java').exists()) + assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/UserLog.java').exists()) + new File('tmp/java_beans/de/test2/jsoncodegen/impl').listFiles(new FileFilter() { + @Override + boolean accept(File file) { + return file.isFile() + } + }).size()==3 +*/ + } + + + void test_initModel2() { + def modelFile = new File('src/test/resources/test_schemas/ds/array_test.json') + assertTrue(modelFile.isFile()) + def builder = new JsonSchemaBuilder() + def model = builder.buildModel(modelFile) + assertEquals(model.description,'Test user model') + assertEquals('User model',model.title) + assertEquals(7,model.types.size()) + } +} diff --git a/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/JsonSchemaBuilder.groovy b/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/JsonSchemaBuilder.groovy index e7a3713..9343d5e 100644 --- a/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/JsonSchemaBuilder.groovy +++ b/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/JsonSchemaBuilder.groovy @@ -2,12 +2,21 @@ package de.lisaplus.atlas.builder.test.jsonschema import de.lisaplus.atlas.builder.JsonSchemaBuilder import de.lisaplus.atlas.codegen.external.ExtSingleFileGenarator +import de.lisaplus.atlas.codegen.test.DoCodeGen import de.lisaplus.atlas.model.AggregationType +import de.lisaplus.atlas.model.ByteType +import de.lisaplus.atlas.model.EnumType +import de.lisaplus.atlas.model.IntType +import de.lisaplus.atlas.model.LongType +import de.lisaplus.atlas.model.RefType import org.junit.Test +import static de.lisaplus.atlas.codegen.test.DoCodeGen.* import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertFalse import static org.junit.Assert.assertNotNull import static org.junit.Assert.assertTrue +import static org.junit.Assert.fail /** * Created by eiko on 18.06.17. @@ -23,14 +32,67 @@ class JsonSchemaBuilder { } @Test - void testExternalReferences_2() { + void testExt() { def modelFile = new File('src/test/resources/test_schemas/ds/user.json') assertTrue(modelFile.isFile()) def builder = new de.lisaplus.atlas.builder.JsonSchemaBuilder() def model = builder.buildModel(modelFile) assertEquals(8,model.types.size()) + de.lisaplus.atlas.DoCodeGen.sortTypesAndProperties(model) + println (model) + def userLogType = model.types.find { it.name=='UserLog' } + assertNotNull(userLogType) + def domainsProp = userLogType.properties.find { it.name=='domains' } + assertNotNull(domainsProp) + assertFalse(domainsProp.isRefTypeOrComplexType()) } + @Test + void testExtWithEnums() { + def modelFile = new File('src/test/resources/test_schemas/ds/user.json') + assertTrue(modelFile.isFile()) + def builder = new de.lisaplus.atlas.builder.JsonSchemaBuilder() + builder.createEnumTypes = true + def model = builder.buildModel(modelFile) + assertEquals(10,model.types.size()) + + def grantsEnumType = model.types.find { it.name == 'GrantsEnum'} + assertNotNull(grantsEnumType) + List expected1 = ['read','write','commit'] + assertEquals(expected1.size(),((EnumType)grantsEnumType).allowedValues.size()) + for (int i=0; i expected2 = ['login','logout'] + assertEquals(expected2.size(),((EnumType)userLogType).allowedValues.size()) + for (int i=0; i println(type.name) } + // test for tags in attributes + def foundReferences=0 + def foundInnerReferences=false + model.types.find { + return (it.name == 'Incident') + }.each { type -> + type.properties.findAll { it.name=='references' || it.name=='innerReferences' }.each { prop -> + assertEquals(1,prop.tags.size()) + assertEquals('recursion',prop.tags.get(0)) + if (prop.name=='references') { + assertTrue(prop.selfReference) + } + foundReferences++ + } + } + model.types.find { + return (it.name == 'IncidentInnerReferencesItem') + }.each { type -> + assertEquals(1,type.tags.size()) + assertEquals('recursion',type.tags.get(0)) + foundInnerReferences=true + } + assertTrue(foundInnerReferences) + assertEquals(2,foundReferences) } + @Test void testRecursion_1() { def modelFile = new File('src/test/resources/test_schemas/ds/shared/options_response.json') @@ -183,10 +271,145 @@ class JsonSchemaBuilder { it.properties.find { if (it.tags) { propsWithTags++ - assertTrue (it.name.equals('grant') || it.name.equals('module_grants')) + assertTrue (it.name.equals('grant') || it.name.equals('moduleGrants')) } } + if (it.name=="Role") { + assertEquals(2,it.version) + } + else if (it.name=="Domain") { + assertEquals(4,it.version) + } + else { + assertEquals(0,it.version) + } } assertEquals(2,propsWithTags) + assertEquals(3,model.version) + } + + @Test + void testTags2() { + def modelFile = new File('src/test/resources/test_schemas/ds/junction.json') + assertTrue(modelFile.isFile()) + def builder = new de.lisaplus.atlas.builder.JsonSchemaBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + def tagCount=0; + model.types.find{ + if (it.name == 'Junction') { + return true + } + return false + }.each { type -> + tagCount = type.tags.size(); + } + // junction has 3 tags + assertEquals(3,tagCount) + tagCount=0 + model.types.find{ + if (it.name == 'JunctionBase') { + return true + } + return false + }.each { type -> + tagCount = type.tags.size(); + } + // junction has 3 tags + assertEquals(2,tagCount) + + } + + @Test + void testSchemaPath() { + def modelFile = new File('src/test/resources/test_schemas/ds/junction.json') + assertTrue(modelFile.isFile()) + def builder = new de.lisaplus.atlas.builder.JsonSchemaBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + model.types.each { type -> + assertNotNull(type.schemaPath) + assertNotNull(type.schemaFileName) + println "schema-path: ${type.schemaPath}, filename: ${type.schemaFileName}" + } + } + + @Test + void testMainTypes() { + def modelFile = new File('src/test/resources/test_schemas/ds/junction.json') + assertTrue(modelFile.isFile()) + def builder = new de.lisaplus.atlas.builder.JsonSchemaBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + model.types.each { type -> + println "type-name: ${type.name}" + if (type.name=='Junction') { + assertTrue (type.isMainType('junction')) + } + else if (type.name=='JunctionDocument') { + assertTrue (type.isMainType('junction')) + } + else if (type.name=='JunctionComment') { + assertTrue (type.isMainType('junction')) + } + else if (type.name=='JunctionState') { + assertTrue (type.isMainType('junction')) + } + else if (type.name=='JunctionType') { + assertTrue (type.isMainType('junction')) + } + else { + assertFalse (type.isMainType('junction')) + } + if (type.name=="Domain") { + assertEquals(4,type.version) + } + else { + assertEquals(0,type.version) + } + } } + + @Test + void testExtraTypes() { + def modelFile = new File('src/test/resources/test_schemas/ds/extra_types.json') + assertTrue(modelFile.isFile()) + def builder = new de.lisaplus.atlas.builder.JsonSchemaBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + model.types.find{ it.name=='ExtraType' }.each { type -> + assertTrue (type.properties.find { it.name=='intAttrib' }.type instanceof IntType) + assertTrue (type.properties.find { it.name=='intAttrib2' }.type instanceof IntType) + assertTrue (type.properties.find { it.name=='noIntAttrib' }.type instanceof LongType) + assertTrue (type.properties.find { it.name=='byteAttrib' }.type instanceof ByteType) + } + } + + @Test + void lisaInesTypes() { + def modelFile = new File('src/test/resources/test_schemas/ds/lisa-ines-network.json') + assertTrue(modelFile.isFile()) + def builder = new de.lisaplus.atlas.builder.JsonSchemaBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + } + + @Test + void lisaInesTypes2() { + def modelFile = new File('src/test/resources/test_schemas/ds/ines_network.json') + assertTrue(modelFile.isFile()) + def builder = new de.lisaplus.atlas.builder.JsonSchemaBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + } + + @Test + void lisaInesTypes_externalRef() { + def modelFile = new File('src/test/resources/test_schemas/ds/lisa-ines-network_new_references.json') + assertTrue(modelFile.isFile()) + def builder = new de.lisaplus.atlas.builder.JsonSchemaBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + } + } diff --git a/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/MultiTypeSchema.groovy b/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/MultiTypeSchema.groovy index e77f7ef..6b5aadf 100644 --- a/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/MultiTypeSchema.groovy +++ b/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/MultiTypeSchema.groovy @@ -27,17 +27,17 @@ class MultiTypeSchema { def typeName = 'Action' ModelTestHelper.checkPropertySize(model,typeName,3) ModelTestHelper.compareProperty (new Property( - name: 'default_title', + name: 'defaultTitle', description: 'Tooltip for the main toolbar icon.', type: new StringType() ),model,typeName) ModelTestHelper.compareProperty (new Property( - name: 'default_popup', + name: 'defaultPopup', description: 'The popup appears when the user clicks the icon.', type: new RefType(typeName: 'Icon') ),model,typeName) ModelTestHelper.compareProperty (new Property( - name: 'default_icon', + name: 'defaultIcon', description: 'Icon for the main toolbar.', type: new ComplexType() ),model,typeName) @@ -50,7 +50,7 @@ class MultiTypeSchema { type: new StringType() ),model,typeName) ModelTestHelper.compareProperty (new Property( - name: 'suggested_key', + name: 'suggestedKey', description: null, type: new UnsupportedType() ),model,typeName) diff --git a/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/SingleTypeSchema.groovy b/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/SingleTypeSchema.groovy index 75ec8f9..a636010 100644 --- a/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/SingleTypeSchema.groovy +++ b/src/test/groovy/de/lisaplus/atlas/builder/test/jsonschema/SingleTypeSchema.groovy @@ -34,5 +34,29 @@ class SingleTypeSchema { type: new StringType(isArray: true), ),model,typeName) + // test that the type has no tags + def singleType = null + model.types.find { type -> return type.name==typeName}.each { type -> + singleType = type; + } + + assertEquals(0, singleType.tags.size()) } + + @Test + void test_tags() { + def modelFile = new File('src/test/resources/test_schemas/ds/shared/icon.json') + assertTrue(modelFile.isFile()) + def builder = new JsonSchemaBuilder() + def model = builder.buildModel(modelFile) + def typeName = 'Icon' + // test that the type has no tags + def singleType = null + model.types.find { type -> return type.name==typeName}.each { type -> + singleType = type; + } + + assertEquals(3, singleType.tags.size()) + } + } diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/HistModel.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/HistModel.groovy index ddbb838..b4e7133 100644 --- a/src/test/groovy/de/lisaplus/atlas/codegen/test/HistModel.groovy +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/HistModel.groovy @@ -11,7 +11,7 @@ import static junit.framework.Assert.assertTrue class HistModel { private static void generateHistModel(String model, String destFile) { de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model=model + doCodeGen.models = [model] doCodeGen.generators.add('hist_model') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add("destFileName=${destFile}") @@ -22,7 +22,7 @@ class HistModel { private static void generatePlantUML(String model, String destFile) { de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model=model + doCodeGen.models=[model] doCodeGen.generators.add('plantuml') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add("destFileName=${destFile}") diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaBeans.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaBeans.groovy index 048ef17..56086cb 100644 --- a/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaBeans.groovy +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaBeans.groovy @@ -10,6 +10,7 @@ import static org.junit.Assert.assertEquals import static org.junit.Assert.assertEquals import static org.junit.Assert.assertEquals import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertFalse import static org.junit.Assert.assertNotNull import static org.junit.Assert.assertNotNull import static org.junit.Assert.assertTrue @@ -25,7 +26,37 @@ class JavaBeans { FileHelper.removeDirectoryIfExists(destDir) def modelFile = new File('src/test/resources/test_schemas/ds/user.json') de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models=[modelFile] + doCodeGen.generators.add('java_beans') + doCodeGen.outputBaseDir = destDir + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.generator_parameters.add('packageName=de.test.jsoncodegen.impl') + doCodeGen.run() + } + + @Test + void createFromUserModelWithEnums() { + def destDir = 'tmp/java_beans_enums' + FileHelper.removeDirectoryIfExists(destDir) + def modelFile = new File('src/test/resources/test_schemas/ds/user.json') + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[modelFile] + doCodeGen.generators.add('java_beans') + doCodeGen.outputBaseDir = destDir + doCodeGen.createEnumTypes = true + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.generator_parameters.add('packageName=de.test.jsoncodegen.impl') + doCodeGen.run() + } + + + @Test + void createFromXsd() { + def destDir = 'tmp/xsd_java_beans' + FileHelper.removeDirectoryIfExists(destDir) + def modelFile = new File('src/test/resources/xsd/ui-tlc.xsd') + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[modelFile] doCodeGen.generators.add('java_beans') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -39,7 +70,7 @@ class JavaBeans { FileHelper.removeDirectoryIfExists(destDir) def modelFile = new File('src/test/resources/test_schemas/ds/base_types/map_object_multi_type.json') de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models=[modelFile] doCodeGen.generators.add('java_beans') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -53,7 +84,7 @@ class JavaBeans { FileHelper.removeDirectoryIfExists(destDir) def modelFile = new File('src/test/resources/test_schemas/ds/base_types/simple_map_object.json') de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models=[modelFile] doCodeGen.generators.add('java_beans') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -67,16 +98,16 @@ class JavaBeans { FileHelper.removeDirectoryIfExists(destDir) def modelFile = new File('src/test/resources/test_schemas/ds/user.json') de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models=[modelFile] doCodeGen.generators.add('java_beans') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') - doCodeGen.generator_parameters.add('containsAttrib=domain_id') + doCodeGen.generator_parameters.add('containsAttrib=domainId') doCodeGen.generator_parameters.add('packageName=de.test2.jsoncodegen.impl') doCodeGen.run() assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/Role.java').exists()) assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/User.java').exists()) - assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/User_log.java').exists()) + assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/UserLog.java').exists()) new File('tmp/java_beans/de/test2/jsoncodegen/impl').listFiles(new FileFilter() { @Override boolean accept(File file) { @@ -91,7 +122,7 @@ class JavaBeans { FileHelper.removeDirectoryIfExists(destDir) def modelFile = new File('src/test/resources/test_schemas/ds/user.json') de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models=[modelFile] doCodeGen.generators.add('java_beans') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -99,7 +130,7 @@ class JavaBeans { doCodeGen.generator_parameters.add('packageName=de.test2.jsoncodegen.impl') doCodeGen.run() assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/Domain.java').exists()) - assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/App_module.java').exists()) + assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/AppModule.java').exists()) assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/Application.java').exists()) assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/RoleDataGrantsItem.java').exists()) assertTrue(new File('tmp/java_beans/de/test2/jsoncodegen/impl/RoleModuleGrantsItem.java').exists()) @@ -111,4 +142,121 @@ class JavaBeans { } }).size()==5 } + + @Test + void testIgnoreTag() { + def destDir = 'tmp/java_beans2' + FileHelper.removeDirectoryIfExists(destDir) + def modelFile = new File('src/test/resources/test_schemas/ds/incident.json') + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[modelFile] + doCodeGen.generators.add('java_beans') + doCodeGen.outputBaseDir = destDir + doCodeGen.generator_parameters.add('versionConst=true') + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.generator_parameters.add('ignoreTag=rest') + doCodeGen.generator_parameters.add('packageName=de.test3') + doCodeGen.run() + + assertFalse(new File('tmp/java_beans2/de/test3/Incident.java').exists()) + } + + @Test + void testNeededTag() { + def destDir = 'tmp/java_beans3' + FileHelper.removeDirectoryIfExists(destDir) + def modelFile = new File('src/test/resources/test_schemas/ds/incident.json') + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[modelFile] + doCodeGen.generators.add('java_beans') + doCodeGen.outputBaseDir = destDir + doCodeGen.tagMainTypes = true + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.generator_parameters.add('neededTag=selList') + doCodeGen.generator_parameters.add('packageName=de.test3') + doCodeGen.run() + + assertFalse (new File('tmp/java_beans3/de/test3/Incident.java').exists()) + assertFalse (new File('tmp/java_beans3/de/test3/IncidentComment.java').exists()) + assertFalse (new File('tmp/java_beans3/de/test3/IncidentTag.java').exists()) + assertTrue (new File('tmp/java_beans3/de/test3/IncidentType.java').exists()) + + assertFalse(new File('tmp/java_beans3/de/test3/Comment.java').exists()) + assertFalse(new File('tmp/java_beans3/de/test3/Domain.java').exists()) + assertFalse(new File('tmp/java_beans3/de/test3/Tag.java').exists()) + } + + @Test + void testNeededTag2() { + def destDir = 'tmp/java_beans3' + FileHelper.removeDirectoryIfExists(destDir) + def modelFile = new File('src/test/resources/test_schemas/ds/incident.json') + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[modelFile] + doCodeGen.generators.add('java_beans') + doCodeGen.outputBaseDir = destDir + doCodeGen.tagMainTypes = true + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.generator_parameters.add('neededTag=mainType:rest') + doCodeGen.generator_parameters.add('packageName=de.test3') + doCodeGen.run() + + assertTrue (new File('tmp/java_beans3/de/test3/Incident.java').exists()) + assertTrue (new File('tmp/java_beans3/de/test3/IncidentComment.java').exists()) + assertTrue (new File('tmp/java_beans3/de/test3/IncidentTag.java').exists()) + assertTrue (new File('tmp/java_beans3/de/test3/IncidentType.java').exists()) + + assertFalse (new File('tmp/java_beans3/de/test3/Comment.java').exists()) + assertFalse (new File('tmp/java_beans3/de/test3/Domain.java').exists()) + assertFalse (new File('tmp/java_beans3/de/test3/Tag.java').exists()) + } + + @Test + void testBlackList() { + def destDir = 'tmp/java_beans_black_list' + FileHelper.removeDirectoryIfExists(destDir) + def modelFile = new File('src/test/resources/test_schemas/ds/user.json') + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[modelFile] + doCodeGen.generators.add('java_beans') + doCodeGen.outputBaseDir = destDir + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.blackListed = ['Domain','Application','App_module','Role'] + doCodeGen.generator_parameters.add('packageName=de.test') + doCodeGen.run() + assertTrue(new File('tmp/java_beans_black_list/de/test/RoleDataGrantsItem.java').exists()) + assertTrue(new File('tmp/java_beans_black_list/de/test/RoleModuleGrantsItem.java').exists()) + assertTrue(new File('tmp/java_beans_black_list/de/test/User.java').exists()) + assertTrue(new File('tmp/java_beans_black_list/de/test/UserLog.java').exists()) + new File('tmp/java_beans_black_list/de/test').listFiles(new FileFilter() { + @Override + boolean accept(File file) { + return file.isFile() + } + }).size()==4 + } + + @Test + void testWhiteList() { + def destDir = 'tmp/java_beans_white_list' + FileHelper.removeDirectoryIfExists(destDir) + def modelFile = new File('src/test/resources/test_schemas/ds/user.json') + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[modelFile] + doCodeGen.generators.add('java_beans') + doCodeGen.outputBaseDir = destDir + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.whiteListed = ['User','Role'] + doCodeGen.generator_parameters.add('packageName=de.test') + doCodeGen.run() + assertTrue(new File('tmp/java_beans_white_list/de/test/Role.java').exists()) + assertTrue(new File('tmp/java_beans_white_list/de/test/User.java').exists()) + new File('tmp/java_beans_white_list/de').listFiles(new FileFilter() { + @Override + boolean accept(File file) { + return file.isFile() + } + }).size()==2 + } + } diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaInterfacedBeans.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaInterfacedBeans.groovy index 9f8d59a..28dc92e 100644 --- a/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaInterfacedBeans.groovy +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaInterfacedBeans.groovy @@ -17,7 +17,7 @@ class JavaInterfacedBeans { def modelFile = new File('src/test/resources/test_schemas/ds/user.json') de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models=[modelFile] doCodeGen.generators.add('java_interfaces') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -25,7 +25,7 @@ class JavaInterfacedBeans { doCodeGen.run() de.lisaplus.atlas.DoCodeGen doCodeGen2 = new de.lisaplus.atlas.DoCodeGen() - doCodeGen2.model = modelFile + doCodeGen2.models = [modelFile] doCodeGen2.generators.add('java_interfaced_beans') doCodeGen2.outputBaseDir = destDir doCodeGen2.generator_parameters.add('removeEmptyLines=true') @@ -41,7 +41,7 @@ class JavaInterfacedBeans { FileHelper.removeDirectoryIfExists(destDir) de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models=[modelFile] doCodeGen.generators.add('java_interfaces') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -49,7 +49,7 @@ class JavaInterfacedBeans { doCodeGen.run() de.lisaplus.atlas.DoCodeGen doCodeGen2 = new de.lisaplus.atlas.DoCodeGen() - doCodeGen2.model = modelFile + doCodeGen2.models = [modelFile] doCodeGen2.generators.add('java_interfaced_beans') doCodeGen2.outputBaseDir = destDir doCodeGen2.generator_parameters.add('removeEmptyLines=true') @@ -65,7 +65,7 @@ class JavaInterfacedBeans { FileHelper.removeDirectoryIfExists(destDir) de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models=[modelFile] doCodeGen.generators.add('java_interfaces') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -73,7 +73,7 @@ class JavaInterfacedBeans { doCodeGen.run() de.lisaplus.atlas.DoCodeGen doCodeGen2 = new de.lisaplus.atlas.DoCodeGen() - doCodeGen2.model = modelFile + doCodeGen2.models = [modelFile] doCodeGen2.generators.add('java_interfaced_beans') doCodeGen2.outputBaseDir = destDir doCodeGen2.generator_parameters.add('removeEmptyLines=true') diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaInterfaces.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaInterfaces.groovy index 55f588d..4053a48 100644 --- a/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaInterfaces.groovy +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/JavaInterfaces.groovy @@ -16,7 +16,7 @@ class JavaInterfaces { FileHelper.removeDirectoryIfExists(destDir) def modelFile = new File('src/test/resources/test_schemas/ds/user.json') de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models = [modelFile] doCodeGen.generators.add('java_interfaces') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -30,7 +30,7 @@ class JavaInterfaces { FileHelper.removeDirectoryIfExists(destDir) def modelFile = new File('src/test/resources/test_schemas/ds/base_types/map_object_multi_type.json') de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models = [modelFile] doCodeGen.generators.add('java_interfaces') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -44,7 +44,7 @@ class JavaInterfaces { FileHelper.removeDirectoryIfExists(destDir) def modelFile = new File('src/test/resources/test_schemas/ds/base_types/simple_map_object.json') de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = modelFile + doCodeGen.models = [modelFile] doCodeGen.generators.add('java_interfaces') doCodeGen.outputBaseDir = destDir doCodeGen.generator_parameters.add('removeEmptyLines=true') diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/JsonSchema.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/JsonSchema.groovy index e07ffde..cd03024 100644 --- a/src/test/groovy/de/lisaplus/atlas/codegen/test/JsonSchema.groovy +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/JsonSchema.groovy @@ -3,6 +3,7 @@ package de.lisaplus.atlas.codegen.test import org.junit.Test import static junit.framework.Assert.assertTrue +import static org.junit.Assert.assertTrue /** * Tests the plantuml generator and template @@ -11,7 +12,7 @@ import static junit.framework.Assert.assertTrue class JsonSchema { private static void generateHistModel(String model, String destFile) { de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model=model + doCodeGen.models=[model] doCodeGen.generators.add('json_schema') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add("destFileName=${destFile}") @@ -22,7 +23,7 @@ class JsonSchema { private static void generatePlantUML(String model, String destFile) { de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model=model + doCodeGen.models=[model] doCodeGen.generators.add('plantuml') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add("destFileName=${destFile}") @@ -67,4 +68,9 @@ class JsonSchema { generatePlantUML("tmp/${histModel}",plantUml) } + @Test + void testXsdInput() { + generateHistModel('src/test/resources/xsd/ui-tlc.xsd','ui-tlc.json') + } + } diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/MongoBeans.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/MongoBeans.groovy new file mode 100644 index 0000000..eef1c18 --- /dev/null +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/MongoBeans.groovy @@ -0,0 +1,26 @@ +package de.lisaplus.atlas.codegen.test + +import de.lisaplus.atlas.codegen.test.base.FileHelper +import org.junit.Test + +import static org.junit.Assert.assertTrue + +/** + * Tests the java bean generator and template + * Created by eiko on 19.06.17. + */ +class MongoBeans { + @Test + void createFromUserModel() { + def destDir = 'tmp/mongo_beans' + FileHelper.removeDirectoryIfExists(destDir) + def modelFile = new File('src/test/resources/test_schemas/ds/junction2.json') + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[modelFile] + doCodeGen.generators.add('mongo_beans') + doCodeGen.outputBaseDir = destDir + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.generator_parameters.add('packageName=de.schlothauer.test.mongo') + doCodeGen.run() + } +} diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/MultiFileTemplates.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/MultiFileTemplates.groovy new file mode 100644 index 0000000..b0d8569 --- /dev/null +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/MultiFileTemplates.groovy @@ -0,0 +1,24 @@ +package de.lisaplus.atlas.codegen.test + +import de.lisaplus.atlas.codegen.test.base.FileHelper +import org.junit.Test + +import static org.junit.Assert.assertFalse +import static org.junit.Assert.assertTrue + +class MultiFileTemplates { + @Test + void testHandlingTemplate() { + def destDir = 'tmp/handling' + FileHelper.removeDirectoryIfExists(destDir) + def modelFile = new File('src/test/resources/test_schemas/ds/incident.json') + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[modelFile] + doCodeGen.generators.add('multifiles=src/test/resources/templates/handling.txt') + doCodeGen.generatorScript = 'src/test/resources/templates/handling_helper.groovy' + doCodeGen.outputBaseDir = destDir + doCodeGen.generator_parameters.add('packageName=de.handling') + doCodeGen.run() + } + +} diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/PlantUml.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/PlantUml.groovy index 10bf9cc..5909a65 100644 --- a/src/test/groovy/de/lisaplus/atlas/codegen/test/PlantUml.groovy +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/PlantUml.groovy @@ -2,7 +2,9 @@ package de.lisaplus.atlas.codegen.test import org.junit.Test +import static junit.framework.Assert.assertEquals import static junit.framework.Assert.assertTrue +import static org.junit.Assert.assertFalse /** * Tests the plantuml generator and template @@ -13,7 +15,7 @@ class PlantUml { void createUserModel() { def destFile = 'tmp/user.puml' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = 'src/test/resources/test_schemas/ds/user.json' + doCodeGen.models = ['src/test/resources/test_schemas/ds/user.json'] doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/plantuml.txt') doCodeGen.outputBaseDir = 'tmp' doCodeGen.generator_parameters.add('destFileName=user.puml') @@ -22,11 +24,305 @@ class PlantUml { assertTrue(new File(destFile).exists()) } + @Test + void createIncidentModel() { + def destFile = 'tmp/incident.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/incident.json'] + doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/plantuml.txt') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('destFileName=incident.puml') + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + + @Test + void createLisaInesNetworkModel() { + def destFileBase = 'lisa_ines_network_json.puml' + def destFile = "tmp/$destFileBase" + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/lisa-ines-network.json'] + doCodeGen.generators.add('plantuml') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add("destFileName=$destFileBase") + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + + @Test + void createIncidentModelAddTags() { + def destFile = 'tmp/incident.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/incident.json'] + doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/plantuml.txt') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('destFileName=incident.puml') + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.typeAddTagList.add('Incident=main') + doCodeGen.typeAddTagList.add('Incident=cool') + doCodeGen.typeAddTagList.add('Domain=cool') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + boolean incidentFound=false + boolean domainFound=false + doCodeGen.dataModel.types.each { type -> + if (type.name=='Incident') { + incidentFound=true + assertEquals(5,type.tags.size()) + assertTrue(type.tags.contains('main')) + assertTrue(type.tags.contains('cool')) + } + else if (type.name=='Domain') { + domainFound=true + assertEquals(1,type.tags.size()) + assertTrue(type.tags.contains('cool')) + } + else { + assertFalse(type.tags.contains('main')) + assertFalse(type.tags.contains('cool')) + } + } + assertTrue(incidentFound) + assertTrue(domainFound) + } + + @Test + void createIncidentModelRemoveTags() { + def destFile = 'tmp/incident.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/incident.json'] + doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/plantuml.txt') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('destFileName=incident.puml') + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.typeRemoveTagList.add('Incident=mongodb') + doCodeGen.typeRemoveTagList.add('Incident=rest') + doCodeGen.typeRemoveTagList.add('Incident=joined') + doCodeGen.typeRemoveTagList.add('IncidentTag=rest') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + boolean incidentFound=false + boolean incidentTagFound=false + doCodeGen.dataModel.types.each { type -> + if (type.name=='Incident') { + incidentFound=true + assertEquals(0,type.tags.size()) + } + else if (type.name=='IncidentTag') { + incidentTagFound=true + assertEquals(1,type.tags.size()) + assertTrue(type.tags.contains('mongodb')) + } + } + assertTrue(incidentFound) + assertTrue(incidentTagFound) + } + + @Test + void createIncidentModelRemoveAllTags() { + def destFile = 'tmp/incident.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/incident.json'] + doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/plantuml.txt') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('destFileName=incident.puml') + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.generator_parameters.add('guidTypeColor=f9f6e0') + doCodeGen.generator_parameters.add('ignoreUnRefTypes=true') + doCodeGen.typeRemoveTagAllList.add('rest') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + boolean incidentFound=false + boolean incidentTagFound=false + doCodeGen.dataModel.types.each { type -> + assertFalse(type.tags.contains('mainType')) // could be implicit set with command line switch + assertFalse(type.tags.contains('rest')) + if (type.name=='Incident') { + incidentFound=true + assertEquals(2,type.tags.size()) + } + else if (type.name=='IncidentTag') { + incidentTagFound=true + assertEquals(1,type.tags.size()) + } + } + assertTrue(incidentFound) + assertTrue(incidentTagFound) + } + + @Test + void addMainTypeTags() { + def destFile = 'tmp/incident_mainTypeTags.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/incident.json'] + doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/plantuml.txt') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('destFileName=incident_mainTypeTags.puml') + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.tagMainTypes = true + doCodeGen.run() + assertTrue(new File(destFile).exists()) + + int mainTypeCount=0 + doCodeGen.dataModel.types.each { type -> + if (type.tags.contains('mainType')) { + mainTypeCount++ + } + if (type.name=='Incident') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentState') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentStateType') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentComment') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentType') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentTag') { + assertTrue(type.tags.contains('mainType')) + } + } + assertEquals(6,mainTypeCount) + } + + //@Test + void addMainTypeTagsMultipleModels() { + def destFile = 'tmp/incident_mainTypeTags_multipleModels.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/incident.json', + 'src/test/resources/test_schemas/ds/junction.json'] + doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/plantuml.txt') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('destFileName=incident_mainTypeTags.puml') + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.tagMainTypes = true + doCodeGen.run() + assertTrue(new File(destFile).exists()) + + int mainTypeCount=0 + doCodeGen.dataModel.types.each { type -> + if (type.tags.contains('mainType')) { + mainTypeCount++ + } + if (type.name=='Incident') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentState') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentStateType') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentComment') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentType') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentTag') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='Junction') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='JunctionState') { + assertTrue(type.tags.contains('mainType')) + } + } + assertEquals(6,mainTypeCount) + } + + @Test + void addMainTypeTagsWithEnums() { + def destFile = 'tmp/incident_mainTypeTags.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/incident.json'] + doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/plantuml.txt') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('destFileName=incident_mainTypeTags.puml') + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.tagMainTypes = true + doCodeGen.createEnumTypes = true + doCodeGen.run() + assertTrue(new File(destFile).exists()) + + int mainTypeCount=0 + doCodeGen.dataModel.types.each { type -> + if (type.tags.contains('mainType')) { + mainTypeCount++ + } + if (type.name=='Incident') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentState') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentStateType') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentComment') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentType') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentTag') { + assertTrue(type.tags.contains('mainType')) + } + } + assertEquals(7,mainTypeCount) + } + + @Test + void addMainTypeTags_attrib() { + def destFile = 'tmp/incident_mainTypeTags.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/incident.json'] + doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/plantuml.txt') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('destFileName=incident_mainTypeTags.puml') + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.tagMainTypes = true + doCodeGen.mainTypeAttrib = 'guid' + doCodeGen.run() + assertTrue(new File(destFile).exists()) + + int mainTypeCount=0 + doCodeGen.dataModel.types.each { type -> + if (type.tags.contains('mainType')) { + mainTypeCount++ + } + if (type.name=='Incident') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentStateType') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentComment') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentType') { + assertTrue(type.tags.contains('mainType')) + } + if (type.name=='IncidentTag') { + assertTrue(type.tags.contains('mainType')) + } + } + assertEquals(5,mainTypeCount) + } + @Test void createLicenseModel() { def destFile = 'tmp/license.puml' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = 'src/test/resources/test_schemas/ds/license.json' + doCodeGen.models = ['src/test/resources/test_schemas/ds/license.json'] doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/plantuml.txt') doCodeGen.outputBaseDir = 'tmp' doCodeGen.generator_parameters.add('destFileName=license.puml') @@ -37,9 +333,9 @@ class PlantUml { @Test void createUserModel_BuiltIn() { - def destFile = 'tmp/user_model.puml' + def destFile = 'tmp/userModel.puml' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = 'src/test/resources/test_schemas/ds/user.json' + doCodeGen.models = ['src/test/resources/test_schemas/ds/user.json'] doCodeGen.generators.add('plantuml') doCodeGen.outputBaseDir = 'tmp' doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -47,11 +343,38 @@ class PlantUml { assertTrue(new File(destFile).exists()) } + @Test + void createJunctionModel_BuiltIn() { + def destFile = 'tmp/junctionModel.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/junction2.json'] + doCodeGen.generators.add('plantuml') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + // TODO remove - start + @Test + void test_Micha1() { + def destFile = 'tmp/KnotenDaten.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/KnotenDaten.json'] + doCodeGen.generators.add('plantuml') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.generator_parameters.add('destFileName=KnotenDaten.puml') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + // TODO remove - end + + @Test void createFlorian_BuiltIn() { def destFile = 'tmp/device.puml' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = 'src/test/resources/test_schemas/ds/Device.json' + doCodeGen.models = ['src/test/resources/test_schemas/ds/Device.json'] doCodeGen.generators.add('plantuml') doCodeGen.outputBaseDir = 'tmp' doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -63,7 +386,7 @@ class PlantUml { void testHeavyReferenced() { def destFile = 'tmp/heavy_referenced.puml' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = 'src/test/resources/test_schemas/ds/referenced_multi_types.json' + doCodeGen.models = ['src/test/resources/test_schemas/ds/referenced_multi_types.json'] doCodeGen.generators.add('plantuml') doCodeGen.outputBaseDir = 'tmp' doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -72,12 +395,25 @@ class PlantUml { assertTrue(new File(destFile).exists()) } + @Test + void testHeavyReferencedWithoutBaseTypes() { + def destFile = 'tmp/heavy_referenced2.puml' + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/test_schemas/ds/referenced_multi_types.json'] + doCodeGen.generators.add('plantuml') + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.generator_parameters.add('destFileName=heavy_referenced2.puml') + doCodeGen.generator_parameters.add('ignoreBaseTypes=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } @Test void createUserModel_Markdown() { def destFile = 'tmp/user_puml.md' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = 'src/test/resources/test_schemas/ds/user.json' + doCodeGen.models = ['src/test/resources/test_schemas/ds/user.json'] doCodeGen.generators.add('plantuml') doCodeGen.outputBaseDir = 'tmp' doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -90,7 +426,7 @@ class PlantUml { static void test_v4_base(def outputFileBase) { def destFile = "tmp/${outputFileBase}.puml" de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model = "src/test/resources/schemas/${outputFileBase}.json" + doCodeGen.models = ["src/test/resources/schemas/${outputFileBase}.json"] doCodeGen.generators.add('plantuml') doCodeGen.outputBaseDir = 'tmp' doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -120,4 +456,5 @@ class PlantUml { test_v4_base("ramwa.schema") test_v4_base("notify") } + } \ No newline at end of file diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/Swagger.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/Swagger.groovy index a41b2a3..0a60d13 100644 --- a/src/test/groovy/de/lisaplus/atlas/codegen/test/Swagger.groovy +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/Swagger.groovy @@ -15,7 +15,7 @@ class Swagger { void createUserModel_SingleFile() { def destFile='tmp/user.swagger' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model='src/test/resources/test_schemas/ds/user.json' + doCodeGen.models=['src/test/resources/test_schemas/ds/user.json'] doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/swagger_file.txt') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add('destFileName=user.swagger') @@ -27,9 +27,9 @@ class Swagger { @Test void createUserModel_BuiltIn() { - def destFile='tmp/user_model.swagger' + def destFile='tmp/userModel.swagger' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model='src/test/resources/test_schemas/ds/user.json' + doCodeGen.models=['src/test/resources/test_schemas/ds/user.json'] doCodeGen.generators.add('swagger') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -40,9 +40,9 @@ class Swagger { @Test void createLicenseModel_BuiltIn() { - def destFile='tmp/license_model.swagger' + def destFile='tmp/licenseModel.swagger' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model='src/test/resources/test_schemas/ds/license.json' + doCodeGen.models=['src/test/resources/test_schemas/ds/license.json'] doCodeGen.generators.add('swagger') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -55,7 +55,7 @@ class Swagger { void createNotifyModel_BuiltIn() { def destFile='tmp/notify_swagger.yaml' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model='src/test/resources/schemas/notify.json' + doCodeGen.models=['src/test/resources/schemas/notify.json'] doCodeGen.generators.add('swagger') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add('destFileName=notify_swagger.yaml') @@ -63,7 +63,7 @@ class Swagger { doCodeGen.generator_parameters.add('removeEmptyLines=true') doCodeGen.generator_parameters.add('host=notify.swarco.com') doCodeGen.run() - assertTrue(new File(destFile).exists()) + assertTrue(destFile, new File(destFile).exists()) } } diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/SwaggerExt.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/SwaggerExt.groovy index 705708f..654378e 100644 --- a/src/test/groovy/de/lisaplus/atlas/codegen/test/SwaggerExt.groovy +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/SwaggerExt.groovy @@ -13,7 +13,7 @@ class SwaggerExt { void createUserModel_SingleFile() { def destFile='tmp/user.swagger' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model='src/test/resources/test_schemas/ds/user.json' + doCodeGen.models=['src/test/resources/test_schemas/ds/user.json'] doCodeGen.generators.add('singlefile=src/main/resources/templates/meta/swagger_ext.txt') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add('destFileName=user.swagger') @@ -25,9 +25,9 @@ class SwaggerExt { @Test void createUserModel_BuiltIn() { - def destFile='tmp/user_model.swagger' + def destFile='tmp/userModel.swagger' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model='src/test/resources/test_schemas/ds/user.json' + doCodeGen.models=['src/test/resources/test_schemas/ds/user.json'] doCodeGen.generators.add('swagger_ext') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add('removeEmptyLines=true') @@ -38,9 +38,9 @@ class SwaggerExt { @Test void createLicenseModel_BuiltIn() { - def destFile='tmp/license_model.swagger' + def destFile='tmp/licenseModel.swagger' de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() - doCodeGen.model='src/test/resources/test_schemas/ds/license.json' + doCodeGen.models=['src/test/resources/test_schemas/ds/license.json'] doCodeGen.generators.add('swagger_ext') doCodeGen.outputBaseDir='tmp' doCodeGen.generator_parameters.add('removeEmptyLines=true') diff --git a/src/test/groovy/de/lisaplus/atlas/codegen/test/Xsd.groovy b/src/test/groovy/de/lisaplus/atlas/codegen/test/Xsd.groovy new file mode 100644 index 0000000..31c1ec6 --- /dev/null +++ b/src/test/groovy/de/lisaplus/atlas/codegen/test/Xsd.groovy @@ -0,0 +1,44 @@ +package de.lisaplus.atlas.codegen.test + +import org.junit.Test + +import static junit.framework.Assert.assertTrue + +/** + * Tests the plantuml generator and template + * Created by eiko on 19.06.17. + */ +class Xsd { + private static void generateXsd(String model, String destFile) { + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[model] + doCodeGen.generators.add('xsd') + doCodeGen.outputBaseDir='tmp' + doCodeGen.generator_parameters.add("destFileName=${destFile}") + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File("tmp/${destFile}").exists()) + } + + private static void generatePlantUML(String model, String destFile) { + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models=[model] + doCodeGen.generators.add('plantuml') + doCodeGen.outputBaseDir='tmp' + doCodeGen.generator_parameters.add("destFileName=${destFile}") + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File("tmp/${destFile}").exists()) + } + + @Test + void createUserModel() { + def model='src/test/resources/test_schemas/ds/user.json' + def xsdModel='user.xsd' + def plantUmlJson='user_xsd.puml' + def plantUmlXsd='user_xsd.puml' + generatePlantUML(model,plantUmlJson) + generateXsd(model,xsdModel) + generatePlantUML("tmp/${xsdModel}",plantUmlXsd) + } +} diff --git a/src/test/groovy/de/lisaplus/atlas/de/lisaplus/atlas/builder/test/xsd/SimpleXsdBuilder.groovy b/src/test/groovy/de/lisaplus/atlas/de/lisaplus/atlas/builder/test/xsd/SimpleXsdBuilder.groovy new file mode 100644 index 0000000..1bd7cdb --- /dev/null +++ b/src/test/groovy/de/lisaplus/atlas/de/lisaplus/atlas/builder/test/xsd/SimpleXsdBuilder.groovy @@ -0,0 +1,146 @@ +package de.lisaplus.atlas.de.lisaplus.atlas.builder.test.xsd + +import de.lisaplus.atlas.builder.XSDBuilder +import org.junit.Test + +import static junit.framework.Assert.assertTrue +import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertNotNull +import static org.junit.Assert.assertTrue + +class SimpleXsdBuilder { + @Test + void simple() { + def modelFile = new File('src/test/resources/xsd/simpleTest.xsd') + assertTrue(modelFile.isFile()) + def builder = new XSDBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + } + + @Test + void arrays() { + def modelFile = new File('src/test/resources/xsd/arrayTest.xsd') + assertTrue(modelFile.isFile()) + def builder = new XSDBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + } + + @Test + void comlplex() { + def modelFile = new File('src/test/resources/xsd/complex/DSRC.xsd') + assertTrue(modelFile.isFile()) + def builder = new XSDBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + } + + @Test + void plantuml_1() { + def fileName = 'plantuml_from_xsd1.puml' + def destFile = "tmp/$fileName" + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/xsd/simpleTest.xsd'] + doCodeGen.generators.add('plantuml') + doCodeGen.generator_parameters.add("destFileName=$fileName") + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + + @Test + void plantuml_2() { + def fileName = 'plantuml_from_xsd2.puml' + def destFile = "tmp/$fileName" + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/xsd/complex/DSRC.xsd'] + doCodeGen.generators.add('plantuml') + doCodeGen.generator_parameters.add("destFileName=$fileName") + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + + @Test + void plantuml_3() { + def fileName = 'plantuml_from_xsd3.puml' + def destFile = "tmp/$fileName" + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/xsd/arrayTest.xsd'] + doCodeGen.generators.add('plantuml') + doCodeGen.generator_parameters.add("destFileName=$fileName") + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + + @Test + void json_schema() { + def fileName = 'lisa_ines_network.json' + def destFile = "tmp/$fileName" + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/xsd/ZEN_Versorgung_OMTC.xsd'] + doCodeGen.generators.add('json_schema') + doCodeGen.generator_parameters.add("destFileName=$fileName") + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + + @Test + void lisaInesNetworkPuml() { + def fileName = 'lisa_ines_network.puml' + def destFile = "tmp/$fileName" + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/xsd/ZEN_Versorgung_OMTC.xsd'] + doCodeGen.generators.add('plantuml') + doCodeGen.generator_parameters.add("destFileName=$fileName") + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + + @Test + void plantuml_4() { + def fileName = 'tlc.puml' + def destFile = "tmp/$fileName" + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/xsd/ui-tlc.xsd'] + doCodeGen.generators.add('plantuml') + doCodeGen.generator_parameters.add("destFileName=$fileName") + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + + @Test + void plantuml_6() { + def fileName = 'plantuml6.puml' + def destFile = "tmp/$fileName" + de.lisaplus.atlas.DoCodeGen doCodeGen = new de.lisaplus.atlas.DoCodeGen() + doCodeGen.models = ['src/test/resources/xsd/LSA_Versorgung_OMTC.xsd'] + doCodeGen.generators.add('plantuml') + doCodeGen.generator_parameters.add("destFileName=$fileName") + doCodeGen.outputBaseDir = 'tmp' + doCodeGen.generator_parameters.add('removeEmptyLines=true') + doCodeGen.run() + assertTrue(new File(destFile).exists()) + } + + @Test + void simple2() { + def modelFile = new File('src/test/resources/xsd/ui-tlc.xsd') + assertTrue(modelFile.isFile()) + def builder = new XSDBuilder() + def model = builder.buildModel(modelFile) + assertNotNull(model) + } + +} diff --git a/src/test/resources/extra/2340.json b/src/test/resources/extra/2340.json new file mode 100644 index 0000000..f750f7a --- /dev/null +++ b/src/test/resources/extra/2340.json @@ -0,0 +1,906 @@ +{ + "id": "http://schlothauer.de/schemas/lsa-process-data.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "JSON Schema example of 2340", + "type": "object", + "properties": { + "props": { + "description": "???", + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "user": { + "type": "string" + }, + "date": { + "type": "number" + }, + "dversion": { + "type": "integer" + } + } + }, + "KnotenData": { + "description": "basically attributes start with lower case char", + "$ref": "#/definitions/KnotenData" + } + }, + "definitions": { + "KnotenData": { + "type": "object", + "properties": { + "knotndat": { + "type": "object", + "properties": { + "kndatattr": { + "type": "string", + "properties": { + "liverk": { + "type": "boolean", + "__tags": ["attribute"] + }, + "inort": { + "type": "boolean", + "__tags": ["attribute"] + }, + "ibara": { + "type": "boolean", + "__tags": ["attribute"] + }, + "sfzgn1": { + "type": "string", + "__tags": ["attribute"] + }, + "sfzgn2": { + "type": "string", + "__tags": ["attribute"] + }, + "nlsaver": { + "type": "integer", + "__tags": ["attribute"] + } + } + }, + "pp": { + "type": "object", + "properties": { + "ppattr": { + "type": "string", + "properties": { + "p1": { + "type": "integer", + "__tags": ["attribute"] + }, + "p2": { + "type": "string", + "__tags": ["attribute"] + }, + "p3": { + "type": "string", + "__tags": ["attribute"] + }, + "p5": { + "type": "integer", + "__tags": ["attribute"] + }, + "p6": { + "type": "integer", + "__tags": ["attribute"] + }, + "p7": { + "type": "integer", + "__tags": ["attribute"] + }, + "p8": { + "type": "boolean", + "__tags": ["attribute"] + }, + "p9": { + "type": "boolean", + "__tags": ["attribute"] + }, + "p10": { + "type": "number", + "__tags": ["attribute"] + }, + "p11": { + "type": "integer", + "__tags": ["attribute"] + }, + "p12": { + "type": "integer", + "__tags": ["attribute"] + }, + "p13": { + "type": "number", + "__tags": ["attribute"] + }, + "p14": { + "type": "number", + "__tags": ["attribute"] + }, + "p15": { + "type": "number", + "__tags": ["attribute"] + }, + "p16": { + "type": "number", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + }, + "armlist": { + "type": "object", + "properties": { + "arm": { + "type": "object", + "properties": { + "armattr": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "nr": { + "type": "integer", + "__tags": ["attribute"] + }, + "winkel": { + "type": "integer", + "__tags": ["attribute"] + }, + "spikr": { + "type": "integer", + "__tags": ["attribute"] + }, + "tknr": { + "type": "integer", + "__tags": ["attribute"] + }, + "name": { + "type": "string", + "__tags": ["attribute"] + }, + "vorftyp": { + "type": "integer", + "__tags": ["attribute"] + }, + "dreiins": { + "type": "boolean", + "__tags": ["attribute"] + }, + "dreickspnr": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufw": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufwl": { + "type": "number", + "__tags": ["attribute"] + }, + "mitins": { + "type": "boolean", + "__tags": ["attribute"] + }, + "nlsaaufln": { + "type": "number", + "__tags": ["attribute"] + }, + "fvfuss": { + "type": "number", + "__tags": ["attribute"] + }, + "tkusch": { + "type": "boolean", + "__tags": ["attribute"] + } + } + }, + "spurli": { + "type": "object", + "properties": { + "spur": { + "type": "object", + "properties": { + "spurattr": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "name": { + "type": "string", + "__tags": ["attribute"] + }, + "art": { + "type": "integer", + "__tags": ["attribute"] + }, + "nr": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstpl": { + "type": "number", + "__tags": ["attribute"] + }, + "hst": { + "type": "boolean", + "__tags": ["attribute"] + }, + "bre": { + "type": "number", + "__tags": ["attribute"] + }, + "lneig": { + "type": "number", + "__tags": ["attribute"] + }, + "abradra": { + "type": "number", + "__tags": ["attribute"] + }, + "abradla": { + "type": "number", + "__tags": ["attribute"] + }, + "abradwd": { + "type": "number", + "__tags": ["attribute"] + }, + "aufstli": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstre": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstrb": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstin": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstib": { + "type": "integer", + "__tags": ["attribute"] + }, + "lae": { + "type": "number", + "__tags": ["attribute"] + }, + "qsp": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstlla": { + "type": "number", + "__tags": ["attribute"] + }, + "aufstlra": { + "type": "number", + "__tags": ["attribute"] + }, + "mitins": { + "type": "boolean", + "__tags": ["attribute"] + } + } + }, + "mitins": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "bre": { + "type": "number", + "__tags": ["attribute"] + }, + "lae": { + "type": "number", + "__tags": ["attribute"] + }, + "auto": { + "type": "boolean", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + }, + "slattr": { + "type": "string", + "properties": { + "lid": { + "type": "integer", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + }, + "strlist": { + "type": "object", + "properties": { + "strom": { + "type": "object", + "properties": { + "strattr": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "varm": { + "type": "integer", + "__tags": ["attribute"] + }, + "narm": { + "type": "integer", + "__tags": ["attribute"] + }, + "richt": { + "type": "integer", + "__tags": ["attribute"] + }, + "exway": { + "type": "integer", + "__tags": ["attribute"] + }, + "crti": { + "type": "integer", + "__tags": ["attribute"] + }, + "sust": { + "type": "integer", + "__tags": ["attribute"] + }, + "tupo": { + "type": "integer", + "__tags": ["attribute"] + } + } + }, + "tslist": { + "type": "object", + "properties": { + "tstrom": { + "type": "object", + "properties": { + "tsattr": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "bre": { + "type": "number", + "__tags": ["attribute"] + }, + "fulae": { + "type": "number", + "__tags": ["attribute"] + }, + "name": { + "type": "string", + "__tags": ["attribute"] + }, + "fidx": { + "type": "integer", + "__tags": ["attribute"] + } + } + }, + "sfzge": { + "type": "string", + "properties": { + "kfz": { + "type": "string", + "__tags": ["attribute"] + }, + "fuss": { + "type": "string", + "__tags": ["attribute"] + } + } + }, + "spnrs": { + "type": "object", + "properties": { + "li": { + "type": "integer" + } + }, + "__version": 13 + }, + "spartn": { + "type": "object", + "properties": { + "li": { + "type": "integer" + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + }, + "fzgval": { + "type": "string", + "properties": { + "val0": { + "type": "number", + "__tags": ["attribute"] + }, + "val1": { + "type": "number", + "__tags": ["attribute"] + }, + "val2": { + "type": "number", + "__tags": ["attribute"] + }, + "val3": { + "type": "number", + "__tags": ["attribute"] + }, + "val4": { + "type": "number", + "__tags": ["attribute"] + }, + "val5": { + "type": "number", + "__tags": ["attribute"] + }, + "val6": { + "type": "number", + "__tags": ["attribute"] + }, + "val7": { + "type": "number", + "__tags": ["attribute"] + }, + "val8": { + "type": "number", + "__tags": ["attribute"] + }, + "val9": { + "type": "number", + "__tags": ["attribute"] + } + } + }, + "mitins": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "bre": { + "type": "number", + "__tags": ["attribute"] + }, + "lae": { + "type": "number", + "__tags": ["attribute"] + }, + "auto": { + "type": "boolean", + "__tags": ["attribute"] + } + } + }, + "kpvli": { + "type": "object", + "properties": { + "kpverb": { + "type": "object", + "properties": { + "kpvattr": { + "type": "string", + "properties": { + "wid": { + "type": "integer", + "__tags": ["attribute"] + }, + "lae": { + "type": "number", + "__tags": ["attribute"] + }, + "spanz": { + "type": "integer", + "__tags": ["attribute"] + }, + "rzt": { + "type": "number", + "__tags": ["attribute"] + }, + "fzanz": { + "type": "integer", + "__tags": ["attribute"] + }, + "ri": { + "type": "integer", + "__tags": ["attribute"] + }, + "name": { + "type": "string", + "__tags": ["attribute"] + } + } + }, + "sfzge": { + "type": "string", + "properties": { + "kfz": { + "type": "string", + "__tags": ["attribute"] + } + } + }, + "qkp": { + "type": "string", + "properties": { + "arm": { + "type": "integer", + "__tags": ["attribute"] + }, + "ort": { + "type": "string", + "__tags": ["attribute"] + }, + "kp": { + "type": "string", + "__tags": ["attribute"] + } + } + }, + "zkp": { + "type": "string", + "properties": { + "arm": { + "type": "integer", + "__tags": ["attribute"] + }, + "ort": { + "type": "string", + "__tags": ["attribute"] + }, + "kp": { + "type": "string", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + } + }, + "__version": 13 + }, + "kplili": { + "type": "object", + "properties": { + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + }, + "tkli": { + "type": "object", + "properties": { + "tk": { + "type": "object", + "properties": { + "tkattr": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "nr": { + "type": "integer", + "__tags": ["attribute"] + }, + "name": { + "type": "string", + "__tags": ["attribute"] + }, + "pktx": { + "type": "integer", + "__tags": ["attribute"] + }, + "pkty": { + "type": "integer", + "__tags": ["attribute"] + }, + "krv": { + "type": "boolean", + "__tags": ["attribute"] + }, + "fzl": { + "type": "number", + "__tags": ["attribute"] + }, + "nzl": { + "type": "number", + "__tags": ["attribute"] + }, + "mzl": { + "type": "number", + "__tags": ["attribute"] + }, + "gzl": { + "type": "number", + "__tags": ["attribute"] + }, + "pch": { + "type": "boolean", + "__tags": ["attribute"] + }, + "vis": { + "type": "boolean", + "__tags": ["attribute"] + }, + "gernr": { + "type": "integer", + "__tags": ["attribute"] + }, + "audm": { + "type": "number", + "__tags": ["attribute"] + }, + "usadd": { + "type": "boolean", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + } + }, + "__version": 13 + }, + "tkverbli": { + "type": "object", + "properties": { + }, + "__version": 13 + }, + "nopfl": { + "type": "string", + "properties": { + "sty": { + "type": "integer", + "__tags": ["attribute"] + }, + "winkel": { + "type": "number", + "__tags": ["attribute"] + }, + "gro": { + "type": "number", + "__tags": ["attribute"] + }, + "vis": { + "type": "boolean", + "__tags": ["attribute"] + }, + "pktx": { + "type": "number", + "__tags": ["attribute"] + }, + "pkty": { + "type": "number", + "__tags": ["attribute"] + } + } + }, + "knotgraf": { + "type": "object", + "properties": { + "knograttr": { + "type": "string", + "properties": { + "fcol": { + "type": "integer", + "__tags": ["attribute"] + }, + "fhei": { + "type": "integer", + "__tags": ["attribute"] + }, + "fname": { + "type": "string", + "__tags": ["attribute"] + } + } + }, + "pp": { + "type": "object", + "properties": { + "ppattr": { + "type": "string", + "properties": { + "p1": { + "type": "integer", + "__tags": ["attribute"] + }, + "p2": { + "type": "string", + "__tags": ["attribute"] + }, + "p3": { + "type": "string", + "__tags": ["attribute"] + }, + "p5": { + "type": "integer", + "__tags": ["attribute"] + }, + "p6": { + "type": "integer", + "__tags": ["attribute"] + }, + "p7": { + "type": "integer", + "__tags": ["attribute"] + }, + "p8": { + "type": "boolean", + "__tags": ["attribute"] + }, + "p9": { + "type": "boolean", + "__tags": ["attribute"] + }, + "p10": { + "type": "number", + "__tags": ["attribute"] + }, + "p11": { + "type": "integer", + "__tags": ["attribute"] + }, + "p12": { + "type": "integer", + "__tags": ["attribute"] + }, + "p13": { + "type": "number", + "__tags": ["attribute"] + }, + "p14": { + "type": "number", + "__tags": ["attribute"] + }, + "p15": { + "type": "number", + "__tags": ["attribute"] + }, + "p16": { + "type": "number", + "__tags": ["attribute"] + } + } + }, + "ptpDim": { + "type": "boolean" + } + }, + "__version": 13 + }, + "nopf": { + "type": "string", + "properties": { + "ppx": { + "type": "number", + "__tags": ["attribute"] + }, + "ppy": { + "type": "number", + "__tags": ["attribute"] + }, + "usmov": { + "type": "boolean", + "__tags": ["attribute"] + } + } + }, + "gtkli": { + "type": "object", + "properties": { + "tk": { + "type": "string", + "properties": { + "mpx": { + "type": "number", + "__tags": ["attribute"] + }, + "mpy": { + "type": "number", + "__tags": ["attribute"] + }, + "usmov": { + "type": "boolean", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + } + } +} diff --git a/src/test/resources/extra/2430.xml b/src/test/resources/extra/2430.xml new file mode 100644 index 0000000..e4480f4 --- /dev/null +++ b/src/test/resources/extra/2430.xml @@ -0,0 +1,918 @@ + + + 7.1.0.600 + m.dombrowski + 43549.3961653009 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
  • 2
  • +
    + +
  • 0
  • +
  • 1
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
  • 1
  • +
  • 2
  • +
    + +
  • 0
  • +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
  • 2
  • +
    + +
  • 0
  • +
  • 0
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  • 2
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  • 3
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
  • 2
  • +
    + +
  • 0
  • +
  • 0
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
  • 2
  • +
    + +
  • 0
  • +
  • 1
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  • 1
  • +
  • 2
  • +
    + +
  • 0
  • +
  • 0
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
  • 2
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + +
  • 1
  • +
    + +
  • 1
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
    + +
  • 0
  • +
    +
    +
    +
    + + + + + + + +
  • 1
  • +
  • 2
  • +
    + +
  • 0
  • +
  • 1
  • +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + 0 + + + + + + + +
    +
    +
    \ No newline at end of file diff --git a/src/test/resources/templates/handling.txt b/src/test/resources/templates/handling.txt new file mode 100644 index 0000000..6b8c6c7 --- /dev/null +++ b/src/test/resources/templates/handling.txt @@ -0,0 +1,1945 @@ +<% + /** For enabling/disabling debug output */ + def verbose = Boolean.parseBoolean(extraParam.verbose) + /** The name of the Java class, which is being masked. */ + def targetType = upperCamelCase.call(currentType.name) + /** This stack holds the property (names) visited while traversing the object hierarchy.*/ + List propStack = [] + /** Indicates that this property is an array. This sack is build while traversing the object hierarchy. */ + List propIsArrayStack = [] + /** + * Indicates that this property or any of its parents was an array and that we therefore have to process an collection. + * This sack is build while traversing the object hierarchy. + */ + List propIsCollectionStack = [] + /** + * Defines overwrites for maskKeys (mapping of property name to associated mask key). + * This may be necessary for e.g. Joined types, where on property holds the Id of the joined object, and another + * property holds the joined object itself (property objectBaseId vs. property objectBase). + */ + Map maskKeyOverwrites = [:] + /** For caching the lines generated by the (recursive) closure calls of the model loops! */ + List allLines = [] + /** Indicates whether the current type is a joined type */ + def joined = targetType.endsWith('Joined') + + if (verbose) println "type=${currentType.name} javaType=${targetType} joined=$joined" + + /** Forward declaration of closure, which calls itself! */ + def tuneType + + /** + * THIS METHOD ALTERS THE MODEL!!! + * This method tunes the properties of a (complex or reference) type and calls itself recursively if the type + * itself holds properties of other (complex or reference) types. + * Checks for properties with the tag prepLookup and + *
      + *
    • joined==true: removes the property completely
    • + *
    • joined==false: removes the suffix Id
    • + * @param type The type to process. + */ + tuneType = { type -> + Closure action + if (joined) { + action = { prop -> + println "// ATTENTION: Removing lookup property ${prop.name}" + type.properties.remove(prop) + } + } else { + action = { prop -> + def orig = prop.name + def shorten = prop.name.take(prop.name.length() - 2) + println "// ATTENTION: Renaming lookup property from $orig to $shorten" + prop.setName(shorten) + } + } + Collection lookupProps = type.properties.findAll { prop -> prop.hasTag('prepLookup') && prop.name.endsWith('Id') } + type.properties.findAll { prop -> prop.implicitRefIsRefType() && !prop.isSelfReference() } each { prop -> tuneType.call(prop.implicitRef.type) } + type.properties.findAll { prop -> prop.isRefTypeOrComplexType() && !prop.isSelfReference() } each { prop -> tuneType.call(prop.type.type) } + lookupProps.each(action) + } + + /** + * Prepares the stacks for running the next loop over the model + */ + def prepareStacks = { + propStack.clear() + propIsArrayStack.clear() + // avoid extra case for handling empty stack! + propIsCollectionStack = [false] + } + + /** + * Adds new elements to the stacks + * @param property The property, which is to be visited + */ + def putStacks = { property -> + propStack.add(property) + propIsArrayStack.add(property.type.isArray) + // If either already collection of if this property is an collection. + propIsCollectionStack.add(propIsCollectionStack.last() || property.type.isArray) + } + + /** + * Pops the latest elements from the stacks + */ + def popStacks = { + propStack.pop() + propIsArrayStack.pop() + propIsCollectionStack.pop() + } + + /** + * Processes a ref or complex property. + * Take the property's type, looks through it properties and returns the one of type UUID. + * That should usually be the entryId or guid! + */ + def findIdProperty = { property -> + def type = property.type.type + List candidates = type.properties.findAll { prop -> prop.type.name() == 'UUID'} // (${type.type.name()}) + if (candidates.isEmpty() && type.name == 'ListEntry') { + // ListEntry usually have a StringType property refId (wanted) and text (to be avoided) + candidates.addAll( type.properties.findAll { prop -> prop.type.name() == 'STRING' && prop.name == 'refId'} ) + } + /* + assert candidates.size() == 1 : "property=${property.name} type=${type.name} props=${type.properties.collect{ it.name }.join(', ')}" + assert candidates[0].name == 'guid' || candidates[0].name == 'entryId' || candidates[0].name == 'refId' : "property=${property.name} type=${type.name} props=${type.properties.collect{ it.name }.join(', ')}" + */ + return candidates.isEmpty() ? null : candidates[0].name + } + + /** Creates a key from current state of propStack, e.g. gis.area */ + Closure currentKey = { + return propStack.collect { prop -> prop.name}.join('.') + } + + /** Forward declaration of closure, which calls itself! */ + def evalDeepNestedListKeys + + /** + * Closure which actually looks for the array of ref or complex properties, for which a method removeXXXById(pojo, targetId) + * needs to be created. This closure calls itself recursively! + * @param type The type, which is to be processed + * @param keys The list of keys, which is to be extended while traversing the model. + */ + evalDeepNestedListKeys = { type, List keys -> + // find all ref & complex properties, which are hold in arrays! These are the candidates for the methods removeXXXById(pojo, targetId) + type.properties.findAll{ prop -> prop.isRefTypeOrComplexType() && prop.type.isArray }.each { prop -> + putStacks.call(prop) + def key = currentKey.call() + if (findIdProperty.call(prop)) { + keys.add(key) + } else { + println "ATTENTION: Skip creating removeXXX(pojo, UUID) as property is missing proper ID: type=${currentType.name} key=${key}" + } + popStacks.call() + } + // recurse into all ref & complex properties, which are not hold in arrays! + type.properties.findAll{ prop -> prop.isRefTypeOrComplexType() && !prop.type.isArray }.each { prop -> + putStacks.call(prop) + evalDeepNestedListKeys.call(prop.type.type, keys) + popStacks.call() + } + } + + /** + * Closure, which kicks off the search for the keys, which are candidates for the method removeXXXById(pojo, targetId) + * -> find array of ref or complex properties + * @param type The type, which is to be processed + */ + def evalDeepNestedListKeysForType = { type -> + // find keys for method removeXXXById(pojo, targetId) -> find array of ref or complex properties + List keys = [] + prepareStacks.call() + evalDeepNestedListKeys.call(type, keys) + return keys + } + + /** Forward declaration of closure, which calls itself! */ + def evalOtherHandlingKeysForArrays + + /** + * Closure which actually looks for the array of ref or complex properties, where another Handling class needs to be called. + * (-> find array of ref or complex properties contained by array of ref or complex type - ignore children unless they lead to another relevant array) + * This closure calls itself recursively! + * @param type The type, which is to be processed + * @param keys The list of keys, which is to be extended while traversing the model. + * @param currDepth The number of array properties, one passed when navigating to the current type + */ + evalOtherHandlingKeysForArrays = { type, List keys, int currDepth -> + // find all ref & complex properties, which are hold in arrays! These are the candidates for the methods removeXXXById(pojo, UUID) + type.properties.findAll{ prop -> prop.isRefTypeOrComplexType() && prop.type.isArray }.each { prop -> + putStacks.call(prop) + def key = currentKey.call() + if (currDepth > 0) { + if (findIdProperty.call(prop)) { + println "Call other handling class for deep nested array property: type=${currentType.name} key=${key}" + keys.add(key) + } else { + println "ATTENTION: Skip calling other Handling classes as array property is missing proper ID: type=${currentType.name} key=${key}" + } + } + popStacks.call() + } + // recurse into all ref & complex properties, array or not! + type.properties.findAll{ prop -> prop.isRefTypeOrComplexType() }.each { prop -> + if (prop.type.isArray) { + currDepth++ + } + putStacks.call(prop) + evalOtherHandlingKeysForArrays.call(prop.type.type, keys, currDepth) + if (prop.type.isArray) { + currDepth-- + } + popStacks.call() + } + } + + /** + * Closure, which kicks off the search for the keys, where another Handling class needs to be called + * -> find array of ref or complex properties contained by array of ref or complex type - ignore children unless they lead to another relevant array + * @param type The type, which is to be processed + */ + def evalOtherHandlingKeysForArraysForType = { type -> + List keys = [] + prepareStacks.call() + evalOtherHandlingKeysForArrays.call(type, keys, 0) + return keys + } + + /** Forward declaration of closure, which calls itself! */ + def evalOtherHandlingKeys + + /** + * Closure which actually looks for the array of ref or complex properties, where another Handling class needs to be called. + * (-> find array of ref or complex properties contained by array of ref or complex type, and their ref of complex children) + * This closure calls itself recursively! + * @param type The type, which is to be processed + * @param keys The list of keys, which is to be extended while traversing the model. + * @param currDepth The number of array properties, one passed when navigating to the current type + */ + evalOtherHandlingKeys = { type, List keys, int currDepth -> + // find all ref & complex properties, which are hold in arrays! These are the candidates for the methods removeXXXById(pojo, UUID) + type.properties.findAll{ prop -> prop.isRefTypeOrComplexType() }.each { prop -> + putStacks.call(prop) + def key = currentKey.call() + def isArray = prop.type.isArray + if (isArray && !findIdProperty.call(prop) ) { + println "ATTENTION: Skip calling other Handling classes as array property is missing proper ID: type=${currentType.name} key=${key}" + return + } + if (isArray) { + currDepth++ + } + if (currDepth > 1) { + println "Call other handling class for deep nested array property: type=${currentType.name} key=${key}" + keys.add(key) + } + // recurse into all ref & complex properties, array or not! + evalOtherHandlingKeys.call(prop.type.type, keys, currDepth) + if (prop.type.isArray) { + currDepth-- + } + popStacks.call() + } + } + + /** + * Closure, which kicks off the search for the keys, where another Handling class needs to be called + * -> find array of ref or complex properties contained by array of ref or complex type, and their ref of complex children + * @param type The type, which is to be processed + */ + def evalOtherHandlingKeysForType = { type -> + List keys = [] + prepareStacks.call() + evalOtherHandlingKeys.call(type, keys, 0) + return keys + } + + /** + * Take a key / chain of property names, and builds a matching property stack. e.g location.streets + */ + def propStackFromKey = { String key -> + List localPropStack = [] + def curType = currentType + def curProp + for (String propName : key.split('\\.')) { + curProp = curType.properties.find { it.name == propName } + assert curProp && curProp.isRefTypeOrComplexType() : "propChain=${propChain} propName=${propName}" + localPropStack.add(curProp) + curType = curProp.type.type + } + return localPropStack + } + + /** + * Splits the property stack into sub-stacks. + * Each sub-stack starts and ends with an array property. + * Exceptions: the first partition may not start with an array property, the last may not end with an array property. + * Inner array properties are referenced twice: As the last element of sub-stack n and first element of sub-stack n+1 + */ + Closure>> partitionArrayPros = { List fullPropStack -> + final List allList = [] + final List currList = [] + final Iterator iter = fullPropStack.iterator() + while (iter.hasNext()) { + final def currProp = iter.next() + currList.add(currProp) + if (currProp.type.isArray) { + allList.add(currList.clone()) + currList.clear() + currList.add(currProp) + } + } + if (currList.size() > 1) { + allList.add(currList) + } + return allList + } + + // Forward declaration of closure, which calls itself! + def evalDeepNestedListChildKeys + + /** + * Closure which actually looks for the single ref or complex child of array of ref or complex properties, for which + * a method setXXXById(pojo, targetId, value) needs to be created. This closure calls itself recursively! + * @param type The type, which is to be processed + * @param key Defines the array of ref or complex properties, where single ref or complex child properties are to be found. + * @param listKey2childKey The mapping of array key to child key, which is to be extended while traversing the model. + */ + evalDeepNestedListChildKeys = { type, String key, Map listKey2childKey -> + type.properties.findAll{ prop -> prop.isRefTypeOrComplexType() && !prop.type.isArray }.each { prop -> + putStacks.call(prop) + def childKey = currentKey.call() + listKey2childKey.put(key, childKey) + // recurse into all ref & complex properties, which are not hold in arrays! + evalDeepNestedListChildKeys.call(prop.type.type, key, listKey2childKey) + popStacks.call() + } + } + + /** + * Closure which kicks off the search for the keys, which are candidates for the method setXXXById(pojo, targetId, value) + * -> find single ref or complex children of array of ref or complex properties + */ + def evalDeepNestedListChildKeysForType = { type, List keys -> + Map listKey2childKey = [:] + for (String key : keys) { + prepareStacks.call() + // use key to restore prop stack of array property + propStackFromKey.call(key).each { prop -> putStacks.call(prop) } + // start recursive search for single ref or complex child + assert propStack.last().isRefTypeOrComplexType() && propStack.last().type.isArray : key + evalDeepNestedListChildKeys.call(propStack.last().type.type, key, listKey2childKey) + } + return listKey2childKey + } + + /** + * Prints the method boolean addXXX(pojo, additional) associated with one key, + * where the array property is a child of the main type + */ + Closure printAddForKeyShallow = { String key -> + if (verbose) println "// printAddForKeyShallow for key $key" + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + def upperLast = upperCamelCase.call(localPropStack.last().name) + def ret = """ + /** + * Tries to add a ${typeNameInner} object to a ${typeName}. + * @param pojo the parent object to extend + * @param additional The ${typeNameInner} object to add to the ${typeName} + * @return True, if the ${typeNameInner} object could be added to the ${typeName}, + * false otherwise. + */ + public static boolean add${keyUpper}(final ${typeName} pojo, final ${typeNameInner} additional) { + if (check${keyUpper}Exists(pojo)) { + get${keyUpper}(pojo).add(additional); + return true; + } + List<${typeNameInner}> list = new ArrayList<>(); + list.add(additional); + pojo.set${upperLast}(list); + return true; + }""" + return ret + } + + /** + * Prints the method boolean addXXX(pojo, additional) associated with one key + * of a deep nested array property. + */ + Closure printAddForKeyDeep = { String key -> + if (verbose) println "// printAddForKeyDeep for key $key" + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + List parentStack = localPropStack.clone() + parentStack.pop() + def keyUpperParent = parentStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def upperSecondLast = upperCamelCase.call(parentStack.last().name) + def upperLast = upperCamelCase.call(localPropStack.last().name) + def ret = """ + /** + * Tries to add a ${typeNameInner} object to a ${typeName}. + * @param pojo the parent object to extend + * @param additional The ${typeNameInner} object to add to the ${typeName} + * @return True, if the ${typeNameInner} object could be added to the ${typeName}, + * false otherwise. + */ + public static boolean add${keyUpper}(final ${typeName} pojo, final ${typeNameInner} additional) { + if (check${keyUpper}Exists(pojo)) { + get${keyUpper}(pojo).add(additional); + return true; + }if (!check${keyUpperParent}Exists(pojo)) { + return false; + } + List<${typeNameInner}> list = new ArrayList<>(); + list.add(additional); + pojo.get${upperSecondLast}().set${upperLast}(list); + return true; + }""" + return ret + } + + /** + * Prints the method boolean addXXX(pojo, additional) associated with one key. + * Delegates to either to printAddForKeyShallow or to printAddForKeyDeep + */ + Closure printAddForKey = { String key -> + int depth = key.split('\\.').size() + if (depth == 1) { + return printAddForKeyShallow.call(key) + } + return printAddForKeyDeep.call(key) + } + + /** + * Prints the method void addXXX(pojo, additional) throws MissingXXXException associated with one key. + */ + Closure printAddForKeyThrows = { String key -> + /* + public static void addObjectBaseTags(final JunctionJoined pojo, final ListEntry additional) + throws MissingParentException, MissingTargetException { + ensureObjectBaseTagsExists(pojo, false); + getObjectBaseTags(pojo).add(additional); + } + */ + if (verbose) println "// printAddForKeyThrows for key $key" + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + def ret = """ + /** + * Adds a ${typeNameInner} object to a ${typeName}. + * @param pojo the parent object to extend + * @param additional The ${typeNameInner} object to add to the ${typeName} + * @throws MissingTargetException if the attribute holding the ${typeNameInner} objects is null + * @throws MissingParentException if any parent of the attribute holding the ${typeNameInner} objects is missing. + */ + public static void add${keyUpper}(final ${typeName} pojo, final ${typeNameInner} additional) + throws MissingParentException, MissingTargetException { + ensure${keyUpper}Exists(pojo, false); + get${keyUpper}(pojo).add(additional); + }""" + return ret + } + + /** + * Prints the method void getXXX(pojo, UUID, replacement) throws MissingXXXException associated with one key + * of a nested array property. + */ + Closure printGetForKeyThrows = { String key -> + /* + public static AddressPerson getAddressPersonsById(final JunctionContact pojo, final UUID targetId) + throws MissingParentException, MissingTargetException { + final ListIterator iter = getAddressPersonsThrows(pojo).listIterator(); + while (iter.hasNext()) { + if (iter.next().getAddressId().equals(targetId)) { + return iter.previous(); + } + } + throw new MissingTargetException("address.persons", targetId); + } + */ + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + // usually one of entryId, guid or refId! + def idProp = upperCamelCase.call(findIdProperty.call(localPropStack.last())) + def ret = """ + /** + * Returns a specific ${typeNameInner} object from a ${typeName}. + * @param pojo The ${typeName} object to process + * @param targetId The ID of the ${typeNameInner}, which is to be returned. + * @throws MissingTargetException if no ${typeNameInner} of that id was found and subsequently returned, + * @throws MissingParentException if the attribute holding the ${typeNameInner} objects or any of its parent objects is + * missing. + */ + public static ${typeNameInner} get${keyUpper}ById(final ${typeName} pojo, final UUID targetId) + throws MissingParentException, MissingTargetException { + final ListIterator<${typeNameInner}> iter = get${keyUpper}Throws(pojo).listIterator(); + while (iter.hasNext()) { + if (iter.next().get${idProp}().equals(targetId)) { + return iter.previous(); + } + } + throw new MissingTargetException("${key}", targetId); + }""" + return ret + } + + /** + * Prints the method replaceXXX(pojo, UUID, replacement) associated with one key + * of a nested array property. + */ + Closure printReplaceForKey = { String key -> + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + // usually one of entryId, guid or refId! + def idProp = upperCamelCase.call(findIdProperty.call(localPropStack.last())) + def ret = """ + /** + * Tries to replace a specific ${typeNameInner} object of a ${typeName}. + * @param pojo The ${typeName} object to process + * @param targetId The ID of the ${typeNameInner}, which is to be replaced. + * @param replacement The ${typeNameInner} object to assign to the ${typeName} + * @return True, if a ${typeNameInner} of that id was found and subsequently replaced, + * false if no object with that id could be found and therefore replaced. + */ + public static boolean replace${keyUpper}ById(final ${typeName} pojo, final UUID targetId, final ${typeNameInner} replacement) { + final ListIterator<${typeNameInner}> iter = get${keyUpper}(pojo).listIterator(); + while (iter.hasNext()) { + if (iter.next().get${idProp}().equals(targetId)) { + iter.set(replacement); + return true; + } + } + return false; + }""" + return ret + } + + /** + * Prints the method void replaceXXXById(pojo, targetId, replacement) throws MissingXXXException associated with one key + * of a nested array property. + */ + Closure printReplaceForKeyThrows = { String key -> + /* + public static void replaceObjectBaseTagsById(final JunctionJoined pojo, final UUID targetId, + final ListEntry replacement) throws MissingParentException, MissingTargetException { + final ListIterator iter = getObjectBaseTagsThrows(pojo).listIterator(); + while (iter.hasNext()) { + if (iter.next().getRefId().equals(targetId)) { + iter.set(replacement); + return; + } + } + throw new MissingTargetException("objectBase.tags", targetId); + } + */ + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + // usually one of entryId, guid or refId! + def idProp = upperCamelCase.call(findIdProperty.call(localPropStack.last())) + def ret = """ + /** + * Replaces a specific ${typeNameInner} object of a ${typeName}. + * @param pojo The ${typeName} object to process + * @param targetId The ID of the ${typeNameInner}, which is to be replaced. + * @param replacement The ${typeNameInner} object to assign to the ${typeName} + * @throws MissingTargetException if no ${typeNameInner} of that id was found and subsequently replaced, + * @throws MissingParentException if the attribute holding the ${typeNameInner} objects or any of its parent objects is + * missing. + */ + public static void replace${keyUpper}ById(final ${typeName} pojo, final UUID targetId, + final ${typeNameInner} replacement) throws MissingParentException, MissingTargetException { + final ListIterator<${typeNameInner}> iter = get${keyUpper}Throws(pojo).listIterator(); + while (iter.hasNext()) { + if (iter.next().get${idProp}().equals(targetId)) { + iter.set(replacement); + return; + } + } + throw new MissingTargetException("${key}", targetId); + }""" + return ret + } + + /** + * Prints the method void setXXXById(pojo, targetId, value) throws MissingXXXException associated for a the single ref + * or complex child of an arrays of ref or complex properties + * @param keyArray the key associated with an array of ref or complex property + * @param keyChild the key associated with the the single ref or complex child of the array property. + */ + Closure printSetForKeyThrows = { String keyArray, String keyChild -> + /* + public static void setAddressPersonsContactById(final JunctionContact pojo, final UUID targetId, + final ContactData value) throws MissingParentException, MissingTargetException { + final ListIterator iter = getAddressPersonsThrows(pojo).listIterator(); + while (iter.hasNext()) { + iter.previous().setContact(value); + // TODO Add missing parents check here in case of deep nested value property! + // Or introduce AddressPersonHandling and utilize here! + // AddressPersonHandling.setContact(iter.previous(), value); + return; + } + throw new MissingTargetException("address.persons", targetId); + } + */ + List propStackChild = propStackFromKey.call(keyChild) + List propStackArray = propStackFromKey.call(keyArray) + List propStackInner = propStackChild.subList(propStackArray.size(), propStackChild.size()) + def keyUpper = propStackChild.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def keyUpperIntermediate = propStackArray.collect { prop -> upperCamelCase.call(prop.name) }.join('') + // in case of deep nested child + def keyUpperInner = propStackInner.collect { prop -> upperCamelCase.call(prop.name) }.join('') + // in case of shallow child + def setterName = upperCamelCase.call(propStackChild.last().name) + + def typeName = upperCamelCase.call(currentType.name) + def typeNameIntermediate = upperCamelCase.call(propStackArray.last().type.type.name) + def typeNameInner = upperCamelCase.call(propStackChild.last().type.type.name) + // usually one of entryId, guid or refId! + def idProp = upperCamelCase.call(findIdProperty.call(propStackArray.last())) + def ret = """ + /** + * Replaces the ${typeNameInner} of a specific ${typeNameIntermediate} object of a ${typeName}. + * @param pojo The ${typeName} object to process + * @param targetId The ID of the ${typeNameIntermediate}, which is to be altered. + * @param value The ${typeNameInner} object to assign to the ${typeNameIntermediate} + * @throws MissingTargetException if no ${typeNameIntermediate} of that id was found and subsequently altered, + * @throws MissingParentException if + *
        + *
      • the attribute holding the ${typeNameIntermediate} objects or any of its parent objects is missing.
      • + *
      • the attribute holding the ${typeNameInner} objects or any of its parent objects is missing.
      • + *
      + */ + public static void set${keyUpper}ById(final ${typeName} pojo, final UUID targetId, + final ${typeNameInner} value) throws MissingParentException, MissingTargetException { + final ListIterator<${typeNameIntermediate}> iter = get${keyUpperIntermediate}Throws(pojo).listIterator(); + while (iter.hasNext()) { + if (iter.next().get${idProp}().equals(targetId)) { + // in case of shallow ref or complex child: + iter.previous().set${setterName}(value); + // in case of deep nested ref or complex child: + // TODO Add missing parents check here in case of deep nested value property! + // Or introduce ${typeNameIntermediate}Handling and utilize here! + // ${typeNameIntermediate}Handling.set${keyUpperInner}(iter.previous(), value); + return; + } + } + throw new MissingTargetException("${keyArray}", targetId); + }""" + return ret + } + + /** + * Prints the method removeXXX(pojo, UUID) associated with one key + * of a nested array property. + */ + Closure printRemoveForKey = { String key -> + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + // usually one of entryId, guid or refId! + def idProp = upperCamelCase.call(findIdProperty.call(localPropStack.last())) + def ret = """ + /** + * Tries to remove a specific ${typeNameInner} object from a ${typeName}. + * @param pojo The object to process + * @param targetId The ID of the ${typeNameInner}, which is to be removed. + * @return True, if a ${typeNameInner} of that id was found and subsequently removed, + * false if no object with that id could be found and therefore removed. + */ + public static boolean remove${keyUpper}ById(${typeName} pojo, UUID targetId) { + final Iterator<${typeNameInner}> iter = get${keyUpper}(pojo).iterator(); + while (iter.hasNext()) { + if (iter.next().get${idProp}().equals(targetId)) { + iter.remove(); + return true; + } + } + return false; + }""" + return ret + } + + /** + * Prints the method void removeXXX(pojo, UUID) associated with one key + * of a nested array property. + */ + Closure printRemoveForKeyThrows = { String key -> + /* + public static void removeLocationStreetsById(final JunctionJoined pojo, final UUID targetId) + throws MissingParentException, MissingTargetException { + final Iterator iter = getLocationStreetsThrows(pojo).iterator(); + while (iter.hasNext()) { + if (iter.next().getEntryId().equals(targetId)) { + iter.remove(); + return; + } + } + throw new MissingTargetException("location.streets", targetId); + } + */ + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + // usually one of entryId, guid or refId! + def idProp = upperCamelCase.call(findIdProperty.call(localPropStack.last())) + def ret = """ + /** + * Removes a specific ${typeNameInner} object from a ${typeName}. + * @param pojo The object to process + * @param targetId The ID of the ${typeNameInner}, which is to be removed. + * @throws MissingTargetException if no ${typeNameInner} of that id was found and subsequently removed, + * @throws MissingParentException if the attribute holding the ${typeNameInner} objects or any of its parent objects is + * missing. + */ + public static void remove${keyUpper}ById(final ${typeName} pojo, final UUID targetId) + throws MissingParentException, MissingTargetException { + final Iterator<${typeNameInner}> iter = get${keyUpper}Throws(pojo).iterator(); + while (iter.hasNext()) { + if (iter.next().get${idProp}().equals(targetId)) { + iter.remove(); + return; + } + } + throw new MissingTargetException("${key}", targetId); + }""" + return ret + } + + /** + * Actually creates the case of the getValue method for properties of a complex or reference class. + * @param prop The property to process + * @param lines Where the created lines of code are to be added. + */ + def createGetValueSimple = { property, List lines -> + def parentCollection = propIsCollectionStack.last() + def parentProp = propStack.empty ? null : propStack.last() + def propStackParent = []; propStackParent.addAll(propStack) + putStacks.call(property) + def key = propStack.collect {prop -> prop.name}.join('.') + def suffix = !joined && property.hasTag('prepLookup') ? 'Id' : '' + if (verbose) println "type=${currentType.name} key=${key} parentCollection=${parentCollection} parCollClass=${parentCollection.class.getName()}" + + if (propStack.size() == 1) { + // in case of normal type and tag 'prepLookup' add suffix Id to method name -> getObjectBaseId()! + lines.add(""" case "${key}": + return pojo.get${firstUpperCase.call(property.name)}${suffix}();""" ) + } else if (parentCollection) { + // parent is collection: stream collection, map to value, collect(Collectors.toList()) + /* Example + case "objectBase.gis.area.points.lon": + return getObjectBaseGisAreaPoints(target).stream().map(p -> p.getLon()).collect(Collectors.toList()); + */ + String methodName = propStackParent.collect{ prop -> upperCamelCase.call(prop.name) }.join('') + String parentChar = parentProp.name.take(1) + lines.add(""" case "${key}": + return get${methodName}(pojo).stream().map(${parentChar} -> ${parentChar}.get${upperCamelCase.call(property.name)}${suffix}()).collect(Collectors.toList());""") + // lines.add(""" case "${propStack.collect {prop -> prop.name}.join('.')}": return null; // TODO""" ) + } else { + // parent not a collection: simple parent null check and return value + /* Example: + case "objectBase.gis.area.projection": + if (checkObjectBaseGisAreaExists(pojo)) { + return pojo.getObjectBase().getGis().getArea().getProjection(); + } else { + return null; + } + */ + String methodName = propStackParent.collect{ prop -> upperCamelCase.call(prop.name) }.join('') + String getChain = propStack.collect{ prop -> upperCamelCase.call(prop.name) }.join('().get') + + lines.add(""" case "${key}": + if (check${methodName}Exists(pojo)) { + return pojo.get${getChain}${suffix}(); + } else { + return null; + }""" ) + } + popStacks.call() + } + + /** Forward declaration of closure, which calls itself! */ + def createGetValueForType + + /** + * Creates the case statements of the getValue method for a certain type, calls itself recursively for reference + * and complex types! + * @param type The type to process + * @param lines Where the created lines of code are to be added. + */ + createGetValueForType = { type, List lines -> + filterProps.call(type, [refComplex:false]).each { prop -> + if (verbose) println "// createGetValueForType/RefTypeOrComplexType=false: type=${type.name} prop=${prop.name}" + createGetValueSimple.call(prop, lines) + } + filterProps.call(type, [refComplex:true]).each { prop -> + createGetValueSimple.call(prop, lines) + // recursive call! + putStacks.call(prop) + createGetValueForType.call(prop.type.type, lines) + popStacks.call() + } + } + + // Forward declaration of closure, which calls itself! + Closure createEnsureMatchingForType + + /** + * Creates the content of the method ensureMatchingEntryId for a certain type, calls itself recursively for reference + * and complex types! + * @param type The type to process + * @param lines Where the created lines of code are to be added. + */ + createEnsureMatchingForType = { type, List lines, int idx -> + // Track the number of increments of idx! + int incCount = 0 + if (propIsCollectionStack.last()) { + def pProp = propStack.last() + boolean parentHasEntryId = pProp.isRefTypeOrComplexType() && pProp.type.type.properties.collect { prop2 -> prop2.name }.contains('entryId') + if (parentHasEntryId) { + /* Example + final Iterator sourceIter1 = getAddressPersons(source).iterator(); + final Iterator targetIter1 = getAddressPersons(target).iterator(); + while(sourceIter1.hasNext()) { + targetIter1.next().setEntryId(sourceIter1.next().getEntryId()); + } + */ + lines.add(" // found pProp=${pProp.name} type=${upperCamelCase.call(type.name)}") + def jType = upperCamelCase.call(type.name) + def methodName = propStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + idx += 1 + incCount += 1 + lines.add( +""" final Iterator<${jType}> sourceIter${idx} = get${methodName}(source).iterator(); + final Iterator<${jType}> targetIter${idx} = get${methodName}(target).iterator(); + while(sourceIter${idx}.hasNext()) { + targetIter${idx}.next().setEntryId(sourceIter${idx}.next().getEntryId()); + }""") + + } + } + filterProps.call(type, [refComplex:true]).each { prop -> + // recursive call! + putStacks.call(prop) + // idx may have been incremented 0 to n times by the recursive call! + int recIncCount = createEnsureMatchingForType.call(prop.type.type, lines, idx) + popStacks.call() + idx += recIncCount + incCount += recIncCount + } + return incCount + } + + // Forward declaration of closure, which calls itself! + def printCheckExistsForType + + /** + * Prints the methods checkXXXExists(target) for a certain type, calls itself recursively for reference and complex types! + * @param type The type to process + */ + printCheckExistsForType = { type -> + int size = propIsCollectionStack.size() + if ( size > 2 && propIsCollectionStack.get(size-2)) { + // can not check for null in children of array property -> stop creating more methods checkXXXExists()! + return + } + if (!propStack.isEmpty()) { + /* + public static boolean checkObjectBaseGisAreaExists(JunctionNumberJoined target) { + return target.getObjectBase() != null + && target.getObjectBase().getGis() != null + && target.getObjectBase().getGis().getArea() != null; + } + */ + def checkMethodPart = propStack.collect{ upperCamelCase.call(it.name) }.join('') // e.g. AddressPersonsContact + // create longest getter call chain and then process it from one to all elements. + List lines = [] + List getCalls = propStack.collect { "get${upperCamelCase.call(it.name)}()"} + for (int i = 0; i < getCalls.size(); i++) { + def cond = getCalls.subList(0, i+1).join('.') + lines.add("target.${cond} != null") + } + def conditions = lines.join('\n && ') +%> + /** + * @param target The object, which is to be processed. + * @return True, if the attribute and all its parents exist, false otherwise. + */ + public static boolean check${checkMethodPart}Exists(${targetType} target) { + return ${conditions}; + } +<% + } + +// type.properties.findAll { prop -> return prop.isRefTypeOrComplexType() }.each { prop -> + filterProps.call(type, [refComplex:true]).each { prop -> + // recursive call! + putStacks.call(prop) + printCheckExistsForType.call(prop.type.type) + popStacks.call() + } + } + + // Forward declaration of closure, which calls itself! + def printEnsureExistsForType + + /** + * Prints the methods ensureXXXExists(target, boolean) for a certain type, calls itself recursively for reference and complex types! + * @param type The type to process + */ + printEnsureExistsForType = { type -> + int size = propIsCollectionStack.size() + if ( size > 2 && propIsCollectionStack.get(size-2)) { + // can not check for null in children of array property -> stop creating more methods checkXXXExists()! + return + } + if (!propStack.isEmpty()) { + /* + public static void ensureObjectBaseGisAreaPointsExists(JunctionJoined target, boolean isParent) + throws MissingParentException, MissingTargetException { + ensureObjectBaseGisAreaExists(target, true); + if (target.getObjectBase().getGis().getArea().getPoints() == null) { + if (isParent) { + throw new MissingParentException("objectBase.gis.area.points"); + } + throw new MissingTargetException("objectBase.gis.area.points"); + } + } + */ + def ensureMethodPart = propStack.collect{ upperCamelCase.call(it.name) }.join('') // e.g. AddressPersonsContact + def key = currentKey.call() + List getCalls = propStack.collect { "get${upperCamelCase.call(it.name)}()"} + def getCall = getCalls.join('.') + def parentCall + int stackDim = propStack.size() + if (stackDim == 1) { + parentCall = '' + } else { + def ensureMethodPartParent = propStack.subList(0, stackDim-1).collect{ upperCamelCase.call(it.name) }.join('') + parentCall = "\n ensure${ensureMethodPartParent}Exists(target, false);" + } +%> + /** + * Before performing an operation on an attribute like e.g. replacing or removing it, this method checks whether + * that attribute and all its parent objects actually exist. + * @param target The object, which is to be processed. + * @param isParent Indicates whether the attribute is regarded as the target of the operation. This parameter + * controls, which Exception is emitted when all parent objects are available but the attribute itself is + * missing! + * @throws MissingTargetException If the attribute is missing and parameter isParent indicates that it is + * regarded as the target of the operation. + * @throws MissingParentException If either any of the parent objects of the attribute is missing or if the + * attribute itself is missing and parameter isParent indicates that the attribute is not + * regarded as the target of the operation. + */ + public static void ensure${ensureMethodPart}Exists(${targetType} target, boolean isParent) + throws MissingParentException, MissingTargetException {${parentCall} + if (target.${getCall} == null) { + if (isParent) { + throw new MissingParentException("${key}"); + } + throw new MissingTargetException("${key}"); + } + } +<% + } + +// type.properties.findAll { prop -> return prop.isRefTypeOrComplexType() }.each { prop -> + filterProps.call(type, [refComplex:true]).each { prop -> + // recursive call! + putStacks.call(prop) + printEnsureExistsForType.call(prop.type.type) + popStacks.call() + } + } + + // Forward declaration of closure, which calls itself! + def printGetForType + + /** + * Prints the methods getXXX(target) (returning a list of objects) for a certain type, calls itself recursively for + * reference and complex types! + * @param type The type to process + */ + printGetForType = { type -> + if (propIsCollectionStack.last()) { + // Example for key address.persons.contact where persons is the only array type + // In case of multiple array types use .flatMap() for 2. to last array type! + // We can not check for null references for objects in the object tree after hitting the first array property. + // -> + // A: the method checkXXXExists only checks for null references up to the first array property. + // B: do use Stream.filter() with a predicate for filtering out the null references! + /* + public static List getAddressPersonsContact(JunctionContactJoined target) { + if (checkAddressPersonsExists(target)) { + return target.getAddress().getPersons().stream() + .filter(p -> p.getContact() != null) + .map(p -> p.getContact()) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + */ + def methodName = propStack.subList(0, propStack.size()).collect { upperCamelCase.call(it.name) }.join('') // e.g. AddressPersonsContact + int maxCheckProp = propIsCollectionStack.last() ? propIsCollectionStack.indexOf(Boolean.TRUE) : propStack.size() // The index of the first entry of propIsCollectionStack indicating value collection + def checkName = propStack.subList(0, maxCheckProp).collect { upperCamelCase.call(it.name) }.join('') // e.g. AddressPersons + + // iterate through propStack and propIsArrayStack + // Before first array type is encountered, add getter calls + // When first array type is encountered, add .stream() and switch mode to .map(...) + // Whenever another array type is encountered, use .flatMap(...) instead of .map(...) + List parts = [] + boolean useGetter = true + for (int i = 0; i < propStack.size(); i++) { + def currUpper = upperCamelCase.call(propStack[i].name) + if (useGetter) { + // getXXX() + parts.add("get${currUpper}()") + } else { + // TODO Teach Eiko and Stephan + def parentProp = propStack[i-1].name.take(1) + if (propIsArrayStack[i]) { + // flatMap(), e.g. flatMap(contact -> contact.getEmail().stream()) + parts.add("filter(${parentProp} -> ${parentProp}.get${currUpper}() != null)") + parts.add("flatMap(${parentProp} -> ${parentProp}.get${currUpper}().stream())") + } else { + // map(), e.g. map(person -> person.getContact()) + parts.add("filter(${parentProp} -> ${parentProp}.get${currUpper}() != null)") + parts.add("map(${parentProp} -> ${parentProp}.get${currUpper}())") + } + } + if (useGetter && propIsArrayStack[i]) { + parts.add("stream()") + useGetter = false + } + } + parts.add('collect(Collectors.toList())') + + // If the very last entry is the first array type, then these is no need to appending + // .stream() and .collect(Collectors.toList()) -> just discard these two entries! + if (parts[parts.size()-2] == "stream()") { + parts = parts.subList(0, parts.size() - 2) + } + def retType = upperCamelCase.call(type.name) + def stream = parts[0] + parts.subList(1,parts.size()).collect {"\n .$it"}.join('') +%> + /** + * This method is lenient: It will always return at least an empty list, even if the parent objects of the + * ${retType} are missing. Actions performed on the returned list will not change the state of the ${targetType} + * object, changes to the ${retType} objects itself do change the ${targetType} object! + * @param target The ${targetType} object to process + * @return The ${retType} objects hold by the ${targetType} or an empty list, if the attribute holding the + * ${retType} objects or any of its parent objects is missing. + */ + public static List<${retType}> get${methodName}(${targetType} target) { + if (check${checkName}Exists(target)) { + return target.${stream}; + } + return Collections.emptyList(); + } +<% + } + +// type.properties.findAll { prop -> return prop.isRefTypeOrComplexType() }.each { prop -> + filterProps.call(type, [refComplex:true]).each { prop -> + // recursive call! + putStacks.call(prop) + printGetForType.call(prop.type.type) + popStacks.call() + } + } + + // Forward declaration of closure, which calls itself! + def printGetThrowsForType + + /** + * Prints the methods getXXXThrows(target) (returning a list of objects) for a certain type, calls itself + * recursively for reference and complex types! + * @param type The type to process + */ + printGetThrowsForType = { type -> + if (propIsCollectionStack.last()) { + // Example for key address.persons.contact where persons is the only array type + // In case of multiple array types use .flatMap() for 2. to last array type! + // We can not check for null references for objects in the object tree after hitting the first array property. + // -> + // A: the method checkXXXExists only checks for null references up to the first array property. + // B: do use Stream.filter() with a predicate for filtering out the null references! + /* + public static List getAddressPersonsContactThrows(JunctionContact target) throws MissingParentException { + // Assumption: List attributes are never null, only empty! + ensureAddressPersonsExists(target, true); + return target.getAddress() + .getPersons() + .stream() + .filter(p -> p.getContact() != null) + .map(p -> p.getContact()) + .collect(Collectors.toList()); + } + */ + def methodName = propStack.subList(0, propStack.size()).collect { upperCamelCase.call(it.name) }.join('') // e.g. AddressPersonsContact + int maxCheckProp = propIsCollectionStack.last() ? propIsCollectionStack.indexOf(Boolean.TRUE) : propStack.size() // The index of the first entry of propIsCollectionStack indicating value collection + def checkName = propStack.subList(0, maxCheckProp).collect { upperCamelCase.call(it.name) }.join('') // e.g. AddressPersons + + // iterate through propStack and propIsArrayStack + // Before first array type is encountered, add getter calls + // When first array type is encountered, add .stream() and switch mode to .map(...) + // Whenever another array type is encountered, use .flatMap(...) instead of .map(...) + List parts = [] + boolean useGetter = true + for (int i = 0; i < propStack.size(); i++) { + def currUpper = upperCamelCase.call(propStack[i].name) + if (useGetter) { + // getXXX() + parts.add("get${currUpper}()") + } else { + // TODO Teach Eiko and Stephan + def parentProp = propStack[i-1].name.take(1) + if (propIsArrayStack[i]) { + // flatMap(), e.g. flatMap(contact -> contact.getEmail().stream()) + parts.add("filter(${parentProp} -> ${parentProp}.get${currUpper}() != null)") + parts.add("flatMap(${parentProp} -> ${parentProp}.get${currUpper}().stream())") + } else { + // map(), e.g. map(person -> person.getContact()) + parts.add("filter(${parentProp} -> ${parentProp}.get${currUpper}() != null)") + parts.add("map(${parentProp} -> ${parentProp}.get${currUpper}())") + } + } + if (useGetter && propIsArrayStack[i]) { + parts.add("stream()") + useGetter = false + } + } + parts.add('collect(Collectors.toList())') + + // If the very last entry is the first array type, then these is no need to appending + // .stream() and .collect(Collectors.toList()) -> just discard these two entries! + if (parts[parts.size()-2] == "stream()") { + parts = parts.subList(0, parts.size() - 2) + } + def retType = upperCamelCase.call(type.name) + def stream = parts[0] + parts.subList(1,parts.size()).collect {"\n .$it"}.join('') +%> + /** + * Actions performed on the returned list will not change the state of the ${targetType} object, changes to the + * ${retType} objects itself do change the ${targetType} object! + * @param target The ${targetType} object to process + * @return The ${retType} objects hold by the ${targetType} + * @throws MissingParentException if the attribute holding the ${retType} objects or any of its parent objects are + * missing. + * @throws MissingTargetException Declared due to method reuse. + */ + public static List<${retType}> get${methodName}Throws(${targetType} target) throws MissingParentException, MissingTargetException { + // Assumption: List attributes are never null, only empty! + ensure${checkName}Exists(target, true); + return target.${stream}; + } +<% + } + +// type.properties.findAll { prop -> return prop.isRefTypeOrComplexType() }.each { prop -> + filterProps.call(type, [refComplex:true]).each { prop -> + // recursive call! + putStacks.call(prop) + printGetThrowsForType.call(prop.type.type) + popStacks.call() + } + } + + // Forward declaration of closure, which calls itself! + def printGetSingleThrowsForType + + /** + * Prints the methods getXXXThrows(target) (returning single object) for a certain type, calls itself recursively + * for reference and complex types! + * @param type The type to process + */ + printGetSingleThrowsForType = { type -> + int stackDim = propStack.size() + if (stackDim > 0) { + /* + public static ObjectBaseGis getObjectBaseGisThrows(JunctionJoined target) throws MissingParentException { + ensureObjectBaseExists(target, true); + return target.getObjectBase().getGis(); + } + */ + def methodName = propStack.collect { upperCamelCase.call(it.name) }.join('') // e.g. ObjectBaseGis + def checkName = propStack.subList(0, stackDim - 1).collect { upperCamelCase.call(it.name) }.join('') // e.g. ObjectBase + def checkLine = stackDim > 1 ? "\n ensure${checkName}Exists(target, true);" : '' + def getterChain = 'get' + propStack.collect { upperCamelCase.call(it.name) }.join('().get') + '()' // e.g. getObjectBase().getGis() + def retType = upperCamelCase.call(type.name) +%> + /** + * @param target The ${targetType} object to process + * @return The ${retType} object hold by the ${targetType} + * @throws MissingParentException if the attribute holding the ${retType} objects or any of its parent objects is + * missing. + * @throws MissingTargetException Declared due to method reuse. + */ + public static ${retType} get${methodName}Throws(${targetType} target) throws MissingParentException, MissingTargetException {${checkLine} + return target.${getterChain}; + } +<% + } + + filterProps.call(type, [refComplex:true, array:false]).each { prop -> + // recursive call! + putStacks.call(prop) + printGetSingleThrowsForType.call(prop.type.type) + popStacks.call() + } + } + + // Forward declaration of closure, which calls itself! + def printSetSingleForType + + /** + * Prints the methods setXXX(target, replacement) throws MissingParentException for a certain type, calls itself recursively + * for reference and complex types! + * @param type The type to process + */ + printSetSingleForType = { type -> + int stackDim = propStack.size() + if (stackDim > 1) { + /* + public static void setObjectBaseGisArea(JunctionJoined target, GeoArea replacement) throws MissingParentException { + ensureObjectBaseGisExists(target, true); + target.getObjectBase().getGis().setArea(replacement); + } + */ + def methodName = propStack.collect { upperCamelCase.call(it.name) }.join('') // e.g. ObjectBaseGisArea + def typeNameInner = upperCamelCase.call(propStack.last().type.type.name) + List parentStack = propStack.subList(0, stackDim - 1) + def checkName = parentStack.collect { upperCamelCase.call(it.name) }.join('') // e.g. ObjectBaseGis + def getterChain = 'get' + parentStack.collect { upperCamelCase.call(it.name) }.join('().get') + '()' // e.g. getObjectBase().getGis() + def setterName = upperCamelCase.call(propStack.last().name) // e.g. Area +%> + /** + * @param target The ${targetType} object to process + * @param replacement The ${typeNameInner} object to assign to the ${targetType} + * @throws MissingParentException if the attribute holding the ${typeNameInner} objects or any of its parent objects is + * missing. + * @throws MissingTargetException Declared due to method reuse. + */ + public static void set${methodName}(${targetType} target, ${typeNameInner} replacement) throws MissingParentException, MissingTargetException { + ensure${checkName}Exists(target, true); + target.${getterChain}.set${setterName}(replacement); + } +<% + } + + filterProps.call(type, [refComplex:true, array:false]).each { prop -> + // recursive call! + putStacks.call(prop) + printSetSingleForType.call(prop.type.type) + popStacks.call() + } + } + + /** + * Print add methods for deep nested array properties. Utilizes other handling classes! + * @param key The key to process + */ + Closure printAddOtherHandlingForKey = { String key -> + /* + public static void addSubIntersectionsFlowLegsApproachLanesSignalGroups(final JunctionFlowDiagram item0, + final UUID subIntersectionFlow_id, final UUID leg_id, final UUID lane_id, final SignalGroup additional) + throws MissingParentException, MissingTargetException { + final SubIntersectionFlow item1 = getSubIntersectionsFlowById(item0, subIntersectionFlow_id); + final Leg item2 = SubIntersectionFlowHandling.getLegsById(item1, leg_id); + final Lane item3 = LegHandling.getApproachLanesById(item2, lane_id); + LaneHandling.addSignalGroups(item3, additional); + } + */ + if (verbose) println "// printAddOtherHandlingForKey for key $key" + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + // List of array properties + List arrayProps = localPropStack.findAll { prop -> prop.type.isArray } + + // Collect method parameter of type UUID + List paramProps = arrayProps.clone() + // If one element is to be added/replaced/removed to deepest array, drop it's UUID from parameter list! + if (localPropStack.last().type.isArray) { + paramProps.pop() + } + def idList = paramProps.collect { prop -> "final UUID ${ lowerCamelCase.call(prop.type.type.name) }_id"}.join(', ') + + // Collect prop stacks for calls XXXHandling.getYYYById(itemI, id_I+1) + // and prop stack for calling final XXXHandling.addYYY(item, additional) + // If an element is to be added to an array, then that part ends up in the addPropStack + List> innerPropStacks = partitionArrayPros.call(localPropStack) + List addPropStack = innerPropStacks.pop() + List firstGetPropStack = innerPropStacks.remove(0) + + List lines = [] + List javaDocLines = [] + /* + * subIntersectionsFlow -> + * final SubIntersectionFlow item1 = getSubIntersectionsFlowById(itme0, subIntersectionFlow_id); + */ + // String firstGetLine = "firstGet: ${ firstGetPropStack.collect{ it.name }.join('.')}" + def firstItemProp = firstGetPropStack.last() + String firstGetter = firstGetPropStack.collect { upperCamelCase.call(it.name) }.join('') + String firstItemId = lowerCamelCase.call(firstItemProp.type.type.name) + '_id' + String firstItemClass = upperCamelCase.call(firstItemProp.type.type.name) + String firstGetLine = "final ${ firstItemClass } item1 = get${firstGetter}ById(item0, ${ firstItemId });" + lines.add(firstGetLine) + javaDocLines.add("@param ${ firstItemId } The ID of the ${ firstItemClass } object, which is to be selected") + + int itemIdx = 1 + /* + * getBy: subIntersectionsFlow.legs -> + * final Leg item2 = SubIntersectionFlowHandling.getLegsById(item1, leg_id); + * getBy: legs.approachLanes -> + * final Lane item3 = LegHandling.getApproachLanesById(item2, leg_id); + */ + // innerPropStacks.each { stack -> lines.add("getBy: ${stack.collect { it.name}.join('.') }")} + innerPropStacks.each { stack -> + def innerHandlerProp = stack.remove(0) + def innerItemProp = stack.last() + String innerGetter = stack.collect { upperCamelCase.call(it.name) }.join('') + String itemId = lowerCamelCase.call(innerItemProp.type.type.name) + '_id' + String itemClass = upperCamelCase.call(innerItemProp.type.type.name) + lines.add("final ${ itemClass } item${itemIdx+1} = ${ upperCamelCase.call(innerHandlerProp.type.type.name) }Handling.get${innerGetter}ById(item${itemIdx}, ${ itemId });") + // e.g. @param leg_id The ID of the Leg object, which is to be selected + javaDocLines.add("@param ${ itemId } The ID of the ${ itemClass } object, which is to be selected") + itemIdx++ + } + + /* + * approachLanes.trafficIsland -> + * item3.setTrafficIsland(additional); + * approachLanes.signalGroups -> + * LaneHandling.addSignalGroups(item3, additional); + */ + // String finalAddLine = "finalAdd: ${ addPropStack.collect{ it.name }.join('.')}" + boolean setValue = addPropStack.size() == 2 && !addPropStack.last().type.isArray + def lastHanderlProp = addPropStack.remove(0) + String addChain = addPropStack.collect { upperCamelCase.call(it.name) }.join('') + String finalAddLine + if (setValue) { + finalAddLine = "item${ itemIdx }.set${ addChain }(additional);" + } else { + finalAddLine = "${ upperCamelCase.call(lastHanderlProp.type.type.name) }Handling.add${ addChain }(item${ itemIdx }, additional);" + } + lines.add(finalAddLine) + + String inner = lines.join('\n ') + String idJavaDocLines = javaDocLines.isEmpty() ? '' : '\n * ' + javaDocLines.join('\n * ') + + def ret = """ + /** + * Adds a ${typeNameInner} object to a ${typeName}. + * @param pojo the parent object to extend${idJavaDocLines} + * @param additional The ${typeNameInner} object to add to the ${typeName} + * @throws MissingTargetException if the attribute holding the ${typeNameInner} objects is null + * @throws MissingParentException if any parent of the attribute holding the ${typeNameInner} objects is missing. + * missing. + */ + public static void add${keyUpper}(final ${typeName} item0, ${idList}, final ${typeNameInner} additional) + throws MissingParentException, MissingTargetException { + ${inner} + }""" + return ret + } + + /** + * Print get single item methods for deep nested array properties. Utilizes other handling classes! + * @param key The key to process + */ + Closure printGetByIdOtherHandlingForKey = { String key -> + /* + public static SignalGroup getSubIntersectionsFlowLegsApproachLanesSignalGroupsById(final JunctionFlowDiagram item0, + final UUID subIntersectionFlow_id, final UUID leg_id, final UUID lane_id, final UUID signalGroup_id) + throws MissingParentException, MissingTargetException { + final SubIntersectionFlow item1 = getSubIntersectionsFlowById(item0, subIntersectionFlow_id); + final Leg item2 = SubIntersectionFlowHandling.getLegsById(item1, leg_id); + final Lane item3 = LegHandling.getApproachLanesById(item2, lane_id); + return LaneHandling.getSignalGroupsById(item3, signalGroup_id); + } + */ + if (verbose) println "// printGetByIdOtherHandlingForKey for key $key" + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + // List of array properties + List arrayProps = localPropStack.findAll { prop -> prop.type.isArray } + + // Collect method parameter of type UUID + List paramProps = arrayProps.clone() + def idList = paramProps.collect { prop -> "final UUID ${ lowerCamelCase.call(prop.type.type.name) }_id"}.join(', ') + + // Collect prop stacks for calls XXXHandling.getYYYById(itemI, id_I+1) + // and prop stack for calling final XXXHandling.addYYY(item, additional) + // If an element is to be added to an array, then that part ends up in the addPropStack + List> innerPropStacks = partitionArrayPros.call(localPropStack) + List lastGetPropStack = innerPropStacks.pop() + List firstGetPropStack = innerPropStacks.remove(0) + + List lines = [] + List javaDocLines = [] + /* + * subIntersectionsFlow -> + * final SubIntersectionFlow item1 = getSubIntersectionsFlowById(itme0, subIntersectionFlow_id); + */ + // String firstGetLine = "// firstGet: ${ firstGetPropStack.collect{ it.name }.join('.')}" + def firstItemProp = firstGetPropStack.last() + String firstGetter = firstGetPropStack.collect { upperCamelCase.call(it.name) }.join('') + String firstItemId = lowerCamelCase.call(firstItemProp.type.type.name) + '_id' + String firstItemClass = upperCamelCase.call(firstItemProp.type.type.name) + String firstGetLine = "final ${ firstItemClass } item1 = get${firstGetter}ById(item0, ${ firstItemId });" + lines.add(firstGetLine) + javaDocLines.add("@param ${ firstItemId } The ID of the ${ firstItemClass } object, which is to be selected") + + int itemIdx = 1 + /* + * getBy: subIntersectionsFlow.legs -> + * final Leg item2 = SubIntersectionFlowHandling.getLegsById(item1, leg_id); + * getBy: legs.approachLanes -> + * final Lane item3 = LegHandling.getApproachLanesById(item2, leg_id); + */ + // innerPropStacks.each { stack -> lines.add("// getBy: ${stack.collect { it.name}.join('.') }")} + innerPropStacks.each { stack -> + def innerHandlerProp = stack.remove(0) + def innerItemProp = stack.last() + String innerGetter = stack.collect { upperCamelCase.call(it.name) }.join('') + String itemId = lowerCamelCase.call(innerItemProp.type.type.name) + '_id' + String itemClass = upperCamelCase.call(innerItemProp.type.type.name) + lines.add("final ${ itemClass } item${itemIdx+1} = ${ upperCamelCase.call(innerHandlerProp.type.type.name) }Handling.get${innerGetter}ById(item${itemIdx}, ${ itemId });") + // e.g. @param leg_id The ID of the Leg object, which is to be selected + javaDocLines.add("@param ${ itemId } The ID of the ${ itemClass } object, which is to be selected") + itemIdx++ + } + + /* + * approachLanes.trafficIsland -> + * return item3.getTrafficIsland(); + * approachLanes.signalGroups -> + * return LaneHandling.getSignalGroupsById(item3, signalGroup_id); + */ + // String finalGetLine = "// finalGet: ${ lastGetPropStack.collect{ it.name }.join('.')}" + // lines.add(finalGetLine) + // lines.add('return null;') + def lastProperty = lastGetPropStack.last() + boolean getValue = lastGetPropStack.size() == 2 && !lastProperty.type.isArray + def lastHandlerProp = lastGetPropStack.remove(0) + String addChain = lastGetPropStack.collect { upperCamelCase.call(it.name) }.join('') + String finalGetLine + if (getValue) { + finalGetLine = "return item${ itemIdx }.get${ addChain }();" + } else { + String lastItemId = lowerCamelCase.call(lastProperty.type.type.name) + '_id' + String lastItemClass = upperCamelCase.call(lastProperty.type.type.name) + finalGetLine = "return ${ upperCamelCase.call(lastHandlerProp.type.type.name) }Handling.get${ addChain }ById(item${ itemIdx }, ${lastItemId});" + javaDocLines.add("@param ${ lastItemId } The ID of the ${ lastItemClass } object, which is to be selected") + + } + lines.add(finalGetLine) + + String inner = lines.join('\n ') + String idJavaDocLines = javaDocLines.isEmpty() ? '' : '\n * ' + javaDocLines.join('\n * ') + + def ret = """ + /** + * Returns a specific ${typeNameInner} object from a ${typeName}. + * @param item0 the ${typeName} object to process${idJavaDocLines} + * @throws MissingTargetException if no ${typeNameInner} of that id was found and subsequently returned. + * @throws MissingParentException if the attribute holding the ${typeNameInner} objects or any of its parent objects is missing. + */ + public static ${typeNameInner} get${keyUpper}ById(final ${typeName} item0, ${idList}) + throws MissingParentException, MissingTargetException { + ${inner} + }""" + return ret + } + + /** + * Print get methods for deep nested array properties, returning all objects of the array. Utilizes other handling classes! + * @param key The key to process + */ + Closure printGetThrowsOtherHandlingForKey = { String key -> + /* + public static List getSubIntersectionsFlowLegsApproachLanesSignalGroupsThrows(final JunctionFlowDiagram item0, + final UUID subIntersectionFlow_id, final UUID leg_id, final UUID lane_id) + throws MissingParentException, MissingTargetException { + final SubIntersectionFlow item1 = getSubIntersectionsFlowById(item0, subIntersectionFlow_id); + final Leg item2 = SubIntersectionFlowHandling.getLegsById(item1, leg_id); + final Lane item3 = LegHandling.getApproachLanesById(item2, lane_id); + return LaneHandling.getSignalGroups(item3); + } + */ + if (verbose) println "// printGetThrowsOtherHandlingForKey for key $key" + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + // List of array properties + List arrayProps = localPropStack.findAll { prop -> prop.type.isArray } + + // Collect method parameter of type UUID + List paramProps = arrayProps.clone() + paramProps.pop() + def idList = paramProps.collect { prop -> "final UUID ${ lowerCamelCase.call(prop.type.type.name) }_id"}.join(', ') + + // Collect prop stacks for calls XXXHandling.getYYYById(itemI, id_I+1) + // and prop stack for calling final XXXHandling.addYYY(item, additional) + // If an element is to be added to an array, then that part ends up in the addPropStack + List> innerPropStacks = partitionArrayPros.call(localPropStack) + List lastGetPropStack = innerPropStacks.pop() + List firstGetPropStack = innerPropStacks.remove(0) + + List lines = [] + List javaDocLines = [] + /* + * subIntersectionsFlow -> + * final SubIntersectionFlow item1 = getSubIntersectionsFlowById(itme0, subIntersectionFlow_id); + */ + // String firstGetLine = "// firstGet: ${ firstGetPropStack.collect{ it.name }.join('.')}" + // lines.add(firstGetLine) + + def firstItemProp = firstGetPropStack.last() + String firstGetter = firstGetPropStack.collect { upperCamelCase.call(it.name) }.join('') + String firstItemId = lowerCamelCase.call(firstItemProp.type.type.name) + '_id' + String firstItemClass = upperCamelCase.call(firstItemProp.type.type.name) + String firstGetLine = "final ${ firstItemClass } item1 = get${firstGetter}ById(item0, ${ firstItemId });" + lines.add(firstGetLine) + javaDocLines.add("@param ${ firstItemId } The ID of the ${ firstItemClass } object, which is to be selected") + + int itemIdx = 1 + /* + * getBy: subIntersectionsFlow.legs -> + * final Leg item2 = SubIntersectionFlowHandling.getLegsById(item1, leg_id); + * getBy: legs.approachLanes -> + * final Lane item3 = LegHandling.getApproachLanesById(item2, leg_id); + */ + // innerPropStacks.each { stack -> lines.add("// getBy: ${stack.collect { it.name}.join('.') }")} + innerPropStacks.each { stack -> + def innerHandlerProp = stack.remove(0) + def innerItemProp = stack.last() + String innerGetter = stack.collect { upperCamelCase.call(it.name) }.join('') + String itemId = lowerCamelCase.call(innerItemProp.type.type.name) + '_id' + String itemClass = upperCamelCase.call(innerItemProp.type.type.name) + lines.add("final ${ itemClass } item${itemIdx+1} = ${ upperCamelCase.call(innerHandlerProp.type.type.name) }Handling.get${innerGetter}ById(item${itemIdx}, ${ itemId });") + // e.g. @param leg_id The ID of the Leg object, which is to be selected + javaDocLines.add("@param ${ itemId } The ID of the ${ itemClass } object, which is to be selected") + itemIdx++ + } + + /* + * approachLanes.trafficIsland -> + * return item3.getTrafficIsland(); + * approachLanes.signalGroups -> + * return LaneHandling.getSignalGroupsById(item3, signalGroup_id); + */ + // String finalGetLine = "// finalGet: ${ lastGetPropStack.collect{ it.name }.join('.')}" + // ines.add(finalGetLine) + // ines.add('return null;') + def lastProperty = lastGetPropStack.last() + boolean getValue = lastGetPropStack.size() == 2 && !lastProperty.type.isArray + def lastHandlerProp = lastGetPropStack.remove(0) + String addChain = lastGetPropStack.collect { upperCamelCase.call(it.name) }.join('') + String finalGetLine + if (getValue) { + finalGetLine = "return item${ itemIdx }.get${ addChain }();" + } else { + finalGetLine = "return ${ upperCamelCase.call(lastHandlerProp.type.type.name) }Handling.get${ addChain }(item${ itemIdx });" + } + lines.add(finalGetLine) + + String inner = lines.join('\n ') + String idJavaDocLines = javaDocLines.isEmpty() ? '' : '\n * ' + javaDocLines.join('\n * ') + String typeParent = upperCamelCase.call(paramProps.last().type.type.name) + + def ret = """ + /** + * Returns all ${typeNameInner} object hold by a special ${typeParent} object of an ${typeName}. + * @param item0 the ${typeName} object to process${idJavaDocLines} + * @throws MissingTargetException if no ${typeNameInner} of that id was found and subsequently returned. + * @throws MissingParentException if the attribute holding the ${typeNameInner} objects or any of its parent objects is missing. + */ + public static List<${typeNameInner}> get${keyUpper}Throws(final ${typeName} item0, ${idList}) + throws MissingParentException, MissingTargetException { + ${inner} + }""" + return ret + } + + /** + * Print replace single item methods for deep nested array properties. Utilizes other handling classes! + * @param key The key to process + */ + Closure printReplaceByIdOtherHandlingForKey = { String key -> + /* + public static void replaceSubIntersectionsFlowLegsApproachLanesById(final JunctionFlowDiagram item0, + final UUID subIntersectionFlow_id, final UUID leg_id, final UUID lane_id, Lane bodyParam) + throws MissingParentException, MissingTargetException { + final SubIntersectionFlow item1 = getSubIntersectionsFlowById(item0, subIntersectionFlow_id); + final Leg item2 = SubIntersectionFlowHandling.getLegsById(item1, leg_id); + LegHandling.replaceApproachLanesById(item2, lane_id, bodyParam); + } + */ + if (verbose) println "// printReplaceByIdOtherHandlingForKey for key $key" + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + // List of array properties + List arrayProps = localPropStack.findAll { prop -> prop.type.isArray } + + // Collect method parameter of type UUID + List paramProps = arrayProps.clone() + def idList = paramProps.collect { prop -> "final UUID ${ lowerCamelCase.call(prop.type.type.name) }_id"}.join(', ') + + // Collect prop stacks for calls XXXHandling.getYYYById(itemI, id_I+1) + // and prop stack for calling final XXXHandling.addYYY(item, additional) + // If an element is to be added to an array, then that part ends up in the addPropStack + List> innerPropStacks = partitionArrayPros.call(localPropStack) + List lastReplacePropStack = innerPropStacks.pop() + List firstGetPropStack = innerPropStacks.remove(0) + + List lines = [] + List javaDocLines = [] + /* + * subIntersectionsFlow -> + * final SubIntersectionFlow item1 = getSubIntersectionsFlowById(itme0, subIntersectionFlow_id); + */ + // String firstGetLine = "// firstGet: ${ firstGetPropStack.collect{ it.name }.join('.')}" + // lines.add(firstGetLine) + def firstItemProp = firstGetPropStack.last() + String firstGetter = firstGetPropStack.collect { upperCamelCase.call(it.name) }.join('') + String firstItemId = lowerCamelCase.call(firstItemProp.type.type.name) + '_id' + String firstItemClass = upperCamelCase.call(firstItemProp.type.type.name) + String firstGetLine = "final ${ firstItemClass } item1 = get${firstGetter}ById(item0, ${ firstItemId });" + lines.add(firstGetLine) + javaDocLines.add("@param ${ firstItemId } The ID of the ${ firstItemClass } object, which is to be selected") + + int itemIdx = 1 + /* + * getBy: subIntersectionsFlow.legs -> + * final Leg item2 = SubIntersectionFlowHandling.getLegsById(item1, leg_id); + * getBy: legs.approachLanes -> + * final Lane item3 = LegHandling.getApproachLanesById(item2, leg_id); + */ + // innerPropStacks.each { stack -> lines.add("// getBy: ${stack.collect { it.name}.join('.') }")} + innerPropStacks.each { stack -> + def innerHandlerProp = stack.remove(0) + def innerItemProp = stack.last() + String innerGetter = stack.collect { upperCamelCase.call(it.name) }.join('') + String itemId = lowerCamelCase.call(innerItemProp.type.type.name) + '_id' + String itemClass = upperCamelCase.call(innerItemProp.type.type.name) + lines.add("final ${ itemClass } item${itemIdx+1} = ${ upperCamelCase.call(innerHandlerProp.type.type.name) }Handling.get${innerGetter}ById(item${itemIdx}, ${ itemId });") + // e.g. @param leg_id The ID of the Leg object, which is to be selected + javaDocLines.add("@param ${ itemId } The ID of the ${ itemClass } object, which is to be selected") + itemIdx++ + } + + /* + * approachLanes.trafficIsland -> + * return item3.setTrafficIsland(bodyParam); + * approachLanes.signalGroups -> + * return LaneHandling.replaceSignalGroupsById(item3, signalGroup_id, bodyParam); + */ + // String finalReplaceLine = "// finalReplace: ${ lastReplacePropStack.collect{ it.name }.join('.')}" + // lines.add(finalReplaceLine) + def lastProperty = lastReplacePropStack.last() + boolean setValue = lastReplacePropStack.size() == 2 && !lastProperty.type.isArray + def lastHandlerProp = lastReplacePropStack.remove(0) + String addChain = lastReplacePropStack.collect { upperCamelCase.call(it.name) }.join('') + String finalReplaceLine + if (setValue) { + finalReplaceLine = "item${ itemIdx }.set${ addChain }(bodyParam);" + } else { + String lastItemId = lowerCamelCase.call(lastProperty.type.type.name) + '_id' + String lastItemClass = upperCamelCase.call(lastProperty.type.type.name) + finalReplaceLine = "${ upperCamelCase.call(lastHandlerProp.type.type.name) }Handling.replace${ addChain }ById(item${ itemIdx }, ${lastItemId}, bodyParam);" + javaDocLines.add("@param ${ lastItemId } The ID of the ${ lastItemClass } object, which is to be replaced") + + } + lines.add(finalReplaceLine) + + String inner = lines.join('\n ') + String idJavaDocLines = javaDocLines.isEmpty() ? '' : '\n * ' + javaDocLines.join('\n * ') + + def ret = """ + /** + * Replaces a specific ${typeNameInner} object of a ${typeName}. + * @param item0 the ${typeName} object to process${idJavaDocLines} + * @param bodyParam The ${typeNameInner} object to assign to the ${typeName} + * @throws MissingTargetException if no ${typeNameInner} of that id was found and subsequently replaced. + * @throws MissingParentException if the attribute holding the ${typeNameInner} objects or any of its parent objects is missing. + */ + public static void replace${keyUpper}ById(final ${typeName} item0, ${idList}, final ${typeNameInner} bodyParam) + throws MissingParentException, MissingTargetException { + ${inner} + }""" + return ret + } + + /** + * Print remove single item methods for deep nested array properties. Utilizes other handling classes! + * @param key The key to process + */ + Closure printRemoveByIdOtherHandlingForKey = { String key -> + /* + public static void removeSubIntersectionsFlowLegsApproachLanesById(final JunctionFlowDiagram item0, + final UUID subIntersectionFlow_id, final UUID leg_id, final UUID lane_id) + throws MissingParentException, MissingTargetException { + final SubIntersectionFlow item1 = getSubIntersectionsFlowById(item0, subIntersectionFlow_id); + final Leg item2 = SubIntersectionFlowHandling.getLegsById(item1, leg_id); + LegHandling.removeApproachLanesById(item2, lane_id); + } + */ + if (verbose) println "// printRemoveByIdOtherHandlingForKey for key $key" + List localPropStack = propStackFromKey.call(key) + def keyUpper = localPropStack.collect { prop -> upperCamelCase.call(prop.name) }.join('') + def typeNameInner = upperCamelCase.call(localPropStack.last().type.type.name) + def typeName = upperCamelCase.call(currentType.name) + // List of array properties + List arrayProps = localPropStack.findAll { prop -> prop.type.isArray } + + // Collect method parameter of type UUID + List paramProps = arrayProps.clone() + def idList = paramProps.collect { prop -> "final UUID ${ lowerCamelCase.call(prop.type.type.name) }_id"}.join(', ') + + // Collect prop stacks for calls XXXHandling.getYYYById(itemI, id_I+1) + // and prop stack for calling final XXXHandling.addYYY(item, additional) + // If an element is to be added to an array, then that part ends up in the addPropStack + List> innerPropStacks = partitionArrayPros.call(localPropStack) + List lastPropStack = innerPropStacks.pop() + List firstGetPropStack = innerPropStacks.remove(0) + + List lines = [] + List javaDocLines = [] + /* + * subIntersectionsFlow -> + * final SubIntersectionFlow item1 = getSubIntersectionsFlowById(itme0, subIntersectionFlow_id); + */ + // String firstGetLine = "// firstGet: ${ firstGetPropStack.collect{ it.name }.join('.')}" + // lines.add(firstGetLine) + def firstItemProp = firstGetPropStack.last() + String firstGetter = firstGetPropStack.collect { upperCamelCase.call(it.name) }.join('') + String firstItemId = lowerCamelCase.call(firstItemProp.type.type.name) + '_id' + String firstItemClass = upperCamelCase.call(firstItemProp.type.type.name) + String firstGetLine = "final ${ firstItemClass } item1 = get${firstGetter}ById(item0, ${ firstItemId });" + lines.add(firstGetLine) + javaDocLines.add("@param ${ firstItemId } The ID of the ${ firstItemClass } object, which is to be selected") + + int itemIdx = 1 + /* + * getBy: subIntersectionsFlow.legs -> + * final Leg item2 = SubIntersectionFlowHandling.getLegsById(item1, leg_id); + * getBy: legs.approachLanes -> + * final Lane item3 = LegHandling.getApproachLanesById(item2, leg_id); + */ + // innerPropStacks.each { stack -> lines.add("// getBy: ${stack.collect { it.name}.join('.') }")} + innerPropStacks.each { stack -> + def innerHandlerProp = stack.remove(0) + def innerItemProp = stack.last() + String innerGetter = stack.collect { upperCamelCase.call(it.name) }.join('') + String itemId = lowerCamelCase.call(innerItemProp.type.type.name) + '_id' + String itemClass = upperCamelCase.call(innerItemProp.type.type.name) + lines.add("final ${ itemClass } item${itemIdx+1} = ${ upperCamelCase.call(innerHandlerProp.type.type.name) }Handling.get${innerGetter}ById(item${itemIdx}, ${ itemId });") + // e.g. @param leg_id The ID of the Leg object, which is to be selected + javaDocLines.add("@param ${ itemId } The ID of the ${ itemClass } object, which is to be selected") + itemIdx++ + } + + /* + * approachLanes.trafficIsland -> + * return item3.setTrafficIsland(null); + * approachLanes.signalGroups -> + * return LaneHandling.removeSignalGroupsById(item3, signalGroup_id); + */ + // String finalRemoveLine = "// finalReplace: ${ lastPropStack.collect{ it.name }.join('.')}" + // lines.add(finalRemoveLine) + def lastProperty = lastPropStack.last() + boolean setValue = lastPropStack.size() == 2 && !lastProperty.type.isArray + def lastHandlerProp = lastPropStack.remove(0) + String addChain = lastPropStack.collect { upperCamelCase.call(it.name) }.join('') + String finalRemoveLine + if (setValue) { + finalRemoveLine = "item${ itemIdx }.set${ addChain }(null);" + } else { + String lastItemId = lowerCamelCase.call(lastProperty.type.type.name) + '_id' + String lastItemClass = upperCamelCase.call(lastProperty.type.type.name) + finalRemoveLine = "${ upperCamelCase.call(lastHandlerProp.type.type.name) }Handling.remove${ addChain }ById(item${ itemIdx }, ${lastItemId});" + javaDocLines.add("@param ${ lastItemId } The ID of the ${ lastItemClass } object, which is to be removed") + + } + lines.add(finalRemoveLine) + + String inner = lines.join('\n ') + String idJavaDocLines = javaDocLines.isEmpty() ? '' : '\n * ' + javaDocLines.join('\n * ') + + def ret = """ + /** + * Removes a specific ${typeNameInner} object of a ${typeName}. + * @param item0 the ${typeName} object to process${idJavaDocLines} + * @throws MissingTargetException if no ${typeNameInner} of that id was found and subsequently removed. + * @throws MissingParentException if the attribute holding the ${typeNameInner} objects or any of its parent objects is missing. + */ + public static void remove${keyUpper}ById(final ${typeName} item0, ${idList}) + throws MissingParentException, MissingTargetException { + ${inner} + }""" + return ret + } + + // Create a private copy of the current type and alter that: tune prepLookup properties to loose suffix Id! + def tunedType = copyType.call(currentType) + tuneType.call(tunedType) + + // find keys for methods, where another Handling class needs to be called + // -> find array of ref or complex properties contained by array of ref or complex type + List otherHandlingKeys = evalOtherHandlingKeysForType.call(tunedType) + // find keys for methods, where another Handling class needs to be called + // -> find array of ref or complex properties contained by array of ref or complex type - ignore children unless they lead to another relevant array + List otherHandlingKeysForArray = evalOtherHandlingKeysForArraysForType.call(tunedType) + + // find keys for method removeXXXById(pojo, targetId) -> find array of ref or complex properties + List deepNestedListKeys = evalDeepNestedListKeysForType.call(tunedType) + // find keys for method setXXXById(pojo, targetId, value) -> find ref or complex children of arrays of ref or complex properties + Map deepNestedList2SingleChildKeys = evalDeepNestedListChildKeysForType.call(tunedType, deepNestedListKeys) +%>package de.lisaplus.lisa.${extraParam.serviceBase}.model.handling; + +/* + * This file is generated by jsonCodeGen. Changes will be overwritten with next code generation run. + * Template: handling.txt + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.UUID; +import java.util.stream.Collectors; + +import de.lisaplus.lisa.${extraParam.serviceBase}.model.*; +import de.schlothauer.services.exceptions.*; + +${script.printHello('test')} + +/** + * This class contains the Unit test for masking of class $targetType. + */ +public class ${targetType}Handling { + + /** + * For restoring of array properties with entryId attributes these entryId values need to match. + * This method assumes that array dimension of objects source and target match! + * @param source The object, which defines the entryId values + * @param target The object, which is to inherit the entryId values. + */ + public static void ensureMatchingEntryId(final ${targetType} source, final ${targetType} target) { +<% allLines.clear() + prepareStacks.call() + createEnsureMatchingForType.call(currentType, allLines, 0) + allLines.each { line -> %>${line} +<% } %> } + + /** + * Returns the value(s) associated with a mask key. + * @param pojo The object to process + * @param maskKey The mask key to process. + * @return The value(s) associated with a mask key. + */ + public static Object getValue(final ${targetType} pojo, final String maskKey) { + switch(maskKey) { +<% allLines.clear() + prepareStacks.call() + createGetValueForType.call(tunedType, allLines) + allLines.each { line -> %>${line} +<% } %> + default: + throw new RuntimeException(String.format("Unsupported mask key '%s'!", maskKey)); + } + } +<% /* 1st loop: method checkXXXExists() */ + prepareStacks.call() + printCheckExistsForType.call(tunedType) + + /* 2nd loop: methods ensureXXXExists() */ + prepareStacks.call() + printEnsureExistsForType.call(tunedType) + + /* 3rd loop: method getXXX() for lists */ + prepareStacks.call() + printGetForType.call(tunedType) + + /* 4th loop: method getXXXThrows() for lists */ + prepareStacks.call() + printGetThrowsForType.call(tunedType) + + /* 5th loop: methods getXXXThrows() for single objects */ + prepareStacks.call() + printGetSingleThrowsForType.call(tunedType) + + /* 6th loop: methods setXXX() */ + prepareStacks.call() + printSetSingleForType.call(tunedType) + + for (String key : deepNestedListKeys) { +%>${printAddForKeyThrows.call(key)} +${printGetForKeyThrows.call(key)} +${printReplaceForKeyThrows.call(key)} +${printRemoveForKeyThrows.call(key)}<% + } + + for (Map.Entry entry : deepNestedList2SingleChildKeys.entrySet()) { + // Paraneter: keyArray, keyChild +%>${printSetForKeyThrows.call(entry.key, entry.value)}<% + } + + if (verbose) println '// start add' + for (String key : otherHandlingKeys) { +%> +${printAddOtherHandlingForKey.call(key)}<% + } + + if (verbose) println '// start get' + for (String key : otherHandlingKeys) { +%> +${printGetByIdOtherHandlingForKey.call(key)}<% + } + + if (verbose) println '// start array get' + for (String key : otherHandlingKeysForArray) { +%> +${printGetThrowsOtherHandlingForKey.call(key)}<% + } + + if (verbose) println '// start replace' + for (String key : otherHandlingKeys) { +%> +${printReplaceByIdOtherHandlingForKey.call(key)}<% + } + + if (verbose) println '// start remove' + for (String key : otherHandlingKeys) { +%> +${printRemoveByIdOtherHandlingForKey.call(key)}<% + } +%> +} diff --git a/src/test/resources/templates/handling_helper.groovy b/src/test/resources/templates/handling_helper.groovy new file mode 100644 index 0000000..b77f51c --- /dev/null +++ b/src/test/resources/templates/handling_helper.groovy @@ -0,0 +1,3 @@ +def printHello(def someString) { + return "Hello: $someString" +} \ No newline at end of file diff --git a/src/test/resources/test_schemas/ds/KnotenDaten.json b/src/test/resources/test_schemas/ds/KnotenDaten.json new file mode 100644 index 0000000..4dbd0a4 --- /dev/null +++ b/src/test/resources/test_schemas/ds/KnotenDaten.json @@ -0,0 +1,930 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "TKnotenDaten data model", + "description": "The LISA-KnotenDaten data model", + "definitions": { + "LISA": { + "type": "object", + "properties": { + "props": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "user": { + "type": "string" + }, + "date": { + "type": "number" + }, + "lid": { + "type": "integer" + }, + "dversion": { + "type": "integer" + }, + "kommentar": { + "type": "string", + "properties": { + "visible": { + "type": "string", + "__tags": ["attribute"] + }, + "font": { + "type": "string", + "__tags": ["attribute"] + }, + "left": { + "type": "string", + "__tags": ["attribute"] + }, + "top": { + "type": "string", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + }, + "KnotenData": { + "type": "object", + "properties": { + "knotndat": { + "type": "object", + "properties": { + "kndatattr": { + "type": "string", + "properties": { + "liverk": { + "type": "boolean", + "__tags": ["attribute"] + }, + "inort": { + "type": "boolean", + "__tags": ["attribute"] + }, + "ibara": { + "type": "boolean", + "__tags": ["attribute"] + }, + "sfzgn1": { + "type": "string", + "__tags": ["attribute"] + }, + "sfzgn2": { + "type": "string", + "__tags": ["attribute"] + }, + "nlsaver": { + "type": "integer", + "__tags": ["attribute"] + } + } + }, + "pp": { + "type": "object", + "properties": { + "ppattr": { + "type": "string", + "properties": { + "p1": { + "type": "integer", + "__tags": ["attribute"] + }, + "p2": { + "type": "string", + "__tags": ["attribute"] + }, + "p3": { + "type": "string", + "__tags": ["attribute"] + }, + "p5": { + "type": "integer", + "__tags": ["attribute"] + }, + "p6": { + "type": "integer", + "__tags": ["attribute"] + }, + "p7": { + "type": "integer", + "__tags": ["attribute"] + }, + "p8": { + "type": "boolean", + "__tags": ["attribute"] + }, + "p9": { + "type": "boolean", + "__tags": ["attribute"] + }, + "p10": { + "type": "number", + "__tags": ["attribute"] + }, + "p11": { + "type": "integer", + "__tags": ["attribute"] + }, + "p12": { + "type": "integer", + "__tags": ["attribute"] + }, + "p13": { + "type": "number", + "__tags": ["attribute"] + }, + "p14": { + "type": "number", + "__tags": ["attribute"] + }, + "p15": { + "type": "number", + "__tags": ["attribute"] + }, + "p16": { + "type": "number", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + }, + "armlist": { + "type": "object", + "properties": { + "arm": { + "type": "object", + "properties": { + "armattr": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "nr": { + "type": "integer", + "__tags": ["attribute"] + }, + "winkel": { + "type": "integer", + "__tags": ["attribute"] + }, + "spikr": { + "type": "integer", + "__tags": ["attribute"] + }, + "tknr": { + "type": "integer", + "__tags": ["attribute"] + }, + "name": { + "type": "string", + "__tags": ["attribute"] + }, + "vorftyp": { + "type": "integer", + "__tags": ["attribute"] + }, + "dreiins": { + "type": "boolean", + "__tags": ["attribute"] + }, + "dreickspnr": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufw": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufwl": { + "type": "number", + "__tags": ["attribute"] + }, + "mitins": { + "type": "boolean", + "__tags": ["attribute"] + }, + "nlsaaufln": { + "type": "number", + "__tags": ["attribute"] + }, + "fvfuss": { + "type": "number", + "__tags": ["attribute"] + }, + "tkusch": { + "type": "boolean", + "__tags": ["attribute"] + } + } + }, + "spurli": { + "type": "object", + "properties": { + "spur": { + "type": "object", + "properties": { + "spurattr": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "name": { + "type": "string", + "__tags": ["attribute"] + }, + "art": { + "type": "integer", + "__tags": ["attribute"] + }, + "nr": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstpl": { + "type": "number", + "__tags": ["attribute"] + }, + "hst": { + "type": "boolean", + "__tags": ["attribute"] + }, + "bre": { + "type": "number", + "__tags": ["attribute"] + }, + "lneig": { + "type": "number", + "__tags": ["attribute"] + }, + "abradra": { + "type": "number", + "__tags": ["attribute"] + }, + "abradla": { + "type": "number", + "__tags": ["attribute"] + }, + "abradwd": { + "type": "number", + "__tags": ["attribute"] + }, + "aufstli": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstre": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstrb": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstin": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstib": { + "type": "integer", + "__tags": ["attribute"] + }, + "lae": { + "type": "number", + "__tags": ["attribute"] + }, + "qsp": { + "type": "integer", + "__tags": ["attribute"] + }, + "aufstlla": { + "type": "number", + "__tags": ["attribute"] + }, + "aufstlra": { + "type": "number", + "__tags": ["attribute"] + }, + "mitins": { + "type": "boolean", + "__tags": ["attribute"] + } + } + }, + "mitins": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "bre": { + "type": "number", + "__tags": ["attribute"] + }, + "lae": { + "type": "number", + "__tags": ["attribute"] + }, + "auto": { + "type": "boolean", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + }, + "slattr": { + "type": "string", + "properties": { + "lid": { + "type": "integer", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + }, + "strlist": { + "type": "object", + "properties": { + "strom": { + "type": "object", + "properties": { + "strattr": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "varm": { + "type": "integer", + "__tags": ["attribute"] + }, + "narm": { + "type": "integer", + "__tags": ["attribute"] + }, + "richt": { + "type": "integer", + "__tags": ["attribute"] + }, + "exway": { + "type": "integer", + "__tags": ["attribute"] + }, + "crti": { + "type": "integer", + "__tags": ["attribute"] + }, + "sust": { + "type": "integer", + "__tags": ["attribute"] + }, + "tupo": { + "type": "integer", + "__tags": ["attribute"] + } + } + }, + "tslist": { + "type": "object", + "properties": { + "tstrom": { + "type": "object", + "properties": { + "tsattr": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "bre": { + "type": "number", + "__tags": ["attribute"] + }, + "fulae": { + "type": "number", + "__tags": ["attribute"] + }, + "name": { + "type": "string", + "__tags": ["attribute"] + }, + "fidx": { + "type": "integer", + "__tags": ["attribute"] + } + } + }, + "sfzge": { + "type": "string", + "properties": { + "kfz": { + "type": "string", + "__tags": ["attribute"] + }, + "fuss": { + "type": "string", + "__tags": ["attribute"] + } + } + }, + "spnrs": { + "type": "object", + "properties": { + "li": { + "type": "integer" + } + }, + "__version": 13 + }, + "spartn": { + "type": "object", + "properties": { + "li": { + "type": "integer" + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + }, + "fzgval": { + "type": "string", + "properties": { + "val0": { + "type": "number", + "__tags": ["attribute"] + }, + "val1": { + "type": "number", + "__tags": ["attribute"] + }, + "val2": { + "type": "number", + "__tags": ["attribute"] + }, + "val3": { + "type": "number", + "__tags": ["attribute"] + }, + "val4": { + "type": "number", + "__tags": ["attribute"] + }, + "val5": { + "type": "number", + "__tags": ["attribute"] + }, + "val6": { + "type": "number", + "__tags": ["attribute"] + }, + "val7": { + "type": "number", + "__tags": ["attribute"] + }, + "val8": { + "type": "number", + "__tags": ["attribute"] + }, + "val9": { + "type": "number", + "__tags": ["attribute"] + } + } + }, + "mitins": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "bre": { + "type": "number", + "__tags": ["attribute"] + }, + "lae": { + "type": "number", + "__tags": ["attribute"] + }, + "auto": { + "type": "boolean", + "__tags": ["attribute"] + } + } + }, + "kpvli": { + "type": "object", + "properties": { + "kpverb": { + "type": "object", + "properties": { + "kpvattr": { + "type": "string", + "properties": { + "wid": { + "type": "integer", + "__tags": ["attribute"] + }, + "lae": { + "type": "number", + "__tags": ["attribute"] + }, + "spanz": { + "type": "integer", + "__tags": ["attribute"] + }, + "rzt": { + "type": "number", + "__tags": ["attribute"] + }, + "fzanz": { + "type": "integer", + "__tags": ["attribute"] + }, + "ri": { + "type": "integer", + "__tags": ["attribute"] + }, + "name": { + "type": "string", + "__tags": ["attribute"] + } + } + }, + "sfzge": { + "type": "string", + "properties": { + "kfz": { + "type": "string", + "__tags": ["attribute"] + } + } + }, + "qkp": { + "type": "string", + "properties": { + "arm": { + "type": "integer", + "__tags": ["attribute"] + }, + "ort": { + "type": "string", + "__tags": ["attribute"] + }, + "kp": { + "type": "string", + "__tags": ["attribute"] + } + } + }, + "zkp": { + "type": "string", + "properties": { + "arm": { + "type": "integer", + "__tags": ["attribute"] + }, + "ort": { + "type": "string", + "__tags": ["attribute"] + }, + "kp": { + "type": "string", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + } + }, + "__version": 13 + }, + "kplili": { + "type": "object", + "properties": { + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + }, + "tkli": { + "type": "object", + "properties": { + "tk": { + "type": "object", + "properties": { + "tkattr": { + "type": "string", + "properties": { + "id": { + "type": "string", + "__tags": ["attribute"] + }, + "dt": { + "type": "string", + "__tags": ["attribute"] + }, + "nr": { + "type": "integer", + "__tags": ["attribute"] + }, + "name": { + "type": "string", + "__tags": ["attribute"] + }, + "pktx": { + "type": "integer", + "__tags": ["attribute"] + }, + "pkty": { + "type": "integer", + "__tags": ["attribute"] + }, + "krv": { + "type": "boolean", + "__tags": ["attribute"] + }, + "fzl": { + "type": "number", + "__tags": ["attribute"] + }, + "nzl": { + "type": "number", + "__tags": ["attribute"] + }, + "mzl": { + "type": "number", + "__tags": ["attribute"] + }, + "gzl": { + "type": "number", + "__tags": ["attribute"] + }, + "pch": { + "type": "boolean", + "__tags": ["attribute"] + }, + "vis": { + "type": "boolean", + "__tags": ["attribute"] + }, + "gernr": { + "type": "integer", + "__tags": ["attribute"] + }, + "audm": { + "type": "number", + "__tags": ["attribute"] + }, + "usadd": { + "type": "boolean", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + } + }, + "__version": 13 + }, + "tkverbli": { + "type": "object", + "properties": { + }, + "__version": 13 + }, + "nopfl": { + "type": "string", + "properties": { + "sty": { + "type": "integer", + "__tags": ["attribute"] + }, + "winkel": { + "type": "number", + "__tags": ["attribute"] + }, + "gro": { + "type": "number", + "__tags": ["attribute"] + }, + "vis": { + "type": "boolean", + "__tags": ["attribute"] + }, + "pktx": { + "type": "number", + "__tags": ["attribute"] + }, + "pkty": { + "type": "number", + "__tags": ["attribute"] + } + } + }, + "knotgraf": { + "type": "object", + "properties": { + "knograttr": { + "type": "string", + "properties": { + "fcol": { + "type": "integer", + "__tags": ["attribute"] + }, + "fhei": { + "type": "integer", + "__tags": ["attribute"] + }, + "fname": { + "type": "string", + "__tags": ["attribute"] + } + } + }, + "pp": { + "type": "object", + "properties": { + "ppattr": { + "type": "string", + "properties": { + "p1": { + "type": "integer", + "__tags": ["attribute"] + }, + "p2": { + "type": "string", + "__tags": ["attribute"] + }, + "p3": { + "type": "string", + "__tags": ["attribute"] + }, + "p5": { + "type": "integer", + "__tags": ["attribute"] + }, + "p6": { + "type": "integer", + "__tags": ["attribute"] + }, + "p7": { + "type": "integer", + "__tags": ["attribute"] + }, + "p8": { + "type": "boolean", + "__tags": ["attribute"] + }, + "p9": { + "type": "boolean", + "__tags": ["attribute"] + }, + "p10": { + "type": "number", + "__tags": ["attribute"] + }, + "p11": { + "type": "integer", + "__tags": ["attribute"] + }, + "p12": { + "type": "integer", + "__tags": ["attribute"] + }, + "p13": { + "type": "number", + "__tags": ["attribute"] + }, + "p14": { + "type": "number", + "__tags": ["attribute"] + }, + "p15": { + "type": "number", + "__tags": ["attribute"] + }, + "p16": { + "type": "number", + "__tags": ["attribute"] + } + } + }, + "ptpDim": { + "type": "boolean" + } + }, + "__version": 13 + }, + "nopf": { + "type": "string", + "properties": { + "ppx": { + "type": "number", + "__tags": ["attribute"] + }, + "ppy": { + "type": "number", + "__tags": ["attribute"] + }, + "usmov": { + "type": "boolean", + "__tags": ["attribute"] + } + } + }, + "gtkli": { + "type": "object", + "properties": { + "tk": { + "type": "string", + "properties": { + "mpx": { + "type": "number", + "__tags": ["attribute"] + }, + "mpy": { + "type": "number", + "__tags": ["attribute"] + }, + "usmov": { + "type": "boolean", + "__tags": ["attribute"] + } + } + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "__version": 13 + } + }, + "type": "object" +} diff --git a/src/test/resources/test_schemas/ds/array_test.json b/src/test/resources/test_schemas/ds/array_test.json new file mode 100644 index 0000000..901e2b4 --- /dev/null +++ b/src/test/resources/test_schemas/ds/array_test.json @@ -0,0 +1,111 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "User model", + "description": "Test user model", + "definitions": { + "role": { + "type": "object", + "properties": { + "domain_id": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json" + }, + "name": { + "description": "Name of the role", + "type": "string", + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "module_grants": { + "type": "array", + "items": { + "type": "object", + "description": "what is granted to do with specific modules", + "properties": { + "module": { + "$ref": "./shared/app_module.json" + }, + "grant": { + "type": "string", + "enum": [ + "read", + "write", + "commit" + ] + } + }, + "__tags": [ + "five", + "six", + "seven" + ] + }, + "__tags": [ + "one", + "two" + ] + }, + "data_grants": { + "type": "array", + "items": { + "type": "object", + "description": "what is granted to do with specific modules", + "properties": { + "data_path": { + "description" : "dummy string to define some data", + "type": "string" + }, + "grant": { + "type": "string", + "enum": [ + "read", + "write", + "commit" + ] + } + } + } + }, + "innerArray": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "object", + "description": "what is granted to do with specific modules", + "properties": { + "data_path": { + "description": "dummy string to define some data", + "type": "string" + }, + "grant": { + "type": "string", + "enum": [ + "read", + "write", + "commit" + ], + "__tags": [ + "one", + "two" + ] + } + } + } + } + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + } + } + } + }, + "type": "object", + "version": 1 +} diff --git a/src/test/resources/test_schemas/ds/array_test_simple.json b/src/test/resources/test_schemas/ds/array_test_simple.json new file mode 100644 index 0000000..48f1bf2 --- /dev/null +++ b/src/test/resources/test_schemas/ds/array_test_simple.json @@ -0,0 +1,113 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "User model", + "description": "Test user model", + "definitions": { + "FeatureAreaGeometry": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + } + } + }, + "projection": { + "type": "string" + } + }, + "__tags": ["additional"] + }, + "role": { + "type": "object", + "properties": { + "name": { + "description": "Name of the role", + "type": "string", + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "innerArray": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "object", + "description": "what is granted to do with specific modules", + "properties": { + "data_path": { + "description": "dummy string to define some data", + "type": "string" + }, + "grant": { + "type": "string", + "enum": [ + "read", + "write", + "commit" + ], + "__tags": [ + "one", + "two" + ] + } + } + } + } + }, + "realDeep": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "object", + "description": "what is granted to do with specific modules", + "properties": { + "data_path": { + "description": "dummy string to define some data", + "type": "string" + }, + "grant": { + "type": "string", + "enum": [ + "read", + "write", + "commit" + ], + "__tags": [ + "one", + "two" + ] + } + } + } + } + } + } + } + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + } + } + } + }, + "type": "object", + "version": 1 +} diff --git a/src/test/resources/test_schemas/ds/extra_types.json b/src/test/resources/test_schemas/ds/extra_types.json new file mode 100644 index 0000000..db97328 --- /dev/null +++ b/src/test/resources/test_schemas/ds/extra_types.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Type-Test", + "description": "Test for more exotic types", + "definitions": { + "ExtraType": { + "type": "object", + "properties": { + "intAttrib": { + "type": "integer", + }, + "intAttrib2": { + "type": "integer", + "format": "int32" + }, + "noIntAttrib": { + "type": "integer", + "format": "int64" + }, + "byteAttrib": { + "type": "integer", + "format": "byte" + } + } + } + }, + "type": "object", + "__version": 3 +} diff --git a/src/test/resources/test_schemas/ds/incident.json b/src/test/resources/test_schemas/ds/incident.json index a698076..ce6ae12 100644 --- a/src/test/resources/test_schemas/ds/incident.json +++ b/src/test/resources/test_schemas/ds/incident.json @@ -3,129 +3,10 @@ "title": "Incident model", "description": "Model describes data that are needed to collect incident states and the task to solve it ", "definitions": { - "incident": { + "Incident": { "type": "object", "description": "A detected failure state that needs some actions to solve it", "properties": { - "stages": { - "description": "what are the stages of that event", - "type": "object", - "properties": { - "entered" : { - "description": "when has the information entered the system", - "type": "object", - "properties": { - "time": { - "description": "date/time when it was first reported", - "type": "string", - "format": "date-time" - }, - "report_type": { - "description": "on what way comes the information to the system", - "type": "string" - }, - "description": { - "description": "Short the description of the problem", - "type": "string" - } - } - }, - "reported": { - "description": "data about the report of that information to another administration", - "type": "object", - "properties": { - "time": { - "description": "date/time when it was reported", - "type": "string", - "format": "date-time" - }, - "report_type": { - "description": "on what way goes the information to the recipient", - "type": "string" - } - } - }, - "transferred": { - "description": "data about the transfer of the responsibility about that event to another entity", - "type": "object", - "properties": { - "time": { - "description": "date/time when it was transferred", - "type": "string", - "format": "date-time" - }, - "report_type": { - "description": "on what way goes the information to the recipient", - "type": "string" - } - } - }, - "repair": { - "description": "data about the repair stages", - "type": "object", - "properties": { - "start": { - "description": "when started the repair", - "type": "object", - "properties": { - "time": { - "description": "date/time when", - "type": "string", - "format": "date-time" - } - } - }, - "finished": { - "description": "when was the repair finished", - "type": "object", - "properties": { - "time": { - "description": "date/time when", - "type": "string", - "format": "date-time" - }, - "problem": { - "description": "what was the problem", - "type": "string" - } - } - }, - "rejected": { - "description": "when was the repair rejected, f.i. as a result of control and the detection of unsolved problems", - "type": "object", - "properties": { - "time": { - "description": "date/time when", - "type": "string", - "format": "date-time" - }, - "reason": { - "description": "why was the repair rejected", - "type": "string" - } - } - }, - "accepted": { - "description": "when was the repair finished", - "type": "object", - "properties": { - "time": { - "description": "date/time when", - "type": "string", - "format": "date-time" - } - } - } - } - }, - "comments": { - "type": "array", - "items": { - "$ref": "./shared/comment.json" - } - } - } - }, "number": { "description": "all the number (technical names) of this object", "type": "object", @@ -140,39 +21,142 @@ } } }, - "gis": { - "type": "object", - "properties": { - "area": { - "description": "geografic area of this object", - "$ref": "./shared/geo_area.json" - }, - "center": { - "description": "geografic area of this object", - "$ref": "./shared/geo_point.json" - } + "tags": { + "type": "array", + "items": { + "type": "string", + "format": "uuid", + "__ref": "#/definitions/IncidentTag" } }, "type": { "description": "what type has this event", "type": "string", - "ref": "#/definitions/incident_type" + "__ref": "#/definitions/IncidentType" }, - "junction": { - "description": "what junction is related to this event", - "$ref": "./shared/junction_short.json" + "urgency": { + "description": "some kind of priority", + "type": "integer" }, - "insurance_relavant": { - "description": "this event is relevant to insurance contracts", - "type": "boolean" + "description": { + "description": "some more words to describe the problem", + "type": "string" + }, + "states": { + "type": "array", + "items": { + "$ref": "#/definitions/IncidentState" + } + }, + "rawData": { + "description": "container for additional, app-specific content", + "type": "object", + "properties": { + "type": { + "description": "to identify the raw data, different versions can be encoded in that string", + "type": "string" + }, + "mimeType": { + "description": "how are the data encoded", + "type": "string" + }, + "data": { + "type": "string" + } + } + }, + "references": { + "type": "array", + "__tags": ["recursion"], + "items": { + "type": "string", + "format": "uuid", + "__ref": "#/definitions/Incident" + } }, - "comments": { + "innerReferences": { "type": "array", + "__tags": ["recursion"], "items": { - "$ref": "./shared/comment.json" + "type": "object", + "properties": { + "startDate": { + "description": "when occurs the event that became the incident", + "type": "string", + "format": "date-time" + }, + "startedBy": { + "description": "user name that starts the processing", + "type": "string" + }, + "startComment": { + "description": "additional information about the state", + "type": "string" + } + } } }, - "gid": { + "guid": { + "type": "string", + "format": "uuid" + } + }, + "__tags": ["rest", "joined","mongodb"] + }, + "IncidentState": { + "type": "object", + "properties": { + "startDate": { + "description": "when occurs the event that became the incident", + "type": "string", + "format": "date-time" + }, + "startedBy": { + "description": "user name that starts the processing", + "type": "string" + }, + "startComment": { + "description": "additional information about the state", + "type": "string" + }, + "endDate": { + "description": "end date of that state", + "type": "string", + "format": "date-time" + }, + "assignTo": { + "description": "user name that ends the", + "type": "string" + }, + "endedBy": { + "description": "user name that ends the", + "type": "string" + }, + "endComment": { + "description": "additional information about closing the state", + "type": "string" + }, + "stateType": { + "description": "name of the state", + "type": "string", + "__ref": "#/definitions/IncidentStateType" + } + } + }, + "IncidentStateType": { + "type": "object", + "description": "occurs, entered, resolved, ...", + "properties": { + "name": { + "description": "a describing name", + "type": "string" + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "guid": { + "description": "global identifier", "type": "string", "format": "uuid" }, @@ -180,25 +164,116 @@ "description": "what is the related domain", "type": "string", "format": "uuid", - "ref": "./shared/domain.json" + "__ref": "./shared/domain.json" + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "order": { + "description": "order in that the states should follow", + "type": "boolean" + }, + "progress": { + "description": "order in that the states should follow", + "type": "string", + "enum": [ + "new", + "doing", + "done" + ] + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean" + }, + "incidentTypeId": { + "description": "what incident types is that state related to (f.e. intersections, INES nets, public transport route, ...)", + "type": "array", + "items": { + "type": "string", + "format": "uuid", + "__ref": "#/definitions/IncidentType" + } } } }, - "incident_type": { - "description": "types of incidents", + "IncidentComment": { + "type": "object", + "description": "Comment entries to incidents", "allOf": [ { - "$ref": "./shared/selection_entry.json" + "$ref": "./shared/comment.json" + }, + { + "properties": { + "domain_id": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json" + }, + "guid": { + "description": "object specific ID of that entry", + "type": "string", + "format": "uuid", + "__tags": ["notDisplayed","notNull"] + }, + "incidentId": { + "type": "string", + "format": "uuid", + "__ref": "#/definitions/Incident" + } + } } - ] + ], + "__tags": ["rest", "joined","mongodb"] }, - "report_type": { - "description": "how a event is reported", + "IncidentType": { + "description": "types of incidents", + "type": "object", + "properties": { + "name": { + "description": "a describing name", + "type": "string" + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid" + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean" + }, + "objectGroupIds": { + "description": "what groups is that object related to (f.e. intersections, INES nets, public transport route, ...)", + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + "__tags": ["rest","mongodb","selList"] + }, + "IncidentTag": { + "type": "object", + "description": "key-word for additional incident tagging", "allOf": [ { - "$ref": "./shared/selection_entry.json" + "$ref": "./shared/tag.json" } - ] + ], + "__tags": ["rest", "mongodb"] } }, "type": "object", diff --git a/src/test/resources/test_schemas/ds/ines_network.json b/src/test/resources/test_schemas/ds/ines_network.json new file mode 100644 index 0000000..163f9fb --- /dev/null +++ b/src/test/resources/test_schemas/ds/ines_network.json @@ -0,0 +1,1800 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "INES network model", + "description": "Model describes data that define an INES network", + "definitions": { + "InesNetwork": { + "type": "object", + "description": "Main type that describes an INES network", + "properties": { + "guid": { + "description": "object specific ID of that entry", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "objectBaseId": { + "type": "string", + "format": "uuid", + "__ref": "./object_base.json#/definitions/ObjectBase", + "__tags": [ + "prepLookup" + ] + }, + "state": { + "description": "what is the current state of that object", + "$ref": "./shared/list_entry.json", + "__ref": "#/definitions/NetworkState", + "__tags": [ + "listEntry" + ] + }, + "operationParameters": { + "description": "??", + "$ref": "#/definitions/OperationParameters" + }, + "interval": { + "description": "??", + "type": "integer", + "format": "int64" + }, + "measurementInterval": { + "description": "??, (remark: seems to prove that the central have to aggregate pd data)", + "type": "integer", + "format": "int64" + }, + "smoothFactor": { + "description": "??", + "type": "number" + }, + "prognoseStep": { + "description": "??", + "type": "integer" + }, + "toleranceVolume": { + "description": "??", + "type": "number" + }, + "toleranceLoss": { + "description": "??", + "type": "number" + }, + "currentGreen": { + "description": "??", + "type": "integer" + }, + "occupancyFormat": { + "description": "??", + "type": "integer" + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "controlMethodParamFormat": { + "description": "string to enable logic to decide how the methodControlParameters should be formatted", + "type": "string" + }, + "isValid": { + "description": "is the network valid and can be deployed", + "type": "boolean" + }, + "tenantId": { + "description": "what is the related tenant to that entry", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "joined", + "mongodb" + ], + "__version": 4 + }, + "OperationParameters": { + "type": "object", + "description": "defines parameter for logic behavior", + "properties": { + "sendCommands": { + "type": "boolean" + }, + "operatorScenarioId": { + "description": "choosen by an operation over GUI, reference to another scenario in the network", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/Scenario", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "autoOff": { + "description": "logic turns itself off in case of errors", + "type": "boolean" + }, + "autoOn": { + "description": "logic turns itself on if previous errors are gone", + "type": "boolean" + }, + "coolDowns": { + "description": "logic turns itself on if previous errors are gone", + "type": "array", + "items": { + "$ref": "#/definitions/CoolDown" + } + }, + "greentimeRedistributions": { + "description": "??", + "$ref": "#/definitions/GreentimeRedistribution" + }, + "greenCorridor": { + "description": "??", + "$ref": "#/definitions/GreenCorridor" + } + } + }, + "GreenCorridor": { + "type": "object", + "description": "??", + "properties": { + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "gcParams": { + "type": "array", + "items": { + "$ref": "#/definitions/GcParam" + } + } + } + }, + "GcParam": { + "type": "object", + "description": "??", + "properties": { + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "test": { + "description": "sending parameters regardless of shadow mode", + "type": "boolean" + }, + "triggerBlocker": { + "type": "array", + "items": { + "$ref": "#/definitions/TriggerBlockerElement" + } + }, + "networkNodeId": { + "description": "reference the related network node of the network", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/NetworkNode", + "__tags": [ + "notDisplayed", + "notNull" + ] + } + }, + "__tags": [ + "noHandling", + "noMask" + ] + }, + "GreentimeRedistribution": { + "type": "object", + "description": "operation parameters for greentime redistribution", + "properties": { + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "gtrParams": { + "type": "array", + "items": { + "$ref": "#/definitions/GtrParam" + } + } + } + }, + "GtrParam": { + "type": "object", + "description": "redistribution parameter for network node", + "properties": { + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "test": { + "description": "sending parameters regardless of shadow mode", + "type": "boolean" + }, + "offsetOnOff": { + "type": "boolean" + }, + "strategy": { + "type": "string" + }, + "networkNodeId": { + "description": "reference the related network node of the network", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/NetworkNode", + "__tags": [ + "notDisplayed", + "notNull" + ] + } + }, + "__tags": [ + "noHandling", + "noMask" + ] + }, + "CoolDown": { + "type": "object", + "description": "number of intervals until switch", + "properties": { + "type": { + "type": "string", + "enum": [ + "UP", + "DOWN", + "EQUAL", + "SEAMLESS" + ] + }, + "intervals": { + "type": "integer" + } + }, + "__tags": [ + "noHandling", + "noMask" + ] + }, + "InesNetworkJoined": { + "type": "object", + "description": "INES network with joined ObjectBase", + "allOf": [ + { + "$ref": "#/definitions/InesNetwork" + }, + { + "properties": { + "objectBase": { + "$ref": "./object_base.json#/definitions/ObjectBase", + "__tags": [ + "join" + ] + } + } + } + ], + "__tags": [ + "rest", + "joinedType", + "navigation" + ], + "__version": 1 + }, + "NetworkNode": { + "type": "object", + "description": "Node in the network, that hold references to nodes from other services (ObjectBase)", + "properties": { + "guid": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "externalId": { + "description": "the external ID of that node, e.g. OCITOutstation-ID", + "type": "string" + }, + "externalCentralId": { + "description": "the external ID of the central system that controls that node", + "type": "string" + }, + "name": { + "description": "name of the item", + "type": "string" + }, + "inesNetworkId": { + "description": "guid index of the related InesNetwork object", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesNetwork", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "type": { + "description": "what type is the network node", + "$ref": "./shared/list_entry.json", + "__ref": "#/definitions/NetworkNodeType", + "__tags": [ + "listEntry" + ] + }, + "subIntersections": { + "type": "array", + "items": { + "$ref": "#/definitions/InesSubIntersection" + } + }, + "signalPrograms": { + "type": "array", + "items": { + "$ref": "#/definitions/InesSignalProgram" + } + }, + "stages": { + "description": "???", + "type": "array", + "items": { + "$ref": "#/definitions/Stage" + }, + "__tags": [ + "restSubPath" + ] + }, + "controlMethodParameters": { + "description": "definition of the node related control method parameters", + "type": "array", + "items": { + "$ref": "#/definitions/InesControlMethodParameter" + } + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "regions": { + "description": "collection of regions where this network node belongs to (e.g. PD server, command handler)", + "type": "array", + "items": { + "$ref": "#/definitions/NetworkNodeRegion" + } + }, + "tenantId": { + "description": "what is the related tenant to that entry", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb" + ], + "__version": 4 + }, + + "NetworkNodeRegion": { + "type": "object", + "description": "A region where a network node belongs to", + "properties": { + "entryId": { + "description": "object specific ID of that entry", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "region": { + "$ref": "./shared/list_entry.json", + "__ref": "#/definitions/NetworkNodeRegions", + "__tags": [ + "listEntry", + "notNull" + ] + }, + "type": { + "type": "string", + "enum": ["allSrc","detectorSrc","stateSrc","greentimeSrc","parameterSrc","cmdDest","parameterDest","allDest"] + } + }, + "__version": 1 + }, + "NetworkNodeRegions": { + "type": "object", + "description": "List of regions availale for NetworkNodes", + "properties": { + "name": { + "description": "Name of the region", + "type": "string", + "__tags": [ + "unique", + "notNull" + ] + }, + "comment": { + "description": "the common field contain all un-modeled stuff", + "type": "string" + }, + "types": { + "description": "for what types is this region available", + "type": "array", + "items": { + "type": "string", + "enum": ["allSrc","detectorSrc","stateSrc","greentimeSrc","parameterSrc","cmdDest","parameterDest","allDest"] + } + }, + "active": { + "description": "is this entry still active", + "type": "boolean", + "__tags": [ + "notNull", + "defaultTrue" + ] + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean", + "__tags": [ + "notNull", + "defaultFalse" + ] + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "tenantId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb", + "selList" + ], + "__version": 1 + }, + + "Stage": { + "description": "so called Phasen", + "type": "object", + "properties": { + "entryId": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "externalId": { + "description": "the external ID of that node, e.g. OCITOutstation-ID", + "type": "string" + }, + "priority": { + "description": "factor for the priority of the stage", + "type": "number" + }, + "freeSignalGroups": { + "description": "stage related free signal groups", + "type": "array", + "items": { + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesSignalGroup" + } + }, + "linkedStages": { + "description": "stages that are linked to that stage", + "type": "array", + "items": { + "type": "string", + "format": "uuid", + "__ref": "#/definitions/Stage" + } + } + }, + "__tags": [ + "noMask" + ], + "__version": 3 + }, + "TriggerBlockerElement": { + "description": "detection station that can trigger or block coordinations", + "type": "object", + "properties": { + "detectionStationId": { + "type": "string", + "format": "uuid", + "__ref": "#/definitions/DetectionStation", + "__tags": [ + "key" + ] + }, + "blocker": { + "description": "if true it acts as blocker, in any other case it acts as trigger", + "type": "boolean" + }, + "threshold1": { + "description": "threshold used to process - magic", + "type": "number" + }, + "threshold2": { + "description": "threshold used to process - magic", + "type": "number" + }, + "maxRunCount": { + "description": "how often should this element be used in sequence", + "type": "integer" + }, + "maxRunTime": { + "description": "how long should this element be used in sequence", + "type": "integer" + } + }, + "__tags": [ + "noMask" + ], + "__version": 1 + }, + "InesSignalGroup": { + "description": "todo", + "type": "object", + "properties": { + "signalGroupId": { + "description": "refers to centralObject->Signalgroup->entryId, modeled creepily :D", + "type": "string", + "format": "uuid", + "__tags": [ + "key" + ] + }, + "name": { + "description": "name of the signalgroup", + "type": "string" + }, + "externalId": { + "description": "the external ID of that node, e.g. OCITOutstation-ID", + "type": "string" + } + }, + "__tags": [ + "noMask" + ], + "__version": 2 + }, + "InesSubIntersection": { + "description": "subintersection information for network nodes", + "type": "object", + "properties": { + "entryId": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "signalGroups": { + "description": "ID/name of signal group", + "type": "array", + "items": { + "$ref": "#/definitions/InesSignalGroup" + } + } + } + }, + "Corridor": { + "type": "object", + "description": "defined coordination inside the INES network", + "properties": { + "guid": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "inesNetworkId": { + "description": "guid index of the related InesNetwork object", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesNetwork", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "name": { + "type": "string" + }, + "segments": { + "type": "array", + "items": { + "$ref": "#/definitions/CorridorSegment" + } + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "tenantId": { + "description": "what is the related tenant to that entry", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb", + "noMask" + ], + "__version": 1 + }, + "CorridorSegment": { + "type": "object", + "description": "link between points of the coordination", + "properties": { + "entryId": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "startNode": { + "description": "start point of that link", + "$ref": "#/definitions/CorridorSegmentNode" + }, + "distance": { + "description": "distance between startNode and the startNode from the next CorridorSegment in meters", + "type": "number" + } + }, + "__tags": [ + "noMask" + ], + "__version": 1 + }, + "CorridorSegmentNode": { + "type": "object", + "description": "link between points of the coordination", + "properties": { + "subIntersectionId": { + "description": "entryId of the related subIntersection", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesSubIntersection" + }, + "signalGroupId": { + "description": "ID/name of signal group", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesSignalGroup" + } + }, + "__tags": [ + "noMask" + ], + "__version": 1 + }, + "InesSignalProgram": { + "description": "subintersection information for network nodes", + "type": "object", + "properties": { + "entryId": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "externalId": { + "description": "the external ID of that node, e.g. OCITOutstation-ID", + "type": "string" + }, + "name": { + "description": "name of the signal program", + "type": "string" + }, + "cycleTime": { + "description": "duration of signal program, in milli seconds", + "type": "integer" + }, + "offset": { + "description": "signal program offset in milli seconds", + "type": "integer" + } + } + }, + "Scenario": { + "type": "object", + "description": "container that define switch relations and switch parameters", + "properties": { + "guid": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "inesNetworkId": { + "description": "guid index of the related InesNetwork object", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesNetwork", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "groupName": { + "description": "a additional name to group some scenarios", + "type": "string" + }, + "name": { + "description": "some kind of visual identifier", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean", + "__tags": [ + "notNull", + "defaultTrue" + ] + }, + "utilityParams": { + "description": "???", + "$ref": "#/definitions/UtilityParams" + }, + "lossTimes": { + "description": "???", + "type": "array", + "items": { + "$ref": "#/definitions/LossTime" + }, + "__tags": [ + "restSubPath" + ] + }, + "scenarioNodes": { + "description": "???", + "type": "array", + "items": { + "$ref": "#/definitions/ScenarioNode" + }, + "__tags": [ + "restSubPath" + ] + }, + "switchRelatedScenarios": { + "description": "???", + "type": "array", + "items": { + "$ref": "#/definitions/SwitchRelatedScenario" + }, + "__tags": [ + "restSubPath" + ] + }, + "cycleTime": { + "description": "??", + "type": "integer" + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "tenantId": { + "description": "what is the related tenant to that entry", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb" + ], + "__version": 2 + }, + "DetectionStation": { + "type": "object", + "description": "bundles some detectors to a so called Messstation :D", + "properties": { + "guid": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "inesNetworkId": { + "description": "guid index of the related InesNetwork object", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesNetwork", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "name": { + "description": "some kind of visual identifier", + "type": "string" + }, + "detectors": { + "description": "ids of detectors related to that bundle", + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + }, + "andRelated": { + "description": "all detectors are combined with an and relation", + "type": "boolean" + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "tenantId": { + "description": "what is the related tenant to that entry", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb" + ], + "__version": 2 + }, + "InesControlMethodParameter": { + "type": "object", + "description": "bundles some detectors to a so called Messstation :D", + "properties": { + "entryId": { + "description": "some kind of network node related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "comment": { + "description": "some words to explain", + "type": "string" + }, + "readable": { + "description": "true if the parameter is readable", + "type": "boolean" + }, + "writable": { + "description": "true if the parameter is writable", + "type": "boolean" + }, + "oitdNo": { + "description": "some kind of semantic information about the type", + "type": "integer", + "format": "int64" + }, + "oitdIndex": { + "description": "index inside the array", + "type": "integer", + "format": "int64" + }, + "comment": { + "description": "some more words to explain", + "type": "string" + } + }, + "__version": 1 + }, + "UtilityParams": { + "type": "object", + "description": "??", + "properties": { + "sgMin": { + "description": "??", + "type": "number" + }, + "sgMin2": { + "description": "??", + "type": "number" + }, + "sgMax": { + "description": "??", + "type": "number" + }, + "sgMax2": { + "description": "??", + "type": "number" + } + }, + "__version": 1 + }, + "LossTime": { + "type": "object", + "description": "??", + "properties": { + "entryId": { + "description": "some kind of scenario related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "sectionId": { + "description": "reference to another section in the network", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/Section", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "averageLossDirection": { + "description": "??", + "type": "number" + } + }, + "__version": 1 + }, + "ScenarioNode": { + "type": "object", + "description": "??", + "properties": { + "entryId": { + "description": "some kind of scenario related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "networkNodeId": { + "description": "reference the related network node of the network", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/NetworkNode", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "scenarioSections": { + "description": "???", + "type": "array", + "items": { + "$ref": "#/definitions/ScenarioSection" + }, + "__tags": [ + "restSubPath" + ] + }, + "signalProgramName": { + "description": "name of the signal program, based on the idea that only the name of the program is used for INES itself, TODO useful?", + "type": "string" + }, + "signalProgramId": { + "description": "network node signal program id, SignalProgram->entryId", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/SignalProgram" + } + }, + "__version": 2 + }, + "ScenarioSection": { + "type": "object", + "description": "??", + "properties": { + "entryId": { + "description": "some kind of scenario related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "sectionId": { + "description": "reference to another section in the network", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/Section", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "greenTime": { + "description": "??", + "type": "integer" + } + }, + "__version": 1 + }, + "SwitchRelatedScenario": { + "type": "object", + "description": "??", + "properties": { + "entryId": { + "description": "some kind of scenario related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "scenarioId": { + "description": "reference to another scenario in the network", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/Scenario", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "switchRelation": { + "description": "??", + "type": "integer" + }, + "allowed": { + "description": "??", + "type": "boolean" + }, + "enforced": { + "description": "??", + "type": "boolean" + } + }, + "__version": 1 + }, + "Section": { + "type": "object", + "description": "Input container that groups related detectors, signal groups and stuff", + "properties": { + "guid": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "inesNetworkId": { + "description": "guid index of the related InesNetwork object", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesNetwork", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "name": { + "description": "caption of scenario", + "type": "string" + }, + "signalGroup": { + "description": "ID/name of signal group", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesSignalGroup" + }, + "headWay": { + "description": "??", + "type": "number" + }, + "outgoingLanes": { + "description": "??", + "type": "integer" + }, + "streamProp": { + "description": "??", + "type": "number" + }, + "weightFactor": { + "description": "??", + "type": "number" + }, + "nextSectionId": { + "description": "reference to another section in the network, have to be renamed later to nextSectionId", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/Section", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "detectors": { + "description": "Detectors related to that section", + "type": "array", + "items": { + "$ref": "#/definitions/SectionDetector" + }, + "__tags": [ + "restSubPath" + ] + }, + "opposingSectionId": { + "description": "reference to another section in the network", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/Section", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "utilityParams": { + "description": "Parameter are used in case that this section for green-time redistribution", + "$ref": "#/definitions/UtilityParams" + }, + "onlyGreenTimeRedistribution": { + "description": "if true then this section only used for green-time redistribution", + "type": "boolean" + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "tenantId": { + "description": "what is the related tenant to that entry", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb" + ], + "__version": 2 + }, + "SectionDetector": { + "type": "object", + "properties": { + "entryId": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "networkDetectorId": { + "description": "reference to the ines network detector list", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/NetworkDetector", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "detectorType": { + "description": "enum based detector type", + "type": "string", + "enum": [ + "incoming", + "outgoing", + "strategic" + ] + }, + "factor": { + "description": "some kind of weight", + "type": "number" + } + }, + "__version": 2 + }, + "NetworkDetector": { + "type": "object", + "description": "Section specific information about the related detectors", + "properties": { + "guid": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "inesNetworkId": { + "description": "guid index of the related InesNetwork object", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesNetwork", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "detectorId": { + "description": "ID to a real world detector, reference to centralObject->detector->guid", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "networkNodeId": { + "description": "reference to the NetworkNode that contains that detector", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/NetworkNode", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "type": { + "description": "deprecated, not used any longer - because it's basically an enum", + "$ref": "./shared/list_entry.json", + "__ref": "#/definitions/DetectorType", + "__tags": [ + "listEntry" + ] + }, + "name": { + "description": "name of the detector", + "type": "string" + }, + "externalId": { + "description": "the external ID of that node, e.g. OCITOutstation-ID", + "type": "string" + }, + "strategic": { + "description": "this attribute is filled if the detector is a strategic detector", + "$ref": "#/definitions/StrategicDetector" + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "tenantId": { + "description": "what is the related tenant to that entry", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb" + ], + "__version": 3 + }, + "StrategicDetector": { + "type": "object", + "description": "Stategic detector information", + "properties": { + "length": { + "description": "??", + "type": "number" + }, + "lengthVeh": { + "description": "??", + "type": "number" + }, + "maxSpeed": { + "description": "??", + "type": "number" + }, + "maxVolume": { + "description": "??", + "type": "number" + }, + "fuzzyFlowDetParam": { + "description": "??", + "type": "array", + "items": { + "$ref": "#/definitions/FuzzyParam" + } + }, + "fuzzyVolumeDetParam": { + "description": "??", + "type": "array", + "items": { + "$ref": "#/definitions/FuzzyParam" + } + }, + "active": { + "description": "is this entry still active", + "type": "boolean", + "__tags": [ + "notNull", + "defaultTrue" + ] + } + }, + "__version": 1 + }, + "FuzzyParam": { + "type": "object", + "description": "parameter type for fuzzy calculation", + "properties": { + "state": { + "type": "string" + }, + "upperLimit": { + "type": "number" + }, + "lowerLimit": { + "type": "number" + } + }, + "__tags": [ + "noHandling", + "noMask" + ], + "__version": 1 + }, + "NetworkState": { + "type": "object", + "description": "state of a network", + "properties": { + "name": { + "description": "Name of the state", + "type": "string", + "__tags": [ + "unique", + "notNull" + ] + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean", + "__tags": [ + "notNull", + "defaultTrue" + ] + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean", + "__tags": [ + "notNull", + "defaultFalse" + ] + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "tenantId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb", + "selList" + ], + "__version": 1 + }, + "DetectorType": { + "type": "object", + "description": "detector type", + "properties": { + "name": { + "description": "Name of the type", + "type": "string", + "__tags": [ + "unique", + "notNull" + ] + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean", + "__tags": [ + "notNull", + "defaultTrue" + ] + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean", + "__tags": [ + "notNull", + "defaultFalse" + ] + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "tenantId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb", + "selList" + ], + "__version": 1 + }, + "NetworkNodeType": { + "type": "object", + "description": "state of a network", + "properties": { + "name": { + "description": "Name of the state", + "type": "string", + "__tags": [ + "unique", + "notNull" + ] + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean", + "__tags": [ + "notNull", + "defaultTrue" + ] + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean", + "__tags": [ + "notNull", + "defaultFalse" + ] + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "tenantId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb", + "selList" + ], + "__version": 1 + }, + "TimeRule": { + "type": "object", + "description": "Time rule entry to define what scenario can be used to specific times", + "properties": { + "guid": { + "description": "some kind of network related index", + "type": "string", + "format": "uuid", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "inesNetworkId": { + "description": "guid index of the related InesNetwork object", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/InesNetwork", + "__tags": [ + "notDisplayed", + "notNull" + ] + }, + "scenarios": { + "type": "array", + "items": { + "type": "string", + "format": "uuid", + "__ref": "#/definitions/Scenario" + } + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean", + "__tags": [ + "notNull", + "defaultTrue" + ] + }, + "monday": { + "description": "the rule is valid on Monday", + "type": "boolean" + }, + "tuesday": { + "description": "the rule is valid on Tuesday", + "type": "boolean" + }, + "wednesday": { + "description": "the rule is valid on Wednesday", + "type": "boolean" + }, + "thursday": { + "description": "the rule is valid on Thursday", + "type": "boolean" + }, + "friday": { + "description": "the rule is valid on Friday", + "type": "boolean" + }, + "saturday": { + "description": "the rule is valid on Saturday", + "type": "boolean" + }, + "sunday": { + "description": "the rule is valid on Sunday", + "type": "boolean" + }, + "startHour": { + "description": "start hour of that rule", + "type": "integer" + }, + "startMin": { + "description": "start minute", + "type": "integer" + }, + "endHour": { + "description": "start hour of that rule", + "type": "integer" + }, + "endMin": { + "description": "start minute", + "type": "integer" + }, + "tenantId": { + "description": "what is the related tenant to that entry", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": [ + "notDisplayed" + ] + } + }, + "__tags": [ + "rest", + "mongodb" + ], + "__version": 1 + }, + "InesNetworkBundle": { + "type": "object", + "description": "This is the container to bundle all information related to one ines network", + "properties": { + "inesNetwork": { + "description": "the basic information", + "$ref": "#/definitions/InesNetwork", + "__tags": [ + "noRestSubPath" + ] + }, + "objectBase": { + "$ref": "./object_base.json#/definitions/ObjectBase", + "__tags": [ + "noRestSubPath" + ] + }, + "networkNodes": { + "description": "network related network nodes", + "type": "array", + "items": { + "$ref": "#/definitions/NetworkNode" + }, + "__tags": [ + "noRestSubPath" + ] + }, + "corridors": { + "description": "network related coordination corridors", + "type": "array", + "items": { + "$ref": "#/definitions/Corridor" + }, + "__tags": [ + "noRestSubPath" + ] + }, + "scenarios": { + "description": "network related scenarios", + "type": "array", + "items": { + "$ref": "#/definitions/Scenario" + }, + "__tags": [ + "noRestSubPath" + ] + }, + "detectionStations": { + "description": "network related detection stations", + "type": "array", + "items": { + "$ref": "#/definitions/DetectionStation" + }, + "__tags": [ + "noRestSubPath" + ] + }, + "sections": { + "description": "network related sections", + "type": "array", + "items": { + "$ref": "#/definitions/Section" + }, + "__tags": [ + "noRestSubPath" + ] + }, + "timeRules": { + "description": "network configured time rules", + "type": "array", + "items": { + "$ref": "#/definitions/TimeRule" + }, + "__tags": [ + "noRestSubPath" + ] + }, + "networkDetectors": { + "description": "network related network detectors", + "type": "array", + "items": { + "$ref": "#/definitions/NetworkDetector" + }, + "__tags": [ + "noRestSubPath" + ] + } + }, + "__tags": [ + "noMask", + "customRestDelegate" + ], + "__version": 3 + }, + "NetworkValidationInfo": { + "type": "object", + "properties": { + "name": { + "description": "name of the network", + "type": "string" + }, + "inesNetworkId": { + "description": "guid of the InesNetwork", + "type": "string", + "format": "uuid" + }, + "isValid": { + "type": "boolean" + }, + "messages": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } +} diff --git a/src/test/resources/test_schemas/ds/junction.json b/src/test/resources/test_schemas/ds/junction.json new file mode 100644 index 0000000..247e5b7 --- /dev/null +++ b/src/test/resources/test_schemas/ds/junction.json @@ -0,0 +1,184 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Junction model", + "description": "Junction is a anchor point of interest of application level", + "definitions": { + "Junction": { + "type": "object", + "description": "Junction is a anchor point of interest of application level", + "properties": { + "junctionId": { + "type": "string", + "format": "uuid", + "__ref": "./junction_base.json#/JunctionBase", + "__tags": ["join"] + }, + "state": { + "description": "what is the current state of that object", + "type": "string", + "__ref": "#/definitions/JunctionState" + }, + "type": { + "description": "what type has this object", + "type": "string", + "__ref": "#/definitions/JunctionType" + }, + "location": { + "description": "where is this object located", + "type": "object", + "properties": { + "country": { + "type": "string" + }, + "county": { + "type": "string" + }, + "city": { + "type": "string" + }, + "district": { + "type": "string" + }, + "streets": { + "type": "array", + "__tags": ["recursion"], + "items": { + "type": "object", + "properties": { + "entryId": { + "description": "object specific ID of that entry", + "type": "string", + "format": "uuid" + }, + "name": { + "description": "name of the street", + "type": "string" + }, + "position": { + "description": "on what position is the object, for instance km", + "type": "number" + }, + "classification": { + "description": "for instance B86", + "type": "string" + }, + "main": { + "description": "is that the main street reference", + "type": "boolean" + } + } + } + } + } + }, + "comments": { + "type": "array", + "items": { + "$ref": "#/definitions/JunctionComment" + } + }, + "startupDate": { + "type": "string", + "format": "date" + }, + "domainId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "ref": "./shared/domain.json" + } + }, + "__tags": ["rest", "joined","mongodb"] + }, + "JunctionType": { + "type": "object", + "description": "types of junctions", + "properties": { + "name": { + "description": "Name of the type", + "type": "string" + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean" + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid" + }, + "domainId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json" + } + }, + "__tags": ["rest","mongodb"] + }, + "JunctionState": { + "type": "object", + "description": "state of junction", + "properties": { + "name": { + "description": "Name of the state", + "type": "string" + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean" + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid", + "__tags": ["internal"] + }, + "domainId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json", + "__tags": ["internal"] + } + }, + "__tags": ["rest","mongodb"] + }, + "JunctionComment": { + "type": "object", + "description": "comment a junction", + "allOf": [ + { + "$ref": "./shared/comment.json" + }, + { + "properties": { + "entryId": { + "description": "object specific ID of that entry", + "type": "string", + "format": "uuid" + } + } + } + ] + } + }, + "type": "object", + "version": 1 +} diff --git a/src/test/resources/test_schemas/ds/junction2.json b/src/test/resources/test_schemas/ds/junction2.json new file mode 100644 index 0000000..5c188b6 --- /dev/null +++ b/src/test/resources/test_schemas/ds/junction2.json @@ -0,0 +1,285 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Junction model", + "description": "Junction is a anchor point of interest of application level", + "definitions": { + "JunctionBase": { + "type": "object", + "description": "Junction is a anchor point of interest of application level, that's the minimal information about that", + "properties": { + "guid": { + "type": "string", + "format": "uuid", + "__tags": ["internal"] + }, + "domainId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json", + "__tags": ["internal"] + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "format": "uuid", + "__ref": "./shared/tag.json" + } + }, + "name": { + "description": "main name of the junction", + "type": "string" + }, + "number": { + "description": "main number of the junction", + "type": "string" + }, + "gis": { + "description": "location of that junciton", + "type": "object", + "properties": { + "area": { + "description": "geografic area of this object", + "$ref": "./shared/geo_area.json" + }, + "center": { + "description": "geografic area of this object", + "$ref": "./shared/geo_point.json" + } + } + } + }, + "__tags": ["rest","mongodb"] + }, + "Junction": { + "type": "object", + "description": "Junction is a anchor point of interest of application level", + "properties": { + "junctionId": { + "type": "string", + "format": "uuid", + "__ref": "#/definitions/JunctionBase", + "__tags": ["join"] + }, + "numbers": { + "description": "all the number (technical names) of this object", + "type": "array", + "items": { + "type": "object", + "properties": { + "number": { + "type": "string" + }, + "type": { + "type": "string", + "__ref": "#/definitions/JunctionNumberType" + } + } + } + }, + "state": { + "description": "what is the current state of that object", + "type": "string", + "__ref": "#/definitions/JunctionState" + }, + "type": { + "description": "what type has this object", + "type": "string", + "__ref": "#/definitions/JunctionType" + }, + "location": { + "description": "where is this object located", + "type": "object", + "properties": { + "country": { + "type": "string" + }, + "county": { + "type": "string" + }, + "city": { + "type": "string" + }, + "district": { + "type": "string" + }, + "streets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "name of the street", + "type": "string" + }, + "position": { + "description": "on what position is the object, for instance km", + "type": "number" + }, + "classification": { + "description": "for instance B86", + "type": "string" + }, + "main": { + "description": "is that the main street reference", + "type": "boolean" + } + } + } + } + } + }, + "comments": { + "type": "array", + "items": { + "$ref": "./shared/comment.json" + } + }, + "startup_date": { + "type": "string", + "format": "date" + } + }, + "__tags": ["rest", "joined","mongodb"] + }, + + "JunctionShortTest": { + "type": "object", + "description": "Junction is a anchor point of interest of application level", + "properties": { + "junctionId": { + "type": "string", + "format": "uuid", + "__ref": "#/definitions/JunctionBase", + "__tags": [ + "join" + ] + }, + "numbers": { + "description": "all the number (technical names) of this object", + "type": "array", + "items": { + "type": "object", + "properties": { + "number": { + "type": "string" + }, + "type": { + "type": "string", + "__ref": "#/definitions/JunctionNumberType" + } + } + } + } + }, + "__tags": ["rest", "joined","mongodb"] + }, + "JunctionType": { + "type": "object", + "description": "types of junctions", + "properties": { + "name": { + "description": "Name of the type", + "type": "string" + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean" + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid" + }, + "domainId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json" + } + }, + "__tags": ["rest","mongodb"] + }, + "JunctionState": { + "type": "object", + "description": "state of junction", + "properties": { + "name": { + "description": "Name of the state", + "type": "string" + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean" + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid", + "__tags": ["internal"] + }, + "domainId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json", + "__tags": ["internal"] + } + }, + "__tags": ["rest","mongodb"] + }, + "JunctionNumberType": { + "type": "object", + "description": "Technical identifier for that junction", + "properties": { + "name": { + "description": "Name of the state", + "type": "string" + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean" + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid" + }, + "domainId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json", + "__tags": ["internal"] + } + } + } + }, + "type": "object", + "version": 1 +} diff --git a/src/test/resources/test_schemas/ds/junction_base.json b/src/test/resources/test_schemas/ds/junction_base.json new file mode 100644 index 0000000..581e749 --- /dev/null +++ b/src/test/resources/test_schemas/ds/junction_base.json @@ -0,0 +1,58 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Junction base model", + "description": "Junction is a anchor point of interest of application level", + "definitions": { + "JunctionBase": { + "type": "object", + "description": "Junction is a anchor point of interest of application level, that's the minimal information about that", + "properties": { + "guid": { + "type": "string", + "format": "uuid", + "__tags": ["internal"] + }, + "domainId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json", + "__tags": ["internal"] + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "format": "uuid", + "__ref": "./shared/tag.json" + } + }, + "name": { + "description": "main name of the junction", + "type": "string" + }, + "number": { + "description": "main number of the junction", + "type": "string" + }, + "gis": { + "description": "location of that junciton", + "type": "object", + "properties": { + "area": { + "description": "geografic area of this object", + "$ref": "./shared/geo_area.json" + }, + "center": { + "description": "geografic area of this object", + "$ref": "./shared/geo_point.json" + } + } + } + }, + "__tags": ["rest","mongodb"] + } + }, + "type": "object", + "version": 1 +} diff --git a/src/test/resources/test_schemas/ds/lisa-ines-network.json b/src/test/resources/test_schemas/ds/lisa-ines-network.json new file mode 100644 index 0000000..8052013 --- /dev/null +++ b/src/test/resources/test_schemas/ds/lisa-ines-network.json @@ -0,0 +1,867 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "null", + "definitions": { + "CentralControllerParameterType": { + "type": "object", + "properties": { + "CentralController": { + "$ref": "#/definitions/CentralControllerType" + }, + "ControllerGroupList": { + "$ref": "#/definitions/ControllerGroupListType" + }, + "DataFormat": { + "type": "integer", + "format": "int32" + }, + "DetectorList": { + "description": "Detektoren zur Erfassung des Individualverkehrs und sonstige digitale Eing nge", + "$ref": "#/definitions/DetectorListType" + }, + "HeaderData": { + "description": "Information ber den Knoten", + "$ref": "#/definitions/HeaderDataType" + }, + "LogicParameterList": { + "description": "Parameter der verkehrsabh ngigen Logik", + "$ref": "#/definitions/LogicParameterType" + }, + "LogicVariableList": { + "$ref": "#/definitions/LogicVariableListType" + }, + "SignalGroupList": { + "description": "Liste aller von der Signalsicherung berwachten Ausg nge", + "$ref": "#/definitions/SignalGroupListType" + }, + "SignalProgramList": { + "description": "Liste der Signalprogramme Festzeit und VA Programme", + "$ref": "#/definitions/SignalProgramListType" + }, + "SubIntersectionList": { + "$ref": "#/definitions/SubIntersectionListType" + }, + "TimerList": { + "description": "Liste der Signalprogramme Festzeit und VA Programme", + "$ref": "#/definitions/TimerListType" + }, + "TrafficControllerList": { + "description": "Allgemeine Angaben zum Steuerger t", + "$ref": "#/definitions/TrafficControllerListType" + } + } + }, + "CentralControllerType": { + "type": "object", + "properties": { + "FgService": { + "$ref": "#/definitions/ServiceType" + }, + "PdCallbackService": { + "$ref": "#/definitions/ServiceType" + }, + "PdService": { + "$ref": "#/definitions/ServiceType" + }, + "URL": { + "type": "string" + }, + "VdService": { + "$ref": "#/definitions/ServiceType" + } + } + }, + "CommentsType": { + "description": "Bemerkungsliste", + "type": "object", + "properties": { + "Comment": { + "description": "Bemerkungszeile Die Zeile kann maximal 250 Zeichen lang sein leere Zeilen sind erlaubt", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "ControllerGroupListType": { + "type": "object", + "properties": { + "ControllerGroup": { + "type": "array", + "items": { + "$ref": "#/definitions/ControllerGroupType" + } + } + } + }, + "ControllerGroupType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "No": { + "type": "integer", + "format": "int32" + }, + "TCList": { + "$ref": "#/definitions/ControllerGroupTypeTCList" + } + } + }, + "ControllerGroupTypeTCList": { + "type": "object", + "properties": { + "TC": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "DetectorListType": { + "type": "object", + "properties": { + "Detector": { + "description": "Siehe OCIT Element DigEingang Sowohl digital als auch seriell angeschlossene Detektoren als auch gleichzeitig seriell UND digital angeschlossene Detektoren werden hier eingetragen Die seriell erfassten Daten werden auf einem DigEingang oder mehreren DigEing ngen abgebildet Wenn eine Schleife auf mehrere Detektoren abgebildet wird werden auch mehrere DigEing nge eingetragen", + "type": "array", + "items": { + "$ref": "#/definitions/DetectorType" + } + } + } + }, + "DetectorType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Controller": { + "type": "array", + "items": { + "type": "string" + } + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "EnumListType": { + "type": "object", + "properties": { + "Enum": { + "type": "array", + "items": { + "$ref": "#/definitions/EnumType" + } + } + } + }, + "EnumType": { + "type": "object", + "properties": { + "Identifier": { + "type": "string" + }, + "Value": { + "type": "integer", + "format": "int32" + } + } + }, + "HeaderDataType": { + "type": "object", + "properties": { + "Comments": { + "description": "kundenspezifische Bemerkungen Wenn keine Bemerkung vorhanden ist wird das Feld weggelassen Systeme die keine Bemerkungen erlauben m ssen das Feld unver ndert beibehalten", + "$ref": "#/definitions/CommentsType" + }, + "DataTimeStamp": { + "description": "Speicherdatum und Uhrzeit der letzten nderung", + "type": "string", + "format": "date-time" + }, + "DataVersion": { + "description": "Planungsversion als String Format beliebig und abh ngig von der Quelle", + "type": "array", + "items": { + "type": "string" + } + }, + "Identification": { + "description": "Identifikation des Knotens Bleibt w hrend der reinen Planungsphase frei wenn noch keine Identifikation vorliegt", + "$ref": "#/definitions/HeaderDataTypeIdentification" + }, + "Machine": { + "description": "Rechner auf dem die Daten der letzten nderung erstellt wurden Wenn der Wert nicht zur Verf gung steht wird der Eintrag weggelassen", + "type": "array", + "items": { + "type": "string" + } + }, + "Name": { + "description": "Langname des Knotens max 250 Zeichen Beispiel Meierstrasse Muellerstrasse", + "type": "array", + "items": { + "type": "string" + } + }, + "Operator": { + "description": "Name des Users der die letzte nderung gespeichert hat", + "type": "array", + "items": { + "type": "string" + } + }, + "ShortName": { + "description": "Kurzbezeichnung maximal 10 Zeichen z B K242", + "type": "string" + }, + "UID": { + "type": "string" + }, + "Uploader": { + "description": "Versorgungsprogramm welches die Datei als letztes bearbeitet hat", + "$ref": "#/definitions/HeaderDataTypeUploader" + } + } + }, + "HeaderDataTypeIdentification": { + "type": "object", + "properties": { + "IntersectionId": { + "description": "Logische Adressierung des Knotens", + "$ref": "#/definitions/HeaderDataTypeIdentificationIntersectionId" + }, + "OCITIdentity": { + "description": "Technische OCIT Kennung des Knotens Darf nur dann ausgef llt werden wenn die Kennung festgelegt ist", + "$ref": "#/definitions/HeaderDataTypeIdentificationOCITIdentity" + } + } + }, + "HeaderDataTypeIdentificationIntersectionId": { + "type": "object", + "properties": { + "City": { + "description": "Stadt in der der Knoten l uft Mu nur gesetzt werden wenn mehrere St dte in einem Projekt gemeinsam vorhanden sind", + "type": "array", + "items": { + "type": "string" + } + }, + "District": { + "description": "Bezirk in dem der Knoten l uft Mu nur in St dten gesetzt werden die in Bezirke aufgeteilt sind Beispiel Wien", + "type": "array", + "items": { + "type": "string" + } + }, + "IntersectionNumber": { + "description": "Stadtweit eindeutige Nummer des Knotens Die Nummer ist die technische Nummer und hat nichts mit Auftragskennungen etc zu tun Mu vor der echten Ger teversorgung gesetzt werden Darf w hrend der reinen Planungsphase zusammen mit der KnotenId frei bleiben", + "type": "integer", + "format": "int32" + } + } + }, + "HeaderDataTypeIdentificationOCITIdentity": { + "type": "object", + "properties": { + "CenNo": { + "description": "znr Nummer entspricht der technischen VSR Nummer Siehe OCIT Dokumentation", + "type": "integer", + "format": "int32" + }, + "SubNo": { + "description": "Nur notwenidg wenn mehr als ein Knoten auf dem Feldger t vorhanden ist Ansonsten ist der Wert auf den Default Wert 1 gesetzt", + "type": "integer", + "format": "int32" + }, + "TCNo": { + "description": "Wird nur gesetzt wenn der Wert ungleich der Knotennummer gesetzt Ansonsten ist der Wert defaultm ig auf knotenNummer gesetzt", + "type": "integer", + "format": "int32" + } + } + }, + "HeaderDataTypeUploader": { + "type": "object", + "properties": { + "Name": { + "description": "Name des Versorgungsprogramm max 32 Zeichen", + "type": "array", + "items": { + "type": "string" + } + }, + "Version": { + "description": "Programmversion frei definierbarer Text mit max 10 Zeichen", + "type": "string" + } + } + }, + "IdentObjectType": { + "description": "Basistyp f r Bezeichner Objekte", + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + } + } + }, + "LastChangeType": { + "description": "Letzte nderung", + "type": "object", + "properties": { + "Datum": { + "description": "Zeitstempel der letzten nderung", + "type": "string", + "format": "date-time" + }, + "Operator": { + "description": "Benutzer der dieses Objekt als letztes bearbeitet hat", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "LogicParameterType": { + "type": "object", + "properties": { + "ParameterBlock": { + "description": "Alle Daten eines Parametertyps", + "type": "array", + "items": { + "$ref": "#/definitions/ParameterBlockType" + } + } + } + }, + "LogicVariableListType": { + "type": "object", + "properties": { + "LogicVariable": { + "type": "array", + "items": { + "$ref": "#/definitions/LogicVariableType" + } + } + } + }, + "LogicVariableType": { + "type": "object", + "properties": { + "ArrayLength": { + "type": "integer", + "format": "int32" + }, + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Controller": { + "type": "array", + "items": { + "type": "string" + } + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + }, + "Type": { + "type": "string" + }, + "Writeable": { + "type": "boolean" + } + } + }, + "ObjectHeader": { + "description": "Header f r die Objekte", + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "ParameterBlockType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + }, + "ParameterSet": { + "type": "array", + "items": { + "$ref": "#/definitions/ParameterSetType" + } + }, + "Structur": { + "description": "Strukturbeschreibung der Parameter einer Parametertabelle", + "$ref": "#/definitions/ParameterStruturType" + }, + "UUID": { + "type": "string" + } + } + }, + "ParameterDefType": { + "description": "Einzelner Parameter Tabellenspalte", + "type": "object", + "properties": { + "EnumList": { + "$ref": "#/definitions/EnumListType" + }, + "Identifier": { + "description": "Bezeichner Tab Spaltenname", + "type": "array", + "items": { + "type": "string" + } + }, + "Referencetype": { + "description": "Elemente aus Versorgorgung", + "type": "string" + }, + "StandardType": { + "description": "float word", + "type": "string" + }, + "bDefault": { + "description": "byte", + "type": "integer", + "format": "int32" + }, + "fDefault": { + "description": "float", + "type": "number", + "format": "double" + }, + "iDefault": { + "description": "integer", + "type": "integer", + "format": "int32" + }, + "sDefault": { + "description": "string", + "type": "string" + }, + "wDefault": { + "description": "word", + "type": "integer", + "format": "int32" + }, + "zDefault": { + "description": "boolean", + "type": "boolean" + } + } + }, + "ParameterSetType": { + "type": "object", + "properties": { + "No": { + "type": "integer", + "format": "int32" + }, + "Record": { + "type": "array", + "items": { + "$ref": "#/definitions/RecordType" + } + } + } + }, + "ParameterStruturType": { + "type": "object", + "properties": { + "MaxRecordCount": { + "type": "integer", + "format": "int32" + }, + "Parameter": { + "type": "array", + "items": { + "$ref": "#/definitions/ParameterDefType" + } + } + } + }, + "ParameterValueType": { + "type": "object", + "properties": { + "Parameter": { + "description": "Referenz auf Tabellenspalte", + "type": "string" + }, + "Reference": { + "description": "Referenz auf ein Element", + "type": "string" + }, + "bItem": { + "description": "byte", + "type": "integer", + "format": "int32" + }, + "fItem": { + "description": "float", + "type": "number", + "format": "double" + }, + "iItem": { + "description": "integer", + "type": "integer", + "format": "int32" + }, + "sItem": { + "description": "string", + "type": "string" + }, + "wItem": { + "description": "word", + "type": "integer", + "format": "int32" + }, + "zItem": { + "description": "boolean", + "type": "boolean" + } + } + }, + "RecordType": { + "type": "object", + "properties": { + "Item": { + "type": "array", + "items": { + "$ref": "#/definitions/ParameterValueType" + } + }, + "No": { + "type": "integer", + "format": "int32" + } + } + }, + "ServiceType": { + "type": "object", + "properties": { + "Service": { + "type": "string" + }, + "URL": { + "type": "string" + } + } + }, + "SignalGroupListType": { + "type": "object", + "properties": { + "SignalGroup": { + "description": "Siehe OCIT Element SignalGruppe Pro Signalgruppe wird hier ein Eintrag vorgenommen", + "type": "array", + "items": { + "$ref": "#/definitions/SignalGroupType" + } + } + } + }, + "SignalGroupType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Controller": { + "type": "array", + "items": { + "type": "string" + } + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "SignalProgramListType": { + "type": "object", + "properties": { + "SignalProgram": { + "description": "Signalprogramm ist die Aufh ngung f r unterschiedliche Signalprogramme Echte Festzeitsignalprogramme k nnen auf zwei Arten gespeichert werden Als eine Liste von Signalbildern in die gewechselt wird oder als eine Liste von Phasen berg ngen die zu bestimmten Zeitpunkten beginnen", + "type": "array", + "items": { + "$ref": "#/definitions/SignalProgramType" + } + } + } + }, + "SignalProgramType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "SubIntersectionListType": { + "type": "object", + "properties": { + "SubIntersection": { + "type": "array", + "items": { + "$ref": "#/definitions/SubIntersectionType" + } + } + } + }, + "SubIntersectionType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Controller": { + "type": "array", + "items": { + "type": "string" + } + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "TCObjectHeader": { + "description": "Header f r die Objekte", + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Controller": { + "type": "array", + "items": { + "type": "string" + } + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "TimerListType": { + "type": "object", + "properties": { + "Timer": { + "type": "array", + "items": { + "$ref": "#/definitions/TimerType" + } + } + } + }, + "TimerType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "TrafficControllerListType": { + "type": "object", + "properties": { + "TrafficController": { + "type": "array", + "items": { + "$ref": "#/definitions/TrafficControllerType" + } + } + } + }, + "TrafficControllerType": { + "type": "object", + "properties": { + "CenNo": { + "type": "integer", + "format": "int32" + }, + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "TCNo": { + "type": "integer", + "format": "int32" + } + } + } + }, + "type": "object" +} diff --git a/src/test/resources/test_schemas/ds/lisa-ines-network_new_references.json b/src/test/resources/test_schemas/ds/lisa-ines-network_new_references.json new file mode 100644 index 0000000..1b3bbff --- /dev/null +++ b/src/test/resources/test_schemas/ds/lisa-ines-network_new_references.json @@ -0,0 +1,870 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "null", + "definitions": { + "CentralControllerParameterType": { + "type": "object", + "properties": { + "CentralController": { + "$ref": "#/definitions/CentralControllerType" + }, + "ControllerGroupList": { + "$ref": "#/definitions/ControllerGroupListType" + }, + "DataFormat": { + "type": "integer", + "format": "int32" + }, + "DetectorList": { + "description": "Detektoren zur Erfassung des Individualverkehrs und sonstige digitale Eing nge", + "$ref": "#/definitions/DetectorListType" + }, + "HeaderData": { + "description": "Information ber den Knoten", + "$ref": "#/definitions/HeaderDataType" + }, + "LogicParameterList": { + "description": "Parameter der verkehrsabh ngigen Logik", + "$ref": "#/definitions/LogicParameterType" + }, + "LogicVariableList": { + "$ref": "#/definitions/LogicVariableListType" + }, + "SignalGroupList": { + "description": "Liste aller von der Signalsicherung berwachten Ausg nge", + "$ref": "#/definitions/SignalGroupListType" + }, + "SignalProgramList": { + "description": "Liste der Signalprogramme Festzeit und VA Programme", + "$ref": "#/definitions/SignalProgramListType" + }, + "SubIntersectionList": { + "$ref": "#/definitions/SubIntersectionListType" + }, + "TimerList": { + "description": "Liste der Signalprogramme Festzeit und VA Programme", + "$ref": "#/definitions/TimerListType" + }, + "TrafficControllerList": { + "description": "Allgemeine Angaben zum Steuerger t", + "$ref": "#/definitions/TrafficControllerListType" + }, + "externalRef": { + "$ref": "./KnotenDaten.json/#/definitions/LISA" + } + } + }, + "CentralControllerType": { + "type": "object", + "properties": { + "FgService": { + "$ref": "#/definitions/ServiceType" + }, + "PdCallbackService": { + "$ref": "#/definitions/ServiceType" + }, + "PdService": { + "$ref": "#/definitions/ServiceType" + }, + "URL": { + "type": "string" + }, + "VdService": { + "$ref": "#/definitions/ServiceType" + } + } + }, + "CommentsType": { + "description": "Bemerkungsliste", + "type": "object", + "properties": { + "Comment": { + "description": "Bemerkungszeile Die Zeile kann maximal 250 Zeichen lang sein leere Zeilen sind erlaubt", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "ControllerGroupListType": { + "type": "object", + "properties": { + "ControllerGroup": { + "type": "array", + "items": { + "$ref": "#/definitions/ControllerGroupType" + } + } + } + }, + "ControllerGroupType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "No": { + "type": "integer", + "format": "int32" + }, + "TCList": { + "$ref": "#/definitions/ControllerGroupTypeTCList" + } + } + }, + "ControllerGroupTypeTCList": { + "type": "object", + "properties": { + "TC": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "DetectorListType": { + "type": "object", + "properties": { + "Detector": { + "description": "Siehe OCIT Element DigEingang Sowohl digital als auch seriell angeschlossene Detektoren als auch gleichzeitig seriell UND digital angeschlossene Detektoren werden hier eingetragen Die seriell erfassten Daten werden auf einem DigEingang oder mehreren DigEing ngen abgebildet Wenn eine Schleife auf mehrere Detektoren abgebildet wird werden auch mehrere DigEing nge eingetragen", + "type": "array", + "items": { + "$ref": "#/definitions/DetectorType" + } + } + } + }, + "DetectorType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Controller": { + "type": "array", + "items": { + "type": "string" + } + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "EnumListType": { + "type": "object", + "properties": { + "Enum": { + "type": "array", + "items": { + "$ref": "#/definitions/EnumType" + } + } + } + }, + "EnumType": { + "type": "object", + "properties": { + "Identifier": { + "type": "string" + }, + "Value": { + "type": "integer", + "format": "int32" + } + } + }, + "HeaderDataType": { + "type": "object", + "properties": { + "Comments": { + "description": "kundenspezifische Bemerkungen Wenn keine Bemerkung vorhanden ist wird das Feld weggelassen Systeme die keine Bemerkungen erlauben m ssen das Feld unver ndert beibehalten", + "$ref": "#/definitions/CommentsType" + }, + "DataTimeStamp": { + "description": "Speicherdatum und Uhrzeit der letzten nderung", + "type": "string", + "format": "date-time" + }, + "DataVersion": { + "description": "Planungsversion als String Format beliebig und abh ngig von der Quelle", + "type": "array", + "items": { + "type": "string" + } + }, + "Identification": { + "description": "Identifikation des Knotens Bleibt w hrend der reinen Planungsphase frei wenn noch keine Identifikation vorliegt", + "$ref": "#/definitions/HeaderDataTypeIdentification" + }, + "Machine": { + "description": "Rechner auf dem die Daten der letzten nderung erstellt wurden Wenn der Wert nicht zur Verf gung steht wird der Eintrag weggelassen", + "type": "array", + "items": { + "type": "string" + } + }, + "Name": { + "description": "Langname des Knotens max 250 Zeichen Beispiel Meierstrasse Muellerstrasse", + "type": "array", + "items": { + "type": "string" + } + }, + "Operator": { + "description": "Name des Users der die letzte nderung gespeichert hat", + "type": "array", + "items": { + "type": "string" + } + }, + "ShortName": { + "description": "Kurzbezeichnung maximal 10 Zeichen z B K242", + "type": "string" + }, + "UID": { + "type": "string" + }, + "Uploader": { + "description": "Versorgungsprogramm welches die Datei als letztes bearbeitet hat", + "$ref": "#/definitions/HeaderDataTypeUploader" + } + } + }, + "HeaderDataTypeIdentification": { + "type": "object", + "properties": { + "IntersectionId": { + "description": "Logische Adressierung des Knotens", + "$ref": "#/definitions/HeaderDataTypeIdentificationIntersectionId" + }, + "OCITIdentity": { + "description": "Technische OCIT Kennung des Knotens Darf nur dann ausgef llt werden wenn die Kennung festgelegt ist", + "$ref": "#/definitions/HeaderDataTypeIdentificationOCITIdentity" + } + } + }, + "HeaderDataTypeIdentificationIntersectionId": { + "type": "object", + "properties": { + "City": { + "description": "Stadt in der der Knoten l uft Mu nur gesetzt werden wenn mehrere St dte in einem Projekt gemeinsam vorhanden sind", + "type": "array", + "items": { + "type": "string" + } + }, + "District": { + "description": "Bezirk in dem der Knoten l uft Mu nur in St dten gesetzt werden die in Bezirke aufgeteilt sind Beispiel Wien", + "type": "array", + "items": { + "type": "string" + } + }, + "IntersectionNumber": { + "description": "Stadtweit eindeutige Nummer des Knotens Die Nummer ist die technische Nummer und hat nichts mit Auftragskennungen etc zu tun Mu vor der echten Ger teversorgung gesetzt werden Darf w hrend der reinen Planungsphase zusammen mit der KnotenId frei bleiben", + "type": "integer", + "format": "int32" + } + } + }, + "HeaderDataTypeIdentificationOCITIdentity": { + "type": "object", + "properties": { + "CenNo": { + "description": "znr Nummer entspricht der technischen VSR Nummer Siehe OCIT Dokumentation", + "type": "integer", + "format": "int32" + }, + "SubNo": { + "description": "Nur notwenidg wenn mehr als ein Knoten auf dem Feldger t vorhanden ist Ansonsten ist der Wert auf den Default Wert 1 gesetzt", + "type": "integer", + "format": "int32" + }, + "TCNo": { + "description": "Wird nur gesetzt wenn der Wert ungleich der Knotennummer gesetzt Ansonsten ist der Wert defaultm ig auf knotenNummer gesetzt", + "type": "integer", + "format": "int32" + } + } + }, + "HeaderDataTypeUploader": { + "type": "object", + "properties": { + "Name": { + "description": "Name des Versorgungsprogramm max 32 Zeichen", + "type": "array", + "items": { + "type": "string" + } + }, + "Version": { + "description": "Programmversion frei definierbarer Text mit max 10 Zeichen", + "type": "string" + } + } + }, + "IdentObjectType": { + "description": "Basistyp f r Bezeichner Objekte", + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + } + } + }, + "LastChangeType": { + "description": "Letzte nderung", + "type": "object", + "properties": { + "Datum": { + "description": "Zeitstempel der letzten nderung", + "type": "string", + "format": "date-time" + }, + "Operator": { + "description": "Benutzer der dieses Objekt als letztes bearbeitet hat", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "LogicParameterType": { + "type": "object", + "properties": { + "ParameterBlock": { + "description": "Alle Daten eines Parametertyps", + "type": "array", + "items": { + "$ref": "#/definitions/ParameterBlockType" + } + } + } + }, + "LogicVariableListType": { + "type": "object", + "properties": { + "LogicVariable": { + "type": "array", + "items": { + "$ref": "#/definitions/LogicVariableType" + } + } + } + }, + "LogicVariableType": { + "type": "object", + "properties": { + "ArrayLength": { + "type": "integer", + "format": "int32" + }, + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Controller": { + "type": "array", + "items": { + "type": "string" + } + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + }, + "Type": { + "type": "string" + }, + "Writeable": { + "type": "boolean" + } + } + }, + "ObjectHeader": { + "description": "Header f r die Objekte", + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "ParameterBlockType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + }, + "ParameterSet": { + "type": "array", + "items": { + "$ref": "#/definitions/ParameterSetType" + } + }, + "Structur": { + "description": "Strukturbeschreibung der Parameter einer Parametertabelle", + "$ref": "#/definitions/ParameterStruturType" + }, + "UUID": { + "type": "string" + } + } + }, + "ParameterDefType": { + "description": "Einzelner Parameter Tabellenspalte", + "type": "object", + "properties": { + "EnumList": { + "$ref": "#/definitions/EnumListType" + }, + "Identifier": { + "description": "Bezeichner Tab Spaltenname", + "type": "array", + "items": { + "type": "string" + } + }, + "Referencetype": { + "description": "Elemente aus Versorgorgung", + "type": "string" + }, + "StandardType": { + "description": "float word", + "type": "string" + }, + "bDefault": { + "description": "byte", + "type": "integer", + "format": "int32" + }, + "fDefault": { + "description": "float", + "type": "number", + "format": "double" + }, + "iDefault": { + "description": "integer", + "type": "integer", + "format": "int32" + }, + "sDefault": { + "description": "string", + "type": "string" + }, + "wDefault": { + "description": "word", + "type": "integer", + "format": "int32" + }, + "zDefault": { + "description": "boolean", + "type": "boolean" + } + } + }, + "ParameterSetType": { + "type": "object", + "properties": { + "No": { + "type": "integer", + "format": "int32" + }, + "Record": { + "type": "array", + "items": { + "$ref": "#/definitions/RecordType" + } + } + } + }, + "ParameterStruturType": { + "type": "object", + "properties": { + "MaxRecordCount": { + "type": "integer", + "format": "int32" + }, + "Parameter": { + "type": "array", + "items": { + "$ref": "#/definitions/ParameterDefType" + } + } + } + }, + "ParameterValueType": { + "type": "object", + "properties": { + "Parameter": { + "description": "Referenz auf Tabellenspalte", + "type": "string" + }, + "Reference": { + "description": "Referenz auf ein Element", + "type": "string" + }, + "bItem": { + "description": "byte", + "type": "integer", + "format": "int32" + }, + "fItem": { + "description": "float", + "type": "number", + "format": "double" + }, + "iItem": { + "description": "integer", + "type": "integer", + "format": "int32" + }, + "sItem": { + "description": "string", + "type": "string" + }, + "wItem": { + "description": "word", + "type": "integer", + "format": "int32" + }, + "zItem": { + "description": "boolean", + "type": "boolean" + } + } + }, + "RecordType": { + "type": "object", + "properties": { + "Item": { + "type": "array", + "items": { + "$ref": "#/definitions/ParameterValueType" + } + }, + "No": { + "type": "integer", + "format": "int32" + } + } + }, + "ServiceType": { + "type": "object", + "properties": { + "Service": { + "type": "string" + }, + "URL": { + "type": "string" + } + } + }, + "SignalGroupListType": { + "type": "object", + "properties": { + "SignalGroup": { + "description": "Siehe OCIT Element SignalGruppe Pro Signalgruppe wird hier ein Eintrag vorgenommen", + "type": "array", + "items": { + "$ref": "#/definitions/SignalGroupType" + } + } + } + }, + "SignalGroupType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Controller": { + "type": "array", + "items": { + "type": "string" + } + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "SignalProgramListType": { + "type": "object", + "properties": { + "SignalProgram": { + "description": "Signalprogramm ist die Aufh ngung f r unterschiedliche Signalprogramme Echte Festzeitsignalprogramme k nnen auf zwei Arten gespeichert werden Als eine Liste von Signalbildern in die gewechselt wird oder als eine Liste von Phasen berg ngen die zu bestimmten Zeitpunkten beginnen", + "type": "array", + "items": { + "$ref": "#/definitions/SignalProgramType" + } + } + } + }, + "SignalProgramType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "SubIntersectionListType": { + "type": "object", + "properties": { + "SubIntersection": { + "type": "array", + "items": { + "$ref": "#/definitions/SubIntersectionType" + } + } + } + }, + "SubIntersectionType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Controller": { + "type": "array", + "items": { + "type": "string" + } + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "TCObjectHeader": { + "description": "Header f r die Objekte", + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Controller": { + "type": "array", + "items": { + "type": "string" + } + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "TimerListType": { + "type": "object", + "properties": { + "Timer": { + "type": "array", + "items": { + "$ref": "#/definitions/TimerType" + } + } + } + }, + "TimerType": { + "type": "object", + "properties": { + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "ObjNo": { + "description": "OCIT Nummer falls das Objekte eine OCIT Nr hat ansonsten logische Nummer In Planungssituationen in denen die Nummer noch nicht festgelegt wird wird das Feld weggelassen Die Nummer muss gr er oder gleich 1 sein", + "type": "integer", + "format": "int32" + } + } + }, + "TrafficControllerListType": { + "type": "object", + "properties": { + "TrafficController": { + "type": "array", + "items": { + "$ref": "#/definitions/TrafficControllerType" + } + } + } + }, + "TrafficControllerType": { + "type": "object", + "properties": { + "CenNo": { + "type": "integer", + "format": "int32" + }, + "Comments": { + "description": "Bemerkungen zu diesem Objekt", + "$ref": "#/definitions/CommentsType" + }, + "Identifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "LastChange": { + "description": "Zeitpunkt der letzten nderung Wenn der Zeitpunkt nicht zur Verf gung steht wird das Feld weggelassen", + "$ref": "#/definitions/LastChangeType" + }, + "TCNo": { + "type": "integer", + "format": "int32" + } + } + } + }, + "type": "object" +} diff --git a/src/test/resources/test_schemas/ds/object_base.json b/src/test/resources/test_schemas/ds/object_base.json new file mode 100644 index 0000000..2077dd2 --- /dev/null +++ b/src/test/resources/test_schemas/ds/object_base.json @@ -0,0 +1,124 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Object base model", + "description": "Object is an anchor point of interest of application level", + "definitions": { + "ObjectBase": { + "type": "object", + "description": "Object is a anchor point of interest of application level, that's the minimal information about that", + "properties": { + "guid": { + "type": "string", + "format": "uuid", + "__tags": ["notDisplayed", "notNull"] + }, + "tenantId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": ["notDisplayed"] + }, + "tags": { + "description": "key word to group entries for customer, f.e. in front-ends", + "type": "array", + "items": { + "$ref": "./shared/list_entry.json", + "__ref": "./shared/tag.json" + }, + "__tags": ["listEntry","noMask"] + }, + "regions": { + "description": "allows vertical grouping of objects, f.e. in sense of grants", + "type": "array", + "items": { + "type": "string", + "format": "uuid", + "__ref": "./shared/region.json" + } + }, + "name": { + "description": "main name of the junction", + "type": "string", + "__tags": ["notNull","inputRequired"] + }, + "number": { + "description": "main number of the junction", + "type": "string", + "__tags": ["unique"] + }, + "gis": { + "description": "location of that junciton", + "type": "object", + "properties": { + "area": { + "description": "geografic area of this object", + "$ref": "./shared/geo_area.json", + "__tags": ["gisAreaFilter"] + }, + "route": { + "description": "geografic multiline of this object", + "$ref": "./shared/geo_multiline.json", + "__tags": ["gisRouteFilter"] + }, + "center": { + "description": "geografic area of this object", + "$ref": "./shared/geo_point.json", + "__tags": ["gisPointFilter"] + } + }, + "__tags": ["restSubPath"] + }, + "objectGroup": { + "description": "what group is that object related to (f.e. intersections, INES nets, public transport route, ...)", + "$ref": "./shared/list_entry.json", + "__ref": "#/definitions/ObjectGroup", + "__tags": ["notDisplayed", "listEntry"] + } + }, + "__tags": ["rest","mongodb","index2d"], + "__version": 1 + }, + "ObjectGroup": { + "description": "Groups of objects (f.e. intersections, INES nets, public transport route, ...)", + "type": "object", + "properties": { + "name": { + "description": "a describing name", + "type": "string" + }, + "comment": { + "description": "some more words to explain", + "type": "string" + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid" + }, + "resStr": { + "description": "a pure resource identifier, or a combined format string, f.e. ${RES_ID}: const text [${RES_ID2}]", + "type": "string" + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean" + }, + "tenantId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/tenant.json", + "__tags": ["notDisplayed"] + } + }, + "__tags": ["rest","mongodb","selList"], + "__version": 1 + } + }, + "type": "object" +} diff --git a/src/test/resources/test_schemas/ds/shared/domain.json b/src/test/resources/test_schemas/ds/shared/domain.json index 3f54035..1ac39ae 100644 --- a/src/test/resources/test_schemas/ds/shared/domain.json +++ b/src/test/resources/test_schemas/ds/shared/domain.json @@ -29,5 +29,5 @@ "name", "domain_id" ], - "version": 1 + "__version": 4 } diff --git a/src/test/resources/test_schemas/ds/shared/icon.json b/src/test/resources/test_schemas/ds/shared/icon.json index a19cfab..3876638 100644 --- a/src/test/resources/test_schemas/ds/shared/icon.json +++ b/src/test/resources/test_schemas/ds/shared/icon.json @@ -13,5 +13,6 @@ "type": "string" } }, + "__tags": ["eins","zwei","drei"], "version": 1 } diff --git a/src/test/resources/test_schemas/ds/shared/list_entry.json b/src/test/resources/test_schemas/ds/shared/list_entry.json new file mode 100644 index 0000000..c9b3868 --- /dev/null +++ b/src/test/resources/test_schemas/ds/shared/list_entry.json @@ -0,0 +1,24 @@ +{ + "id": "http://schlothauer.de/schemas/shared/tenant.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "type for entries that have references to look-up tables - as an more dynamic alternative to enums", + "type": "object", + "properties": { + "text": { + "description": "a default text to display", + "type": "string", + "__tags": ["formatString"] + }, + "refId": { + "description": "guid in the lookup-table to enable later editing of texts", + "type": "string", + "format": "uuid", + "__tags": ["notDisplayed"] + } + }, + "__tags": ["noHandling"], + "required": [ + "text" + ], + "__version": 1 +} diff --git a/src/test/resources/test_schemas/ds/shared/region.json b/src/test/resources/test_schemas/ds/shared/region.json new file mode 100644 index 0000000..d9e4d9c --- /dev/null +++ b/src/test/resources/test_schemas/ds/shared/region.json @@ -0,0 +1,54 @@ + + +{ + "id": "http://schlothauer.de/schemas/shared/tenant.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "A region is some kind of a logical group that could be set for some entries. It allows 'vertical' restrictions of object visibility", + "type": "object", + "properties": { + "guid": { + "type": "string", + "format": "uuid", + "__tags": ["notDisplayed", "notNull"] + }, + "name": { + "description": "Name of the tag", + "type": "string", + "__tags": ["notNull","unique"] + }, + "group": { + "description": "Short text that describes the tag", + "type": "string" + }, + "comment": { + "description": "Comment to that tab", + "type": "string" + }, + "created": { + "type": "string", + "format": "datetime" + }, + "active": { + "type": "boolean", + "__tags": ["notNull","defaultTrue"] + }, + "locked": { + "description": "entry is needed and should not be deleted", + "type": "boolean", + "__tags": ["notNull","defaultFalse"] + }, + "tenantId": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./tenant.json", + "__tags": ["notDisplayed"] + } + }, + "required": [ + "guid", + "text" + ], + "__tags": ["rest","mongodb","selList"], + "__version": 1 +} diff --git a/src/test/resources/test_schemas/ds/shared/tag.json b/src/test/resources/test_schemas/ds/shared/tag.json new file mode 100644 index 0000000..53eb2de --- /dev/null +++ b/src/test/resources/test_schemas/ds/shared/tag.json @@ -0,0 +1,45 @@ + + +{ + "id": "http://schlothauer.de/schemas/shared/domain.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "A tag is some kind of a global keyword that could be set for some entries", + "type": "object", + "properties": { + "guid": { + "type": "string", + "format": "uuid" + }, + "text": { + "description": "Short text that describes the tag", + "type": "string" + }, + "group": { + "description": "Short text that describes the tag", + "type": "string" + }, + "comment": { + "description": "Comment to that tab", + "type": "string" + }, + "color": { + "description": "color that marks this tag", + "type": "string" + }, + "created": { + "type": "string", + "format": "datetime" + }, + "active": { + "type": "boolean" + }, + "internal": { + "type": "boolean" + } + }, + "required": [ + "guid", + "text" + ], + "version": 1 +} diff --git a/src/test/resources/test_schemas/ds/shared/tenant.json b/src/test/resources/test_schemas/ds/shared/tenant.json new file mode 100644 index 0000000..1c46c65 --- /dev/null +++ b/src/test/resources/test_schemas/ds/shared/tenant.json @@ -0,0 +1,34 @@ +{ + "id": "http://schlothauer.de/schemas/shared/tenant.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Tenant of a running application.", + "type": "object", + "properties": { + "name": { + "description": "Tenant name", + "type": "string", + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", + "__tags": ["notNull","unique"] + }, + "description": { + "description": "some words to explain", + "type": "string" + }, + "guid": { + "description": "global identifier", + "type": "string", + "format": "uuid", + "__tags": ["notDisplayed", "notNull"] + }, + "active": { + "description": "is this entry still active", + "type": "boolean", + "__tags": ["notNull","defaultTrue"] + } + }, + "required": [ + "name", + "guid" + ], + "__version": 1 +} diff --git a/src/test/resources/test_schemas/ds/user.json b/src/test/resources/test_schemas/ds/user.json index 94682ed..d59d744 100644 --- a/src/test/resources/test_schemas/ds/user.json +++ b/src/test/resources/test_schemas/ds/user.json @@ -18,6 +18,15 @@ "format": "uuid", "__ref": "#/definitions/user" }, + "domains": { + "description": "dummy to test UUID arrays", + "type": "array", + "items": { + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json" + } + }, "type": { "description": "type of the entry", "type": "string", @@ -109,7 +118,8 @@ "read", "write", "commit" - ] + ], + "__enumName": "GrantsEnum" } }, "__tags": [ @@ -140,10 +150,22 @@ "write", "commit" ], + "__enumName": "GrantsEnum", "__tags": [ "one", "two" ] + }, + "byteTest": { + "type": "integer", + "format": "byte" + }, + "byteArrayTest": { + "type": "array", + "items": { + "type": "integer", + "format": "byte" + } } } } @@ -152,9 +174,10 @@ "description": "is this entry still active", "type": "boolean" } - } + }, + "__version": 2 } }, "type": "object", - "version": 1 + "__version": 3 } diff --git a/src/test/resources/test_schemas/ds/user_different_enum_values.json b/src/test/resources/test_schemas/ds/user_different_enum_values.json new file mode 100644 index 0000000..e4fe16d --- /dev/null +++ b/src/test/resources/test_schemas/ds/user_different_enum_values.json @@ -0,0 +1,175 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "User model", + "description": "Test user model", + "definitions": { + "user_log": { + "type": "object", + "properties": { + "domain_id": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json" + }, + "user_id": { + "description": "What user owns this entry", + "type": "string", + "format": "uuid", + "__ref": "#/definitions/user" + }, + "type": { + "description": "type of the entry", + "type": "string", + "enum": [ + "login", + "logout" + ] + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + } + } + }, + "user": { + "type": "object", + "properties": { + "domain_id": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json" + }, + "name": { + "description": "login name of the user", + "type": "string", + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + }, + "real_name": { + "description": "used to display the user in applications", + "type": "string", + "__visKey": true + }, + "gid": { + "type": "string", + "format": "uuid" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/role" + } + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + } + }, + "__tags": [ + "five", + "six", + "seven" + ] + }, + "role": { + "type": "object", + "properties": { + "domain_id": { + "description": "what is the related domain", + "type": "string", + "format": "uuid", + "__ref": "./shared/domain.json" + }, + "name": { + "description": "Name of the role", + "type": "string", + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + }, + "comment": { + "description": "the common field contain all unmodeled stuff", + "type": "string" + }, + "module_grants": { + "type": "array", + "items": { + "type": "object", + "description": "what is granted to do with specific modules", + "properties": { + "module": { + "$ref": "./shared/app_module.json" + }, + "grant": { + "type": "string", + "enum": [ + "read", + "write", + "commit", + "foo" + ], + "__enumName": "GrantsEnum" + } + }, + "__tags": [ + "five", + "six", + "seven" + ] + }, + "__tags": [ + "one", + "two" + ] + }, + "data_grants": { + "type": "array", + "items": { + "type": "object", + "description": "what is granted to do with specific modules", + "properties": { + "data_path": { + "description" : "dummy string to define some data", + "type": "string" + }, + "grant": { + "type": "string", + "enum": [ + "read", + "write", + "commit" + ], + "__enumName": "GrantsEnum", + "__tags": [ + "one", + "two" + ] + }, + "byteTest": { + "type": "integer", + "format": "byte" + }, + "byteArrayTest": { + "type": "array", + "items": { + "type": "integer", + "format": "byte" + } + } + } + } + }, + "active": { + "description": "is this entry still active", + "type": "boolean" + } + }, + "__version": 2 + } + }, + "type": "object", + "__version": 3 +} diff --git a/src/test/resources/xsd/LSA_Versorgung_OMTC.xsd b/src/test/resources/xsd/LSA_Versorgung_OMTC.xsd new file mode 100644 index 0000000..79ce1f7 --- /dev/null +++ b/src/test/resources/xsd/LSA_Versorgung_OMTC.xsd @@ -0,0 +1,3435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 250 Zeichen String + + + + + + + + + 10 Zeichen String + + + + + + + + + String zur eindeutigen Bezeichnung (32 Zeichen lang) + + + + + + + + + Zeitangaben als Dezimalzahl mit max 2 Stellen nach dem Komma + + + + + + + + + + + Laengenangaben als Dezimalzahl mit max 2 Stellen nach dem Komma + + + + + + + + logisches OCIT Farbbild (AESignalBild) laut OCIT-O-Lstg_V1.0. Die üblicherweise verwendeten Signalbilder werden durch "lesbare" Strings dargestellt (rot, rotgelb, gelb, gruen, dunkel, rotgruen, gelbgruen, rotblk, gelbblk, gruenblk, wbl_rotgruen, wbl_rotgelb, wbl_gelbgruen, rotblk2hz, gelbblk2hz, gruenblk2hz, wbl2hz_rotgruen, wbl2hz_rotgelb, wbl2hz_gelbgruen oder ein Hexcode, z.B. 0A) + + + + + + + + Muster, um Signaluebergänge standardisiert (und lesbar) zu bezeichnen. Beispiel: "gruen_3sgelb_rot" oder "gruen_3sgruenblk_3sgelb_rot"". Das Muster wird durch die Regular-Expression rudimentär geprüft, ob das Muster inhaltlich zum Signaluebergang paßt, muß getrennt geprüft werden. + + + + + + + + Detektorbauart + + + + + + + + + + + + + + + + + + + + + + + + Header für die Objekte + + + + + Kundenbezeichnung des Objektes, wie es an der Oberfläche dargestellt wird. Die Bezeichnung innerhalb einer Liste muß eindeutig sein. Bezeichnungen dürfen doppelt vorhanden sein, wenn sie sich auf Objekte unterschiedlichen Typs beziehen. Beispiel: Zwei Signalgruppen der Bezeichnung "A" sind verboten, ein Detektor und eine Signalgruppe mit dem Namen "A" sind hingegen erlaubt. + + + + + OCIT-Nummer, falls das Objekte eine OCIT-Nr hat,ansonsten logische Nummer. In Planungssituationen, in denen die Nummer noch nicht festgelegt wird, wird das Feld weggelassen. Die Nummer muß größer oder gleich 1 sein. + + + + + + + + + + Zeitpunkt der letzten Änderung. Wenn der Zeitpunkt nicht zur Verfügung steht wird das Feld weggelassen. + + + + + + Benutzer, der dieses Objekt als letztes bearbeitet hat. + + + + + Zeitstempel der letzten Änderung. + + + + + + + + Bemerkungen zu diesem Objekt. + + + + + + + Bemerkungsliste + + + + + + Bemerkungszeile. Die Zeile kann maximal 250 + Zeichen lang sein, leere Zeilen sind erlaubt. + + + + + + + + + + + + + + + + + + + Zwischenzeitmatrix + + + + + + + Matrix ist kein Geräteelement, dient nur zur Dokumentation + + + + + Eintrag in der Zwischenzeitmatrix + + + + + + Identifiziert die räumende Signalgruppe (= Zeile) + + + + + Identifiziert die einfahrende Signalgruppe (= Spalte) + + + + + Zeit in Sekunden. + + + + + + + + + + + + Versatzzeitenmatrix + + + + + + + Matrix ist kein Geräteelement, dient nur zur Dokumentation + + + + + Versatzbedingungen zwischen +- Beginn -> Beginn +- Ende -> Ende +- Beginn -> Ende + + + + + + + + + + + + Eintrag in der Versatzzeitmatrix. Der Eintrag bezieht sich immer auf das Verhältnis der Abhängigen Signalgruppe zur Basissignalgruppe. Beispiel: Bei Art = "BeginnBeginn" SGrBasis = "Bas", SGrAbhaengig = "Abh", Wert = 12 und Operator = "gleich" muß der Beginn der Freigabezeit der Signalgruppe "Abh" 12 Sekunden hinter der Signalgruppe "Bas" liegen. + + + + + + Basissignalgruppe + + + + + Abhängige Signalgruppe + + + + + Versatzzeit in Sek mit max. 2 Nachkommastellen. Kann negativ sein!!! + + + + + Operator: Bei gleich muß der Wert exakt eingehalten werden, bei kleinergleich muß der Wert kleiner oder gleich dem angegebenen Wert sein und bei groessergleich größer oder gleich dem angegebenen Wert. +(mathematische Sichtweise z.B. -6 kleinergleich -5) + + + + + + + + + + + + + + + + + + + Typ für Signalzustände + + + + + Farbbild des Standard-Signalsignalbilds. + + + + + Zusätzliche erlaubte Farbbilder. Auch alle Übergangs-Signalbilder (z.B. grünblinken, gelb, rotgelb) sind hier eingetragen!!! + + + + + + + Übergangsliste für Signalgruppe + + + + + Teil eines Übergangs zwischen verschiedenen Sicherungszuständen. Die meisten Übergänge bestehen nur aus einem Element, manche Übergänge können jedoch aus mehreren Übergängen bestehen. Es ist möglich, das die einzelnen Elemente eines Übergangs einen unterschiedlichen verkehrstechnischen Zustand haben (Beispiel Österreich: Grünblinken frei, Gelb gesperrt) Es ist nicht erlaubt, innerhalb eines Übergangs den Verkehrstechnischen Zustand mehr als einmal zu wechseln (Grün/Gelb/Rot/Rotgelb/Dunkel ist beispielsweise kein gültiger Übergang, sondern muß als zwei Übergänge Grün/Gelb/Rot und Rot/Rotgelb/Dunkel codiert werden.) + + + + + + + Ein/Aus-Signalplan + + + + + + + Programm ist kein Geräteelement, dient nur zur Dokumentation + + + + + "Umlaufzeit" des Ein- bzw. Ausschaltplans + + + + + Zeitpunkt, an dem die Signalsicherung ein- bzw. ausgeschaltet wird. Offen: Dokumentation, was vor bzw. nach dem Signalsicherungszeitpunkt anders ist. + + + + + Pro Signalgruppe wird hier eingetragen, wie der Signalbildverlauf ist. + + + + + + Bezeichnung der Signalgruppe, die geschaltet wird. + + + + + Das StartSignalbild zeigt an, welches Signalbild vor Beginn des EA-Plans für die Signalgruppe gültig ist. Das Start-Signalbild wird vor allem zur Anzeige in einem Planungstool verwendet, da Einschaltpläne aus unterschiedlichen Auszuständen aktiviert werden. Bei Ausschaltplänen muß das Farbbild mit dem Farbbild übereinstimmen, das die SIgnalgruppe am AP-Zeitpunkt innerhalb des Signalplans hat. + + + + + Schaltung der Signalgruppe in ein neues Farbbild + + + + + + Zeitpunkt, an dem die Farbe beginnt (0.. Dauer) + + + + + OCIT-Signalbild +Im E/A-Plan werden keine Übergänge benutzt. Die Schaltzeiten sind die Farbfolge bzw. Übergang+Zielfarbbild.. + + + + + + + + + + + + + + + + + + + Schaltzeit für Signalgruppe + + + + + + Zeitpunkt, an dem die Farbe beginnt (0..TU bzw. Dauer) + + + + + + OCIT-Farbe des Ziel-Zustands. Übergangszustände dürfen hier nicht eingetragen werden. Wenn zwischen zwei Farben ein Zustandswechsel eintritt wird der Anwurf bzw. Abwurf automatisch dazwischengelegt. Welcher Übergang verwendet wird, ergibt sich aus dem Signalplan/Phasenübergang innerhalb dessen dieser Übergang geschaltet wird. + + + + + Alternativ zur OCIT-Farbe kann ein Befehl(Übergang) aus der zugehörigen Signalgruppe direkt angegeben werden. + + + + + + + Alternativ zur OCIT-Farbe kann der Zeitpunkt des Anwurf-Befehls direkt angegeben werden. + + + + + Alternativ zur OCIT-Farbe kann der Zeitpunkt des Abwurf-Befehls direkt angegeben werden. + + + + + + + Programmwunsch der Schaltuhr + + + + + Verkehrsabhängigkeit Ein/Aus +wenn nicht anggeben = unbeeinflusst + + + + + ÖPNV-Beeinflussung Ein/Aus +wenn nicht anggeben = unbeeinflusst + + + + + IV-Beeinflussung Ein/Aus +wenn nicht anggeben = unbeeinflusst + + + + + Signalplan, in den gewechselt werden soll. + + + + + + + Wochenplan für Schaltuhr + + + + + Bezeichnung des Wochenplans zur Referenzierung + + + + + Referenz auf den Tagesplan, der Montag verwendet wird + + + + + Referenz auf den Tagesplan, der Dienstag verwendet wird + + + + + Referenz auf den Tagesplan, der Mittwoch verwendet wird + + + + + Referenz auf den Tagesplan, der Donnerstag verwendet wird + + + + + Referenz auf den Tagesplan, der Freitag verwendet wird + + + + + Referenz auf den Tagesplan, der Samstag verwendet wird + + + + + Referenz auf den Tagesplan, der Sonntag verwendet wird + + + + + + + Befehl für Tagesplan + + + + + (sekundengenaue) Uhrzeit, an dem der Befehl begonnen wird. Zu einem Zeitpunkt können mehrere Befehle begonnen werden, die gleiche Uhrzeit kann also mehrfach auftreten. + + + + + + mögliche Befehle + + + + + + Programmwunsch (Wechsel in neuen Signalplan) + + + + + + + Einschalten von Teilknoten oder komplett + + + + + + + + Teilknoten, die geschaltet + werden sollen + + + + + + + + + + AusGbb von Teilknoten oder des gesamten + Gerätes + + + + + + + AllesAus von Teilknoten oder des gesamten + Gerätes + + + + + + + AllesGbb von Teilknoten oder des gesamten + Gerätes + + + + + + AusGbbNR von Teilknoten oder des gesamten Gerätes + + + + + + Gerätespezifische Befehle. + + + + + + + + Name des Gerätespezifischen + Befehls. Welche Befehle möglich + sind, ist geräteabhängig + + + + + + + weitere Parameter des Befehles + mit Name und Wert. Die Parameter + sind befehlsabhängig. + + + + + + + + Name des Parameters + + + + + + + Wert des Parameters + + + + + + + + + + + + + Bei koordinierten Betrieb erfolgt entspechend des Rückrechenverfahrens eine Synchronisation des Programmzeitzählers. + + + + + + + + + + + Nummer der Modifikation + + + + + Wert der Modifikation + + + + + + + + + + + + + + + + + + + + Zeitbereich mit Beginn - Ende + + + + + Beginn des Zeitbereiches in Sekunden + + + + + Ende des Zeitbereiches in Sekunden + + + + + + + Bereiche eines Rahmens(Vorberitung, Anforderung...) + + + + + + + + + + + + + + + + Version des Datenformates. Für die aktuelle Version: 3 Bei späteren Versionen muß maxIncl entsprechend heraufgesetzt werden. + + + + + + + + + + + Information über den Knoten. + + + + + Detektoren zur Erfassung des Individualverkehrs und sonstige digitale Eingänge + + + + + Parameter der verkersabhängigen Logik + + + + + Allgemeine Angaben zum Steuergerät + + + + + Liste von ÖV-Meldepunkten und ÖV-Strecken + + + + + Liste aller Phasen. Es sind nur verkehrstechnisch zulässige Phasen erlaubt, bei denen die Feindlichkeitsmatrix nicht verletzt wird. + + + + + Liste aller Phasenübergänge + + + + + Liste der Phasenübergangsmatrizen + + + + + Rahmenprogramme + + + + + + + + + Versorgungen zum Themenkomplex Schaltuhr bzw. Zeitautomatik der LSA. + + + + + Liste aller Signalgeber und Maste der Außenanlage + + + + + Liste aller von der Signalsicherung überwachten Ausgänge. + + + + + Liste der Signalprogramme +(Festzeitpprogramme und VA-Programme) + + + + + Liste der verkehrtechnischen und Hardwareteilknoten +existiert nur 1 Teilknoten, kann die Liste entfallen + + + + + Liste aller nicht von der Signalsicherung überwachten Ausgänge(z.B. Quittungssignale) + + + + + Unverträglichkeitsmatrix + + + + + + + + + + Liste aller Versatz-Matrizen. Alle Versatzzeitmatrizen werden in der gleichen Liste gespeichert und nur nach der Art unterschieden. + + + + + Liste aller Zwischenzeitmatrizen, mindestens der Sicherheitsmatrix + + + + + + Zusätzliche Daten +Die Daten sind beliebig strukturiert, müssen aber mit einem Präfix in der XML-Datei gekennzeichnet sein. D.h., es muss ein XML-Namensraum definiert werden. + + + + + + + + + Erlaubt/Nicht erlaubt + + + + + + + Beginn des Zeitbereiches in Sekunden + + + + + Ende des Zeitbereiches in Sekunden + + + + + + + + + + + + + + + + + + + Zugeordnete Phase für diese Rahmenzeit + + + + + Zugeordnete Signalgruppe für diese Rahmernzeit + + + + + Zugeordneter Phasenübergang + + + + + Zusatzrahmen für diese Rahmenzeit + + + + + + Maximaldauer, unabhängig von der Rahmenzeit + + + + + Bereiche für IV + + + + + Bereiche für ÖV + + + + + + + + + + Signalgruppe,deren Phasenübergang dargestellt wird. Die gleiche Sgr darf nur einmal pro PUeElementListe auftreten. + + + + + OCIT-Farbe des Start-Zustands, mit dem der Phasenübergang angezeigt wird. Der Eintrag darf nur gesetzt sein, wenn keine StartPhase angegeben ist! Wenn der Phasenübergang geschaltet wird, ohne dass die Signalgruppe diese Startfarbe anzeigt, ist das Verhalten undefiniert und herstellerabhängig. Wenn das StartSignalbild fehlt wird als Ersatz die Farbe der Signalgruppe in der StartPhase verwendet. + + + + + Schaltungen im Phasenübergang + + + + + + + + + Teilknoten, die geschaltet werden sollen + + + + + + + + + Siehe OCIT-Element DigEingang. +Sowohl digital angeschlossene Detektoren als auch seriell angeschlossene Detektoren als auch gleichzeitig seriell UND digital angeschlossene Detektoren werden hier eingetragen. Die seriel erfassten Daten werden auf einem DigEingang oder mehreren DigEingängen abgebildet. Wenn eine Schleife auf mehrere Detektoren abgebildet wird, werden auch mehrere DigEingänge eingetragen. + + + + + + + + + + + Hier wird gekennzeichnet, ob der Detektor als IV-Detektor verwendet wird. + + + + + Bauart des Detektors. Wenn nicht gesetzt ist die Bauart unbekannt. + + + + + Werte, die der Detektor erfassen soll. Wenn die Werte nicht gesetzt sind, sind die Werte auch nicht gefordert + + + + + + des Detektors im 10ms Zyklus + + + + + Fz/h + + + + + % pro Intervalldauer aus dem OCIT Auftrag + + + + + + + + + + + Wenn gesetzt, wird der Detektor einer oder mehreren Signalgruppen zugeordnet. Wenn nicht gesetzt, ist keine spezielle Zuordnung bekannt. + + + + + + + + + Löschsekunde Anforderung + + + + + Mindestbelegung zur Erkennung einer Anforderung + + + + + Alle Flanken, die in kleineren Abständen als die Prellzeit kommen, werden ignoriert + + + + + + + + Referenz auf Mast + + + + + + + + + + + + + + + + + + + + + + + + + + + zugeordnete Signalgruppe + + + + + + + + + + Referenz zum ÖV-Meldepunkt + + + + + + + Funktion des Meldepunktes + + + + + + + + + + + + + + + + + theoretische Fahrzeit bis + zur Abmeldung + + + + + + + Abstand zur HL in m + + + + + + + Zwangslöschzeit in Sekunden + + + + + + + Anmeldeverzögerung in + Sekunden + + + + + + + + + + Linie/Route/Kurs, die der Strecke + zugeordnet sind. + + + + + + + + Liniennummer laut + Funktelegramm + + + + + + + Routennummer laut + Funktelegramm + + + + + + + Kursnummer laut + Funktelegramm + + + + + + + + + + Richtung Hand des ÖV-Telegramms, die der + Strecke zugeordnet ist + + + + + + + + + + + + + + Max. Anzahl von verwalteten Fahrzeugen + Defaultwert ist 4 + + + + + + + + + + + + + + + + + Es sind nur verkehrstechnisch zulässige Phasen erlaubt, bei denen die Feindlichkeitsmatrix nicht verletzt wird. + + + + + + + + + + + + + Phasenübergang. Es ist nicht notwendig, dass ein Phasenübergang eine Start- und Zielphase besitzt, falls dies im verkehrstechnischen Verfahren nicht gefordert ist. Es sind zusätzlich Phasenübergänge möglich, die erst im Feldgerät automatisch berechnet werden. + + + + + + + + + In der Phasenübergangsmatrix werden die erlaubten Phasenübergänge aufgeführt. + + + + + + + + + + + + + + + + + + + + + Meldepunktnummer + + + + + + wenn Aktivierung durch DigEingang + + + + + + Linie/Route/Kurs, die dem Meldepunkt zugeordnet sind. + + + + + + Liniennummer laut Funktelegramm + + + + + Routennummer laut Funktelegramm + + + + + Kursnummer laut Funktelegramm + + + + + + + + Richtung Hand des ÖV-Telegramms, die dem Meldepunkt zugeordnet ist + + + + + + + + + + + + + + + + + + + + + + Phase ist kein Geräteelement, dient nur zur Dokumentation + + + + + wenn nicht angegeben keine Einschaltphase + + + + + wenn nicht vorhanden alle Eigenschaften= true + + + + + + Phase für VA-Steuerung + + + + + Phase für Handsteuerung + + + + + + + + Signalbilder der Signalgruppen. Es ist nicht notwendig, das alle Signalgruppen aufgeführt sind. Signalgruppen, die hier nicht aufgeführt sind, werden von der Phase nicht erfaßt. + + + + + + Signalgruppe, die zur Phase gehört. Signalgruppen, die in der Phase undefiniert sind, werden hier nicht aufgeführt.. + + + + + Farbbild, welches die Signbalgruppe annimmt. + + + + + + + + + + + + + + + + Übergang ist kein Geräteelement, dient nur zur Dokumentation + + + + + Startphase, aus der der Phasenübergang heraus startet. + + + + + Zielphase, in die der Übergang wechselt. Die Zielphase muß kompatibel zu den Schaltinformationen und der Startphase sein. Es ist nicht zulässig, das Signalgruppe "S1" in Rot wechselt und in der Zielphase "Gruen" eingetragen ist. + + + + + Wenn die Zielphase vor Ende des PÜs gesetzt werden soll, kann hier eine zeit eingetragen werden + + + + + Diese Übergänge werden nur benötigt, wenn die Standardübergänge nicht ausreichen oder überschrieben werden sollen. Achtung: Die Übergänge beziehen sich nur auf den PÜ selbst und haben die höchste Priorität. Sie überschreiben auch Übergänge, die im Signalplan schon überschrieben wurden. + + + + + + Verweis auf die Signalgruppe, deren Übergang umdefiniert wird. + + + + + nur Angabe, wenn nicht Standard-An/Abwurfübergang. Diese Übergänge werden mit höherer Priorität verwendet als die Standard-Übergänge, falls die Farbfolge überschrieben wird. Das Start- und Zielfarbbild kann dem Übergang entnommen werden. Es ist pro Signalgruppe und Start/Zielfarbild-Kombination nur ein Übergang erlaubt. + + + + + + + + + Schaltbefehle des Phasenüberganges + + + + + + Dauer des Phasenübergangs. Die Dauer muß größer oder gleich der Schaltzeit des letzten PUeElementes sein. Es ist nicht notwendig, das zum Zeitpunkt der Dauer bereits alle Übergänge abgeschlossen sind! + + + + + Einzelner Schaltbefehe des Phasenübergangs. Welche Übergänge für den Wechsel von einem Zielfarbbild in das nächste Zielfarbbild verwendet werden, wird im Signalprogramm festgelegt, innerhalb dessen der Phasenübergang verwendet wird. Der Übergang kann in Sonderfällen auch im Phasenübergang selbst überschrieben werden. + + + + + + + + + + + + + + + + Bildung wird gesetzt, wenn der Phasenübergang vom Feldgeraet (bzw der VA auf dem Feldgeraet) dynamisch gebildet wird. Bildung gibt die Kritrien an, nach dem der PÜ automatisch gebildet wird. + + + + + + + + + + + + + + + + + + + + + Matrix ist kein Geräteelement, dient nur zur Dokumentation + + + + + Erlaubter Übergang von einer Phase zur nächsten + + + + + + Referenz zu einer Phase + + + + + Referenz zu einer Phase + + + + + + + + + + + + + + Name des Feier- bzw. Sondertags. + + + + + + festes Datum des Tages (ohne Jahresangabe) + + + + + Offset zum (gregorianischen) Ostersonntag in Tagen. 0 = Ostersonntag. + + + + + + Jahr, in dem der Eintrag gültig ist. Wenn der Wert fehlt, ist der Eintrag immer gültig. + + + + + + + + + + + Kurzbezeichnung des Tagesplans, der an diesem Tag geschaltet werden soll. + + + + + Prioritaet der Verwendung. Größere Zahlen haben eine höhere Priorität. 1=Normale Feiertage, 2=Normale Sondertage, 3..9=Hochpriore Sondertage (zur Zeit nicht von jedem Hersteller unterstützt) - Zwei Tage gleicher Priorität mit unterschiedlichem Tagesplan, die auf dasselbe Datum fallen führen zu einer Ablehnung der Versorgung. Wenn ein Tag in einem Sonderbereich gleicher Priorität liegt, wird dies ebenfalls zurückgewiesen. + + + + + + + + + + + + + + + Name des Zeitbereiches + + + + + Beginn des Zeitbereiches + + + + + Ende der Zeitbereiches + + + + + Jahr, in dem der Zeitbereich gültig ist. Wenn der Wert fehlt, ist der Bereich jedes Jahr gültig. + + + + + + + + + + + Kurzbezeichnung des Wochenplans, der zum Einsatz kommt. + + + + + Prioritaet der Verwendung. 0=normaler Sonderbereich 1..9=höherpriore Sonderbereiche (zur Zeit nicht von jedem Hersteller unterstützt) Zwei Zeitbereiche, die sich überschneiden und auf einen unterschiedlichen Wochenplan verweisen, sind nicht zulässig. Ein Zeitbereich, der die gleiche Priorität wie ein Sondertag hat und der sich mit dem Sondertag überschneidet ist nicht zugelassen. + + + + + + + + + + + + + + + Signalbild, den das Element im Übergang hat. + + + + + Dauer in Sekunden, den das Element im Übergang hat. + + + + + + + + + + + + + + + + + + + + + Liste aller Maste + + + + + Bemerkungen zu dieser Liste, die nicht einem Objekt selbst zugeordnet sind. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Im Normalfall ist ein Signalgeber genau einer Signalgruppe zugeordnet. In diesem Fall wird lediglich SgBezeichnung gesetzt. In Sonderfällen können die einzelnen Kammern des Signalgebers von unterschiedlichen Signalgruppen und/oder unüberwachten Ausgängen zugeordnet sein. In diesem Fall werden die Kammer-Einträge verwendet. + + + + Bezeichnung der zugehörigen Signalgruppe, wenn der Geber vollständig einer Signalgruppe zugeordnet ist. + + + + + Signalgeberkammern von oben nach unten bzw. von links nach rechts geordnet + + + + + + Kammer wird von einer Signalgruppe angesteuert + + + + + + Bezeichnung der zugehörigen Signalgruppe + + + + + Rot, Gelb, Gruen + + + + + + + + + + + + SigGeberNr, wenn mehrere existieren. + + + + + + + + + + + + + Kammer wird von einem unüberwachtem Ausgang angesteuert + + + + + + + + + + + + Referenz auf Mast + + + + + z.B.: Stand, Ausleger + + + + + + + + + + + + + + + + + + + + z.B.: Stand, Ausleger + + + + + + + + + Bezeichnung des Parametertyps + + + + + GUID des Parametertyps + + + + + Strukturbeschreibung der Parameter (einer Parametertabelle) + + + + + + + Maximale Anzahl der Records + + + + + + + + Einzelner Parameter(Tabellenspalte) + + + + + + + + Bezeichner/ + Tab-Spaltenname + + + + + + + + + float, word... + + + + + + + + + + + + + + + + + Auswahl + entsprechend Typ + Defaultwert, + wenn kein + Eintrag im + Parametersatz + Werte, die den + Defaultwert + besitzen sollten + nicht noch + einmal bei den + Parametersatzwerten + eingetragen + werden + + + + + + byte + + + + + + + word + + + + + + + integer + + + + + + + float + + + + + + + string + + + + + + + boolean + + + + + + + + + Elemente aus + Versorgorgung + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Werte eines Parametersatzes + + + + + + Bezeichnung des Parametersatzes + + + + + Nr. des Parametersatzes + + + + + + + + + + Alle Werte eines Records(Tabzeile) + + + + + + + + + + Referenz auf Tabellenspalte + + + + + + byte + + + + + word + + + + + integer + + + + + float + + + + + string + + + + + boolean + + + + + Referenz auf ein Element + + + + + + + + + + + + + + + + + + + Alle Daten eines Parametertyps + + + + + Zeitzaehler + + + + + + + + + Zykluszeit in ms + + + + + + + + + + + Rückrechenverfahren für Koordinierung + + + + + + + + + + + + + + + Meldepunkte zur ÖV-Erfassung + + + + + + + Erfassungsstrecken von der Voranmeldung bis zur + Abmeldung + + + + + + + + + Max. Anzahl von verwalteten + Fahrzeugen Defaultwert ist 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Programm ist kein Geräteelement, dient nur zur Dokumentation + + + + + + 1 Rahmen sind mehrere Zeitbereiche im Signalprogramm, die zusammen gehören + + + + + + + + + + + + + + + Programmschaltzeiten, die zu Tagesplänen zusammengefaßt sind. + + + + + + 1 Standardtagesplan muss immer vorhanden sein (Name: "Standard"). Es ist möglich, das dieser Tagesplan keine Befehle enthält. + + + + + + + + + + + + + + weitere Tagespläne + + + + + + + + + + + + + + + + + + + Zuletzt wird hier gesucht. Es ist mindestens ein StandardWochenplan vorhanden, der immer dann ausgeführt wird, wenn keine andere Regelung greift. + + + + + + Jeder Tag referenziert einen Tagesplan aus der Tagesplanliste. Dieser Wochenplan muss immer vorhanden sein. Der Standard-Wochenplan hat immer die Bezeichnung "Standard" + + + + + Zusätzliche Wochenpläne z.B. für Ferien. Diese Wochenpläne werden nie per Default ausgewählt. + + + + + + + + + + + + + + Wenn nur 1 Teilknoten existiert, kann die Angabe entfallen. Existieren mehrere Teilknoten muss dieser hier angegeben erden. + + + + + true, wenn die Signalgruppe in der Hauptrichtung liegt. +z.B. bei Verkehrszeichen "Vorfahrtstraße" + + + + + Wird auf "StandardGelbblinken" gesetzt, wenn beim LSA-Gerätezustand "Nebenrichtung GelbBlinken" das Farbbild Gelbblinken eingetragen wird. Wird auf "StandardDunkel" gesetzt, wenn beim LSA Zustand "Nebenrichtung Gelbblinken" das Farbbild "Dunkel" verwendet wird. + + + + + + + + + + + Alle erlaubten Farbbilder werden hier definiert. Alle Farbbilder, die hier nicht definiert sind, sind nicht zulässig. Jeder Zustand wird eindeutig durch das OCIT-Farbbild gekennzeichnet. D.h. ein OCIT-Farbbild darf nicht mehrfach auftreten. +Bei Zuständen mit gleichem Sicherungszustand erfolgt ein direkter (kein bildbehafteter) Übergang. +Alle Übergänge müssen in den Elementen +- AnwurfUebergang +- AbwurfUebergang +- ZusatzUebergang +definiert werden. + + + + + + alle Signalzustände im Normalbetrieb, bei dem der Sicherungszustand "frei" ist. + + + + + alle Signalzustände im Normalbetrieb, bei dem der Sicherungszustand "gesperrt" ist. + + + + + Signalbild, das beim Gerätezustand "Aus" benutzt wird. Achtung: Wenn das hier eingetragene Signalbild auch als außerhalb der Ein-/Ausschaltpläne benutzt wird, muß es zusätzlich unter "Frei" bzw. "Gesperrt" eingetragen sein. + + + + + Signalbild, das beim Gerätezustand "GelbBlinken Nebenrichtung" benutzt wird. Achtung: Wenn das hier eingetragene Signalbild auch als außerhalb der Ein-/Ausschaltpläne benutzt wird, muß es zusätzlich unter "Frei" bzw. "Gesperrt" eingetragen sein. . Wenn nicht gesetzt, wird das unter StandardAusDunkel eingetragene Signalbild angenommen. + + + + + Hier werden alle Farbbilder aufgelistet, die zur Ermittlung der Mindest-Frei/Gespert-Zeit herangezogen werden. + + + + + + + + + + + + Mindestfreigabe. Welche Farbbilder dazugehören wird in DauerUebFb definiert. + + + + + Maximalfreigabe. Welche Farbbilder dazugehören wird in DauerUebFb definiert. + + + + + Minimale Sperrzeit. . Welche Farbbilder dazugehören wird in DauerUebFb definiert. + + + + + Maximale Sperrzeit. . Welche Farbbilder dazugehören wird in DauerUebFb definiert. + + + + + Ein Anwurf ist der Übergang vom Signalzustand Gesperrt nach Frei. Wenn kein Anwurfuebergang definiert ist, wechselt das Signal ohne Übergangsbilder von Gesperrt nach Frei. + + + + + Ein Abwurf ist der Übergang vom Signalzustand Frei nach Gesperrt Wenn kein Anwurfuebergang definiert ist, wechselt das Signal ohne Übergangsbilder von Frei nach Gesperrt. + + + + + Alle erlaubten Übergänge zwischen Endzuständen einer Signalgruppe werden hier eingetragen. Andere Folgen von Übergängen zwischen Endzuständen sind weder im Festzeitprogramm noch in Phasenüergängen zulässig. Wenn zwischen zwei bestimmten Farbbildern nicht der Standardanwurf verwendet werden soll, muss im Signalprogramm (bzw. im Phasenübergang) auf einen speziellen Anwurf verwiesen werden, der hier als Zusatzübergang definiert ist. Dies gilt sowohl für andere "Gelb"-Zeiten als auch für andere Signalbilder, die für den Übergang eingesetzt werden. +Einschränkung: Wenn das Startlfarbbild den gleichen Sicherungszustand wie das Zielfarbbild hat, dürfen keine Übergangselemente eingetragen werden. +Übergänge mit gleichem Startfarbbild und Zielfarbbild müssen die gleiche Farbfolge besitzen. + + + + + + Name, über die dieser im Signalprogramm ausgewählt wird. Der Name wird künstlich gebildet, um die Referenz besser zu verstehen. Sie beginnt mit dem Start-Farbbild, enthält alle Übergangsbilder mit ihrer Dauer und endet mit dem End-Farbbild. Beispiel: Übergang on Dunkel nach Rot mit 2 Sekunden gelb wird bezeichnet als dunkel_2sgelb_rot. +Die Bezeichnungen 'Anwurf' und 'Abwurf' dürfen nicht verwendet werden + + + + + Hier wird das Startfarbbild angegeben. +Wenn das Startfarbbild darf den gleichen Sicherungszustand wie das Zielfarbbild hat, darf kein Uebergang-Element vorhanden sein. + + + + + Hier wird das Zielfarbbild angegeben. +Wenn das Startfarbbild darf den gleichen Sicherungszustand wie das Zielfarbbild hat, darf kein Uebergang-Element vorhanden sein. + + + + + Zwischenfolge, die zwischen einem der Start- und einem der Zielfarbbild Verwendung findet. Ein Uebergang darf nur zwischen Farbbildern gesetzt werden, die einen unterschiedlichen Sicherungszustand haben. + + + + + + + + OCIT-Kammern und Kanäle für die Ansteuerung der Signalgeber + + + + + + Ansteuerung der OCIT-Rot-Kammer + + + + + + Kundenbezeichnung der Kammer, wenn anders als Rot + + + + + Wird von der OCIT-Fehlermeldung und den Signalgebern benutzt + + + + + + + + + + Wird von der OCIT-Fehlermeldung, den Signalgebern und der Lampenüberwachung benutzt + + + + + + + + + + + + + Ansteuerkanäle der OCIT-Gelb-Kammer + + + + + + Kundenbezeichnung der Kammer, wenn anders als Gelb + + + + + Wird von der OCIT-Fehlermeldung und den Signalgebern benutzt + + + + + + + + + + Wird von der OCIT-Fehlermeldung, den Signalgebern und der Lampenüberwachung benutzt + + + + + + + + + + + + + Ansteuerkanäle der OCIT-Gruen-Kammer + + + + + + Kundenbezeichnung der Kammer, wenn anders als Grün + + + + + Wird von der OCIT-Fehlermeldung und den Signalgebern benutzt + + + + + + + + + + Wird von der OCIT-Fehlermeldung, den Signalgebern und der Lampenüberwachung benutzt + + + + + + + + + + + + + + + + Logischer Ausdruck mit and/or und SiGeberNr +z.B.: rt1 or (rt2 and rt3) +(rt|ge|gn)\d+(\s+(and|or)\s+(rt|ge|gn)\d+)* + + + + + + + + + + + + + + Verweis auf die Zwischenzeitmatrix, + falls nicht die + Sicherheits-Zwischenzeitmatrix verwendet + wird. Wenn die Sicherheits-ZZ-Matrix + verwendet wird, fehlt dieser Eintrag. + + + + + + + Verweis auf die Versatzzeitenmatrizen. + Es ist nur eine Versatzzeitmatrix pro + Typ zulässig. + + + + + + + Verweis auf zugehörige + Phasenübergangsmatrix + + + + + + + Verweis auf zugehöriges Rahmenprogramm + + + + + + + Verweis auf Zugehörigen + Logikparametersatz + + + + + + + + Programm ist kein Geräteelement, dient + nur zur Dokumentation + + + + + + + wird der Typ nicht angegeben gelten die + Defaultwerte + + + + + + + + Festzeitprogramm + + + + + + + Voll verkehrsabhängig + + + + + + + Alternativprogramm + + + + + + + + + + Sondereingriff, wenn vorhanden + + + + + + + + Nr. des Sondereingriffes + + + + + + + + + + Umlaufzeit. Die Umlaufzeit muß in + ganzzahligen Sekunden angegeben sein und + sie muß > 0 sein, da sich alle Zeiten + auf TU beziehen. + + + + + + + + + + + + + Einschaltpunkt, sofern definiert. Der EP + wird weggelassen, falls kein EP + definiert wurde. Der Zeitpunkt TU wird + als TU und nicht als 0 codiert. Nach + Beenden des Einschaltplanes wird in die + EP-Sekunde des Signalplanes gesprungen. + + + + + + + Ausschaltpunkt, sofern definiert. Der AP + wird weggelassen, falls kein AP + definiert wurde. Der Zeitpunkt TU wird + als TU und nicht als 0 codiert. Bei der + Ausschaltung wird von der AP-Sekunde in + den Ausschaltplan gesprungen. + + + + + + + Umschaltpunkt. In Siemens-Terminologie + GSP + + + + + + + Synchronisationspunkte, falls notwendig. + + + + + + + + SY-Vor + + + + + + + SY-Haupt + + + + + + + Maximale Dauer, für die + synchronisiert wird + + + + + + + + + + Versatzzeit bezogen auf die + Versatzzeit-Berechnungsvorschrift des + Gerätes. Der Wert kann auf Geräten mit + unterschiedlichen Versatz-Berechnungen + zu verschiedenen Zeiten führen. Siehe + hierzu auch OCIT-Outstation. + + + + + + + zugehöriges Einschaltprogramm, falls + definiert + + + + + + + Zugehöriges Ausschaltprogramm, falls + definiert. + + + + + + + Diese Übergänge werden nur benötigt, + wenn die Standardübergänge nicht + ausreichen oder überschrieben werden + sollen. Achtung: Die Übergänge beziehen + sich nicht nur auf den Plan selbst + sondern auch auf alle Phasenübergänge + die direkt oder von der VA im Rahmen + dieses Signalplans verwendet werden! + + + + + + + Wenn sich ein Signalplan aus + Phasenübergängen zusammensetzt, werden + nur die Phasenübergangszeitpunkte + gesetzt; bei direkter Auswahl der + Signalbilder werden diese bei der + SPZeile eingetragen. + + + + + + Phasenübergänge, aus denen sich der + Signalplan zusammensetzt. Die + Phasenübergänge können beliebig + zusammengestellt werden und vor + allem beliebig eng aufeinander + sitzen. Es ist ebenfalls möglich, + das mehrere Phasenübergänge zum + gleichen Zeitpunkt gestartet werden! + Dauerfarbbilder werden wie folgt + bestimmt: Wenn in allen + Phasenübergängen das gleiche + Startfarbbild für den Übergang + gesetzt ist (auch ohne Schaltung in + der StartPhase bzw. im + StartSignalbild des Phasenübergangs + möglich), wird dieses als + Dauerfarbbild verwendet. + (Phasenübergänge ohne Startfarbbild + für die entsprechende Signalgruppe + werden ignoriert) Wenn in keinem + Übergang ein Startfarbbild gesetzt + ist oder unterschiedliche + Startfarbbilder zu Anwendung kommen, + liegt ein Fehler vor. + + + + + + + Es ist ausdrücklich zulässig, das + mehrere gleiche Signalbilder + hintereinander stehen können und das + Mindestzeiten und Zwischenzeiten + nicht eingehalten werden müssen. + Diese Fälle müssen von generellen + Import/Export-Filtern 1:1 übernommen + werden. Das Verhalten ist + hersteller- und geräteabhängig und + steht in der Verantwortung des + Planers. + + + + + + + + + + + + + + + + + Referenz auf zugehöriges Signalprogramm + + + + + Stopppunkt-Standardsteuerung ist aktiv + + + + + Stopppunkte zur Handweitertastung über das Bedienteil + + + + + + von hier wird zuerst zum Haupt-Stopppunkt gesprungen + + + + + Der Tx-Zähler des Signalprogramms bleibt hier solange stehen, bis eine Weiter-Taste gedrückt wird + + + + + + + + + + + + Siehe OCIT-Element SignalGruppe. +Pro Signalgruppe wird hier ein Eintrag vorgenommen. + + + + + + + + + allgemeine Bemerkungen zu dieser Liste, die nicht direkt einer speziellen Signalgruppe zuzurechnen sind. + + + + + + + + + Signalprogramm ist die Aufhängung für unterschiedliche Signalprogramme: Echte Festzeitsignalprogramme können auf zwei Arten gespeichert werden: +Als eine Liste von Signalbildern, in die gewechselt wird oder als eine Liste von Phasenübergängen, die zu bestimmten Zeitpunkten beginnen. + + + + + + + + + Im Handbetrieb wird dieses Programm aktiviert. Es muss vorher als Signalprogramm definiert werden. + + + + + Bei Ein- und Ausschaltprogrammen wird anders als bei Signalprogrammen und Phasenübergängen jedes Farbbild direkt gespeichert. Anwürfe und Abwürfe werden in den Ein- und Ausschaltprogrammen nicht ausgeführt. Bei einem Wechsel von Rot nach Grün über RotGelb z.B. wird in einem Signalplan nur "Grün" angegeben, während in einem Ein- und Ausschaltprogramm auch das "Rotgelb" selbst eingetragen wird. + + + + + + + + + Bei Ein- und Ausschaltprogrammen wird anders als bei Signalprogrammen und Phasenübergängen jedes Farbbild direkt gespeichert. Anwürfe und Abwürfe werden in den Ein- und Ausschaltprogrammen nicht ausgeführt. Bei einem Wechsel von Rot nach Grün über RotGelb z.B. wird in einem Signalplan nur "Grün" angegeben, während in einem Ein- und Ausschaltprogramm auch das "Rotgelb" selbst eingetragen wird. + + + + + + + + + + + + + verkehrstechnischer Teilknoten + + + + + + + + + + Binäre Ausgänge, die nicht von der Signalsicherung überwacht werden (Strassenbeleuchtungen, Quittungslampen, Schilder, ...) + + + + + Bemerkungen zu dieser Liste, die nicht einem Objekt selbst zugeordnet sind. + + + + + + + + + + + zugeordneter Hardwareteilknoten + + + + + + + + + + + + + Feindlichkeit zwischen 2 Signalgruppen + + + + + + + + + + + + + + + + + Die in der SicherheitsZwischenzeitmatrix eingetragenen Zeiten werden vom Feldgerät überwacht und führen bei einer Verletzung zur Abschaltung. + + + + + + + + + + Zusätzliche Zwischenzeiten für besondere Signalprogramme (z.B. Schlechtwetter). Alle Werte der GeraeteZwischenzeitenMatrix müssen grösser oder gleich den Werten der Sicherheits-ZwischenzeitenMatrix sein. + + + + + + + + + + + + + + Kurzbezeichnung, maximal 10 Zeichen, z.B. K242 + + + + + Langname des Knotens, max. 250 Zeichen. Beispiel: "Meierstrasse / Muellerstrasse" + + + + + Identifikation des Knotens. Bleibt während der reinen Planungsphase frei, wenn noch keine Identifikation vorliegt. + + + + + + Logische Adressierung des Knotens + + + + + + Stadt, in der der Knoten läuft. Muß nur gesetzt werden, wenn mehrere Städte in einem Projekt gemeinsam vorhanden sind. + + + + + Bezirk, in dem der Knoten läuft. Muß nur in Städten gesetzt werden, die in Bezirke aufgeteilt sind (Beispiel: Wien) + + + + + Stadtweit eindeutige Nummer des Knotens. Die Nummer ist die technische Nummer und hat nichts mit Auftragskennungen etc. zu tun. Muß vor der echten Geräteversorgung gesetzt werden. Darf während der reinen Planungsphase zusammen mit der KnotenId frei bleiben. + + + + + + + + + + + + + Technische OCIT-Kennung des Knotens. Darf nur dann ausgefüllt werden, wenn die Kennung festgelegt ist + + + + + + znr Nummer (entspricht der technischen VSR-Nummer). Siehe OCIT-Dokumentation. + + + + + + + + + + Wird nur gesetzt, wenn der Wert ungleich der Knotennummer gesetzt. Ansonsten ist der Wert defaultmäßig auf knotenNummer gesetzt. + + + + + + + + + + Nur notwenidg, wenn mehr als ein Knoten auf dem Feldgerät vorhanden ist. Ansonsten ist der Wert auf den Default-Wert 1 gesetzt. + + + + + + + + + + + + + Logische Adressierung des Knotenpunktes + + + + + + Stadt + + + + + + + + + + Amts- bzw. Bezirkskennung wie z.B. Wien. + + + + + + + + + + Eindeutige Nummer des Knotenpunktes im Steuerungsgebiet. + + + + + + + + + + + + + + + + Versorgungsprogramm, welches die Datei als letztes bearbeitet hat. + + + + + + Name des Versorgungsprogramm (max 32 Zeichen) + + + + + Programmversion, frei definierbarer Text mit max. 10 Zeichen + + + + + + + + Name des Users, der die letzte Änderung gespeichert hat + + + + + Speicherdatum und Uhrzeit der letzten Änderung + + + + + Planungsversion als String. Format beliebig und abhängig von der Quelle + + + + + Richtlinie nach der der Knoten erzeugt wurde. Bei unbekannter Richtlinie wird das Feld weggelassen. Zur Zeit ist nur "Rilsa92" erlaubt. + + + + + + + + + + Rechner auf dem die Daten der letzten Änderung erstellt wurden. Wenn der Wert nicht zur Verfügung steht, wird der Eintrag weggelassen. + + + + + kundenspezifische Bemerkungen. Wenn keine Bemerkung vorhanden ist, wird das Feld weggelassen. Systeme, die keine Bemerkungen erlauben müssen das Feld unverändert beibehalten. + + + + + + + + + + + Referenz auf verkehrstechnischen Teilknoten + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kundenbezeichnung des Objektes, wie es an der Oberfläche dargestellt wird. Die Bezeichnung innerhalb einer Liste muß eindeutig sein. Bezeichnungen dürfen doppelt vorhanden sein, wenn sie sich auf Objekte unterschiedlichen Typs beziehen. Beispiel: Zwei Signalgruppen der Bezeichnung "A" sind verboten, ein Detektor und eine Signalgruppe mit dem Namen "A" sind hingegen erlaubt. + + + + + OCIT-Nummer, falls das Objekte eine OCIT-Nr hat,ansonsten logische Nummer. In Planungssituationen, in denen die Nummer noch nicht festgelegt wird, wird das Feld weggelassen. Die Nummer muß größer oder gleich 1 sein. + + + + + + + + + + + diff --git a/src/test/resources/xsd/ZEN_Versorgung_OMTC.xsd b/src/test/resources/xsd/ZEN_Versorgung_OMTC.xsd new file mode 100644 index 0000000..ae2a4cd --- /dev/null +++ b/src/test/resources/xsd/ZEN_Versorgung_OMTC.xsd @@ -0,0 +1,845 @@ + + + + + + + + + + + + + + + + + + + + 250 Zeichen String + + + + + + + + + 10 Zeichen String + + + + + + + + + + String zur eindeutigen Bezeichnung (32 Zeichen lang) + + + + + + + + + + + + + + Basistyp für Bezeichner-Objekte + + + + + + + + Zeitpunkt der letzten Änderung. Wenn der + Zeitpunkt nicht zur Verfügung steht wird das + Feld weggelassen. + + + + + + + Bemerkungen zu diesem Objekt. + + + + + + + + Header für die Objekte + + + + + + + + OCIT-Nummer, falls das Objekte eine + OCIT-Nr hat,ansonsten logische Nummer. + In Planungssituationen, in denen die + Nummer noch nicht festgelegt wird, wird + das Feld weggelassen. Die Nummer muss + größer oder gleich 1 sein. + + + + + + + + + + Header für die Objekte + + + + + + + + + + + + Bemerkungsliste + + + + + + Bemerkungszeile. Die Zeile kann maximal 250 + Zeichen lang sein, leere Zeilen sind erlaubt. + + + + + + + + Letzte Änderung + + + + + + Benutzer, der dieses Objekt als letztes + bearbeitet hat. + + + + + + + Zeitstempel der letzten Änderung. + + + + + + + + + + + + + + + + + + + + Information über den Knoten. + + + + + + + + Allgemeine Angaben zum Steuergerät + + + + + + + + + Liste der Signalprogramme (Festzeit- und + VA-Programme) + + + + + + + Detektoren zur Erfassung des Individualverkehrs + und sonstige digitale Eingänge + + + + + + + Liste aller von der Signalsicherung überwachten + Ausgänge. + + + + + + + Liste der Signalprogramme (Festzeit- und + VA-Programme) + + + + + + + Parameter der verkehrsabhängigen Logik + + + + + + + + Zusätzliche Daten Die Daten sind beliebig + strukturiert, müssen aber mit einem Präfix in + der XML-Datei gekennzeichnet sein, d.h. es muss + ein XML-Namensraum definiert werden. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Siehe OCIT-Element DigEingang. Sowohl digital + als auch seriell angeschlossene Detektoren als + auch gleichzeitig seriell UND digital + angeschlossene Detektoren werden hier + eingetragen. Die seriell erfassten Daten werden + auf einem DigEingang oder mehreren DigEingängen + abgebildet. Wenn eine Schleife auf mehrere + Detektoren abgebildet wird, werden auch mehrere + DigEingänge eingetragen. + + + + + + + + + + + + + + + + Kurzbezeichnung, maximal 10 Zeichen, z.B. K242 + + + + + + + Langname des Knotens, max. 250 Zeichen. + Beispiel: "Meierstrasse / Muellerstrasse" + + + + + + + Identifikation des Knotens. Bleibt während der + reinen Planungsphase frei, wenn noch keine + Identifikation vorliegt. + + + + + + + + Logische Adressierung des Knotens + + + + + + + + Stadt, in der der Knoten + läuft. Muß nur gesetzt + werden, wenn mehrere + Städte in einem Projekt + gemeinsam vorhanden + sind. + + + + + + + Bezirk, in dem der + Knoten läuft. Muß nur in + Städten gesetzt werden, + die in Bezirke + aufgeteilt sind + (Beispiel: Wien) + + + + + + + Stadtweit eindeutige + Nummer des Knotens. Die + Nummer ist die + technische Nummer und + hat nichts mit + Auftragskennungen etc. + zu tun. Muß vor der + echten Geräteversorgung + gesetzt werden. Darf + während der reinen + Planungsphase zusammen + mit der KnotenId frei + bleiben. + + + + + + + + + + + Technische OCIT-Kennung des Knotens. + Darf nur dann ausgefüllt werden, + wenn die Kennung festgelegt ist + + + + + + + + znr Nummer (entspricht + der technischen + VSR-Nummer). Siehe + OCIT-Dokumentation. + + + + + + + + Wird nur gesetzt, wenn + der Wert ungleich der + Knotennummer gesetzt. + Ansonsten ist der Wert + defaultmäßig auf + knotenNummer gesetzt. + + + + + + + + Nur notwenidg, wenn mehr + als ein Knoten auf dem + Feldgerät vorhanden ist. + Ansonsten ist der Wert + auf den Default-Wert 1 + gesetzt. + + + + + + + + + + + + + + + Versorgungsprogramm, welches die Datei als + letztes bearbeitet hat. + + + + + + + + Name des Versorgungsprogramm (max. + 32 Zeichen) + + + + + + + Programmversion, frei definierbarer + Text mit max. 10 Zeichen + + + + + + + + + + Name des Users, der die letzte Änderung + gespeichert hat. + + + + + + + Speicherdatum und Uhrzeit der letzten Änderung. + + + + + + + Planungsversion als String. Format beliebig und + abhängig von der Quelle + + + + + + + Rechner auf dem die Daten der letzten Änderung + erstellt wurden. Wenn der Wert nicht zur + Verfügung steht, wird der Eintrag weggelassen. + + + + + + + kundenspezifische Bemerkungen. Wenn keine + Bemerkung vorhanden ist, wird das Feld + weggelassen. Systeme, die keine Bemerkungen + erlauben müssen das Feld unverändert + beibehalten. + + + + + + + + + + + Alle Daten eines Parametertyps + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Strukturbeschreibung der Parameter + (einer Parametertabelle) + + + + + + + + + + + + + + + + + + + + + + + + + + Signalprogramm ist die Aufhängung für + unterschiedliche Signalprogramme: Echte + Festzeitsignalprogramme können auf zwei Arten + gespeichert werden: Als eine Liste von + Signalbildern, in die gewechselt wird oder als + eine Liste von Phasenübergängen, die zu + bestimmten Zeitpunkten beginnen. + + + + + + + + + + + + + + + + Siehe OCIT-Element SignalGruppe. Pro + Signalgruppe wird hier ein Eintrag vorgenommen. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Referenz auf Tabellenspalte + + + + + + + byte + + + + + word + + + + + integer + + + + + float + + + + + string + + + + + boolean + + + + + + Referenz auf ein Element + + + + + + + + + + Einzelner Parameter(Tabellenspalte) + + + + + + + Bezeichner/ Tab-Spaltenname + + + + + + + + + float, word... + + + + + + + + + + + + + + + + + Auswahl entsprechend Typ Defaultwert, + wenn kein Eintrag im Parametersatz + Werte, die den Defaultwert besitzen + sollten nicht noch einmal bei den + Parametersatzwerten eingetragen werden + + + + + + byte + + + + + + + word + + + + + + + integer + + + + + + + float + + + + + + + string + + + + + + + boolean + + + + + + + + + Elemente aus Versorgorgung + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/xsd/arrayTest.json b/src/test/resources/xsd/arrayTest.json new file mode 100644 index 0000000..3b0197c --- /dev/null +++ b/src/test/resources/xsd/arrayTest.json @@ -0,0 +1,229 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "elementArray": { + "$ref": "#/definitions/ElementArray" + }, + "inlineElementArray": { + "$ref": "#/definitions/InlineElementArray" + }, + "inlineElementArray2": { + "$ref": "#/definitions/InlineElementArray2" + }, + "arrayFromExternList": { + "$ref": "#/definitions/ArrayFromExternList" + }, + "arrayFromExternList2": { + "$ref": "#/definitions/ArrayFromExternList2" + }, + "arrayFromExternList3": { + "$ref": "#/definitions/ArrayFromExternList3" + }, + "arrayFromExternList4": { + "$ref": "#/definitions/ArrayFromExternList4" + } + }, + "definitions": { + "ElementArray": { + "type": "object", + "additionalProperties": false, + "properties": { + "lineItem": { + "type": "array", + "items": { + "$ref": "#/definitions/LineItem" + } + } + } + }, + "LineItem": { + "type": "object", + "additionalProperties": false, + "properties": { + "description": { + "type": "string" + }, + "perUnitOunces": { + "type": "number" + }, + "price": { + "type": "number" + }, + "quantity": { + "$ref": "#/definitions/Quantity" + } + } + }, + "Quantity": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "address": { + "type": "string" + }, + "age": { + "type": "integer" + }, + "moo": { + "type": "integer" + }, + "poo": { + "type": "integer" + } + }, + "required": [ + "moo", + "poo" + ] + }, + "InlineElementArray": { + "type": "object", + "additionalProperties": false, + "properties": { + "lineItem": { + "$ref": "#/definitions/LineItem_1" + } + } + }, + "LineItem_1": { + "type": "object", + "additionalProperties": false, + "properties": { + "item": { + "type": "array", + "items": { + "$ref": "#/definitions/LineItem_1" + } + } + } + }, + "InlineElementArray2": { + "type": "object", + "additionalProperties": false, + "properties": { + "lineItem": { + "$ref": "#/definitions/LineItem_2" + } + } + }, + "LineItem_2": { + "type": "object", + "additionalProperties": false, + "properties": { + "item": { + "type": "array", + "items": { + "$ref": "#/definitions/LineItem_2" + } + } + } + }, + "ArrayFromExternList": { + "type": "object", + "additionalProperties": false, + "properties": { + "lineItem": { + "$ref": "#/definitions/LineItemList" + } + } + }, + "LineItemList": { + "type": "object", + "additionalProperties": false, + "properties": { + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/LineItem" + } + } + } + }, + "ArrayFromExternList2": { + "type": "object", + "additionalProperties": false, + "properties": { + "lineItem": { + "$ref": "#/definitions/LineItemList2" + } + } + }, + "LineItemList2": { + "type": "object", + "additionalProperties": false, + "properties": { + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/LineItem" + } + } + } + }, + "ArrayFromExternList3": { + "type": "object", + "additionalProperties": false, + "properties": { + "lineItem": { + "$ref": "#/definitions/LineItemListInline" + } + } + }, + "LineItemListInline": { + "type": "object", + "additionalProperties": false, + "properties": { + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + } + } + }, + "Value": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "string" + } + } + }, + "ArrayFromExternList4": { + "type": "object", + "additionalProperties": false, + "properties": { + "lineItem": { + "$ref": "#/definitions/LineItemList2Inline" + } + } + }, + "LineItemList2Inline": { + "type": "object", + "additionalProperties": false, + "properties": { + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/Value_1" + } + } + } + }, + "Value_1": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/xsd/arrayTest.xsd b/src/test/resources/xsd/arrayTest.xsd new file mode 100644 index 0000000..ff129d7 --- /dev/null +++ b/src/test/resources/xsd/arrayTest.xsd @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + type with element defined array + + + + + + + + + + type with an inner type array + + + + + + + + + + + + + + + type with an inner type array - make no sense + + + + + + + + + + + + + + + identical to inline-element-array + + + + + + + + + identical to ... + + + + + + + + + identical to ... + + + + + + + + + + identical to ... + + + + + + + + + + + + + + + + + + This is a simple test2 + over more than one line + + + + + + That's a name + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/xsd/complex/AddGrpC.xsd b/src/test/resources/xsd/complex/AddGrpC.xsd new file mode 100644 index 0000000..88f3f3d --- /dev/null +++ b/src/test/resources/xsd/complex/AddGrpC.xsd @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/xsd/complex/DSRC.xsd b/src/test/resources/xsd/complex/DSRC.xsd new file mode 100644 index 0000000..3a61dce --- /dev/null +++ b/src/test/resources/xsd/complex/DSRC.xsd @@ -0,0 +1,2289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/xsd/complex/ElectronicRegistrationIdentificationVehicleDataModule.xsd b/src/test/resources/xsd/complex/ElectronicRegistrationIdentificationVehicleDataModule.xsd new file mode 100644 index 0000000..102f8d3 --- /dev/null +++ b/src/test/resources/xsd/complex/ElectronicRegistrationIdentificationVehicleDataModule.xsd @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/xsd/complex/ITS-Container.xsd b/src/test/resources/xsd/complex/ITS-Container.xsd new file mode 100644 index 0000000..2e23097 --- /dev/null +++ b/src/test/resources/xsd/complex/ITS-Container.xsd @@ -0,0 +1,1409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/xsd/complex/REGION.xsd b/src/test/resources/xsd/complex/REGION.xsd new file mode 100644 index 0000000..3741766 --- /dev/null +++ b/src/test/resources/xsd/complex/REGION.xsd @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/src/test/resources/xsd/complex/asn1.xsd b/src/test/resources/xsd/complex/asn1.xsd new file mode 100644 index 0000000..7cd7fc5 --- /dev/null +++ b/src/test/resources/xsd/complex/asn1.xsd @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/xsd/lisa_ines_network.xsd b/src/test/resources/xsd/lisa_ines_network.xsd new file mode 100644 index 0000000..206b069 --- /dev/null +++ b/src/test/resources/xsd/lisa_ines_network.xsd @@ -0,0 +1,278 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/xsd/simpleTest.xsd b/src/test/resources/xsd/simpleTest.xsd new file mode 100644 index 0000000..8ddd521 --- /dev/null +++ b/src/test/resources/xsd/simpleTest.xsd @@ -0,0 +1,92 @@ + + + + This is a simple test + + + + + + + + + + + + + + This is a simple test2 + over more than one line + + + + + + That's a name + + + + + + + + + + + + + + + + + + This is a simple test2 + over more than one line + + + + + + That's a name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/xsd/ui-tlc.xsd b/src/test/resources/xsd/ui-tlc.xsd new file mode 100644 index 0000000..ae3c127 --- /dev/null +++ b/src/test/resources/xsd/ui-tlc.xsd @@ -0,0 +1,840 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TrafficLightController model used in the API-Gateway. + It is a flat representation of the TLC device, optimized for UI or other + third party systems. Most of the attributes are optional, to enable this object to have a complex set of data, + but provide subsets of information using projection + + + + + + The complex device information + + + + + The detailed status of the TLC device. + + + + + The configuration for the connection client. Usually the device driver uses this + information to establish a connection and activate the configured features. + + + + + + + Device groups coming from the User-Manager. there is the assignment from user-group to + devices. + + + + + + + The unique identifier of a TLC device + + + + + The full qualified device name + + + + + + + A type of the device like OCITV1, OCITV2, ACTROS.xxx. Maybe we can use an Enum here + + + + + + + TrafficLightController model used in the API-Gateway. + It is a flat representation of the TLC device, optimized for UI or other + third party systems. Most of the attributes are optional, to enable this object to have a complex set of data, + but provide subsets of information using projection + + + + + + The configuration of the device drvier client, that communicates with the TLC device + + + + + If the variables should be read from the TLC device. + + + + + + If the aggregated detector values should be read from the TLC device. + + + + + + If the public transport telegrams should be read from the TLC device. + + + + + + If the signal state information should be read from the TLC device. + + + + + + Default period to check the controller for new data. Unit: seconds + + + + + The interval, to aggregate the detector values. Unit: seconds + + + + + + The start time-stamp for recording signal state data, when signalStateArchive=true + + + + + + The end time-stamp for a recording of signal state data, when signalStateArchive=true + + + + + + A general connection timeout. Unit: seconds + + + + + House-keeping information: How long to keep aggregated detector values. Unit:day + + + + + + House-keeping information: How long to keep system messages. Unit:day + + + + + House-keeping information: How long to keep operational state values. Unit:day + + + + + + House-keeping information: How long to keep public transport values. Unit:day + + + + + + House-keeping information: How long to keep signal state values. Unit:day + + + + + House-keeping information: How long to keep variable data values. Unit:day + + + + + + + The configuration of the device drvier client, that communicates with the TLC device + + + + + + The current operational hardware and software status of the TLC device. + + + + + A list of defect detectors + + + + + A list of defect signal chambers. + + + + + A list of operational states. There can be multiple states, because one hardware can handle + more than one relative node. So there is an operational state for each relative node. + + + + + + + + + + + + + + Returns the timesource. The TLC device can run on different time modes like lokal, DCF77, + central, NTP ... + + + + + + + + + The current operational hardware and software status of the TLC device. + + + + + The operational state of a relative node. It contains several information about the current + state like running signal program, state, modification and interventions + + + + + + + + Sub node states + + + + + + Traffic related modifications + + + + + + + The state of this relative node + + + + + + + + + + + + + + + + + + + + + + + The operational state of a relative node. It contains several information about the current + state like running signal program, state, modification and interventions + + + + + + Specific device information. It belongs to revision, manufactorer, device designation. + + + + + + + + + + + + + + Specific device information. It belongs to revision, manufactorer, device designation. + + + + + + Represents one data entry. It refers to its TLC device. There are several timestamps and + indeces. They defined the timestamp, when the data occur on the device and on the other hand, when the data are + processed on the device driver. The same is for indeces, because it can happen, that indeces are resetted on a + device, when it was restarted. So, the devive driver has to handle its own index. + + + + + + Link to the TLCDevice + + + + + Index from the device driver + + + + + Index on the TLC device + + + + + + Timestmap on the TLC device + + + + + Timestamp when data was fetched by the DeviceDriver + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A default request object, to define a query. + + + + + The object id filter. What kind of object defines the requestType. + + + + + A list of attributes, that are neccessary to be submitted. If this list is empty, all + values are submitted, if available, the given attributes are returned, as they are available in the model + + + + + + + + + Start value for a time range query + + + + + End value for a time range query + + + + + Size of data entries per page + + + + + The start offset is the number of entries to skip, when start getting the entries. Together + with the page size, paging can be handled + + + + + + Specifies, that kind of object ids are given, e.g. Device ids, Message ids + + + + + + + A default request object, to define a query. + + + + + A default response object, that is returned from the gateway. + + + + + A container object, that hold the data + + + + + + The response timestamp in ms + + + + + Number of entries in the container + + + + + True + + + + + The error code for the response. Default is 0 = OK + + + + + An additional human readable message, about the error that occured + + + + + + A default response object, that is returned from the gateway. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Entry that defines a field (key) and the corresponding sort-type + + + + The dort key attribute + + + + + + + Entry that defines a field (key) and the corresponding sort-type + + + + + Entry that defines a filter field (key) and the corresponding filter values. If there multiple + values they have to be ORed + + + + + + + + + + Entry that defines a filter field (key) and the corresponding filter values. If there multiple + values they have to be ORed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +