Skip to content

Commit

Permalink
Merge pull request #1181 from atlanhq/FT-717
Browse files Browse the repository at this point in the history
Adds interchangeable Excel and CSV options for export packages
  • Loading branch information
cmgrote authored Dec 30, 2024
2 parents 6b139d0 + 91c5bbd commit 5c11642
Show file tree
Hide file tree
Showing 29 changed files with 1,244 additions and 389 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: Apache-2.0
Copyright 2023 Atlan Pte. Ltd. */
package com.atlan.pkg.serde

/**
* Generic interface through which to write out tabular content.
*/
interface TabularWriter {
/**
* Create a header row for the tabular output.
*
* @param headers ordered map of header names and descriptions
*/
fun writeHeader(headers: Map<String, String>)

/**
* Create a header row for the tabular output.
*
* @param values ordered list of header column names
*/
fun writeHeader(values: Iterable<String>)

/**
* Write a row of data into the tabular output, where key of the map is the column name and the value
* is the value to write for that column of the row of data.
* Note: be sure you have first called {@code writeHeader} to output the header row.
*
* @param values map keyed by column name with values for the row of data
*/
fun writeRecord(values: Map<String, Any?>?)

/**
* Add a row of data to the end of the tabular output.
*
* @param data the row of data to add
*/
fun writeRecord(data: Iterable<Any?>?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package com.atlan.pkg.serde.csv

import com.atlan.model.assets.Asset
import com.atlan.pkg.Utils
import com.atlan.pkg.serde.TabularWriter
import de.siegmar.fastcsv.writer.CsvWriter
import de.siegmar.fastcsv.writer.LineDelimiter
import de.siegmar.fastcsv.writer.QuoteStrategies
Expand All @@ -22,7 +23,7 @@ import java.util.stream.Stream
*/
class CSVWriter
@JvmOverloads
constructor(path: String, fieldSeparator: Char = ',') : Closeable {
constructor(path: String, fieldSeparator: Char = ',') : Closeable, TabularWriter {
private val writer =
CsvWriter.builder()
.fieldSeparator(fieldSeparator)
Expand All @@ -33,12 +34,24 @@ class CSVWriter

private val header = mutableListOf<String>()

/**
* Write a header row into the CSV file.
* Note: since this is a CSV output, the description will be dropped (no standard way to add
* a comment to a CSV file).
*
* @param headers ordered map of header names and descriptions
*/
override fun writeHeader(headers: Map<String, String>) {
header.addAll(headers.keys)
writeRecord(headers.keys)
}

/**
* Write a header row into the CSV file.
*
* @param values to use for the header
*/
fun writeHeader(values: Iterable<String>) {
override fun writeHeader(values: Iterable<String>) {
header.addAll(values)
writeRecord(values)
}
Expand All @@ -50,9 +63,9 @@ class CSVWriter
*
* @param values map keyed by column name with values for the row of data
*/
fun writeRecord(values: Map<String, String?>?) {
override fun writeRecord(values: Map<String, Any?>?) {
if (values != null) {
val list = mutableListOf<String>()
val list = mutableListOf<Any>()
header.forEach { name ->
list.add(values.getOrDefault(name, "") ?: "")
}
Expand All @@ -64,11 +77,11 @@ class CSVWriter
* Write a row of data into the CSV file, where the values are already sequenced
* in the same order as the header columns.
*
* @param values to use for the row of data
* @param data to use for the row of data
*/
fun writeRecord(values: Iterable<String?>?) {
if (values != null) {
synchronized(writer) { writer.writeRecord(values) }
override fun writeRecord(data: Iterable<Any?>?) {
if (data != null) {
synchronized(writer) { writer.writeRecord(data.map { it?.toString() ?: "" }) }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,27 @@ abstract class CSVXformer(
fun trimWhitespace(s: String): String {
return s.trim().trim('\uFEFF', '\u200B')
}

/**
* Translate a row of input values into a map, keyed by input header name
* with the value being the value for that column on the row.
*
* @param header list of header column names
* @param values list of values, in the same order as the header columns
* @return map from header name to value on that row
*/
fun getRowByHeader(
header: List<String>,
values: List<String>,
): Map<String, String> {
val map = mutableMapOf<String, String>()
header.forEachIndexed { index, s ->
// Explicitly trim all whitespace from headers, including byte order mark (BOM) or zero-width space (ZWSP) characters
val trimmed = trimWhitespace(s)
map[trimmed] = values.getOrElse(index) { "" }
}
return map.toMap()
}
}

/**
Expand Down Expand Up @@ -147,16 +168,11 @@ abstract class CSVXformer(
* Translate a row of input values into a map, keyed by input header name
* with the value being the value for that column on the row.
*
* @param values a row of values, in the same order as the headers
* @return map from header name to value on that row
*/
private fun getRowByHeader(values: List<String>): Map<String, String> {
val map = mutableMapOf<String, String>()
header.forEachIndexed { index, s ->
// Explicitly trim all whitespace from headers, including byte order mark (BOM) or zero-width space (ZWSP) characters
val trimmed = trimWhitespace(s)
map[trimmed] = values.getOrElse(index) { "" }
}
return map.toMap()
return getRowByHeader(header, values)
}

/** {@inheritDoc} */
Expand Down
Loading

0 comments on commit 5c11642

Please sign in to comment.