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

Skip Notification Event #131

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,25 @@ val tsvReader = csvReader {
escapeChar = '\\'
}
```
#### Listening Interface for 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 to be notified
* `SkipNotify` - functional interface template
```kotlin
fun interface SkipNotify {
fun onSkipped(row: SkippedRow)
}
```
| Option | default value | description |
|--------------------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| logger | _no-op_ | Logger instance for logging debug information at runtime. |
Expand All @@ -175,6 +193,7 @@ 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. |

### 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 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