package net.sourceforge.javautil.ui.cli.widget;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import net.sourceforge.javautil.common.StringUtil;
import net.sourceforge.javautil.ui.cli.CommandLineContext;
import net.sourceforge.javautil.ui.cli.CommandLineInterface;
import net.sourceforge.javautil.ui.model.UITableModel;
import net.sourceforge.javautil.ui.widget.UIWidgetAbstract;
/**
* A table expressed in text. This will recognized the following attributes:<br/><br/>
*
* <b>caption</b>: An optional caption for the table. The string form of the value will be
* used for the display of the caption<br/><br/>
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: TextTable.java 1581 2009-12-22 18:45:09Z ponderator $
*/
public class TextTable extends UIWidgetAbstract implements TextWidget {
/**
* The model for the table
*/
private UITableModel model;
/**
* Padding between columns
*/
private int padding = 2;
/**
* Border between columns
*/
private int border = 1;
public TextTable() {}
public TextTable(UITableModel model) {
this.model = model;
}
/**
* @return The {@link #model}
*/
public UITableModel getModel() { return model; }
public void setModel(UITableModel model) { this.model = model; }
public void render (PrintStream writer) {
UITableModel model = this.model;
int[] widths = this.calculateRealColumnWidths(model);
int totalWidth = this.calculateTotalWidth(widths);
Object caption = model.getAttribute("caption");
if (caption != null) {
this.renderBorderHorizontal(writer, totalWidth);
this.renderSingleColumnRow(writer, String.valueOf(caption), totalWidth, UITableModel.CENTER);
}
if (model.isShowHeaders())
this.renderHeaders(writer, model, totalWidth, widths);
else
this.renderBorderHorizontal(writer, widths);
for (int r=0; r<model.getRowCount(); r++) {
this.renderColumns(writer, model, r, widths);
this.renderBorderHorizontal(writer, widths);
}
}
/**
* @param model The model in question
* @param column The column to get alignment for
* @return The specified alignment or the default
*/
protected int getAlignment (UITableModel model, int column) {
return model.getAlignment(column) == -1 ? UITableModel.LEFT : model.getAlignment(column);
}
/**
* Render all the columns in a single row
*
* @param out The output stream
* @param model The table model
* @param row The row to render
* @param widths The column widths
*/
protected void renderColumns (PrintStream out, UITableModel model, int row, int... widths) {
out.print("|");
for (int c=0; c<model.getColumnCount(); c++) {
Object value = model.getValueAt(row, c);
if (value instanceof byte[]) value = new String( (byte[]) value );
else if (value instanceof char[]) value = new String( (char[]) value );
else if (value != null && value.getClass().isArray()) {
value = Array.getLength(value) + " " + value.getClass().getComponentType();
}
String text = model.getValueAt(row, c) == null ? "" : value.toString();
this.renderColumnData(out, text, widths[c], this.getAlignment(model, c));
}
out.println();
}
/**
* Render a column of data.
*
* @param out The output stream
* @param data The data for the column
* @param width The max width of the column
* @param alignment The alignment of the column
*/
protected void renderColumnData (PrintStream out, String data, int width, int alignment) {
out.print(" " + StringUtil.getStrictFixedWidthElipsis(data, width, alignment) + " |");
}
/**
* Render text on an entire row with all columns joined.
*
* @param out The output stream
* @param data The data for the column
* @param width The max width of the column
* @param alignment The alignment of the column
*/
protected void renderSingleColumnRow (PrintStream out, String data, int width, int alignment) {
out.println("| " + StringUtil.getStrictFixedWidthElipsis(data, width, alignment) + " |");
}
/**
* Render the table headers.
*
* @param out The output stream
* @param model The table model
* @param totalWidth The total width of the table
* @param widths The column widths
*/
protected void renderHeaders (PrintStream out, UITableModel model, int totalWidth, int[] widths) {
this.renderBorderHorizontal(out, widths);
out.print("|");
for (int c=0; c<widths.length; c++)
this.renderColumnData(out, model.getHeader(c), widths[c], this.getAlignment(model, c));
out.println();
this.renderBorderHorizontal(out, widths);
}
/**
* Render a horizontal border.
*
* @param out The output stream
* @param widths The column widths
*/
protected void renderBorderHorizontal (PrintStream out, int... widths) {
out.print("+");
for (int width : widths) {
out.print(StringUtil.repeat('-', width + padding) + "+");
}
out.println();
}
/**
* @param widths The column widths
* @return A total width including borders and padding
*/
protected int calculateTotalWidth (int... widths) {
int total = border;
for (int w=0; w<widths.length; w++) {
total += widths[w] + padding + border;
}
return total - ((border * 2) + padding);
}
/**
* This calls {@link #calculateRealColumnWidth(TableModel, int)} for
* each column.
*
* @param model The model for which to calculate widths
* @return The array of widths corresponding to the coumns
*/
protected int[] calculateRealColumnWidths (UITableModel model) {
int[] widths = new int[model.getColumnCount()];
for (int w=0; w<widths.length; w++) {
widths[w] = this.calculateRealColumnWidth(model, w);
}
return widths;
}
/**
* If the max width has been specified ({@link #setColumnWidths(int...)}) then
* it will simply return that, otherwise it will calculate the greatest width
* of each cell of the model. It will also take into account the width of any
* {@link #columnHeaders} that may have been defined.
*
* @param model The model for which to calculate the width
* @param column The column of the model
* @return The max width
*/
protected int calculateRealColumnWidth (UITableModel model, int column) {
if (model.isHasFixedColumnWidths() && model.getWidth(column) > 0) {
return model.getWidth(column);
} else {
int maxwidth = !"".equals(model.getHeader(column)) ? model.getHeader(column).length() : 1;
for (int r=0; r<model.getRowCount(); r++) {
Object value = model.getValueAt(r, column);
if (value != null) {
String str = value.toString();
if (str.length() > maxwidth) maxwidth = str.length();
}
}
return maxwidth;
}
}
}