DynamicTable.java revision 4cd4763a855d8eb7d25fd4963babc432eb4d25e6
1package autotest.common.table; 2 3import autotest.common.SimpleCallback; 4import autotest.common.table.DataSource.DataCallback; 5import autotest.common.table.DataSource.SortDirection; 6import autotest.common.table.DataSource.SortSpec; 7import autotest.common.ui.Paginator; 8 9import com.google.gwt.json.client.JSONArray; 10import com.google.gwt.json.client.JSONObject; 11import com.google.gwt.user.client.ui.Composite; 12import com.google.gwt.user.client.ui.HTMLPanel; 13import com.google.gwt.user.client.ui.Image; 14 15import java.util.ArrayList; 16import java.util.Collections; 17import java.util.Iterator; 18import java.util.List; 19 20/** 21 * Extended DataTable supporting sorting, filtering and pagination. 22 */ 23public class DynamicTable extends DataTable implements DataCallback { 24 public static final int NO_COLUMN = -1; 25 public static final String SORT_UP_IMAGE = "arrow_up.png", 26 SORT_DOWN_IMAGE = "arrow_down.png"; 27 28 public static interface DynamicTableListener extends DataTableListener { 29 public void onTableRefreshed(); 30 } 31 32 static class SortIndicator extends Composite { 33 public int column; 34 private Image image = new Image(); 35 36 public SortIndicator(int column) { 37 this.column = column; 38 initWidget(image); 39 setVisible(false); 40 } 41 42 public void sortOn(SortDirection direction) { 43 image.setUrl(direction == SortDirection.ASCENDING ? SORT_UP_IMAGE : SORT_DOWN_IMAGE); 44 setVisible(true); 45 } 46 47 public void sortOff() { 48 setVisible(false); 49 } 50 } 51 52 protected DataSource dataSource; 53 54 private boolean clientSortable = false; 55 private SortIndicator[] sortIndicators; 56 private List<SortSpec> sortColumns = new ArrayList<SortSpec>(); 57 58 protected List<Filter> filters = new ArrayList<Filter>(); 59 protected List<Paginator> paginators = new ArrayList<Paginator>(); 60 protected Integer rowsPerPage; 61 62 protected List<DynamicTableListener> dynamicTableListeners = 63 new ArrayList<DynamicTableListener>(); 64 65 public DynamicTable(String[][] columns, DataSource dataSource) { 66 super(columns); 67 setDataSource(dataSource); 68 } 69 70 // SORTING 71 72 /** 73 * Makes the table client sortable, that is, sortable by the user by 74 * clicking on column headers. 75 */ 76 public void makeClientSortable() { 77 this.clientSortable = true; 78 table.getRowFormatter().addStyleName(0, 79 DataTable.HEADER_STYLE + "-sortable"); 80 81 sortIndicators = new SortIndicator[columns.length]; 82 for(int i = 0; i < columns.length; i++) { 83 sortIndicators[i] = new SortIndicator(i); 84 85 // we have to use an HTMLPanel here to preserve styles correctly and 86 // not break hover 87 // we add a <span> with a unique ID to hold the sort indicator 88 String name = columns[i][COL_TITLE]; 89 String id = HTMLPanel.createUniqueId(); 90 HTMLPanel panel = new HTMLPanel(name + 91 " <span id=\"" + id + "\"></span>"); 92 panel.add(sortIndicators[i], id); 93 table.setWidget(0, i, panel); 94 } 95 } 96 97 private void updateSortIndicators() { 98 if (!clientSortable) { 99 return; 100 } 101 102 SortSpec firstSpec = getFirstSortSpec(); 103 for (SortIndicator indicator : sortIndicators) { 104 if (columns[indicator.column][COL_NAME].equals(firstSpec.getField())) { 105 indicator.sortOn(firstSpec.getDirection()); 106 } else { 107 indicator.sortOff(); 108 } 109 } 110 } 111 112 private SortSpec getFirstSortSpec() { 113 if (sortColumns.isEmpty()) { 114 return null; 115 } 116 return sortColumns.get(0); 117 } 118 119 /** 120 * Set column on which data is sorted. You must call <code>refresh()</code> 121 * after this to display the results. 122 * @param columnField field of the column to sort on 123 * @param sortDirection DynamicTable.ASCENDING or DynamicTable.DESCENDING 124 */ 125 public void sortOnColumn(String columnField, SortDirection sortDirection) { 126 // remove any existing sort on this column 127 for (Iterator<SortSpec> i = sortColumns.iterator(); i.hasNext(); ) { 128 if (i.next().getField().equals(columnField)) { 129 i.remove(); 130 break; 131 } 132 } 133 134 sortColumns.add(0, new SortSpec(columnField, sortDirection)); 135 updateSortIndicators(); 136 } 137 138 /** 139 * Defaults to ascending order. 140 */ 141 public void sortOnColumn(String columnField) { 142 sortOnColumn(columnField, SortDirection.ASCENDING); 143 } 144 145 public void clearSorts() { 146 sortColumns.clear(); 147 updateSortIndicators(); 148 } 149 150 // PAGINATION 151 152 /** 153 * Attach a new paginator to this table. 154 */ 155 public void attachPaginator(Paginator paginator) { 156 assert rowsPerPage != null; 157 paginators.add(paginator); 158 paginator.addCallback(new SimpleCallback() { 159 public void doCallback(Object source) { 160 setPaginatorStart(((Paginator) source).getStart()); 161 refresh(); 162 } 163 }); 164 paginator.setResultsPerPage(rowsPerPage.intValue()); 165 } 166 167 /** 168 * Set the page size of this table (only useful if you attach paginators). 169 */ 170 public void setRowsPerPage(int rowsPerPage) { 171 assert rowsPerPage > 0; 172 this.rowsPerPage = Integer.valueOf(rowsPerPage); 173 for (Paginator paginator : paginators) { 174 paginator.setResultsPerPage(rowsPerPage); 175 } 176 } 177 178 /** 179 * Set start row for pagination. You must call 180 * <code>refresh()</code> after this to display the results. 181 */ 182 public void setPaginatorStart(int start) { 183 for (Paginator paginator : paginators) { 184 paginator.setStart(start); 185 } 186 } 187 188 protected void refreshPaginators() { 189 for (Paginator paginator : paginators) { 190 paginator.update(); 191 } 192 } 193 194 protected void updatePaginatorTotalResults(int totalResults) { 195 for (Paginator paginator : paginators) { 196 paginator.setNumTotalResults(totalResults); 197 } 198 } 199 200 201 // FILTERING 202 203 public void addFilter(Filter filter) { 204 filters.add(filter); 205 filter.addCallback(new SimpleCallback() { 206 public void doCallback(Object source) { 207 setPaginatorStart(0); 208 refresh(); 209 } 210 }); 211 } 212 213 protected void addFilterParams(JSONObject params) { 214 for (Filter filter : filters) { 215 if (filter.isActive()) { 216 filter.addParams(params); 217 } 218 } 219 } 220 221 222 // DATA MANAGEMENT 223 224 public void refresh() { 225 JSONObject params = new JSONObject(); 226 addFilterParams(params); 227 dataSource.updateData(params, this); 228 } 229 230 public void onGotData(int totalCount) { 231 Integer start = null, limit = null; 232 SortSpec[] sortOn = null; 233 if (!paginators.isEmpty()) { 234 updatePaginatorTotalResults(totalCount); 235 Paginator p = paginators.get(0); 236 start = Integer.valueOf(p.getStart()); 237 limit = Integer.valueOf(p.getResultsPerPage()); 238 } 239 240 if (!sortColumns.isEmpty()) { 241 sortOn = new SortSpec[sortColumns.size()]; 242 sortColumns.toArray(sortOn); 243 } 244 dataSource.getPage(start, limit, sortOn, this); 245 } 246 247 public void handlePage(JSONArray data) { 248 clear(); 249 addRows(data); 250 refreshPaginators(); 251 notifyListenersRefreshed(); 252 } 253 254 public String[] getRowData(int row) { 255 String[] data = new String[columns.length]; 256 for (int i = 0; i < columns.length; i++) { 257 if(isWidgetColumn(i)) 258 continue; 259 data[i] = table.getHTML(row, i); 260 } 261 return data; 262 } 263 264 public DataSource getDataSource() { 265 return dataSource; 266 } 267 268 public void setDataSource(DataSource dataSource) { 269 this.dataSource = dataSource; 270 } 271 272 273 // INPUT 274 275 @Override 276 protected void onCellClicked(int row, int cell, boolean isRightClick) { 277 if (row == headerRow) { 278 if (isWidgetColumn(cell)) { 279 // ignore sorting on widget columns 280 return; 281 } 282 String columnName = columns[cell][COL_NAME]; 283 SortDirection newSortDirection = SortDirection.ASCENDING; 284 SortSpec firstSortSpec = getFirstSortSpec(); 285 // when clicking on the last sorted field, invert the sort 286 if (firstSortSpec != null && columnName.equals(firstSortSpec.getField())) { 287 newSortDirection = invertSortDirection(firstSortSpec.getDirection()); 288 } 289 290 sortOnColumn(columnName, newSortDirection); 291 refresh(); 292 return; 293 } 294 295 super.onCellClicked(row, cell, isRightClick); 296 } 297 298 private SortDirection invertSortDirection(SortDirection direction) { 299 return direction == SortDirection.ASCENDING ? 300 SortDirection.DESCENDING : SortDirection.ASCENDING; 301 } 302 303 public void addListener(DynamicTableListener listener) { 304 super.addListener(listener); 305 dynamicTableListeners.add(listener); 306 } 307 308 public void removeListener(DynamicTableListener listener) { 309 super.removeListener(listener); 310 dynamicTableListeners.remove(listener); 311 } 312 313 protected void notifyListenersRefreshed() { 314 for (DynamicTableListener listener : dynamicTableListeners) { 315 listener.onTableRefreshed(); 316 } 317 } 318 319 public List<SortSpec> getSortSpecs() { 320 return Collections.unmodifiableList(sortColumns); 321 } 322 323 public void onError(JSONObject errorObject) { 324 // nothing to do 325 } 326} 327