Skip to content

Commit

Permalink
Split the XML deserialization into jvmMain
Browse files Browse the repository at this point in the history
  • Loading branch information
hufman committed Apr 6, 2024
1 parent 71767db commit ba02b58
Show file tree
Hide file tree
Showing 30 changed files with 890 additions and 773 deletions.
Original file line number Diff line number Diff line change
@@ -1,46 +1,9 @@
package io.bimmergestalt.idriveconnectkit.rhmi

import io.bimmergestalt.idriveconnectkit.rhmi.mocking.RHMIApplicationMock
import io.bimmergestalt.idriveconnectkit.xmlutils.XMLUtils
import io.bimmergestalt.idriveconnectkit.xmlutils.getAttributesMap
import io.bimmergestalt.idriveconnectkit.xmlutils.getChildElements
import io.bimmergestalt.idriveconnectkit.xmlutils.getChildNamed
import org.w3c.dom.Node

abstract class RHMIAction protected constructor(open val app: RHMIApplication, open val id: Int) {

abstract class RHMIAction private constructor(open val app: RHMIApplication, open val id: Int) {

companion object {
fun loadFromXML(app: RHMIApplication, node: Node): RHMIAction? {
val attrs = node.getAttributesMap()

val id = attrs["id"]?.toInt() ?: return null

if (node.nodeName == "combinedAction") {
val subactions = node.getChildNamed("actions").getChildElements().mapNotNull { subactionNode ->
loadFromXML(app, subactionNode)
}
val raAction = subactions.firstOrNull { it is RAAction } as RAAction?
val hmiAction = subactions.firstOrNull { it is HMIAction } as HMIAction?
val action = CombinedAction(app, id, raAction, hmiAction)
XMLUtils.unmarshalAttributes(action, attrs)
return action
}

val action = when (node.nodeName) {
"hmiAction" -> HMIAction(app, id)
"raAction" -> RAAction(app, id)
"linkAction" -> LinkAction(app, id)
else -> null
}

if (action != null) {
XMLUtils.unmarshalAttributes(action, attrs)
}
return action
}

}
companion object { }

class CombinedAction(override val app: RHMIApplication, override val id: Int, val raAction: RAAction?, val hmiAction: HMIAction?): RHMIAction(app, id) {
var sync: String = ""
Expand Down Expand Up @@ -87,32 +50,6 @@ abstract class RHMIAction private constructor(open val app: RHMIApplication, ope
}
}

class MockAction(override val app: RHMIApplicationMock, override val id: Int): RHMIAction(app, id) {
override fun asCombinedAction(): CombinedAction {
return app.actions.computeIfWrongType(id) {
CombinedAction(app, id, RAAction(app, id), HMIAction(app, id))
}
}

override fun asHMIAction(): HMIAction {
return app.actions.computeIfWrongType(id) {
HMIAction(app, id)
}
}

override fun asRAAction(): RAAction {
return app.actions.computeIfWrongType(id) {
RAAction(app, id)
}
}

override fun asLinkAction(): LinkAction {
return app.actions.computeIfWrongType(id) {
LinkAction(app, id)
}
}
}

open fun asCombinedAction(): CombinedAction? {
return this as? CombinedAction
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
package io.bimmergestalt.idriveconnectkit.rhmi

import de.bmw.idrive.BMWRemoting
import de.bmw.idrive.BMWRemotingServer
import io.bimmergestalt.idriveconnectkit.xmlutils.XMLUtils
import io.bimmergestalt.idriveconnectkit.xmlutils.getAttribute
import io.bimmergestalt.idriveconnectkit.xmlutils.getChildElements
import io.bimmergestalt.idriveconnectkit.xmlutils.getChildNamed
import org.w3c.dom.Document


abstract class RHMIApplication {
abstract val models: MutableMap<Int, RHMIModel>
Expand All @@ -18,85 +10,14 @@ abstract class RHMIApplication {

var ignoreUpdates = false

fun loadFromXML(description: String) {
return this.loadFromXML(description.toByteArray())
}
fun loadFromXML(description: ByteArray) {
return this.loadFromXML(XMLUtils.loadXML(description))
}
fun loadFromXML(description: Document) {
ignoreUpdates = true
description.getChildNamed("pluginApps").getChildElements().forEach { pluginAppNode ->
pluginAppNode.getChildNamed("models").getChildElements().forEach { modelNode ->
val model = RHMIModel.loadFromXML(this, modelNode)
if (model != null) {
models[model.id] = model
if (model is RHMIModel.FormatDataModel) {
model.submodels.forEach { models[it.id] = it }
}
}
}
pluginAppNode.getChildNamed("actions").getChildElements().forEach { actionNode ->
val action = RHMIAction.loadFromXML(this, actionNode)
if (action != null) {
actions[action.id] = action
if (action is RHMIAction.CombinedAction) {
if (action.raAction != null) actions[action.raAction.id] = action.raAction
if (action.hmiAction != null) actions[action.hmiAction.id] = action.hmiAction
}
}
}
pluginAppNode.getChildNamed("events").getChildElements().forEach { actionNode ->
val event = RHMIEvent.loadFromXML(this, actionNode)
if (event != null) {
events[event.id] = event
}
}
pluginAppNode.getChildNamed("hmiStates").getChildElements().forEach { stateNode ->
val state = RHMIState.loadFromXML(this, stateNode)
if (state != null) {
states[state.id] = state
components.putAll(state.components)
if (state is RHMIState.ToolbarState) {
components.putAll(state.toolbarComponents)
}
}
}
val entryButtonNode = pluginAppNode.getChildNamed("entryButton")
if (entryButtonNode != null) {
val component = RHMIComponent.loadFromXML(this, entryButtonNode)
if (component is RHMIComponent.EntryButton) {
pluginAppNode.getAttribute("applicationType")?.also {
component.applicationType = it
}
pluginAppNode.getAttribute("applicationWeight")?.toIntOrNull()?.also {
component.applicationWeight = it
}
components[component.id] = component
}
}
val instrumentClusterNode = pluginAppNode.getChildNamed("instrumentCluster")
if (instrumentClusterNode != null) {
val component = RHMIComponent.loadFromXML(this, instrumentClusterNode)
if (component != null) {
components[component.id] = component
}
}
}
ignoreUpdates = false
}

@Throws(BMWRemoting.SecurityException::class, BMWRemoting.IllegalArgumentException::class, BMWRemoting.ServiceException::class)
abstract fun setModel(modelId: Int, value: Any?)

open fun getModel(modelId: Int): Any? = null

@Throws(BMWRemoting.SecurityException::class, BMWRemoting.IllegalArgumentException::class, BMWRemoting.ServiceException::class)
abstract fun setProperty(componentId: Int, propertyId: Int, value: Any?)

open fun getProperty(componentId: Int, propertyId: Int): Any? = null

@Throws(BMWRemoting.SecurityException::class, BMWRemoting.IllegalArgumentException::class, BMWRemoting.ServiceException::class)
abstract fun triggerHMIEvent(eventId: Int, args: Map<Any, Any?>)
}

Expand Down Expand Up @@ -130,46 +51,3 @@ class RHMIApplicationConcrete : RHMIApplication() {
}

}

class RHMIApplicationEtch(val remoteServer: BMWRemotingServer, val rhmiHandle: Int) : RHMIApplication() {
/** Represents an application layout that is backed by a Car connection
* Most data is not retained, so if you want to read data back out,
* use RHMIApplicationConcrete or RHMIApplicationIdempotent
* */
override val models = HashMap<Int, RHMIModel>()
override val actions = HashMap<Int, RHMIAction>()
override val events = HashMap<Int, RHMIEvent>()
override val states = HashMap<Int, RHMIState>()
override val components = HashMap<Int, RHMIComponent>()

// remember a little bit of properties and small ints
val modelData = HashMap<Int, Any?>()
val propertyData = HashMap<Int, MutableMap<Int, Any?>>()

@Throws(BMWRemoting.SecurityException::class, BMWRemoting.IllegalArgumentException::class, BMWRemoting.ServiceException::class)
override fun setModel(modelId: Int, value: Any?) {
if (value is Int || value is BMWRemoting.RHMIResourceIdentifier) {
modelData[modelId] = value
} else {
modelData.remove(modelId)
}
if (ignoreUpdates) return
this.remoteServer.rhmi_setData(this.rhmiHandle, modelId, value)
}
override fun getModel(modelId: Int): Any? = modelData[modelId]

@Throws(BMWRemoting.SecurityException::class, BMWRemoting.IllegalArgumentException::class, BMWRemoting.ServiceException::class)
override fun setProperty(componentId: Int, propertyId: Int, value: Any?) {
propertyData.getOrPut(componentId){HashMap()}[propertyId] = value
if (ignoreUpdates) return
val propertyValue = HashMap<Int, Any?>()
propertyValue[0] = value
this.remoteServer.rhmi_setProperty(rhmiHandle, componentId, propertyId, propertyValue)
}
override fun getProperty(componentId: Int, propertyId: Int): Any? = propertyData[componentId]?.get(propertyId)

@Throws(BMWRemoting.SecurityException::class, BMWRemoting.IllegalArgumentException::class, BMWRemoting.ServiceException::class)
override fun triggerHMIEvent(eventId: Int, args: Map<Any, Any?>) {
this.remoteServer.rhmi_triggerEvent(rhmiHandle, eventId, args)
}
}
Original file line number Diff line number Diff line change
@@ -1,58 +1,17 @@
package io.bimmergestalt.idriveconnectkit.rhmi

import io.bimmergestalt.idriveconnectkit.Utils.etchAsInt
import io.bimmergestalt.idriveconnectkit.rhmi.mocking.RHMIApplicationMock
import io.bimmergestalt.idriveconnectkit.withRealDefault
import io.bimmergestalt.idriveconnectkit.xmlutils.XMLUtils
import io.bimmergestalt.idriveconnectkit.xmlutils.getAttributesMap
import io.bimmergestalt.idriveconnectkit.xmlutils.getChildElements
import io.bimmergestalt.idriveconnectkit.xmlutils.getChildNamed
import org.w3c.dom.Node


abstract class RHMIComponent private constructor(open val app: RHMIApplication, open val id: Int) {
abstract class RHMIComponent protected constructor(open val app: RHMIApplication, open val id: Int) {

val properties: MutableMap<Int, RHMIProperty> = HashMap<Int, RHMIProperty>().withRealDefault { propertyId ->
// look up from the app storage if the property wasn't loaded from xml
RHMIProperty.AppProperty(app, id, propertyId)
}

companion object {
fun loadFromXML(app: RHMIApplication, node: Node): RHMIComponent? {
val attrs = node.getAttributesMap()

val id = attrs["id"]?.toInt() ?: return null

val component = when (node.nodeName) {
"separator" -> Separator(app, id)
"image" -> Image(app, id)
"label" -> Label(app, id)
"list" -> List(app, id)
"entryButton" -> EntryButton(app, id)
"instrumentCluster" -> InstrumentCluster(app, id)
"button" -> if (attrs["model"] == null) ToolbarButton(app, id) else Button(app, id)
"checkbox" -> Checkbox(app, id)
"gauge" -> Gauge(app, id)
"input" -> Input(app, id)
"calendarDay" -> CalendarDay(app, id)
else -> null
}

if (component != null) {
XMLUtils.unmarshalAttributes(component, attrs)

val propertyNodes = node.getChildNamed("properties")
if (propertyNodes != null) {
propertyNodes.getChildElements().filter { it.nodeName == "property" }.forEach {
val property = RHMIProperty.loadFromXML(app, component.id, it)
if (property != null)
component.properties[property.id] = property
}
}
}
return component
}
}
companion object { }

fun setProperty(property: RHMIProperty.PropertyId, value: Any) {
this.setProperty(property.id, value)
Expand Down Expand Up @@ -342,69 +301,6 @@ abstract class RHMIComponent private constructor(open val app: RHMIApplication,
}
}

class MockComponent(override val app: RHMIApplicationMock, override val id: Int): RHMIComponent(app, id) {
override fun asSeparator(): Separator {
return app.components.computeIfWrongType(id) {
Separator(app, id)
}
}
override fun asImage(): Image {
return app.components.computeIfWrongType(id) {
Image(app, id)
}
}
override fun asLabel(): Label {
return app.components.computeIfWrongType(id) {
Label(app, id)
}
}
override fun asList(): List {
return app.components.computeIfWrongType(id) {
List(app, id)
}
}
override fun asEntryButton(): EntryButton {
return app.components.computeIfWrongType(id) {
EntryButton(app, id)
}
}
override fun asInstrumentCluster(): InstrumentCluster {
return app.components.computeIfWrongType(id) {
InstrumentCluster(app, id)
}
}
override fun asToolbarButton(): ToolbarButton {
return app.components.computeIfWrongType(id) {
ToolbarButton(app, id)
}
}
override fun asButton(): Button {
return app.components.computeIfWrongType(id) {
Button(app, id)
}
}
override fun asCheckbox(): Checkbox {
return app.components.computeIfWrongType(id) {
Checkbox(app, id)
}
}
override fun asGauge(): Gauge {
return app.components.computeIfWrongType(id) {
Gauge(app, id)
}
}
override fun asInput(): Input {
return app.components.computeIfWrongType(id) {
Input(app, id)
}
}
override fun asCalendarDay(): CalendarDay {
return app.components.computeIfWrongType(id) {
CalendarDay(app, id)
}
}
}

open fun asSeparator(): Separator? {
return this as? Separator
}
Expand Down
Loading

0 comments on commit ba02b58

Please sign in to comment.