Skip to content

Commit

Permalink
Merge branch 'bugfix/IJMP-1958-Remote-conflict-in-file' into 'release…
Browse files Browse the repository at this point in the history
…/v2.0.2'

IJMP-1958-Remote-conflict-in-file

See merge request ijmp/for-mainframe!616
  • Loading branch information
ATsikhamirau committed Nov 20, 2024
2 parents 33f2341 + fd013fd commit 82b6fb7
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class DefaultContentAdapter(dataOpsManager: DataOpsManager) : MFContentAdapterBa
override val vFileClass = MFVirtualFile::class.java
override val attributesClass = FileAttributes::class.java

override fun <T> adaptWhitespaces(content: T): T = content

/**
* Passes content to mainframe with no changes.
* @see MFContentAdapterBase.adaptContentToMainframe
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,11 @@ abstract class LReclContentAdapter<Attributes : FileAttributes>(
} else {
var nextLine = it
while (nextLine.length > lrecl) {
resultRows.add(nextLine.slice(IntRange(0, lrecl - 1)))
nextLine = nextLine.slice(IntRange(lrecl, nextLine.length - 1))
// make sure that substring we are going to split does not end with whitespace
// If it does, trim the trailing whitespace and move the remaining string to the next line
val lineToInspect = nextLine.substring(0, lrecl)
resultRows.add(if (lineToInspect.last().isWhitespace()) lineToInspect.trimEnd() else lineToInspect)
nextLine = nextLine.substring(lrecl, nextLine.length)
}
resultRows.add(nextLine)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,12 @@ interface MFContentAdapter {
* @return adapted content bytes.
*/
fun adaptContentFromMainframe(content: ByteArray, file: VirtualFile): ByteArray

/**
* Additionally prepares content for uploading to the mainframe. Function adapts trailing whitespaces
* based on content adapter. Needs to be overriden by any content adapter which extends this interface
* @param content content to adapt
* @return adapted content
*/
fun <T> adaptWhitespaces(content: T) : T
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ abstract class MFContentAdapterBase<Attributes : FileAttributes>(
@Suppress("UNCHECKED_CAST")
override fun <T>prepareContentToMainframe(content: T, file: VirtualFile): T {
val attributes = dataOpsManager.tryToGetAttributes(file) ?: return content
return if (attributes.`is`(attributesClass)) adaptContentToMainframe(content, attributes as Attributes) else content
return if (attributes.`is`(attributesClass)) { adaptContentToMainframe(content, attributes as Attributes) } else content
}

/**
Expand All @@ -79,4 +79,17 @@ abstract class MFContentAdapterBase<Attributes : FileAttributes>(
attributes as Attributes
) else content
}

/**
* Function is used to remove all trailing whitespaces x'40' from each line of content
* separated by CRLF(\r\n or \r or \n) flag
* @param content content in string format (ISO codepage)
* @return content in string format without trailing whitespaces
*/
protected fun removeTrailingWhitespaces(content: String) : String {
val lineSeparatorRegex = "\r\n|\n|\r"
val contentRows = content.split(Regex(lineSeparatorRegex))
return contentRows
.joinToString("\n") { if (it.isNotEmpty() && it.last().isWhitespace()) it.trimEnd() else it }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ class MemberContentAdapter(
override val vFileClass = MFVirtualFile::class.java
override val attributesClass = RemoteMemberAttributes::class.java

@Suppress("UNCHECKED_CAST")
override fun <T> adaptWhitespaces(content: T): T {
content.castOrNull<String>()?.let {
return removeTrailingWhitespaces(it) as T
}
content.castOrNull<ByteArray>()?.let {
return removeTrailingWhitespaces(String(it)).toByteArray() as T
}
return content
}

/**
* Checks if text exceed record length of parent dataset. If it is then it transfers
* the end of the line on the next row. It also checks file on variable format.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ class SeqDatasetContentAdapter(
override val vFileClass = MFVirtualFile::class.java
override val attributesClass = RemoteDatasetAttributes::class.java

@Suppress("UNCHECKED_CAST")
override fun <T> adaptWhitespaces(content: T): T {
content.castOrNull<String>()?.let {
return removeTrailingWhitespaces(it) as T
}
content.castOrNull<ByteArray>()?.let {
return removeTrailingWhitespaces(String(it)).toByteArray() as T
}
return content
}

/**
* Checks if text exceed record length of dataset. If it is then it transfers
* the end of the line on the next row. It also checks file on variable format.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ abstract class RemoteAttributedContentSynchronizer<FAttributes : FileAttributes>
if (doUploadContent && isFileUploadNeeded(syncProvider)) {
log.info("Save strategy decided to forcefully update file content on mainframe.")
val newContentPrepared = contentAdapter.prepareContentToMainframe(fileContent, syncProvider.file)
.let { contentAdapter.adaptWhitespaces(it) }
runWriteActionInEdtAndWait { syncProvider.loadNewContent(newContentPrepared) }
uploadNewContent(attributes, newContentPrepared, progressIndicator)
successfulStatesStorage.writeStream(recordId).use { it.write(newContentPrepared) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBA Group
* Zowe Community
*/

package eu.ibagroup.formainframe.dataops.content.adapters

import eu.ibagroup.formainframe.dataops.DataOpsManager
import eu.ibagroup.formainframe.testutils.WithApplicationShouldSpec
import eu.ibagroup.formainframe.testutils.testServiceImpl.TestDataOpsManagerImpl
import io.kotest.assertions.assertSoftly
import io.kotest.matchers.shouldBe
import io.mockk.clearAllMocks
import io.mockk.spyk
import io.mockk.unmockkAll

class DefaultContentAdapterTestSpec: WithApplicationShouldSpec({

afterSpec {
clearAllMocks()
unmockkAll()
}

context("content/adapters: DefaultContentAdapter") {
val dataOpsManager = DataOpsManager.getService() as TestDataOpsManagerImpl
val classUnderTest = spyk(DefaultContentAdapter(dataOpsManager), "DefaultContentAdapter")

should("shouldReturnAdaptedContent_whenAdaptWhitespaces_givenContentToAdapt") {
val contentToAdapt = "This is a test string.\n Content should be replaced by \n" +
"this content without trailing \n" +
"\n" +
" whitespaces..."
val expected = "This is a test string.\n Content should be replaced by \n" +
"this content without trailing \n" +
"\n" +
" whitespaces..."
val adaptedContent = classUnderTest.adaptWhitespaces(contentToAdapt)

assertSoftly {
adaptedContent shouldBe expected
}
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBA Group
* Zowe Community
*/

package eu.ibagroup.formainframe.dataops.content.adapters

import eu.ibagroup.formainframe.dataops.DataOpsManager
import eu.ibagroup.formainframe.testutils.WithApplicationShouldSpec
import eu.ibagroup.formainframe.testutils.testServiceImpl.TestDataOpsManagerImpl
import io.kotest.assertions.assertSoftly
import io.kotest.matchers.shouldBe
import io.mockk.clearAllMocks
import io.mockk.spyk
import io.mockk.unmockkAll

class MemberContentAdapterTestSpec: WithApplicationShouldSpec({

afterSpec {
clearAllMocks()
unmockkAll()
}

context("content/adapters: MemberContentAdapter") {
val dataOpsManager = DataOpsManager.getService() as TestDataOpsManagerImpl
val classUnderTest = spyk(MemberContentAdapter(dataOpsManager), "MemberContentAdapter")

should("shouldReturnAdaptedContent_whenAdaptWhitespaces_givenContentToAdapt") {
val contentToAdapt = "This is a test string.\n Content should be replaced by \n" +
"this content without trailing \n" +
"\n" +
" whitespaces..."
val expected = "This is a test string.\n Content should be replaced by\n" +
"this content without trailing\n" +
"\n" +
" whitespaces..."
val adaptedContent = classUnderTest.adaptWhitespaces(contentToAdapt)

assertSoftly {
adaptedContent shouldBe expected
}
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBA Group
* Zowe Community
*/

package eu.ibagroup.formainframe.dataops.content.adapters

import eu.ibagroup.formainframe.dataops.DataOpsManager
import eu.ibagroup.formainframe.testutils.WithApplicationShouldSpec
import eu.ibagroup.formainframe.testutils.testServiceImpl.TestDataOpsManagerImpl
import io.kotest.assertions.assertSoftly
import io.kotest.matchers.shouldBe
import io.mockk.clearAllMocks
import io.mockk.spyk
import io.mockk.unmockkAll

class SeqDatasetContentAdapterTestSpec: WithApplicationShouldSpec({

afterSpec {
clearAllMocks()
unmockkAll()
}

context("content/adapters: SeqDatasetContentAdapter") {
val dataOpsManager = DataOpsManager.getService() as TestDataOpsManagerImpl
val classUnderTest = spyk(SeqDatasetContentAdapter(dataOpsManager), "SeqDatasetContentAdapter")

should("shouldReturnAdaptedContent_whenAdaptWhitespaces_givenContentToAdapt") {
val contentToAdapt = "This is a test string.\n Content should be replaced by \n" +
"this content without trailing \n" +
"\n" +
" whitespaces..."
val expected = "This is a test string.\n Content should be replaced by\n" +
"this content without trailing\n" +
"\n" +
" whitespaces..."
val adaptedContent = classUnderTest.adaptWhitespaces(contentToAdapt)

assertSoftly {
adaptedContent shouldBe expected
}
}
}
})

0 comments on commit 82b6fb7

Please sign in to comment.