From 8556bdcdeb2ef07029485fea239ec5ba0768a15d Mon Sep 17 00:00:00 2001 From: n1474335 Date: Fri, 4 May 2018 16:10:22 +0000 Subject: [PATCH] Tidied up 'To Table' operation, adding better CSV parsing support. --- src/core/Utils.js | 26 +++++++++------ src/core/config/Categories.js | 2 +- src/core/config/OperationConfig.js | 22 ++++++------- src/core/operations/ToTable.js | 51 ++++++++++-------------------- 4 files changed, 43 insertions(+), 58 deletions(-) diff --git a/src/core/Utils.js b/src/core/Utils.js index 16f581b3..4b20208b 100755 --- a/src/core/Utils.js +++ b/src/core/Utils.js @@ -737,37 +737,43 @@ const Utils = { * Parses CSV data and returns it as a two dimensional array or strings. * * @param {string} data + * @param {string[]} [cellDelims=[","]] + * @param {string[]} [lineDelims=["\n", "\r"]] * @returns {string[][]} * * @example * // returns [["head1", "head2"], ["data1", "data2"]] * Utils.parseCSV("head1,head2\ndata1,data2"); */ - parseCSV: function(data) { - + parseCSV: function(data, cellDelims=[","], lineDelims=["\n", "\r"]) { let b, - ignoreNext = false, + next, + renderNext = false, inString = false, cell = "", line = [], lines = []; + // Remove BOM, often present in Excel CSV files + if (data.length && data[0] === "\uFEFF") data = data.substr(1); + for (let i = 0; i < data.length; i++) { b = data[i]; - if (ignoreNext) { + next = data[i+1] || ""; + if (renderNext) { cell += b; - ignoreNext = false; + renderNext = false; } else if (b === "\\") { - cell += b; - ignoreNext = true; + renderNext = true; } else if (b === "\"" && !inString) { inString = true; } else if (b === "\"" && inString) { - inString = false; - } else if (b === "," && !inString) { + if (next === "\"") renderNext = true; + else inString = false; + } else if (!inString && cellDelims.indexOf(b) >= 0) { line.push(cell); cell = ""; - } else if ((b === "\n" || b === "\r") && !inString) { + } else if (!inString && lineDelims.indexOf(b) >= 0) { line.push(cell); cell = ""; lines.push(line); diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js index 33ec4a99..9d531f8d 100755 --- a/src/core/config/Categories.js +++ b/src/core/config/Categories.js @@ -67,7 +67,6 @@ const Categories = [ "Encode text", "Decode text", "Swap endianness", - "To Table", ] }, { @@ -183,6 +182,7 @@ const Categories = [ "To Lower case", "Add line numbers", "Remove line numbers", + "To Table", "Reverse", "Sort", "Unique", diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index d3b8c27c..500a8803 100644 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -616,26 +616,22 @@ const OperationConfig = { }, "To Table": { module: "Default", - description: "Renders data as a table. Data can be split on different characters and output as a HTML or ASCII table with optional header row.", + description: "Data can be split on different characters and rendered as an HTML or ASCII table with an optional header row.

Supports the CSV (Comma Separated Values) file format by default. Change the cell delimiter argument to \\t to support TSV (Tab Separated Values) or | for PSV (Pipe Separated Values).

You can enter as many delimiters as you like. Each character will be treat as a separate possible delimiter.", inputType: "string", outputType: "html", - highlight: false, - highlightReverse: false, - manualBake: false, args: [ { - name: "Select separator", - type: "populateOption", - value: ToTable.SEPARATORS, - target: 1 - }, - { - name: "Separator", - type: "string", + name: "Cell delimiters", + type: "binaryShortString", value: "," }, { - name: "First row header?", + name: "Row delimiters", + type: "binaryShortString", + value: "\\n\\r" + }, + { + name: "Make first row header", type: "boolean", value: false }, diff --git a/src/core/operations/ToTable.js b/src/core/operations/ToTable.js index 83887f26..b18b56f1 100755 --- a/src/core/operations/ToTable.js +++ b/src/core/operations/ToTable.js @@ -2,19 +2,14 @@ * ToTable operations. * * @author Mark Jones [github.com/justanothermark] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + * * @namespace */ +import Utils from "../Utils.js"; + const ToTable = { - /** - * @constant - * @default - */ - SEPARATORS: [ - {name: "Comma", value: ","}, - {name: "Tab", value: "\\t"}, - {name: "Pipe", value: "|"}, - {name: "Custom", value: ""} - ], /** * @constant @@ -25,6 +20,7 @@ const ToTable = { "HTML" ], + /** * To Table operation. * @@ -33,42 +29,26 @@ const ToTable = { * @returns {html} */ runToTable: function (input, args) { - let separator = args[1]; - let firstRowHeader = args[2]; - let format = args[3]; - let tableData = []; - - // If the separator contains any tabs, convert them to tab characters. - separator = separator.replace("\\t", "\t"); + const [cellDelims, rowDelims, firstRowHeader, format] = args; // Process the input into a nested array of elements. - let rows = input.split("\n"); - rows.forEach(function(element) { - if (separator === "") { - tableData.push([element]); - } else { - tableData.push(element.split(separator)); - } - }); + const tableData = Utils.parseCSV(input, cellDelims.split(""), rowDelims.split("")); + + if (!tableData.length) return ""; // Render the data in the requested format. - let output = ""; switch (format) { case "ASCII": - output = asciiOutput(tableData); - break; - + return asciiOutput(tableData); + case "HTML": default: - output = htmlOutput(tableData); - break; + return htmlOutput(tableData); } - return output; - /** * Outputs an array of data as an ASCII table. * - * @param {Array[]} tableData + * @param {string[][]} tableData * @returns {string} */ function asciiOutput(tableData) { @@ -137,6 +117,9 @@ const ToTable = { /** * Outputs a table of data as a HTML table. + * + * @param {string[][]} tableData + * @returns {string} */ function htmlOutput(tableData) { // Start the HTML output with suitable classes for styling.