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