com.databasepreservation.visualization.client.common.lists.AsyncTableCell.java Source code

Java tutorial

Introduction

Here is the source code for com.databasepreservation.visualization.client.common.lists.AsyncTableCell.java

Source

/**
 * The contents of this file are based on those found at https://github.com/keeps/roda
 * and are subject to the license and copyright detailed in https://github.com/keeps/roda
 */
package com.databasepreservation.visualization.client.common.lists;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.roda.core.data.adapter.facet.Facets;
import org.roda.core.data.adapter.filter.Filter;
import org.roda.core.data.adapter.sort.SortParameter;
import org.roda.core.data.adapter.sort.Sorter;
import org.roda.core.data.adapter.sublist.Sublist;
import org.roda.core.data.v2.index.IndexResult;
import org.roda.core.data.v2.index.IsIndexed;
import org.roda.core.data.v2.index.SelectedItems;
import org.roda.core.data.v2.index.SelectedItemsFilter;
import org.roda.core.data.v2.index.SelectedItemsList;

import com.databasepreservation.visualization.shared.client.ClientLogger;
import com.databasepreservation.visualization.shared.client.widgets.MyCellTableResources;
import com.databasepreservation.visualization.shared.client.widgets.wcag.AccessibleCellTable;
import com.databasepreservation.visualization.shared.client.widgets.wcag.AccessibleSimplePager;
import com.google.gwt.cell.client.CheckboxCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortList;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.user.cellview.client.LoadingStateChangeEvent;
import com.google.gwt.user.cellview.client.PageSizePager;
import com.google.gwt.user.cellview.client.SafeHtmlHeader;
import com.google.gwt.user.cellview.client.SimplePager;
import com.google.gwt.user.cellview.client.ColumnSortEvent.AsyncHandler;
import com.google.gwt.user.cellview.client.ColumnSortList.ColumnSortInfo;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.view.client.CellPreviewEvent;
import com.google.gwt.view.client.DefaultSelectionEventManager;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.RowCountChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.gwt.view.client.CellPreviewEvent.Handler;

import config.i18n.client.ClientMessages;

// import config.i18n.client.BrowseMessages;

public abstract class AsyncTableCell<T extends IsIndexed, O> extends FlowPanel
        implements HasValueChangeHandlers<IndexResult<T>> {

    private static final ClientMessages messages = GWT.create(ClientMessages.class);

    private final MyAsyncDataProvider<T> dataProvider;
    private final SingleSelectionModel<T> selectionModel;
    private final AsyncHandler columnSortHandler;

    private final AccessibleSimplePager resultsPager;
    private final PageSizePager pageSizePager;
    private final CellTable<T> display;
    private FlexTable exportButtons;
    private Anchor exportVisibleButton;
    private Anchor exportAllButton;

    private ScrollPanel displayScroll;
    private SimplePanel displayScrollWrapper;

    private FlowPanel selectAllPanel;
    private FlowPanel selectAllPanelBody;
    private Label selectAllLabel;
    private CheckBox selectAllCheckBox;

    private Column<T, Boolean> selectColumn;
    private Set<T> selected = new HashSet<T>();
    private final List<CheckboxSelectionListener<T>> listeners = new ArrayList<AsyncTableCell.CheckboxSelectionListener<T>>();

    private Filter filter;
    private boolean justActive;
    private Facets facets;
    private boolean selectable;

    private final ClientLogger logger = new ClientLogger(getClass().getName());

    private int initialPageSize = 20;
    private int pageSizeIncrement = 100;

    private Class<T> selectedClass;
    private final O object;

    public AsyncTableCell() {
        this(null, false, null, null, false, false, 20, 100, null);
    }

    public AsyncTableCell(Filter filter, boolean justActive, Facets facets, String summary, boolean selectable,
            boolean exportable, O object) {
        this(filter, justActive, facets, summary, selectable, exportable, 20, 100, object);
    }

    public AsyncTableCell(Filter filter, boolean justActive, Facets facets, String summary, boolean selectable,
            boolean exportable, int initialPageSize, int pageSizeIncrement, O object) {
        super();

        this.initialPageSize = initialPageSize;
        this.pageSizeIncrement = pageSizeIncrement;
        this.object = object;

        if (summary == null) {
            summary = "summary" + Random.nextInt(1000);
        }

        this.filter = filter;
        this.justActive = justActive;
        this.facets = facets;
        this.selectable = selectable;

        display = new AccessibleCellTable<T>(getInitialPageSize(),
                (MyCellTableResources) GWT.create(MyCellTableResources.class), getKeyProvider(), summary);
        display.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED);
        display.setLoadingIndicator(new HTML(SafeHtmlUtils.fromSafeConstant(
                "<div class='spinner'><div class='double-bounce1'></div><div class='double-bounce2'></div></div>")));

        configure(display);

        this.dataProvider = new MyAsyncDataProvider<T>(display, new IndexResultDataProvider<T>() {

            @Override
            public void getData(Sublist sublist, ColumnSortList columnSortList,
                    AsyncCallback<IndexResult<T>> callback) {
                AsyncTableCell.this.getData(sublist, columnSortList, callback);
            }
        }) {

            @Override
            protected void fireChangeEvent(IndexResult<T> result) {
                ValueChangeEvent.fire(AsyncTableCell.this, result);
            }
        };

        dataProvider.addDataDisplay(display);

        if (exportable) {
            // mimic PageSizePager
            exportButtons = new FlexTable();
            exportButtons.setCellPadding(0);
            exportButtons.setCellSpacing(0);

            exportVisibleButton = new Anchor("Export visible");
            exportVisibleButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    AsyncTableCell.this.exportVisibleClickHandler();
                }
            });

            exportAllButton = new Anchor("Export all");
            exportAllButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    AsyncTableCell.this.exportAllClickHandler();
                }
            });

            exportButtons.setWidget(0, 0, exportVisibleButton);
            exportButtons.setText(0, 1, " | ");
            exportButtons.setWidget(0, 2, exportAllButton);
        }

        resultsPager = new AccessibleSimplePager(AccessibleSimplePager.TextLocation.LEFT,
                (SimplePager.Resources) GWT.create(SimplePager.Resources.class), false, initialPageSize, false,
                false, (SimplePager.ImageButtonsConstants) GWT.create(SimplePager.ImageButtonsConstants.class));
        resultsPager.setDisplay(display);

        pageSizePager = new PageSizePager(getPageSizePagerIncrement());
        pageSizePager.setDisplay(display);

        createSelectAllPanel();

        displayScroll = new ScrollPanel(display);
        displayScrollWrapper = new SimplePanel(displayScroll);

        add(selectAllPanel);
        add(displayScrollWrapper);
        add(resultsPager);
        if (exportButtons != null) {
            add(exportButtons);
        }
        add(pageSizePager);

        selectionModel = new SingleSelectionModel<>(getKeyProvider());

        Handler<T> selectionEventManager = getSelectionEventManager();
        if (selectionEventManager != null) {
            display.setSelectionModel(selectionModel, selectionEventManager);
        } else {
            display.setSelectionModel(selectionModel);
        }

        columnSortHandler = new AsyncHandler(display);
        display.addColumnSortHandler(columnSortHandler);

        display.addLoadingStateChangeHandler(new LoadingStateChangeEvent.Handler() {
            @Override
            public void onLoadingStateChanged(LoadingStateChangeEvent event) {
                if (LoadingStateChangeEvent.LoadingState.LOADED.equals(event.getLoadingState())) {
                    handleScrollChanges();
                }
            }
        });

        addStyleName("my-asyncdatagrid");
        resultsPager.addStyleName("my-asyncdatagrid-pager-results");
        pageSizePager.addStyleName("my-asyncdatagrid-pager-pagesize");
        displayScrollWrapper.addStyleName("my-asyncdatagrid-display-scroll-wrapper");
        display.addStyleName("my-asyncdatagrid-display");
        if (exportButtons != null) {
            exportButtons.addStyleName("my-asyncdatagrid-pager-pagesize");
            // exportVisibleButton.addStyleName("btn btn-export btn-export-visible");
            // exportAllButton.addStyleName("btn btn-export btn-export-all");
        }

        displayScroll.addScrollHandler(new ScrollHandler() {
            @Override
            public void onScroll(ScrollEvent event) {
                handleScrollChanges();
            }
        });

        addValueChangeHandler(new ValueChangeHandler<IndexResult<T>>() {
            @Override
            public void onValueChange(ValueChangeEvent<IndexResult<T>> event) {
                selected = new HashSet<T>();
                hideSelectAllPanel();
            }
        });

        Label emptyInfo = new Label(messages.noItemsToDisplay());
        display.setEmptyTableWidget(emptyInfo);
    }

    protected void handleScrollChanges() {
        int maximumHorizontalScrollPosition = displayScroll.getMaximumHorizontalScrollPosition();
        if (maximumHorizontalScrollPosition > 0) {
            double percent = displayScroll.getHorizontalScrollPosition() * 100F
                    / displayScroll.getMaximumHorizontalScrollPosition();

            GWT.log(String.valueOf(percent));

            if (percent > 0) {
                // show left shadow
                displayScrollWrapper.addStyleName("my-asyncdatagrid-display-scroll-wrapper-left");
            } else {
                // hide left shadow
                displayScrollWrapper.removeStyleName("my-asyncdatagrid-display-scroll-wrapper-left");
            }

            if (percent < 100) {
                // show right shadow
                displayScrollWrapper.addStyleName("my-asyncdatagrid-display-scroll-wrapper-right");
            } else {
                // hide right shadow
                displayScrollWrapper.removeStyleName("my-asyncdatagrid-display-scroll-wrapper-right");
            }
        } else {
            // hide both shadows
            displayScrollWrapper.removeStyleName("my-asyncdatagrid-display-scroll-wrapper-left");
            displayScrollWrapper.removeStyleName("my-asyncdatagrid-display-scroll-wrapper-right");
        }
    }

    private void configure(final CellTable<T> display) {
        if (selectable) {
            selectColumn = new Column<T, Boolean>(new CheckboxCell(true, false)) {
                @Override
                public Boolean getValue(T object) {
                    return selected.contains(object);
                }
            };

            selectColumn.setFieldUpdater(new FieldUpdater<T, Boolean>() {
                @Override
                public void update(int index, T object, Boolean isSelected) {
                    if (isSelected) {
                        selected.add(object);
                    } else {
                        selected.remove(object);
                    }

                    // update header
                    display.redrawHeaders();
                    fireOnCheckboxSelectionChanged();
                }
            });

            Header<Boolean> selectHeader = new Header<Boolean>(new CheckboxCell(true, true)) {

                @Override
                public Boolean getValue() {
                    Boolean ret;

                    if (selected.isEmpty()) {
                        ret = false;
                    } else if (selected.containsAll(getVisibleItems())) {
                        ret = true;
                        showSelectAllPanel();
                    } else {
                        // some are selected
                        ret = false;
                        hideSelectAllPanel();
                    }

                    return ret;
                }
            };

            selectHeader.setUpdater(new ValueUpdater<Boolean>() {

                @Override
                public void update(Boolean value) {
                    if (value) {
                        selected.addAll(getVisibleItems());
                        showSelectAllPanel();
                    } else {
                        selected.clear();
                        hideSelectAllPanel();
                    }
                    redraw();
                    fireOnCheckboxSelectionChanged();
                }
            });

            display.addColumn(selectColumn, selectHeader);
            display.setColumnWidth(selectColumn, "45px");
        }
        configureDisplay(display);
    }

    protected abstract void configureDisplay(CellTable<T> display);

    protected int getInitialPageSize() {
        return initialPageSize;
    }

    protected ProvidesKey<T> getKeyProvider() {
        return new ProvidesKey<T>() {

            @Override
            public Object getKey(T item) {
                return item.getUUID();
            }
        };
    }

    protected abstract void getData(Sublist sublist, ColumnSortList columnSortList,
            AsyncCallback<IndexResult<T>> callback);

    protected int getPageSizePagerIncrement() {
        return pageSizeIncrement;
    }

    protected CellPreviewEvent.Handler<T> getSelectionEventManager() {
        if (selectable) {
            return DefaultSelectionEventManager.<T>createBlacklistManager(0);
        } else {
            return null;
        }
    }

    public SingleSelectionModel<T> getSelectionModel() {
        return selectionModel;
    }

    public void refresh() {
        selected = new HashSet<T>();
        hideSelectAllPanel();
        display.setVisibleRangeAndClearData(new Range(0, getInitialPageSize()), true);
        getSelectionModel().clear();
    }

    public void update() {
        dataProvider.update();
    }

    private Timer autoUpdateTimer = null;
    private int autoUpdateTimerMillis = 0;

    public void autoUpdate(int periodMillis) {
        if (autoUpdateTimer != null) {
            autoUpdateTimer.cancel();
        }

        autoUpdateTimer = new Timer() {

            @Override
            public void run() {
                dataProvider.update(new AsyncCallback<Void>() {

                    @Override
                    public void onFailure(Throwable caught) {
                        // disable auto-update
                        autoUpdateTimer.cancel();
                    }

                    @Override
                    public void onSuccess(Void result) {
                        // do nothing
                    }
                });
            }
        };

        autoUpdateTimerMillis = periodMillis;
        if (this.isAttached()) {
            autoUpdateTimer.scheduleRepeating(periodMillis);
        }

    }

    @Override
    protected void onDetach() {
        if (autoUpdateTimer != null) {
            autoUpdateTimer.cancel();
        }
        super.onDetach();
    }

    @Override
    protected void onLoad() {
        if (autoUpdateTimer != null && autoUpdateTimerMillis > 0 && !autoUpdateTimer.isRunning()) {
            autoUpdateTimer.scheduleRepeating(autoUpdateTimerMillis);
        }
        super.onLoad();
        getSelectionModel().clear();
    }

    public void redraw() {
        display.redraw();
    }

    public Filter getFilter() {
        return filter;
    }

    public boolean getJustActive() {
        return justActive;
    }

    public void setFilter(Filter filter) {
        this.filter = filter;
        refresh();
    }

    @Override
    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<IndexResult<T>> handler) {
        return addHandler(handler, ValueChangeEvent.getType());
    }

    public Facets getFacets() {
        return facets;
    }

    public void setFacets(Facets facets) {
        this.facets = facets;
        refresh();
    }

    public List<T> getVisibleItems() {
        return display.getVisibleItems();
    }

    protected Sorter createSorter(ColumnSortList columnSortList,
            Map<Column<T, ?>, List<String>> columnSortingKeyMap) {
        Sorter sorter = new Sorter();
        for (int i = 0; i < columnSortList.size(); i++) {
            ColumnSortInfo columnSortInfo = columnSortList.get(i);

            List<String> sortParameterKeys = columnSortingKeyMap.get(columnSortInfo.getColumn());

            if (sortParameterKeys != null) {
                for (String sortParameterKey : sortParameterKeys) {
                    sorter.add(new SortParameter(sortParameterKey, !columnSortInfo.isAscending()));
                }
            } else {
                logger.warn("Selecting a sorter that is not mapped");
            }
        }
        return sorter;
    }

    public void nextItemSelection() {
        nextItemSelection(false);
    }

    public void nextItemSelection(boolean nextPageJump) {
        if (getSelectionModel().getSelectedObject() != null) {
            T selectedItem = getSelectionModel().getSelectedObject();
            int selectedIndex = getVisibleItems().indexOf(selectedItem);

            if (nextPageJump) {
                if (selectedIndex == -1) {
                    getSelectionModel().setSelected(getVisibleItems().get(0), true);
                } else {
                    getSelectionModel().setSelected(getVisibleItems().get(selectedIndex + 1), true);
                }
            } else {
                if (selectedIndex < getVisibleItems().size() - 1) {
                    getSelectionModel().setSelected(getVisibleItems().get(selectedIndex + 1), true);
                }
            }
        } else {
            getSelectionModel().setSelected(getVisibleItems().get(0), true);
        }
    }

    public void previousItemSelection() {
        previousItemSelection(false);
    }

    public void previousItemSelection(boolean previousPageJump) {
        if (getSelectionModel().getSelectedObject() != null) {
            T selectedItem = getSelectionModel().getSelectedObject();
            int selectedIndex = getVisibleItems().indexOf(selectedItem);

            if (previousPageJump) {
                if (selectedIndex == -1) {
                    getSelectionModel().setSelected(getVisibleItems().get(getVisibleItems().size() - 1), true);
                } else {
                    getSelectionModel().setSelected(getVisibleItems().get(selectedIndex - 1), true);
                }
            } else {
                if (selectedIndex > 0) {
                    getSelectionModel().setSelected(getVisibleItems().get(selectedIndex - 1), true);
                }
            }
        } else {
            getSelectionModel().setSelected(getVisibleItems().get(0), true);
        }
    }

    public boolean nextPageOnNextFile() {
        boolean nextPage = false;
        if (getSelectionModel().getSelectedObject() != null) {
            T selectedItem = getSelectionModel().getSelectedObject();
            if (getVisibleItems().indexOf(selectedItem) == (resultsPager.getPageSize() - 1)
                    && resultsPager.hasNextPage()) {
                nextPage = true;
            }
        }
        return nextPage;
    }

    public boolean previousPageOnPreviousFile() {
        boolean previousPage = false;
        if (getSelectionModel().getSelectedObject() != null) {
            T selectedItem = getSelectionModel().getSelectedObject();
            if (getVisibleItems().indexOf(selectedItem) == 0 && resultsPager.hasPreviousPage()) {
                previousPage = true;
            }
        }
        return previousPage;
    }

    public void nextPage() {
        resultsPager.nextPage();
    }

    public void prevousPage() {
        resultsPager.previousPage();
    }

    public boolean isSelectable() {
        return selectable;
    }

    public void setSelectable(boolean selectable) {
        this.selectable = selectable;
    }

    public SelectedItems<T> getSelected() {
        SelectedItems<T> ret;
        if (isAllSelected()) {
            ret = new SelectedItemsFilter<T>(getFilter(), selectedClass.getName(), getJustActive());
        } else {
            List<String> ids = new ArrayList<>();

            for (T item : selected) {
                ids.add(item.getUUID());
            }

            ret = new SelectedItemsList<T>(ids, selectedClass.getName());
        }

        return ret;
    }

    public void setSelected(Set<T> newSelected) {
        selected.clear();
        selected.addAll(newSelected);
        redraw();
        fireOnCheckboxSelectionChanged();
    }

    public void clearSelected() {
        selected.clear();
        redraw();
        fireOnCheckboxSelectionChanged();
    }

    // LISTENER

    public interface CheckboxSelectionListener<T extends IsIndexed> {
        public void onSelectionChange(SelectedItems<T> selected);
    }

    public void addCheckboxSelectionListener(CheckboxSelectionListener<T> checkboxSelectionListener) {
        listeners.add(checkboxSelectionListener);
    }

    public void removeCheckboxSelectionListener(CheckboxSelectionListener<T> listener) {
        listeners.remove(listener);
    }

    public void fireOnCheckboxSelectionChanged() {
        for (CheckboxSelectionListener<T> listener : listeners) {
            listener.onSelectionChange(getSelected());
        }
    }

    // SELECT ALL PANEL

    public void createSelectAllPanel() {
        selectAllPanel = new FlowPanel();
        selectAllPanelBody = new FlowPanel();
        selectAllCheckBox = new CheckBox();
        selectAllLabel = new Label("Select all");

        selectAllPanelBody.add(selectAllCheckBox);
        selectAllPanelBody.add(selectAllLabel);
        selectAllPanel.add(selectAllPanelBody);
        selectAllPanel.setVisible(false);

        selectAllPanel.addStyleName("panel");
        selectAllPanelBody.addStyleName("panel-body");

        selectAllCheckBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {

            @Override
            public void onValueChange(ValueChangeEvent<Boolean> event) {
                fireOnCheckboxSelectionChanged();
            }
        });

    }

    public void showSelectAllPanel() {
        if (!selectAllPanel.isVisible() && resultsPager.hasNextPage() || resultsPager.hasPreviousPage()) {
            // selectAllLabel.setText(messages.listSelectAllMessage(dataProvider.getRowCount()));
            selectAllLabel.setText("Select all " + dataProvider.getRowCount() + " items");
            selectAllCheckBox.setValue(false);
            selectAllPanel.setVisible(true);
        }
    }

    public void hideSelectAllPanel() {
        selectAllCheckBox.setValue(false);
        selectAllPanel.setVisible(false);
    }

    public Boolean isAllSelected() {
        return selectAllCheckBox.getValue();
    }

    public O getObject() {
        return object;
    }

    public int getRowCount() {
        return dataProvider.getRowCount();
    }

    public Date getDate() {
        return dataProvider.getDate();
    }

    public List<CheckboxSelectionListener<T>> getListeners() {
        return this.listeners;
    }

    public Class<T> getSelectedClass() {
        return this.selectedClass;
    }

    public void setSelectedClass(Class<T> selectedClass) {
        this.selectedClass = selectedClass;
    }

    protected void addColumn(Column<T, ?> column, SafeHtml headerHTML, boolean nowrap, boolean alignRight) {
        SafeHtmlHeader header = new SafeHtmlHeader(headerHTML);

        display.addColumn(column, header);

        if (nowrap && alignRight) {
            header.setHeaderStyleNames("nowrap text-align-right");
            column.setCellStyleNames("nowrap text-align-right");
        } else if (nowrap) {
            header.setHeaderStyleNames("cellTableFadeOut");
            column.setCellStyleNames("cellTableFadeOut");
        }
    }

    protected void addColumn(Column<T, ?> column, SafeHtml headerHTML, boolean nowrap, boolean alignRight,
            double fixedSize) {
        addColumn(column, headerHTML, nowrap, alignRight);
        display.setColumnWidth(column, fixedSize, Style.Unit.EM);
    }

    protected void addColumn(Column<T, ?> column, String headerText, boolean nowrap, boolean alignRight) {
        addColumn(column, SafeHtmlUtils.fromString(headerText), nowrap, alignRight);
    }

    protected void addColumn(Column<T, ?> column, String headerText, boolean nowrap, boolean alignRight,
            double fixedSize) {
        addColumn(column, SafeHtmlUtils.fromString(headerText), nowrap, alignRight, fixedSize);
    }

    public abstract void exportVisibleClickHandler();

    public abstract void exportAllClickHandler();
}