Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TH2-3857 #15

Open
wants to merge 51 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
e69c873
Self-closing tag and th2-codec error events (#13)
eugene-zheltov May 27, 2022
717b38d
[TH2-3778] Set codec version from snapshot with fixes
eugene-zheltov Jun 7, 2022
6ab2f0b
[TH2-3778] Set codec version from snapshot with fixes
eugene-zheltov Jun 8, 2022
75773ee
[TH2-3778] Change verison of codec core
eugene-zheltov Jun 8, 2022
d785d78
[TH2-3778] Change verison of codec core
eugene-zheltov Jun 8, 2022
542bde8
[TH2-3778] Change version of codec core
eugene-zheltov Jun 9, 2022
a47c0b1
[TH2-3778] Change version of codec core
eugene-zheltov Jun 10, 2022
09e44f9
[TH2-3778] Change version of codec core to 4.7.1-TH2-3778-tmp-2495038…
eugene-zheltov Jun 14, 2022
1e3527b
[TH2-3857] tmp
eugene-zheltov Jun 28, 2022
4764809
[TH2-3857] tmp-2
eugene-zheltov Jul 4, 2022
4f61923
[TH2-3857] tmp-3
eugene-zheltov Jul 4, 2022
8d785c8
[TH2-3857] tmp-4
eugene-zheltov Jul 7, 2022
74c2c5a
[TH2-3857] tmp-4
eugene-zheltov Jul 11, 2022
68f0a0c
[TH2-3857] Successful parsing; to be refactored
eugene-zheltov Jul 12, 2022
cec90fe
[TH2-3857] Refactor XMLSchemaCore
eugene-zheltov Jul 14, 2022
16f85d0
[TH2-3857] tmp-5
eugene-zheltov Jul 18, 2022
07d3a28
TH2-3778 (#14)
eugene-zheltov Jul 19, 2022
8cfce4c
[TH2-3857] tmp-6
eugene-zheltov Jul 20, 2022
8cf2d64
[TH2-3857] Fix the absence of some list values
eugene-zheltov Jul 25, 2022
62c3030
[TH2-3857] Remove prints and change xsd processing a bit
eugene-zheltov Jul 26, 2022
24d6d97
[TH2-3857] Disable tests
eugene-zheltov Jul 26, 2022
06e3d3e
[TH2-3857] Minor changes
eugene-zheltov Jul 26, 2022
725b78e
Merge branch 'master' into TH2-3857
eugene-zheltov Jul 26, 2022
0c6a616
[TH2-3857] Move StreamReaderDelegateDecorator
eugene-zheltov Jul 27, 2022
7697106
[TH2-3857] Chagne xsdProperties loading
eugene-zheltov Jul 27, 2022
468a849
[TH2-3857] Add th2-id to the log when th2-codec-xml-via-xsd cannot de…
eugene-zheltov Jul 28, 2022
6e996da
[TH2-3857] Minor changes
eugene-zheltov Jul 29, 2022
90a4ce7
Merge remote-tracking branch 'origin/TH2-3857' into TH2-3857
eugene-zheltov Jul 29, 2022
652cf79
[TH2-3857] tmp
eugene-zheltov Aug 1, 2022
c8474de
[TH2-3857] tmp 2
eugene-zheltov Aug 2, 2022
14be420
[TH2-3857] tmp 3
eugene-zheltov Aug 2, 2022
71ff9c2
[TH2-3857] Implement new decoding
eugene-zheltov Aug 3, 2022
17507d0
[TH2-3857] Remove comments
eugene-zheltov Aug 3, 2022
207e5c1
[TH2-3857] Fix repeated elements of lists
eugene-zheltov Aug 3, 2022
cba5310
[TH2-3857] Fix missing empty tags
eugene-zheltov Aug 3, 2022
3b6e346
[TH2-3857] Remove usage of XmlPipelineCodec for now
eugene-zheltov Aug 3, 2022
fe488b8
[TH2-3857] Remove garbage
eugene-zheltov Aug 4, 2022
8d5a873
[TH2-3857] Fix truncated values
eugene-zheltov Aug 5, 2022
cea2111
[TH2-3857] Add missing attributes
eugene-zheltov Aug 5, 2022
bbe67f7
[TH2-3857] Disable test
eugene-zheltov Aug 5, 2022
0fb452b
[TH2-3857] Add prefixes to node names
eugene-zheltov Aug 5, 2022
7791388
[TH2-3857] Remove redundant ":" from node names
eugene-zheltov Aug 5, 2022
06dc868
[TH2-3857] Migrated to StAX
Nikita-Smirnov-Exactpro Aug 9, 2022
23f0005
[TH2-3857] Fixed problem related to release sub-nodes
Nikita-Smirnov-Exactpro Aug 10, 2022
a6aab96
[TH2-3857] Updated common libraries
Nikita-Smirnov-Exactpro Aug 10, 2022
7a66236
[TH2-3857] Consider empty xmlns property
eugene-zheltov Oct 6, 2022
a127615
[TH2-3857] Add a new parameter encodeValidation
eugene-zheltov Feb 16, 2023
3f9843f
[TH2-3857] Update dev-docker-publish.yml and docker-publish.yml
eugene-zheltov Feb 20, 2023
1d327a4
[TH2-3857] Update common version
eugene-zheltov Feb 20, 2023
bb17598
[TH2-3857] Update codec-core version
eugene-zheltov Feb 21, 2023
0510415
[TH2-3857] Update slf4j dependencies
eugene-zheltov Feb 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Codec Xml via Xsd
![version](https://img.shields.io/badge/version-0.0.3-blue.svg)
![version](https://img.shields.io/badge/version-0.0.4-blue.svg)

# How it works:

Expand Down Expand Up @@ -142,6 +142,12 @@ spec:

## Changelog

### v0.0.4

#### Feature:

* Self-closing tags are ignored by the codec

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write about used technology


### v0.0.3

#### Feature:
Expand All @@ -159,4 +165,4 @@ spec:

#### Feature:

* First realization using underscore library as parser for json
* First realization using underscore library as parser for json
6 changes: 4 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jar {
dependencies {
api platform('com.exactpro.th2:bom:3.1.0')
implementation 'com.exactpro.th2:common:3.31.5'
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved
implementation 'com.exactpro.th2:codec:4.2.0'
implementation 'com.exactpro.th2:codec:4.7.2'
implementation 'com.exactpro.th2:sailfish-utils:3.12.3'

implementation "com.exactpro.sf:sailfish-core:${sailfishVersion}"
Expand All @@ -82,7 +82,9 @@ dependencies {

implementation 'com.fasterxml.jackson.core:jackson-core:2.13.1'
implementation 'com.github.javadev:underscore:1.69'


implementation group: 'org.apache.ws.xmlschema', name: 'xmlschema-core', version: '2.3.0'

testImplementation "org.jetbrains.kotlin:kotlin-test-junit5:${kotlin_version}"
testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
testImplementation "org.junit.jupiter:junit-jupiter-params:${junitVersion}"
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
#

release_version=0.0.3
release_version=0.0.5
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved
kotlin_version=1.5.31
description = 'th2 codec xml via xsd'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
package com.exactpro.th2.codec.xml
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved

import com.exactpro.th2.codec.xml.xsd.XMLSchemaCore
import com.exactpro.th2.codec.xml.xsd.XmlElementWrapper
import com.exactpro.th2.common.grpc.ListValue
import com.exactpro.th2.common.grpc.Message
import com.exactpro.th2.common.grpc.MessageMetadata
import com.exactpro.th2.common.grpc.RawMessage
import com.exactpro.th2.common.grpc.Value
import mu.KotlinLogging
import javax.xml.namespace.QName
import javax.xml.stream.XMLStreamException
import javax.xml.stream.XMLStreamReader
import javax.xml.stream.util.StreamReaderDelegate
import com.exactpro.th2.common.grpc.Value.KindCase.SIMPLE_VALUE
import com.exactpro.th2.common.grpc.Value.KindCase.MESSAGE_VALUE
import com.exactpro.th2.common.grpc.Value.KindCase.LIST_VALUE
import com.exactpro.th2.common.grpc.Value.KindCase.KIND_NOT_SET
import com.exactpro.th2.common.message.message
import com.exactpro.th2.common.message.set
import com.exactpro.th2.common.value.add
import com.exactpro.th2.common.value.listValue
import com.exactpro.th2.common.value.toValue
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap

class StreamReaderDelegateDecorator(reader: XMLStreamReader,
private val rawMessage: RawMessage,
private val xmlSchemaCore: XMLSchemaCore, ) : StreamReaderDelegate(reader) {
private val elementStack = ArrayList<QName>()
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved
private val elementTypeStack = Stack<Value.KindCase>()

private val cachedURIXsds = LinkedList<String>()
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved

private val simpleValueStack = Stack<Value.Builder>()
private val messageValueStack = Stack<Message.Builder>()
private val listValueStack = Stack<ListValue.Builder>()

// key - qName of the parent
private val msgBuilderWrapperMap = HashMap<QName, MessageBuilderWrapper>()

private lateinit var messageBuilder: Message.Builder

private var metadataBuilder = MessageMetadata.newBuilder()

private var foundMsgType = false

private val allElements = mutableMapOf<QName, Value.KindCase>()
private val xsdElements = mutableMapOf<QName, List<XmlElementWrapper>>()

@Throws(XMLStreamException::class)
override fun next(): Int {
val n: Int = super.next()

when (n) {
START_ELEMENT -> {
val qName = QName(namespaceURI, localName, namespaceContext.getPrefix(namespaceURI))

elementStack.add(qName)

if (namespaceURI.startsWith("http") && !cachedURIXsds.contains(namespaceURI)) {
cacheXsdFromNamespaceURI(namespaceURI)
cachedURIXsds.add(namespaceURI)
}

for (i in 0 until attributeCount) {
val attributeName = getAttributeName(i).localPart
val attributeValue = getAttributeValue(i)

if (attributeName == "schemaLocation") {
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved
cacheXsdFromAttribute(attributeValue)
}
}

val elementType = if (allElements[qName] != KIND_NOT_SET) {
allElements[qName]
} else {
checkNotNull(xsdElements[elementStack[elementStack.lastIndex - 1]]?.find { it.qName == qName }).elementType
}

when(elementType) {
SIMPLE_VALUE -> {
elementTypeStack.push(SIMPLE_VALUE)
}
MESSAGE_VALUE -> {
elementTypeStack.push(MESSAGE_VALUE)

val builder = message()

if (attributeCount > 0) {
writeAttributes(builder)
}

messageValueStack.push(builder)
}
LIST_VALUE -> {
elementTypeStack.push(LIST_VALUE)

val parentName = checkNotNull(xsdElements[elementStack[elementStack.lastIndex - 1]]?.find { it.qName == qName }).qName

val msgBuilderWrapper: MessageBuilderWrapper = if (msgBuilderWrapperMap.contains(parentName)) {
checkNotNull(msgBuilderWrapperMap[parentName])
} else {
MessageBuilderWrapper(qName).also {
msgBuilderWrapperMap[qName] = it
}
}

val listBuilder = if (msgBuilderWrapper.contains(qName)) {
checkNotNull(msgBuilderWrapper[qName])
} else {
val list = listValue()
msgBuilderWrapper[qName] = list
list
}

if (attributeCount > 0) {
writeAttributes(listBuilder)
}

listValueStack.push(listBuilder)
}
null -> { throw IllegalArgumentException("There's no element for $qName") }
else -> { throw IllegalArgumentException("Element $qName is not a simpleValue, messageValue or listValue") }
}

// TODO: also use pointer
if (!foundMsgType) {
metadataBuilder.messageType = localName
foundMsgType = true
}
}
CHARACTERS -> {
if (text.isNotBlank()) {
val qName = elementStack[elementStack.lastIndex]
val localElementName = qName.localPart

val elementType = if (allElements[qName] != KIND_NOT_SET) {
allElements[qName]
} else {
checkNotNull(xsdElements[elementStack[elementStack.lastIndex - 1]]?.find { it.qName == qName }).elementType
}

when(elementType) {
SIMPLE_VALUE -> {
simpleValueStack.add(Value.newBuilder().setSimpleValue(text))
}
MESSAGE_VALUE -> {
val builder = messageValueStack.peek()
builder[localElementName] = text.toValue()
}
else -> { throw IllegalArgumentException("Element is not a simpleValue, messageValue or listValue") }
}
}
}
END_ELEMENT -> {
elementTypeStack.pop()

if (elementTypeStack.isNotEmpty()) {
val qName = elementStack.removeLast()

val parentType = elementTypeStack.peek()

val elementType = if (allElements[qName] != KIND_NOT_SET) {
allElements[qName]
} else {
checkNotNull(xsdElements[elementStack[elementStack.lastIndex]]?.find { it.qName == qName }).elementType
}

when(elementType) {
SIMPLE_VALUE -> {
if (parentType == MESSAGE_VALUE) {
messageValueStack.peek()[qName.localPart] = simpleValueStack.pop()
} else {
listValueStack.peek().add(simpleValueStack.pop())
}
}
MESSAGE_VALUE -> {
if (parentType == MESSAGE_VALUE) {
val message = messageValueStack.pop()
messageValueStack.peek()[qName.localPart] = message

message.addLists(qName)
} else {
val message = messageValueStack.pop()
listValueStack.peek().add(message)

message.addLists(qName)
}
}
LIST_VALUE -> {
if (parentType == MESSAGE_VALUE) {
val list = listValueStack.pop()
messageValueStack.peek()[qName.localPart] = list
} else {
val list = listValueStack.pop()
listValueStack.peek().add(list)

list.addLists(qName)
}
}
else -> { throw IllegalArgumentException("Element $qName is not a simpleValue, messageValue or listValue") }
}
} else {
messageBuilder = messageValueStack.pop()
}
}
}

return n
}

fun getMessage(): Message {
val metadata = rawMessage.metadata

metadataBuilder.apply {
id = metadata.id
timestamp = metadata.timestamp
protocol = metadata.protocol
// messageType = pointer?.let { map.getNode<String>(it) } ?: map.keys.first()
putAllProperties(metadata.propertiesMap)
}

messageBuilder.metadata = metadataBuilder.build()

if (rawMessage.hasParentEventId()) {
messageBuilder.parentEventId = rawMessage.parentEventId
}

val message = messageBuilder.build()
messageBuilder.clear()
msgBuilderWrapperMap.clear()

return message
}

fun clearElements() { elementStack.clear() }

private fun writeAttributes(messageBuilder: Message.Builder) {
for (i in 0 until attributeCount) {
messageBuilder[getAttributeName(i).localPart] = getAttributeValue(i)
}
}

private fun writeAttributes(listBuilder: ListValue.Builder) {
val builder = message()

for (i in 0 until attributeCount) {
builder.apply {
this[getAttributeName(i).localPart] = getAttributeValue(i)
}
}

listBuilder.add(builder)
}

private fun Message.Builder.addLists(qName: QName) {
if (msgBuilderWrapperMap.contains(qName)) {
checkNotNull(msgBuilderWrapperMap[qName]).listBuilderMap.forEach {
this[it.key.toString()] = it.value
}
}
}

private fun ListValue.Builder.addLists(qName: QName) {
if (msgBuilderWrapperMap.contains(qName)) {
checkNotNull(msgBuilderWrapperMap[qName]).listBuilderMap.forEach {
add(it.value)
}
}
}

private fun cacheXsdFromAttribute(attributeValue: String) {
val xsdFileName = "tmp/" + attributeValue.split(' ')[1]
val map = mutableMapOf<QName, Value.KindCase>()

xmlSchemaCore.getXSDElements(xsdFileName).forEach {
xsdElements.putIfAbsent(it.key, it.value)
}

// TODO: figure out something better
xsdElements.values.flatten().distinct().map { it.qName to it.elementType }
.forEach { if (!map.containsKey(it.first)) map[it.first] = it.second else map[it.first] = KIND_NOT_SET }

allElements.putAll(map)
}

private fun cacheXsdFromNamespaceURI(namespaceURI: String) {
val props = xmlSchemaCore.xsdProperties

val xsdFileName = props.getProperty(namespaceURI.substring(7))
val map = mutableMapOf<QName, Value.KindCase>()

xmlSchemaCore.getXSDElements(xsdFileName).forEach {
xsdElements.putIfAbsent(it.key, it.value)
}

// TODO: figure out something better
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved
xsdElements.values.flatten().distinct().map { it.qName to it.elementType }
.forEach { if (!map.containsKey(it.first)) map[it.first] = it.second else map[it.first] = KIND_NOT_SET }

allElements.putAll(map)
}

companion object {
private val LOGGER = KotlinLogging.logger { }
}

private inner class MessageBuilderWrapper(qName: QName, val listBuilderMap: MutableMap<QName, ListValue.Builder> = mutableMapOf()) {
lateinit var messageBuilder: Message.Builder

operator fun set(key: QName, value: ListValue.Builder) {
listBuilderMap[key] = value
}

fun contains(key: QName): Boolean = listBuilderMap.contains(key)

operator fun get(key: QName): ListValue.Builder? = listBuilderMap[key]
}
}
Loading