DataTable.java revision e8819cdf80ca0e0602d22551a50f970aa68e108d
1package afeclient.client;
2
3
4
5import com.google.gwt.json.client.JSONArray;
6import com.google.gwt.json.client.JSONObject;
7import com.google.gwt.json.client.JSONValue;
8import com.google.gwt.user.client.ui.Composite;
9import com.google.gwt.user.client.ui.FlexTable;
10import com.google.gwt.user.client.ui.Widget;
11
12/**
13 * A table to display data from JSONObjects.  Each row displays data from one
14 * JSONObject.  A header row with column titles is automatically generated, and
15 * support is included for adding other arbitrary header rows.
16 * <br><br>
17 * Styles:
18 * <ul>
19 * <li>.data-table - the entire table
20 * <li>.data-row-header - the column title row
21 * <li>.data-row-one/.data-row-two - data row styles.  These two are alternated.
22 * </ul>
23 */
24public class DataTable extends Composite {
25    public static final String HEADER_STYLE = "data-row-header";
26    public static final String CLICKABLE_STYLE = "data-row-clickable";
27
28    protected FlexTable table;
29
30    protected String[][] columns;
31    protected int headerRow = 0;
32    protected boolean clickable = false;
33
34    /**
35     * @param columns An array specifying the name of each column and the field
36     * to which it corresponds.  The array should have the form
37     * {{'field_name1', 'Column Title 1'},
38     *  {'field_name2', 'Column Title 2'}, ...}.
39     */
40    public DataTable(String[][] columns) {
41        this.columns = columns;
42        table = new FlexTable();
43        initWidget(table);
44
45        table.setCellSpacing(0);
46        table.setCellPadding(0);
47        table.setStyleName("data-table");
48
49        for (int i = 0; i < columns.length; i++) {
50            table.setText(0, i, columns[i][1]);
51        }
52
53        table.getRowFormatter().setStylePrimaryName(0, HEADER_STYLE);
54    }
55
56    protected void setRowStyle(int row) {
57        table.getRowFormatter().setStyleName(row, "data-row");
58        if ((row & 1) == 0) {
59            table.getRowFormatter().addStyleName(row, "data-row-alternate");
60        }
61        if (clickable) {
62            table.getRowFormatter().addStyleName(row, CLICKABLE_STYLE);
63        }
64    }
65
66    public void setClickable(boolean clickable) {
67        this.clickable = clickable;
68        for(int i = headerRow + 1; i < table.getRowCount(); i++)
69            setRowStyle(i);
70    }
71
72    /**
73     * Clear all data rows from the table.  Leaves the header rows intact.
74     */
75    public void clear() {
76        while (getRowCount() > 0) {
77            removeRow(0);
78        }
79    }
80
81    /**
82     * This gets called for every JSONObject that gets added to the table using
83     * addRow().  This allows subclasses to customize objects before they are
84     * added to the table, for example to reformat fields or generate new
85     * fields from the existing data.
86     * @param row The row object about to be added to the table.
87     */
88    protected void preprocessRow(JSONObject row) {}
89
90    protected String getTextForValue(JSONValue value) {
91        if (value.isNumber() != null)
92            return Integer.toString((int) value.isNumber().getValue());
93        else if (value.isString() != null)
94            return  value.isString().stringValue();
95        else if (value.isNull() != null)
96            return "";
97        else
98            throw new IllegalArgumentException(value.toString());
99    }
100
101    protected String[] getRowText(JSONObject row) {
102        String[] rowText = new String[columns.length];
103        for (int i = 0; i < columns.length; i++) {
104            String columnKey = columns[i][0];
105            JSONValue columnValue = row.get(columnKey);
106            rowText[i] = getTextForValue(columnValue);
107        }
108        return rowText;
109    }
110
111    /**
112     * Add a row from an array of Strings, one String for each column.
113     * @param rowData Data for each column, in left-to-right column order.
114     */
115    public void addRowFromData(String[] rowData) {
116        int row = table.getRowCount();
117        for(int i = 0; i < columns.length; i++)
118            table.setHTML(row, i, rowData[i]);
119        setRowStyle(row);
120    }
121
122    /**
123     * Add a row from a JSONObject.  Columns will be populated by pulling fields
124     * from the objects, as dictated by the columns information passed into the
125     * DataTable constructor.
126     */
127    public void addRow(JSONObject row) {
128        preprocessRow(row);
129        addRowFromData(getRowText(row));
130    }
131
132    /**
133     * Add all objects in a JSONArray.
134     * @param rows An array of JSONObjects
135     * @throws IllegalArgumentException if any other type of JSONValue is in the
136     * array.
137     */
138    public void addRows(JSONArray rows) {
139        for (int i = 0; i < rows.size(); i++) {
140            JSONObject row = rows.get(i).isObject();
141            if (row == null)
142                throw new IllegalArgumentException("rows must be JSONObjects");
143            addRow(row);
144        }
145    }
146
147    /**
148     * Remove a data row from the table.
149     * @param row The index of the row, where the first data row is indexed 0.
150     * Header rows are ignored.
151     */
152    public void removeRow(int row) {
153        int realRow = row + getHeaderRowCount();
154        table.removeRow(realRow);
155        for(int i = realRow; i < table.getRowCount(); i++)
156            setRowStyle(i);
157    }
158
159    /**
160     * Returns the number of data rows in the table.  The actual number of
161     * visible table rows is more than this, due to the header rows.
162     */
163    public int getRowCount() {
164        return table.getRowCount() - getHeaderRowCount();
165    }
166
167    /**
168     * Adds a header row to the table.  This is an extra row that is added above
169     * the row of column titles and below any other header rows that have been
170     * added.  The row consists of a single cell.
171     * @param widget A widget to add to the cell.
172     * @return The row index of the new header row.
173     */
174    public int addHeaderRow(Widget widget) {
175        int row = table.insertRow(headerRow);
176        headerRow++;
177        table.getFlexCellFormatter().setColSpan(row, 0, columns.length);
178        table.setWidget(row, 0, widget);
179        return row;
180    }
181
182    /**
183     * Returns the number of header rows, including the column title row.
184     */
185    public int getHeaderRowCount() {
186        return headerRow + 1;
187    }
188}
189