package com.insightfullogic.honest_profiler.ports.javafx.util.report;
import static com.insightfullogic.honest_profiler.ports.javafx.util.report.Table.Alignment.LEFT;
import static com.insightfullogic.honest_profiler.ports.javafx.util.report.Table.Alignment.RIGHT;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
/**
* Simple table data structure which can be formatted nicely and written to a {@link PrintWriter}, with borders and
* everyting.
* <p>
* The data is internally modeled as a {@link List} of rows which are themselves {@link List}s of String column values.
* The input data consists of {@link List}s of {@link Object}s, which are transformed by formatting functions attached
* to the columns, which transform the {@link Object} into a String.
*/
public class Table
{
// Class Properties
/** Enumeration of possible alignments of values in a column. */
public static enum Alignment
{
LEFT, RIGHT
}
// Instance Properties
private List<String> columnTitles;
private List<Function<Object, String>> columnFormatters;
private List<Alignment> columnAlignments;
private List<Integer> columnValueMaxWidths;
private List<List<String>> rows;
// Instance Constructors
/**
* Construct an empty Table.
*/
public Table()
{
columnTitles = new ArrayList<>();
columnFormatters = new ArrayList<>();
columnAlignments = new ArrayList<>();
columnValueMaxWidths = new ArrayList<>();
rows = new ArrayList<>();
}
/**
* Add a column specification to the Table, consisting of a title (or header) for the column, a formatting
* {@link Function} to format a data {@link Object} belonging to the column to a String, and an {@link Alignment}.
* <p>
* @param title the column header
* @param formatter a {@link Function} which maps data {@link Object}s in this column to Strings
* @param alignment the alignment for the column in the output
*/
public void addColumn(String title, Function<Object, String> formatter, Alignment alignment)
{
columnTitles.add(title);
columnFormatters.add(formatter);
columnAlignments.add(alignment);
columnValueMaxWidths.add(title.length());
}
/**
* Adds a data row to the {@link Table}. The number of data {@link Object}s must match the number of columns which
* were added to the Table. null data {@link Object}s are rendered as "<NULL>".
* <p>
* @param data The {@link Object}s in the row, one for each column
* @throws RuntimeException if the number of data {@link Object}s doesn't match the number of columns in the Table
*/
public void addRow(Object... data)
{
if (data == null || data.length != columnTitles.size())
{
throw new RuntimeException(
"Incorrect number of column entries when trying to add data to a table : "
+ columnTitles.size()
+ " columns defined, but adding "
+ (data == null ? 0 : data.length)
+ " column entries.");
}
List<String> row = new ArrayList<>();
for (int i = 0; i < columnFormatters.size(); i++)
{
String columnEntry = columnFormatters.get(i).apply(data[i]);
if (columnEntry == null)
{
columnEntry = "<NULL>";
}
row.add(columnEntry);
if (columnValueMaxWidths.get(i) < columnEntry.length())
{
columnValueMaxWidths.set(i, columnEntry.length());
}
}
rows.add(row);
}
/**
* Prints the formatted Table to the specified {@link PrintWriter}.
* <p>
* @param out the {@link PrintWriter} the Table is printed to
*/
public void print(PrintWriter out)
{
int width = columnValueMaxWidths.stream().mapToInt(i -> i).sum()
+ (columnTitles.size() * 2)
+ 2;
printLine(out, width);
printRow(out, columnTitles, LEFT);
printLine(out, width);
rows.forEach(row -> printRow(out, row));
printLine(out, width);
}
// Internal Helper Methods
/**
* Prints a row, using the {@link Alignment} as specified by the column definitions.
* <p>
* @param out the {@link PrintWriter} the Table is printed to
* @param entries the {@link List} of String entries to be printed
*/
private void printRow(PrintWriter out, List<String> entries)
{
printRow(out, entries, null);
}
/**
* Prints a row, overriding the {@link Alignment} as specified by the column definitions if the specified
* {@link Alignment} is not null.
* <p>
* @param out the {@link PrintWriter} the Table is printed to
* @param entries the {@link List} of String entries to be printed
* @param alignment an {@link Alignment} overriding the defined column alignments
*/
private void printRow(PrintWriter out, List<String> entries, Alignment alignment)
{
out.print("|");
for (int i = 0; i < columnTitles.size(); i++)
{
out.print(" ");
out.print(pad(
entries.get(i),
columnValueMaxWidths.get(i),
alignment == null ? columnAlignments.get(i) : alignment));
out.print(" |");
}
out.println();
}
/**
* Prints a line of dashes.
* <p>
* @param out the {@link PrintWriter} the Table is printed to
* @param width the number of dashes to be printed
*/
private void printLine(PrintWriter out, int width)
{
for (int i = 0; i < width; i++)
{
out.print("-");
}
out.println();
}
/**
* Returns a String constructed by padding the input String to the specified width and aligning the input according
* to the specified {@link Alignment}.
* <p>
* @param string the input String
* @param width the width of the resulting String
* @param alignment the alignment of the input String in the result String
* @return the constructed String
*/
private String pad(String string, int width, Alignment alignment)
{
StringBuilder result = new StringBuilder();
if (alignment == RIGHT)
{
for (int i = 0; i < width - string.length(); i++)
{
result.append(" ");
}
}
result.append(string);
if (alignment == LEFT)
{
for (int i = 0; i < width - string.length(); i++)
{
result.append(" ");
}
}
return result.toString();
}
}