blob: 6df18a06a17985da10f3d475489c0765c13cbefc [file] [log] [blame]
package autotest.common.table;
import autotest.common.SimpleCallback;
import autotest.common.table.DataSource.DataCallback;
import autotest.common.table.DataSource.Query;
import autotest.common.table.DataSource.SortDirection;
import autotest.common.table.DataSource.SortSpec;
import autotest.common.ui.Paginator;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Image;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* Extended DataTable supporting sorting, filtering and pagination.
*/
public class DynamicTable extends DataTable implements DataCallback {
public static final int NO_COLUMN = -1;
public static final String SORT_UP_IMAGE = "arrow_up.png",
SORT_DOWN_IMAGE = "arrow_down.png";
public static interface DynamicTableListener extends DataTableListener {
public void onTableRefreshed();
}
static class SortIndicator extends Composite {
public int column;
private Image image = new Image();
public SortIndicator(int column) {
this.column = column;
initWidget(image);
setVisible(false);
}
public void sortOn(SortDirection direction) {
image.setUrl(direction == SortDirection.ASCENDING ? SORT_UP_IMAGE : SORT_DOWN_IMAGE);
setVisible(true);
}
public void sortOff() {
setVisible(false);
}
}
protected DataSource dataSource;
private Query currentQuery;
private boolean clientSortable = false;
private SortIndicator[] sortIndicators;
private List<SortSpec> sortColumns = new ArrayList<SortSpec>();
protected List<Filter> filters = new ArrayList<Filter>();
protected List<Paginator> paginators = new ArrayList<Paginator>();
protected Integer rowsPerPage;
protected List<DynamicTableListener> dynamicTableListeners =
new ArrayList<DynamicTableListener>();
public DynamicTable(String[][] columns, DataSource dataSource) {
super(columns);
setDataSource(dataSource);
}
// SORTING
/**
* Makes the table client sortable, that is, sortable by the user by
* clicking on column headers.
*/
public void makeClientSortable() {
this.clientSortable = true;
table.getRowFormatter().addStyleName(0,
DataTable.HEADER_STYLE + "-sortable");
sortIndicators = new SortIndicator[columns.length];
for(int i = 0; i < columns.length; i++) {
sortIndicators[i] = new SortIndicator(i);
// we have to use an HTMLPanel here to preserve styles correctly and
// not break hover
// we add a <span> with a unique ID to hold the sort indicator
String name = columns[i][COL_TITLE];
String id = HTMLPanel.createUniqueId();
HTMLPanel panel = new HTMLPanel(name +
" <span id=\"" + id + "\"></span>");
panel.add(sortIndicators[i], id);
table.setWidget(0, i, panel);
}
}
private void updateSortIndicators() {
if (!clientSortable) {
return;
}
SortSpec firstSpec = getFirstSortSpec();
for (SortIndicator indicator : sortIndicators) {
if (columns[indicator.column][COL_NAME].equals(firstSpec.getField())) {
indicator.sortOn(firstSpec.getDirection());
} else {
indicator.sortOff();
}
}
}
private SortSpec getFirstSortSpec() {
if (sortColumns.isEmpty()) {
return null;
}
return sortColumns.get(0);
}
/**
* Set column on which data is sorted. You must call <code>refresh()</code>
* after this to display the results.
* @param columnField field of the column to sort on
* @param sortDirection DynamicTable.ASCENDING or DynamicTable.DESCENDING
*/
public void sortOnColumn(String columnField, SortDirection sortDirection) {
// remove any existing sort on this column
for (Iterator<SortSpec> i = sortColumns.iterator(); i.hasNext(); ) {
if (i.next().getField().equals(columnField)) {
i.remove();
break;
}
}
sortColumns.add(0, new SortSpec(columnField, sortDirection));
updateSortIndicators();
}
/**
* Defaults to ascending order.
*/
public void sortOnColumn(String columnField) {
sortOnColumn(columnField, SortDirection.ASCENDING);
}
public void clearSorts() {
sortColumns.clear();
updateSortIndicators();
}
// PAGINATION
/**
* Attach a new paginator to this table.
*/
public void attachPaginator(Paginator paginator) {
assert rowsPerPage != null;
paginators.add(paginator);
paginator.addCallback(new SimpleCallback() {
public void doCallback(Object source) {
setPaginatorStart(((Paginator) source).getStart());
fetchPage();
}
});
paginator.setResultsPerPage(rowsPerPage.intValue());
}
/**
* Set the page size of this table (only useful if you attach paginators).
*/
public void setRowsPerPage(int rowsPerPage) {
assert rowsPerPage > 0;
this.rowsPerPage = Integer.valueOf(rowsPerPage);
for (Paginator paginator : paginators) {
paginator.setResultsPerPage(rowsPerPage);
}
}
/**
* Set start row for pagination. You must call
* <code>refresh()</code> after this to display the results.
*/
public void setPaginatorStart(int start) {
for (Paginator paginator : paginators) {
paginator.setStart(start);
}
}
protected void refreshPaginators() {
for (Paginator paginator : paginators) {
paginator.update();
}
}
protected void updatePaginatorTotalResults(int totalResults) {
for (Paginator paginator : paginators) {
paginator.setNumTotalResults(totalResults);
}
}
// FILTERING
public void addFilter(Filter filter) {
filters.add(filter);
filter.addCallback(new SimpleCallback() {
public void doCallback(Object source) {
setPaginatorStart(0);
refresh();
}
});
}
protected void addFilterParams(JSONObject params) {
for (Filter filter : filters) {
if (filter.isActive()) {
filter.addParams(params);
}
}
}
public boolean isAnyUserFilterActive() {
for (Filter filter : filters) {
if (filter.isUserControlled() && filter.isActive()) {
return true;
}
}
return false;
}
// DATA MANAGEMENT
public void refresh() {
JSONObject params = new JSONObject();
addFilterParams(params);
dataSource.query(params, this);
}
@Override
public void onQueryReady(Query query) {
currentQuery = query;
if (!paginators.isEmpty()) {
query.getTotalResultCount(this);
}
fetchPage();
}
private void fetchPage() {
Integer start = null, limit = null;
SortSpec[] sortOn = null;
if (!paginators.isEmpty()) {
Paginator p = paginators.get(0);
start = Integer.valueOf(p.getStart());
limit = Integer.valueOf(p.getResultsPerPage());
}
if (!sortColumns.isEmpty()) {
sortOn = new SortSpec[sortColumns.size()];
sortColumns.toArray(sortOn);
}
currentQuery.getPage(start, limit, sortOn, this);
}
@Override
public void handleTotalResultCount(int totalCount) {
updatePaginatorTotalResults(totalCount);
refreshPaginators();
notifyListenersRefreshed();
}
public void handlePage(List<JSONObject> data) {
clear();
addRows(data);
refreshPaginators();
notifyListenersRefreshed();
}
public String[] getRowData(int row) {
String[] data = new String[columns.length];
for (int i = 0; i < columns.length; i++) {
if(isWidgetColumn(i)) {
continue;
}
data[i] = table.getHTML(row, i);
}
return data;
}
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Query getCurrentQuery() {
return currentQuery;
}
// INPUT
@Override
protected void onCellClicked(int row, int cell, boolean isRightClick) {
if (row == headerRow) {
if (isWidgetColumn(cell)) {
// ignore sorting on widget columns
return;
}
String columnName = columns[cell][COL_NAME];
SortDirection newSortDirection = SortDirection.ASCENDING;
SortSpec firstSortSpec = getFirstSortSpec();
// when clicking on the last sorted field, invert the sort
if (firstSortSpec != null && columnName.equals(firstSortSpec.getField())) {
newSortDirection = invertSortDirection(firstSortSpec.getDirection());
}
sortOnColumn(columnName, newSortDirection);
refresh();
return;
}
super.onCellClicked(row, cell, isRightClick);
}
private SortDirection invertSortDirection(SortDirection direction) {
return direction == SortDirection.ASCENDING ?
SortDirection.DESCENDING : SortDirection.ASCENDING;
}
public void addListener(DynamicTableListener listener) {
super.addListener(listener);
dynamicTableListeners.add(listener);
}
public void removeListener(DynamicTableListener listener) {
super.removeListener(listener);
dynamicTableListeners.remove(listener);
}
protected void notifyListenersRefreshed() {
for (DynamicTableListener listener : dynamicTableListeners) {
listener.onTableRefreshed();
}
}
public List<SortSpec> getSortSpecs() {
return Collections.unmodifiableList(sortColumns);
}
public void onError(JSONObject errorObject) {
// nothing to do
}
}