Skip to content

Commit

Permalink
Skip Notification Event
Browse files Browse the repository at this point in the history
  • Loading branch information
blackmo18 committed Sep 23, 2023
1 parent c9d8800 commit f8f3d5a
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 4 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,27 @@ val tsvReader = csvReader {
| ~~skipMissMatchedRow~~ | `false` | Deprecated. Replace with appropriate values in `excessFieldsRowBehaviour` and `insufficientFieldsRowBehaviour`, e.g. both set to `IGNORE`. ~~Whether to skip an invalid row. If `ignoreExcessCols` is true, only rows with less than the expected number of columns will be skipped.~~ |
| excessFieldsRowBehaviour | `ERROR` | Behaviour to use when a row has more fields (columns) than expected. `ERROR` (default), `IGNORE` (skip the row) or `TRIM` (remove the excess fields at the end of the row to match the expected number of fields). |
| insufficientFieldsRowBehaviour | `ERROR` | Behaviour to use when a row has fewer fields (columns) than expected. `ERROR` (default), `IGNORE` (skip the row) or `EMPTY_STRING` (replace missing fields with an empty string). |
| onSkippedEvent | `null` | A pluggable Interface on listening when skipping either an insufficient or an excess number of fields. |

#### Interface Listing to Skipped Row Events`
```kotlin
csvReader {
excessFieldsRowBehaviour = ExcessFieldsRowBehaviour.IGNORE
insufficientFieldsRowBehaviour = InsufficientFieldsRowBehaviour.IGNORE
onSkippedEvent = skipNotification {
listeners["id"] = SkipNotify { println(it) }
}
}
```

* skipNotification - builder function for creating notification listener
* listeners `Mutable<String, ISkippedRow>` - map of listener function that will be triggered
* `SkipNotify` - functional interface template
```kotlin
fun interface SkipNotify {
fun onSkipped(row: SkippedRow)
}
```

### CSV Write examples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.github.doyaaaaaken.kotlincsv.client
import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext
import com.github.doyaaaaaken.kotlincsv.dsl.context.ExcessFieldsRowBehaviour
import com.github.doyaaaaaken.kotlincsv.dsl.context.InsufficientFieldsRowBehaviour
import com.github.doyaaaaaken.kotlincsv.event.skip.SkipType
import com.github.doyaaaaaken.kotlincsv.event.skip.SkippedRow
import com.github.doyaaaaaken.kotlincsv.parser.CsvParser
import com.github.doyaaaaaken.kotlincsv.util.CSVAutoRenameFailedException
import com.github.doyaaaaaken.kotlincsv.util.CSVFieldNumDifferentException
Expand Down Expand Up @@ -55,13 +57,13 @@ class CsvFileReader internal constructor(
logger.info("trimming excess rows. [csv row num = ${idx + 1}, fields num = ${row.size}, fields num of row = $numFieldsInRow]")
row.subList(0, numFieldsInRow)
} else if (ctx.skipMissMatchedRow || ctx.excessFieldsRowBehaviour == ExcessFieldsRowBehaviour.IGNORE) {
skipMismatchedRow(idx, row, numFieldsInRow)
skipMismatchedRow(idx, row, numFieldsInRow, SkipType.ExcessFieldsRowBehaviour)
} else {
throw CSVFieldNumDifferentException(numFieldsInRow, row.size, idx + 1)
}
} else if (numFieldsInRow != row.size) {
if (ctx.skipMissMatchedRow || ctx.insufficientFieldsRowBehaviour == InsufficientFieldsRowBehaviour.IGNORE) {
skipMismatchedRow(idx, row, numFieldsInRow)
skipMismatchedRow(idx, row, numFieldsInRow, SkipType.InsufficientFieldsRowBehaviour)
} else if (ctx.insufficientFieldsRowBehaviour == InsufficientFieldsRowBehaviour.EMPTY_STRING) {
val numOfMissingFields = numFieldsInRow - row.size
row.plus(List(numOfMissingFields) { "" })
Expand All @@ -77,9 +79,13 @@ class CsvFileReader internal constructor(
private fun skipMismatchedRow(
idx: Int,
row: List<String>,
numFieldsInRow: Int
numFieldsInRow: Int,
skipType: SkipType
): Nothing? {
logger.info("skip miss matched row. [csv row num = ${idx + 1}, fields num = ${row.size}, fields num of first row = $numFieldsInRow]")
val message = "skip miss matched row. [csv row num = ${idx + 1}, fields num = ${row.size}, fields num of first row = $numFieldsInRow]"
val skippedRow = SkippedRow(idx, row, message, skipType)
ctx.onSkippedEvent?.notify(skippedRow)
logger.info(message)
return null
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.doyaaaaaken.kotlincsv.dsl.context

import com.github.doyaaaaaken.kotlincsv.event.skip.INotifySkippedEvent
import com.github.doyaaaaaken.kotlincsv.util.Const
import com.github.doyaaaaaken.kotlincsv.util.CsvDslMarker
import com.github.doyaaaaaken.kotlincsv.util.logger.Logger
Expand Down Expand Up @@ -88,6 +89,11 @@ interface ICsvReaderContext {
* If a row exceeds have the expected number of fields (columns), how, and if, the reader should proceed
*/
val excessFieldsRowBehaviour: ExcessFieldsRowBehaviour

/**
* A pluggable Interface on listening when skipping either an insufficient or an excess number of fields
*/
val onSkippedEvent: INotifySkippedEvent?
}

enum class InsufficientFieldsRowBehaviour {
Expand Down Expand Up @@ -142,4 +148,5 @@ class CsvReaderContext : ICsvReaderContext {
override var autoRenameDuplicateHeaders: Boolean = false
override var insufficientFieldsRowBehaviour: InsufficientFieldsRowBehaviour = InsufficientFieldsRowBehaviour.ERROR
override var excessFieldsRowBehaviour: ExcessFieldsRowBehaviour = ExcessFieldsRowBehaviour.ERROR
override var onSkippedEvent: INotifySkippedEvent? = null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.doyaaaaaken.kotlincsv.event.skip


fun interface SkipNotify {
fun onSkipped(row: SkippedRow)
}

/**
* interface function that will be triggered when skipping either insufficient or excess number of fields
*/
fun interface INotifySkippedEvent {
fun notify(skippedRow: SkippedRow)
}


fun skipNotification(block: NotifySkipEvent.() -> Unit): INotifySkippedEvent {
return NotifySkipEvent().apply(block)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.github.doyaaaaaken.kotlincsv.event.skip

class NotifySkipEvent: INotifySkippedEvent {
val listeners = mutableMapOf<String, SkipNotify>()
override fun notify(skippedRow: SkippedRow) {
listeners.forEach { (_, value) -> value.onSkipped(skippedRow) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.github.doyaaaaaken.kotlincsv.event.skip
enum class SkipType {
InsufficientFieldsRowBehaviour,
ExcessFieldsRowBehaviour
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.github.doyaaaaaken.kotlincsv.event.skip

/**
* data class containing relevant information
*/
data class SkippedRow(
/**
* row index in the csv file
*/
val idx: Int,
/**
* row values
*/
val row: List<String>,
/**
* reason for skipping
*/
val message: String,
/**
* type of skip
*/
val skipType: SkipType

)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext
import com.github.doyaaaaaken.kotlincsv.dsl.context.ExcessFieldsRowBehaviour
import com.github.doyaaaaaken.kotlincsv.dsl.context.InsufficientFieldsRowBehaviour
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
import com.github.doyaaaaaken.kotlincsv.event.skip.SkipNotify
import com.github.doyaaaaaken.kotlincsv.event.skip.skipNotification
import com.github.doyaaaaaken.kotlincsv.util.CSVFieldNumDifferentException
import com.github.doyaaaaaken.kotlincsv.util.CSVParseFormatException
import com.github.doyaaaaaken.kotlincsv.util.Const
Expand Down Expand Up @@ -206,6 +208,22 @@ class CsvReaderTest : WordSpec({
actual.size shouldBe 1
}
}
"it should be be possible to listen to to skip events for insufficient or excess fields " {
val expected = listOf(listOf("a", "b"))
val actual =
csvReader {
excessFieldsRowBehaviour = ExcessFieldsRowBehaviour.IGNORE
insufficientFieldsRowBehaviour = InsufficientFieldsRowBehaviour.IGNORE
onSkippedEvent = skipNotification {
listeners["id"] = SkipNotify { println(it) }
}
}.readAll(readTestDataFile("varying-column-lengths.csv"))

assertSoftly {
actual shouldBe expected
actual.size shouldBe 1
}
}
"it should be be possible to replace insufficient fields with strings and skip rows with excess fields" {
val expected = listOf(listOf("a", "b"), listOf("c", ""))
val actual =
Expand Down

0 comments on commit f8f3d5a

Please sign in to comment.