class DataFramePrinter {

  public void print(Relation frame) {
    try {
      final String[] headers = getHeaderTokens(frame);
      final String[][] data = getDataTokens(frame);
      final int[] widths = getWidths(headers, data);
      final String dataTemplate = getDataTemplate(widths);
      final String headerTemplate = getHeaderTemplate(widths, headers);
      final int totalWidth = IntStream.of(widths).map(w -> w + 5).sum() - 1;
      final int totalHeight = data.length + 1;
      int capacity = totalWidth * totalHeight;
      if (capacity < 0) {
        capacity = 0;
      }
      final StringBuilder text = new StringBuilder(capacity);
      if (frame.name() != null) {
        text.append(tableName(frame, totalWidth)).append(System.lineSeparator());
      }
      final String headerLine = String.format(headerTemplate, (Object[]) headers);
      text.append(headerLine).append(System.lineSeparator());
      for (int j = 0; j < totalWidth; j++) {
        text.append("-");
      }
      for (String[] row : data) {
        final String dataLine = String.format(dataTemplate, (Object[]) row);
        text.append(System.lineSeparator());
        text.append(dataLine);
      }
      final byte[] bytes = text.toString().getBytes();
      this.stream.write(bytes);
      this.stream.flush();
    } catch (IOException ex) {
      throw new IllegalStateException("Failed to print DataFrame", ex);
    }
  }

  public  DataFramePrinter(int maxRows, OutputStream stream);

}