Table implements Iterable<Row>, Option { @Override public String toString() { Cell.Separator cellSeparator = optionsByType.get(Cell.Separator.class); ArrayList<Integer> cellWidths = new ArrayList<>(); for (Row row : this) { int rowWidth = row.width(); while (cellWidths.size() < rowWidth) { cellWidths.add(0); } int i = 0; for (Cell cell : row) { int currentWidth = cellWidths.get(i); Cell.Width width = cell.getOptions().getOrDefault(Cell.Width.class, row.getOptions().getOrDefault(Cell.Width.class, this.getOptions() .get(Cell .Width.class))); int cellWidth; if (width.isAutoDetect()) { cellWidth = cell.width(); if (cell.containsNull()) { Cell.DisplayNull displayNull = cell.getOptions().getOrDefault(Cell.DisplayNull.class, row.getOptions() .getOrDefault(Cell .DisplayNull.class, this.getOptions() .get(Cell .DisplayNull.class))); cellWidth = Math.max(cellWidth, displayNull.getValue().length()); } } else { cellWidth = width.getCharacters(); } if (cellWidth > currentWidth) { cellWidths.set(i, cellWidth); } i++; } } Row.Comparator comparator = optionsByType.get(Row.Comparator.class); Row[] orderedRows = new Row[rows.size()]; rows.toArray(orderedRows); if (comparator != null) { Arrays.sort(orderedRows, comparator); } StringBuilder builder = new StringBuilder(); for (int rowIndex = 0; rowIndex < orderedRows.length; rowIndex++) { Row row = orderedRows[rowIndex]; if (rowIndex > 0) { builder.append("\n"); } int line = 0; int rowHeight; do { rowHeight = 0; int rowWidth = row.width(); for (int cellIndex = 0; cellIndex < rowWidth; cellIndex++) { Cell cell = row.getCell(cellIndex); Cell.Justification justification = cell.getOptions().getOrDefault(Cell.Justification.class, row.getOptions() .getOrDefault(Cell .Justification.class, this.getOptions() .get(Cell .Justification.class))); int cellHeight = cell.height(); rowHeight = cellHeight > rowHeight ? cellHeight : rowHeight; String justifiedContent; if (cell.isEmpty() || line >= cellHeight) { if (cellIndex > 0) { builder.append(" "); builder.append(cellSeparator.getSeparator()); builder.append(" "); } justifiedContent = justification.format("", cellWidths.get(cellIndex)); } else { if (cellIndex > 0) { builder.append(" "); builder.append(cellSeparator.getSeparator()); builder.append(" "); } String content = cell.getLine(line); if (content == null) { Cell.DisplayNull displayNull = cell.getOptions().getOrDefault(Cell.DisplayNull.class, row.getOptions() .getOrDefault(Cell .DisplayNull.class, this.getOptions() .get(Cell .DisplayNull.class))); content = displayNull.getValue(); } justifiedContent = justification.format(content, cellWidths.get(cellIndex)); } if (cellIndex == rowWidth - 1) { justifiedContent = trimTrailingWhiteSpace(justifiedContent); } builder.append(justifiedContent); } line++; if (line < rowHeight) { builder.append("\n"); } } while (line < rowHeight); } return builder.toString(); } Table(Row... rows); static Row.Comparator orderByColumn(final int column); OptionsByType getOptions(); Table addRow(Row row); Table addRow(Cell... cells); Table addRow(String... cells); Row getRow(int index); int size(); @Override Iterator<Row> iterator(); @Override String toString();  }
@Test public void shouldProduceAnEmptyTable() { Table table = new Table(); Assert.assertThat(table.toString(), is("")); }