Skip to content

Commit

Permalink
Merge pull request #19 from 4sh/issue-18-fix-next-value
Browse files Browse the repository at this point in the history
fix #18 : don't iterate over next value when calling hasNext if next …
  • Loading branch information
xhanin authored Nov 2, 2023
2 parents a35e7cd + e9157a8 commit f43b0bf
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 3 deletions.
21 changes: 18 additions & 3 deletions src/main/kotlin/io/retable/Retable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,29 @@ abstract class BaseSupport<T : RetableColumns, O : ReadOptions>(val columns: T,
*
* Note that input is consumed when sequence is consumed, if the end is not reached the reader
* should be closed.
*
*/
fun read(input: InputStream): Retable<T> {
val rawData = object : Iterator<List<String>> {
val raw = iterator(input)
var nextConsumed: Boolean = true
var next: List<String>? = null
var lineNumber: Long = 0

/**
* Check current value consumption ([nextConsumed] is true) then process to check if iterator has next value and iterate to next value if it has. Set next value to null otherwise.
*/
override fun hasNext(): Boolean {
lineNumber++
next = if (raw.hasNext()) { raw.next() } else { null }
while (next != null && ignoreLine(next)) {
if (nextConsumed) {
lineNumber++
next = if (raw.hasNext()) { raw.next() } else { null }
while (next != null && ignoreLine(next)) {
lineNumber++
next = if (raw.hasNext()) { raw.next() } else { null }
}
if (next != null) {
nextConsumed = false
}
}
return next != null
}
Expand All @@ -104,10 +114,15 @@ abstract class BaseSupport<T : RetableColumns, O : ReadOptions>(val columns: T,
private fun isEmptyLine(list: List<String>?): Boolean =
list == null || list.isEmpty() || list.filter { !it.trim().isEmpty() }.isEmpty()

/**
* Returns current value of iterator. Iterate to next value if current value is null.
* Acknowledge current value consumption when called. [nextConsumed] is set to true
*/
override fun next(): List<String> {
if (next == null) hasNext() // make sure hasNext has been called so that next is fetched
return (next ?: throw IllegalStateException("no more records"))
.map { if (options.trimValues) it.trim() else it }
.also { nextConsumed = true }
}
}

Expand Down
39 changes: 39 additions & 0 deletions src/test/kotlin/io/retable/RetableCSVTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,45 @@ class RetableCSVTest {
)
}

@Test
fun `should not iterate to next record until iterator current value is consumed`() {
val csv = """
Prénom,Nom
Ñino,Dalton0
Ñino,Dalton1
Ñino,Dalton2
Ñino,Dalton3
Ñino,Dalton4
""".trimIndent()

val retable = Retable.csv(
options = CSVReadOptions(
charset = Charsets.ISO_8859_1
)
).read(ByteArrayInputStream(csv.toByteArray(Charsets.ISO_8859_1)))

val iterator = retable.records.iterator()
val values = mutableListOf<RetableRecord>()

while (iterator.hasNext()) {
iterator.hasNext()
values.add(iterator.next())
}

val columns = RetableColumns.ofNames(listOf("Prénom", "Nom"))

expectThat(values) {
get { size }.isEqualTo(5)
containsExactly(
RetableRecord(columns, 1, 2, listOf("Ñino", "Dalton0")),
RetableRecord(columns, 2, 3, listOf("Ñino", "Dalton1")),
RetableRecord(columns, 3, 4, listOf("Ñino", "Dalton2")),
RetableRecord(columns, 4, 5, listOf("Ñino", "Dalton3")),
RetableRecord(columns, 5, 6, listOf("Ñino", "Dalton4")),
)
}
}

@Test
fun `should parse csv with custom settings`() {
val csv = """
Expand Down

0 comments on commit f43b0bf

Please sign in to comment.