Implemented not splitting at masked separator symbols and unmasking masked characters
This commit is contained in:
parent
5fc3e4cc63
commit
8d16cab528
|
@ -9,6 +9,8 @@ class Separators {
|
||||||
const val DataElementGroupsSeparator = "+"
|
const val DataElementGroupsSeparator = "+"
|
||||||
|
|
||||||
const val DataElementsSeparator = ":"
|
const val DataElementsSeparator = ":"
|
||||||
|
|
||||||
|
const val MaskingCharacter = "?"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -22,7 +22,7 @@ open class ResponseParser {
|
||||||
|
|
||||||
open fun parse(response: String): Response {
|
open fun parse(response: String): Response {
|
||||||
try {
|
try {
|
||||||
val segments = response.split(Separators.SegmentSeparator)
|
val segments = splitIntoPartsAndUnmask(response, Separators.SegmentSeparator)
|
||||||
|
|
||||||
val parsedSegments = segments.mapNotNull { parseSegment(it) }
|
val parsedSegments = segments.mapNotNull { parseSegment(it) }
|
||||||
|
|
||||||
|
@ -43,7 +43,8 @@ open class ResponseParser {
|
||||||
protected open fun parseSegment(segment: String): ReceivedSegment? {
|
protected open fun parseSegment(segment: String): ReceivedSegment? {
|
||||||
try {
|
try {
|
||||||
if (segment.isNotEmpty()) { // filter out empty lines
|
if (segment.isNotEmpty()) { // filter out empty lines
|
||||||
val dataElementGroups = segment.split(Separators.DataElementGroupsSeparator)
|
val dataElementGroups = splitIntoPartsAndUnmask(segment, Separators.DataElementGroupsSeparator)
|
||||||
|
|
||||||
val segmentId = segment.substring(0, segment.indexOf(Separators.DataElementsSeparator))
|
val segmentId = segment.substring(0, segment.indexOf(Separators.DataElementsSeparator))
|
||||||
|
|
||||||
return parseSegment(segment, segmentId, dataElementGroups)
|
return parseSegment(segment, segmentId, dataElementGroups)
|
||||||
|
@ -72,36 +73,36 @@ open class ResponseParser {
|
||||||
|
|
||||||
|
|
||||||
protected open fun parseMessageHeaderSegment(segment: String, dataElementGroups: List<String>): ReceivedMessageHeader {
|
protected open fun parseMessageHeaderSegment(segment: String, dataElementGroups: List<String>): ReceivedMessageHeader {
|
||||||
val messageSize = dataElementGroups[1].toInt()
|
val messageSize = parseInt(dataElementGroups[1])
|
||||||
val finTsVersion = dataElementGroups[2].toInt()
|
val finTsVersion = parseInt(dataElementGroups[2])
|
||||||
val dialogId = dataElementGroups[3]
|
val dialogId = parseString(dataElementGroups[3])
|
||||||
val messageNumber = dataElementGroups[4].toInt()
|
val messageNumber = parseInt(dataElementGroups[4])
|
||||||
|
|
||||||
return ReceivedMessageHeader(messageSize, finTsVersion, dialogId, messageNumber, segment)
|
return ReceivedMessageHeader(messageSize, finTsVersion, dialogId, messageNumber, segment)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun parseSynchronization(segment: String, dataElementGroups: List<String>): ReceivedSynchronization {
|
protected open fun parseSynchronization(segment: String, dataElementGroups: List<String>): ReceivedSynchronization {
|
||||||
val customerSystemId = dataElementGroups[1]
|
val customerSystemId = parseString(dataElementGroups[1])
|
||||||
val lastMessageNumber = if (dataElementGroups.size > 2) dataElementGroups[2] else null
|
val lastMessageNumber = if (dataElementGroups.size > 2) parseString(dataElementGroups[2]) else null
|
||||||
val securityReferenceNumberForSigningKey = if (dataElementGroups.size > 3) dataElementGroups[3] else null
|
val securityReferenceNumberForSigningKey = if (dataElementGroups.size > 3) parseString(dataElementGroups[3]) else null
|
||||||
val securityReferenceNumberForDigitalSignature = if (dataElementGroups.size > 4) dataElementGroups[4] else null
|
val securityReferenceNumberForDigitalSignature = if (dataElementGroups.size > 4) parseString(dataElementGroups[4]) else null
|
||||||
|
|
||||||
return ReceivedSynchronization(segment, customerSystemId, lastMessageNumber,
|
return ReceivedSynchronization(segment, customerSystemId, lastMessageNumber,
|
||||||
securityReferenceNumberForSigningKey, securityReferenceNumberForDigitalSignature)
|
securityReferenceNumberForSigningKey, securityReferenceNumberForDigitalSignature)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun parseBankParameters(segment: String, dataElementGroups: List<String>): BankParameters {
|
protected open fun parseBankParameters(segment: String, dataElementGroups: List<String>): BankParameters {
|
||||||
val bpdVersion = dataElementGroups[1].toInt()
|
val bpdVersion = parseInt(dataElementGroups[1])
|
||||||
val bankDetails = parseBankDetails(dataElementGroups[2])
|
val bankDetails = parseBankDetails(dataElementGroups[2])
|
||||||
val bankName = dataElementGroups[3]
|
val bankName = parseString(dataElementGroups[3])
|
||||||
|
|
||||||
val countMaxJobsPerMessage = dataElementGroups[4].toInt()
|
val countMaxJobsPerMessage = parseInt(dataElementGroups[4])
|
||||||
val supportedLanguages = parseLanguages(dataElementGroups[5])
|
val supportedLanguages = parseLanguages(dataElementGroups[5])
|
||||||
val supportedHbciVersions = parseHbciVersions(dataElementGroups[6])
|
val supportedHbciVersions = parseHbciVersions(dataElementGroups[6])
|
||||||
|
|
||||||
val maxMessageSize = if (dataElementGroups.size > 7) dataElementGroups[7].toInt() else null
|
val maxMessageSize = if (dataElementGroups.size > 7) parseInt(dataElementGroups[7]) else null
|
||||||
val minTimeout = if (dataElementGroups.size > 8) dataElementGroups[8].toInt() else null
|
val minTimeout = if (dataElementGroups.size > 8) parseInt(dataElementGroups[8]) else null
|
||||||
val maxTimeout = if (dataElementGroups.size > 9) dataElementGroups[9].toInt() else null
|
val maxTimeout = if (dataElementGroups.size > 9) parseInt(dataElementGroups[9]) else null
|
||||||
|
|
||||||
return BankParameters(bpdVersion, bankDetails.bankCountryCode, bankDetails.bankCode, bankName,
|
return BankParameters(bpdVersion, bankDetails.bankCountryCode, bankDetails.bankCode, bankName,
|
||||||
countMaxJobsPerMessage, supportedLanguages, supportedHbciVersions, maxMessageSize, minTimeout, maxTimeout, segment)
|
countMaxJobsPerMessage, supportedLanguages, supportedHbciVersions, maxMessageSize, minTimeout, maxTimeout, segment)
|
||||||
|
@ -116,11 +117,11 @@ open class ResponseParser {
|
||||||
|
|
||||||
|
|
||||||
protected open fun parseUserParameters(segment: String, dataElementGroups: List<String>): UserParameters {
|
protected open fun parseUserParameters(segment: String, dataElementGroups: List<String>): UserParameters {
|
||||||
val customerId = dataElementGroups[1]
|
val customerId = parseString(dataElementGroups[1])
|
||||||
val updVersion = dataElementGroups[2].toInt()
|
val updVersion = parseInt(dataElementGroups[2])
|
||||||
val areListedJobsBlocked = dataElementGroups[3] == "0"
|
val areListedJobsBlocked = dataElementGroups[3] == "0"
|
||||||
val username = if (dataElementGroups.size > 4) returnNullIfEmpty(dataElementGroups[4]) else null
|
val username = if (dataElementGroups.size > 4) parseStringToNullIfEmpty(dataElementGroups[4]) else null
|
||||||
val extension = if (dataElementGroups.size > 5) returnNullIfEmpty(dataElementGroups[5]) else null
|
val extension = if (dataElementGroups.size > 5) parseStringToNullIfEmpty(dataElementGroups[5]) else null
|
||||||
|
|
||||||
return UserParameters(customerId, updVersion, areListedJobsBlocked, username, extension, segment)
|
return UserParameters(customerId, updVersion, areListedJobsBlocked, username, extension, segment)
|
||||||
}
|
}
|
||||||
|
@ -128,19 +129,19 @@ open class ResponseParser {
|
||||||
protected open fun parseAccountInfo(segment: String, dataElementGroups: List<String>): AccountInfo {
|
protected open fun parseAccountInfo(segment: String, dataElementGroups: List<String>): AccountInfo {
|
||||||
// this is parsing a Kontoverbindung. May extract a method for it.
|
// this is parsing a Kontoverbindung. May extract a method for it.
|
||||||
val accountDetails = getDataElements(dataElementGroups[1])
|
val accountDetails = getDataElements(dataElementGroups[1])
|
||||||
val accountNumber = accountDetails[0]
|
val accountNumber = parseString(accountDetails[0])
|
||||||
val subAccountAttribute = returnNullIfEmpty(accountDetails[1])
|
val subAccountAttribute = parseStringToNullIfEmpty(accountDetails[1])
|
||||||
val bankCountryCode = accountDetails[2].toInt()
|
val bankCountryCode = parseInt(accountDetails[2])
|
||||||
val bankCode = accountDetails[3]
|
val bankCode = parseString(accountDetails[3])
|
||||||
|
|
||||||
val iban = returnNullIfEmpty(dataElementGroups[2])
|
val iban = parseStringToNullIfEmpty(dataElementGroups[2])
|
||||||
val customerId = dataElementGroups[3]
|
val customerId = parseString(dataElementGroups[3])
|
||||||
val accountType = parseCodeEnum(dataElementGroups[4], AccountTypeCode.values()).type
|
val accountType = parseCodeEnum(dataElementGroups[4], AccountTypeCode.values()).type
|
||||||
val currency = dataElementGroups[5]
|
val currency = parseString(dataElementGroups[5])
|
||||||
val accountHolderName1 = dataElementGroups[6]
|
val accountHolderName1 = parseString(dataElementGroups[6])
|
||||||
val accountHolderName2 = returnNullIfEmpty(dataElementGroups[7])
|
val accountHolderName2 = parseStringToNullIfEmpty(dataElementGroups[7])
|
||||||
val productName = returnNullIfEmpty(dataElementGroups[8])
|
val productName = parseStringToNullIfEmpty(dataElementGroups[8])
|
||||||
val limit = returnNullIfEmpty(dataElementGroups[9]) // TODO: parse limit
|
val limit = parseStringToNullIfEmpty(dataElementGroups[9]) // TODO: parse limit
|
||||||
|
|
||||||
// TODO: parse allowed jobs
|
// TODO: parse allowed jobs
|
||||||
// TODO: parse extension
|
// TODO: parse extension
|
||||||
|
@ -153,7 +154,7 @@ open class ResponseParser {
|
||||||
protected open fun parseBankDetails(dataElementsGroup: String): Kreditinstitutskennung {
|
protected open fun parseBankDetails(dataElementsGroup: String): Kreditinstitutskennung {
|
||||||
val detailsStrings = getDataElements(dataElementsGroup)
|
val detailsStrings = getDataElements(dataElementsGroup)
|
||||||
|
|
||||||
return Kreditinstitutskennung(detailsStrings[0].toInt(), detailsStrings[1])
|
return Kreditinstitutskennung(parseInt(detailsStrings[0]), parseString(detailsStrings[1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun parseLanguages(dataElementsGroup: String): List<Dialogsprache> {
|
protected open fun parseLanguages(dataElementsGroup: String): List<Dialogsprache> {
|
||||||
|
@ -185,7 +186,7 @@ open class ResponseParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun parseSecurityMethodVersion(versionString: String): VersionDesSicherheitsverfahrens {
|
protected open fun parseSecurityMethodVersion(versionString: String): VersionDesSicherheitsverfahrens {
|
||||||
val versionInt = versionString.toInt()
|
val versionInt = parseInt(versionString)
|
||||||
|
|
||||||
return VersionDesSicherheitsverfahrens.values().first { it.methodNumber == versionInt }
|
return VersionDesSicherheitsverfahrens.values().first { it.methodNumber == versionInt }
|
||||||
}
|
}
|
||||||
|
@ -201,7 +202,47 @@ open class ResponseParser {
|
||||||
|
|
||||||
|
|
||||||
protected open fun getDataElements(dataElementGroup: String): List<String> {
|
protected open fun getDataElements(dataElementGroup: String): List<String> {
|
||||||
return dataElementGroup.split(Separators.DataElementsSeparator)
|
return splitIntoPartsAndUnmask(dataElementGroup, Separators.DataElementsSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If separator symbols are used in data values, they are masked with '?'.
|
||||||
|
* (e. g. 'https?://' should not be split into two data elements.)
|
||||||
|
*
|
||||||
|
* Don't split string at masked separators.
|
||||||
|
*
|
||||||
|
* After string is split, unmask separator
|
||||||
|
*
|
||||||
|
* Also binary data shouldn't be taken into account (TODO: really?).
|
||||||
|
*/
|
||||||
|
protected open fun splitIntoPartsAndUnmask(dataString: String, separator: String): List<String> {
|
||||||
|
val separatorMask = Separators.MaskingCharacter + separator
|
||||||
|
val maskedSymbolsGuard = Separators.MaskingCharacter + "§"
|
||||||
|
|
||||||
|
val maskedDataString = dataString.replace(separatorMask, maskedSymbolsGuard)
|
||||||
|
|
||||||
|
val elements = maskedDataString.split(separator)
|
||||||
|
|
||||||
|
return elements.map { it.replace(maskedSymbolsGuard, separator) }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun parseInt(string: String): Int {
|
||||||
|
return parseString(string).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun parseStringToNullIfEmpty(string: String): String? {
|
||||||
|
val parsedString = parseString(string)
|
||||||
|
|
||||||
|
return if (parsedString.isEmpty()) null else parsedString
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun parseString(string: String): String {
|
||||||
|
|
||||||
|
return string
|
||||||
|
// unmask mask data elements separator ('?:')
|
||||||
|
.replace(Separators.MaskingCharacter + Separators.DataElementsSeparator, Separators.DataElementsSeparator)
|
||||||
|
// masking character '?' is also masked, in his case with '??'
|
||||||
|
.replace(Separators.MaskingCharacter + Separators.MaskingCharacter, Separators.MaskingCharacter)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun parseBoolean(dataElement: String): Boolean {
|
protected open fun parseBoolean(dataElement: String): Boolean {
|
||||||
|
@ -212,8 +253,4 @@ open class ResponseParser {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun returnNullIfEmpty(string: String): String? {
|
|
||||||
return if (string.isEmpty()) null else string
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -18,6 +18,67 @@ class ResponseParserTest {
|
||||||
private val underTest = ResponseParser()
|
private val underTest = ResponseParser()
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun doNotSplitMaskedSegmentSeparator() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
val result = underTest.parse(
|
||||||
|
"HNHBK:1:3+000000000596+300+abcd?'efg+2'" +
|
||||||
|
"HKIDN:2:2+280:12345678+9999999999+0+0'"
|
||||||
|
)
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result.receivedSegments).hasSize(2)
|
||||||
|
|
||||||
|
assertThat(result.messageHeader?.dialogId).isEqualTo("abcd'efg")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun doNotSplitMaskedDataElementGroupsSeparator() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
val result = underTest.parse(
|
||||||
|
"HNHBK:1:3+000000000596+300+abcd?+efg+2'" +
|
||||||
|
"HKIDN:2:2+280:12345678+9999999999+0+0'"
|
||||||
|
)
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result.receivedSegments).hasSize(2)
|
||||||
|
|
||||||
|
assertThat(result.messageHeader?.dialogId).isEqualTo("abcd+efg")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun doNotSplitMaskedDataElementsSeparator() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
val result = underTest.parse(
|
||||||
|
"HNHBK:1:3+000000000596+300+https?://www.example.org+2'" +
|
||||||
|
"HKIDN:2:2+280:12345678+9999999999+0+0'"
|
||||||
|
)
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result.receivedSegments).hasSize(2)
|
||||||
|
|
||||||
|
assertThat(result.messageHeader?.dialogId).isEqualTo("https://www.example.org")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unmaskMaskingCharacter() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
val result = underTest.parse(
|
||||||
|
"HNHBK:1:3+000000000596+300+abcd??efg+2'" +
|
||||||
|
"HKIDN:2:2+280:12345678+9999999999+0+0'"
|
||||||
|
)
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result.receivedSegments).hasSize(2)
|
||||||
|
|
||||||
|
assertThat(result.messageHeader?.dialogId).isEqualTo("abcd?efg")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun parseMessageHeader() {
|
fun parseMessageHeader() {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue