DataTable.java revision 35dbd8414c0e7022a6a4b54f7ef16b5ff51ae53b
1package autotest.common.table; 2 3 4import com.google.gwt.json.client.JSONArray; 5import com.google.gwt.json.client.JSONObject; 6import com.google.gwt.json.client.JSONValue; 7import com.google.gwt.user.client.ui.Composite; 8import com.google.gwt.user.client.ui.FlexTable; 9import com.google.gwt.user.client.ui.Widget; 10 11import java.util.ArrayList; 12import java.util.List; 13 14/** 15 * A table to display data from JSONObjects. Each row displays data from one 16 * JSONObject. A header row with column titles is automatically generated, and 17 * support is included for adding other arbitrary header rows. 18 * <br><br> 19 * Styles: 20 * <ul> 21 * <li>.data-table - the entire table 22 * <li>.data-row-header - the column title row 23 * <li>.data-row-one/.data-row-two - data row styles. These two are alternated. 24 * </ul> 25 */ 26public class DataTable extends Composite { 27 public static final String HEADER_STYLE = "data-row-header"; 28 public static final String CLICKABLE_STYLE = "data-row-clickable"; 29 public static final String HIGHLIGHTED_STYLE = "data-row-highlighted"; 30 public static final String WIDGET_COLUMN = "_WIDGET_COLUMN_"; 31 // use CLICKABLE_WIDGET_COLUMN for widget that expect to receive clicks. The table will ignore 32 // click events coming from these columns. 33 public static final String CLICKABLE_WIDGET_COLUMN = "_CLICKABLE_WIDGET_COLUMN_"; 34 // for indexing into column subarrays (i.e. columns[1][COL_NAME]) 35 public static final int COL_NAME = 0, COL_TITLE = 1; 36 37 protected FlexTable table; 38 39 protected String[][] columns; 40 protected int headerRow = 0; 41 protected boolean clickable = false; 42 43 protected TableWidgetFactory widgetFactory = null; 44 45 // keep a list of JSONObjects corresponding to rows in the table 46 protected List<JSONObject> jsonObjects = new ArrayList<JSONObject>(); 47 48 49 public static interface TableWidgetFactory { 50 public Widget createWidget(int row, int cell, JSONObject rowObject); 51 } 52 53 /** 54 * @param columns An array specifying the name of each column and the field 55 * to which it corresponds. The array should have the form 56 * {{'field_name1', 'Column Title 1'}, 57 * {'field_name2', 'Column Title 2'}, ...}. 58 */ 59 public DataTable(String[][] columns) { 60 int rows = columns.length; 61 this.columns = new String[rows][2]; 62 for (int i = 0; i < rows; i++) { 63 System.arraycopy(columns[i], 0, this.columns[i], 0, 2); 64 } 65 66 table = new FlexTable(); 67 initWidget(table); 68 69 table.setCellSpacing(0); 70 table.setCellPadding(0); 71 table.setStyleName("data-table"); 72 73 for (int i = 0; i < columns.length; i++) { 74 table.setText(0, i, columns[i][1]); 75 } 76 77 table.getRowFormatter().setStylePrimaryName(0, HEADER_STYLE); 78 } 79 80 public void setWidgetFactory(TableWidgetFactory widgetFactory) { 81 this.widgetFactory = widgetFactory; 82 } 83 84 protected void setRowStyle(int row) { 85 table.getRowFormatter().setStyleName(row, "data-row"); 86 if ((row & 1) == 0) { 87 table.getRowFormatter().addStyleName(row, "data-row-alternate"); 88 } 89 if (clickable) { 90 table.getRowFormatter().addStyleName(row, CLICKABLE_STYLE); 91 } 92 } 93 94 public void setClickable(boolean clickable) { 95 this.clickable = clickable; 96 for(int i = headerRow + 1; i < table.getRowCount(); i++) 97 setRowStyle(i); 98 } 99 100 /** 101 * Clear all data rows from the table. Leaves the header rows intact. 102 */ 103 public void clear() { 104 while (getRowCount() > 0) { 105 removeRow(0); 106 } 107 jsonObjects.clear(); 108 } 109 110 /** 111 * This gets called for every JSONObject that gets added to the table using 112 * addRow(). This allows subclasses to customize objects before they are 113 * added to the table, for example to reformat fields or generate new 114 * fields from the existing data. 115 * @param row The row object about to be added to the table. 116 */ 117 protected void preprocessRow(JSONObject row) {} 118 119 protected String getTextForValue(JSONValue value) { 120 if (value == null || value.isNull() != null) 121 return ""; 122 else if (value.isNumber() != null) 123 return Integer.toString((int) value.isNumber().doubleValue()); 124 else if (value.isString() != null) 125 return value.isString().stringValue(); 126 else 127 throw new IllegalArgumentException(value.toString()); 128 } 129 130 protected String[] getRowText(JSONObject row) { 131 String[] rowText = new String[columns.length]; 132 for (int i = 0; i < columns.length; i++) { 133 if (isWidgetColumn(i)) 134 continue; 135 136 String columnKey = columns[i][0]; 137 JSONValue columnValue = row.get(columnKey); 138 rowText[i] = getTextForValue(columnValue); 139 } 140 return rowText; 141 } 142 143 /** 144 * Add a row from an array of Strings, one String for each column. 145 * @param rowData Data for each column, in left-to-right column order. 146 */ 147 protected void addRowFromData(String[] rowData) { 148 int row = table.getRowCount(); 149 for(int i = 0; i < columns.length; i++) { 150 if(isWidgetColumn(i)) { 151 table.setWidget(row, i, widgetFactory.createWidget(row - 1, i, 152 jsonObjects.get(row - 1))); 153 } else { 154 table.setHTML(row, i, rowData[i]); 155 } 156 } 157 setRowStyle(row); 158 } 159 160 protected boolean isWidgetColumn(int column) { 161 return columns[column][COL_NAME].equals(WIDGET_COLUMN) || isClickableWidgetColumn(column); 162 } 163 164 protected boolean isClickableWidgetColumn(int column) { 165 return columns[column][COL_NAME].equals(CLICKABLE_WIDGET_COLUMN); 166 } 167 168 /** 169 * Add a row from a JSONObject. Columns will be populated by pulling fields 170 * from the objects, as dictated by the columns information passed into the 171 * DataTable constructor. 172 */ 173 public void addRow(JSONObject row) { 174 preprocessRow(row); 175 jsonObjects.add(row); 176 addRowFromData(getRowText(row)); 177 } 178 179 /** 180 * Add all objects in a JSONArray. 181 * @param rows An array of JSONObjects 182 * @throws IllegalArgumentException if any other type of JSONValue is in the 183 * array. 184 */ 185 public void addRows(JSONArray rows) { 186 for (int i = 0; i < rows.size(); i++) { 187 JSONObject row = rows.get(i).isObject(); 188 if (row == null) 189 throw new IllegalArgumentException("rows must be JSONObjects"); 190 addRow(row); 191 } 192 } 193 194 /** 195 * Remove a data row from the table. 196 * @param rowIndex The index of the row, where the first data row is indexed 0. 197 * Header rows are ignored. 198 */ 199 public void removeRow(int rowIndex) { 200 jsonObjects.remove(rowIndex); 201 int realRow = rowIndex + 1; // header row 202 table.removeRow(realRow); 203 for(int i = realRow; i < table.getRowCount(); i++) 204 setRowStyle(i); 205 } 206 207 /** 208 * Returns the number of data rows in the table. The actual number of 209 * visible table rows is more than this, due to the header row. 210 */ 211 public int getRowCount() { 212 return table.getRowCount() - 1; 213 } 214 215 /** 216 * Get the JSONObject corresponding to the indexed row. 217 */ 218 public JSONObject getRow(int rowIndex) { 219 return jsonObjects.get(rowIndex); 220 } 221 222 public void highlightRow(int row) { 223 row++; // account for header row 224 table.getRowFormatter().addStyleName(row, HIGHLIGHTED_STYLE); 225 } 226 227 public void unhighlightRow(int row) { 228 row++; // account for header row 229 table.getRowFormatter().removeStyleName(row, HIGHLIGHTED_STYLE); 230 } 231} 232