Skip to content

Commit

Permalink
Merge pull request #92 from boschglobal/feature-86
Browse files Browse the repository at this point in the history
feature(Processor): Add JSON Parser to VSS Processor module
  • Loading branch information
Chrylo authored Mar 7, 2024
2 parents a00b4a1 + adb2002 commit f5ca5f1
Show file tree
Hide file tree
Showing 31 changed files with 26,930 additions and 98 deletions.
2 changes: 2 additions & 0 deletions docs/QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ For a more convenient usage you can opt in to auto generate Kotlin models via [S
of the same specification the Databroker uses. For starters you can retrieve an extensive default specification from the
release page of the [COVESA Vehicle Signal Specification GitHub repository](https://github.com/COVESA/vehicle_signal_specification/releases).

Currently VSS specification files in .yaml and .json format are supported by the vss-processor.

*app/build.gradle.kts*
```
plugins {
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ datastore = "1.0.0"
constraintlayoutCompose = "1.0.1"
datastorePreferences = "1.0.0"
dokka = "1.9.10"
gson = "2.10.1"
kotlin = "1.9.22"
kotlinpoet = "1.16.0"
kotlinxSerializationJson = "1.6.1"
Expand Down Expand Up @@ -36,6 +37,7 @@ androidx-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedat
grpc-okhttp = { group = "io.grpc", name = "grpc-okhttp", version.ref = "grpc" }
grpc-protobuf = { group = "io.grpc", name = "grpc-protobuf-lite", version.ref = "grpc" }
grpc-stub = { group = "io.grpc", name = "grpc-stub", version.ref = "grpc" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" }
kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinpoet" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import io.kotest.matchers.shouldBe
import org.eclipse.kuksa.databroker.DataBrokerConnectorProvider
import org.eclipse.kuksa.model.Property
import org.eclipse.kuksa.proto.v1.Types
import org.eclipse.kuksa.test.TestResourceFile
import org.eclipse.kuksa.test.kotest.Authentication
import org.eclipse.kuksa.test.kotest.CustomDatabroker
import org.eclipse.kuksa.test.kotest.Insecure
Expand Down Expand Up @@ -170,6 +171,7 @@ enum class JwtType(private val fileName: String) {
;

fun asInputStream(): InputStream {
return DataBrokerConnectionTest::class.java.classLoader?.getResourceAsStream(fileName)!!
val resourceFile = TestResourceFile(fileName)
return resourceFile.inputStream()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package org.eclipse.kuksa

import io.kotest.core.spec.style.BehaviorSpec
import org.eclipse.kuksa.databroker.DataBrokerConnectorProvider
import org.eclipse.kuksa.test.TestResourceFile
import org.eclipse.kuksa.test.kotest.CustomDatabroker
import org.eclipse.kuksa.test.kotest.Integration
import org.eclipse.kuksa.test.kotest.Secure
Expand All @@ -37,8 +38,9 @@ class DataBrokerConnectorSecureTest : BehaviorSpec({
val dataBrokerConnectorProvider = DataBrokerConnectorProvider()

and("a secure DataBrokerConnector with valid Host, Port and TLS certificate") {
val certificate = DataBrokerConnectionTest::class.java.classLoader?.getResourceAsStream("CA.pem")!!
val dataBrokerConnector = dataBrokerConnectorProvider.createSecure(rootCertFileStream = certificate)
val certificate = TestResourceFile("CA.pem")
val dataBrokerConnector =
dataBrokerConnectorProvider.createSecure(rootCertFileStream = certificate.inputStream())

`when`("Trying to establish a secure connection") {
val connection = dataBrokerConnector.connect()
Expand Down
23 changes: 1 addition & 22 deletions test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,8 @@
* SPDX-License-Identifier: Apache-2.0
*
*/

@Suppress("DSL_SCOPE_VIOLATION") // Remove once KTIJ-19369 is fixed
plugins {
id("com.android.library")
kotlin("android")
}

android {
namespace = "org.eclipse.kuksa.test"
compileSdk = 33

defaultConfig {
minSdk = 24
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = "1.8"
}
kotlin("jvm")
}

dependencies {
Expand Down
28 changes: 28 additions & 0 deletions test/src/main/java/org/eclipse/kuksa/test/TestResourceFile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/

package org.eclipse.kuksa.test

import java.io.File

private val classLoader: ClassLoader? = TestResourceFile::class.java.classLoader

class TestResourceFile(path: String) : File(
classLoader!!.getResource(path)?.file ?: error("File does not exist: '$path'"),
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ package org.eclipse.kuksa.vsscore.annotation

/**
* Add this annotation to any class to trigger the model generation (Kotlin Symbol Processing) for the given
* Vehicle Signal Specification definition file by the "vss-processor-plugin". Only .yaml files are currently supported.
* Vehicle Signal Specification definition file by the "vss-processor-plugin". Currently VSS specification files in
* .yaml and .json format are supported by the vss-processor.
* Use the "VSS Processor Plugin" to provide the Symbol Processor with the necessary definition file(s).
*
* ### Plugin Example
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,6 @@ private abstract class ProvideVssDefinitionTask : DefaultTask() {
}

companion object {
private val validVssExtension = setOf("yml", "yaml")
private val validVssExtension = setOf("yml", "yaml", "json") // keep VssFileExtension aligned
}
}
3 changes: 3 additions & 0 deletions vss-processor/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ dependencies {
implementation(kotlin("stdlib"))
implementation(kotlin("reflect"))

implementation(libs.gson)
implementation(libs.kotlinpoet)
implementation(libs.kotlinpoet.ksp)
implementation(libs.symbol.processing.api)

testImplementation(project(":test"))

testImplementation(libs.kotest)
testImplementation(libs.mockk)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ import com.squareup.kotlinpoet.ksp.writeTo
import org.eclipse.kuksa.vsscore.annotation.VssDefinition
import org.eclipse.kuksa.vsscore.model.VssNode
import org.eclipse.kuksa.vsscore.model.parentClassName
import org.eclipse.kuksa.vssprocessor.parser.YamlDefinitionParser
import org.eclipse.kuksa.vssprocessor.parser.factory.VssParserFactory
import org.eclipse.kuksa.vssprocessor.spec.VssNodeSpecModel
import org.eclipse.kuksa.vssprocessor.spec.VssPath
import org.eclipse.kuksa.vssprocessor.spec.VssSpecificationSpecModel
import java.io.File

/**
Expand All @@ -51,12 +51,12 @@ import java.io.File
* @param codeGenerator to generate class files with
* @param logger to log output with
*/
class VssDefinitionProcessor(
class VssModelGeneratorProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger,
) : SymbolProcessor {
private val visitor = VssDefinitionVisitor()
private val yamlParser = YamlDefinitionParser()
private val vssParserFactory = VssParserFactory()

override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation(VssDefinition::class.qualifiedName.toString())
Expand Down Expand Up @@ -86,14 +86,20 @@ class VssDefinitionProcessor(
return
}

val simpleSpecificationElements = mutableListOf<VssNodeSpecModel>()
definitionFiles.forEach { definitionFile ->
val simpleSpecificationElements = yamlParser.parseSpecifications(definitionFile)
val vssPathToSpecificationElement = simpleSpecificationElements
.associateBy({ VssPath(it.vssPath) }, { it })
logger.info("Parsing models for definition file: ${definitionFile.name}")
val vssDefinitionParser = vssParserFactory.create(definitionFile)
val specModels = vssDefinitionParser.parseNodes(definitionFile)

logger.info("Generating models for definition file: ${definitionFile.name}")
generateModelFiles(vssPathToSpecificationElement)
simpleSpecificationElements.addAll(specModels)
}

val vssPathToSpecificationElement = simpleSpecificationElements
.distinctBy { it.uuid }
.associateBy({ VssPath(it.vssPath) }, { it })

generateModelFiles(vssPathToSpecificationElement)
}

// Uses the default file path for generated files (from the code generator) and searches for the given file.
Expand All @@ -110,7 +116,7 @@ class VssDefinitionProcessor(
.toSet()
}

private fun generateModelFiles(vssPathToSpecification: Map<VssPath, VssSpecificationSpecModel>) {
private fun generateModelFiles(vssPathToSpecification: Map<VssPath, VssNodeSpecModel>) {
val duplicateSpecificationNames = vssPathToSpecification.keys
.groupBy { it.leaf }
.filter { it.value.size > 1 }
Expand Down Expand Up @@ -153,7 +159,7 @@ class VssDefinitionProcessor(
// If the actual parent is a sub class (Driver) in another class file (e.g. Vehicle) then this method returns
// a sub import e.g. "Vehicle.Driver". Otherwise just "Vehicle" is returned.
private fun buildParentImport(
specModel: VssSpecificationSpecModel,
specModel: VssNodeSpecModel,
parentVssPathToClassName: Map<String, String>,
): String {
var availableParentVssPath = specModel.vssPath
Expand Down Expand Up @@ -194,12 +200,12 @@ class VssDefinitionProcessor(
}

/**
* Provides the environment for the [VssDefinitionProcessor].
* Provides the environment for the [VssModelGeneratorProcessor].
*/
class VssDefinitionProcessorProvider : SymbolProcessorProvider {
override fun create(
environment: SymbolProcessorEnvironment,
): SymbolProcessor {
return VssDefinitionProcessor(environment.codeGenerator, environment.logger)
return VssModelGeneratorProcessor(environment.codeGenerator, environment.logger)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
* Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,16 +19,14 @@

package org.eclipse.kuksa.vssprocessor.parser

import org.eclipse.kuksa.vssprocessor.spec.VssSpecificationSpecModel
import org.eclipse.kuksa.vssprocessor.spec.VssNodeSpecModel
import java.io.File

internal interface VssDefinitionParser {
internal interface VssParser {
/**
* @param definitionFile to parse [VssSpecificationSpecModel] with
* @param elementDelimiter which is the separator string between the specifications. The default is an empty line.
* @param definitionFile to parse [VssNodeSpecModel] with
*
* @throws java.io.IOException will be thrown when parsing the SpecModels failed
*/
fun parseSpecifications(
definitionFile: File,
elementDelimiter: String = "",
): List<VssSpecificationSpecModel>
fun parseNodes(definitionFile: File): List<VssNodeSpecModel>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/

package org.eclipse.kuksa.vssprocessor.parser.factory

// keep VssProcessorPlFugin#validVssExtensions aligned
internal enum class VssFileExtension(vararg val fileExtensions: String) {
JSON("json"),
YAML("yaml", "yml"),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/

package org.eclipse.kuksa.vssprocessor.parser.factory

import org.eclipse.kuksa.vssprocessor.parser.VssParser
import org.eclipse.kuksa.vssprocessor.parser.factory.VssFileExtension.JSON
import org.eclipse.kuksa.vssprocessor.parser.factory.VssFileExtension.YAML
import org.eclipse.kuksa.vssprocessor.parser.json.JsonVssParser
import org.eclipse.kuksa.vssprocessor.parser.yaml.YamlVssParser
import java.io.File

internal class VssParserFactory {

/**
* @throws IllegalStateException when the specified extension is not supported
*/
fun create(extension: String): VssParser {
return when {
JSON.fileExtensions.contains(extension) -> {
JsonVssParser()
}

YAML.fileExtensions.contains(extension) -> {
YamlVssParser()
}

else -> {
error("Could not create VssDefinitionParser: File Extension '$extension' not supported")
}
}
}

/**
* @throws IllegalStateException when the extension of the specified file is not supported
*/
fun create(file: File): VssParser {
val fileName = file.name // with extension
val fileExtension = fileName.substringAfterLast(".")

return create(fileExtension)
}
}
Loading

0 comments on commit f5ca5f1

Please sign in to comment.