Skip to content

Commit

Permalink
datatable/java: DataTable print with configurable indent + without es…
Browse files Browse the repository at this point in the history
…caping (#1624)


Co-authored-by: M.P. Korstanje <rien.korstanje@gmail.com>
Co-authored-by: M.P. Korstanje <mpkorstanje@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 1, 2021
1 parent c50616f commit 3a1eec4
Show file tree
Hide file tree
Showing 8 changed files with 302 additions and 150 deletions.
4 changes: 4 additions & 0 deletions datatable/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]

### Added
* [Java] Add `DataTablePrinter` to the public API, allow configurable printing, and deprecate legacy DataTable.print() methods.
([cucumber-jvm/2320](https://github.com/cucumber/cucumber-jvm/issues/2320)
[1624](https://github.com/cucumber/cucumber/pull/1624)
[artysidorenko], [mpkorstanje])

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -612,34 +612,44 @@ public int width() {
*/
@Override
public String toString() {
StringBuilder result = new StringBuilder();
print(result);
return result.toString();
return DataTableFormatter.builder()
.build()
.format(this);
}

/**
* Prints a string representation of this
* table to the {@code appendable}.
*
* @deprecated superseded by {@link DataTableFormatter#formatTo(DataTable, Appendable)}
*
* @param appendable to append the string representation
* of this table to.
* @throws IOException If an I/O error occurs
*/
@Deprecated
public void print(Appendable appendable) throws IOException {
TablePrinter printer = new TablePrinter();
printer.printTable(raw, appendable);
DataTableFormatter.builder()
.prefixRow(" ")
.build()
.formatTo(this, appendable);
}

/**
* Prints a string representation of this
* table to the {@code appendable}.
*
* @deprecated superseded by {@link DataTableFormatter#formatTo(DataTable, StringBuilder)}
*
* @param appendable to append the string representation
* of this table to.
*/
@Deprecated
public void print(StringBuilder appendable) {
TablePrinter printer = new TablePrinter();
printer.printTable(raw, appendable);
DataTableFormatter.builder()
.prefixRow(" ")
.build()
.formatTo(this, appendable);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,22 @@ public boolean isEmpty() {
@Override
public String toString() {
StringBuilder result = new StringBuilder();
DiffTablePrinter printer = new DiffTablePrinter(diffTypes);
printer.printTable(table, result);
DataTableFormatter.builder()
.prefixRow(this::indentForRow)
.build()
.formatTo(DataTable.create(table), result);
return result.toString();
}

private String indentForRow(Integer rowIndex) {
switch (diffTypes.get(rowIndex)) {
case DELETE:
return " - ";
case INSERT:
return " + ";
default:
return " ";
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package io.cucumber.datatable;

import org.apiguardian.api.API;

import java.io.IOException;
import java.util.function.Function;

import static java.util.Objects.requireNonNull;

@API(status = API.Status.STABLE)
public final class DataTableFormatter {

private final Function<Integer, String> rowPrefix;
private final boolean escapeDelimiters;

private DataTableFormatter(Function<Integer, String> rowPrefix, boolean escapeDelimiters) {
this.rowPrefix = rowPrefix;
this.escapeDelimiters = escapeDelimiters;
}

public static DataTableFormatter.Builder builder() {
return new Builder();
}

public String format(DataTable table) {
StringBuilder result = new StringBuilder();
formatTo(table, result);
return result.toString();
}

public void formatTo(DataTable table, StringBuilder appendable) {
try {
formatTo(table, (Appendable) appendable);
} catch (IOException e) {
throw new CucumberDataTableException(e.getMessage(), e);
}
}

public void formatTo(DataTable table, Appendable appendable) throws IOException {
requireNonNull(table, "table may not be null");
requireNonNull(appendable, "appendable may not be null");

if (table.isEmpty()) {
return;
}
// datatables are always square and non-sparse.
int height = table.height();
int width = table.width();

// render the individual cells
String[][] renderedCells = new String[height][width];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
renderedCells[i][j] = renderCell(table.cell(i, j));
}
}

// find the longest rendered cell in each column
int[] longestCellInColumnLength = new int[width];
for (String[] row : renderedCells) {
for (int colIndex = 0; colIndex < width; colIndex++) {
int current = longestCellInColumnLength[colIndex];
int candidate = row[colIndex].length();
longestCellInColumnLength[colIndex] = Math.max(current, candidate);
}
}

// print the rendered cells with padding
for (int rowIndex = 0; rowIndex < height; rowIndex++) {
printRowPrefix(appendable, rowIndex);
appendable.append("| ");
for (int colIndex = 0; colIndex < width; colIndex++) {
String cellText = renderedCells[rowIndex][colIndex];
appendable.append(cellText);
int padding = longestCellInColumnLength[colIndex] - cellText.length();
padSpace(appendable, padding);
if (colIndex < width - 1) {
appendable.append(" | ");
} else {
appendable.append(" |");
}
}
appendable.append("\n");
}
}

void printRowPrefix(Appendable buffer, int rowIndex) throws IOException {
String prefix = rowPrefix.apply(rowIndex);
if (prefix != null) {
buffer.append(prefix);
}
}

private String renderCell(String cell) {
if (cell == null) {
return "";
}

if (cell.isEmpty()) {
return "[empty]";
}

if (!escapeDelimiters) {
return cell;
}

return cell
.replaceAll("\\\\(?!\\|)", "\\\\\\\\")
.replaceAll("\\n", "\\\\n")
.replaceAll("\\|", "\\\\|");
}

private void padSpace(Appendable buffer, int indent) throws IOException {
for (int i = 0; i < indent; i++) {
buffer.append(" ");
}
}

public static final class Builder {
private Function<Integer, String> rowPrefix = rowIndex -> "";
private boolean escapeDelimiters = true;

public Builder prefixRow(Function<Integer, String> rowPrefix) {
requireNonNull(rowPrefix, "rowPrefix may not be null");
this.rowPrefix = rowPrefix;
return this;
}

public Builder prefixRow(String rowPrefix) {
requireNonNull(rowPrefix, "rowPrefix may not be null");
return prefixRow(rowIndex -> rowPrefix);
}

public Builder escapeDelimiters(boolean escapeDelimiters) {
this.escapeDelimiters = escapeDelimiters;
return this;
}

public DataTableFormatter build() {
return new DataTableFormatter(rowPrefix, escapeDelimiters);
}

}

}

This file was deleted.

This file was deleted.

Loading

0 comments on commit 3a1eec4

Please sign in to comment.