com.haulmont.cuba.web.widgets.CubaGroupTable.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.web.widgets.CubaGroupTable.java

Source

/*
 * Copyright (c) 2008-2017 Haulmont.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.haulmont.cuba.web.widgets;

import com.haulmont.cuba.web.widgets.data.AggregationContainer;
import com.haulmont.cuba.web.widgets.data.GroupTableContainer;
import com.haulmont.cuba.web.widgets.data.util.NullGroupTableContainer;
import com.vaadin.server.KeyMapper;
import com.vaadin.server.PaintException;
import com.vaadin.server.PaintTarget;
import com.vaadin.v7.data.Container;
import com.vaadin.v7.data.util.IndexedContainer;
import com.vaadin.v7.ui.Table;
import org.apache.commons.collections4.CollectionUtils;

import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Collectors;

@SuppressWarnings("deprecation")
public class CubaGroupTable extends CubaTable implements GroupTableContainer {

    protected KeyMapper groupIdMap = new KeyMapper();

    protected List<Object> groupDisallowedProperties;

    protected GroupPropertyValueFormatter groupPropertyValueFormatter;

    protected boolean fixedGrouping = false;

    protected boolean requestColumnReorderingAllowed = true;

    /**
     * Attention: this method is copied from the parent class: {@link Table#setColumnOrder(java.lang.Object[])}
     */
    public void setColumnOrder(Object[] columnOrder) {
        if (columnOrder == null || !isColumnReorderingAllowed()) {
            return;
        }
        List<Object> newOrder = new ArrayList<>();
        for (Object aColumnOrder : columnOrder) {
            if (aColumnOrder != null && _visibleColumns().contains(aColumnOrder)) {
                _visibleColumns().remove(aColumnOrder);
                newOrder.add(aColumnOrder);
            }
        }
        for (final Object columnId : _visibleColumns()) {
            if (!newOrder.contains(columnId)) {
                newOrder.add(columnId);
            }
        }
        _visibleColumns().clear();
        _visibleColumns().addAll(newOrder);

        // Assure visual refresh
        refreshRowCache();
    }

    @Override
    public void setContainerDataSource(Container newDataSource) {
        if (newDataSource == null || newDataSource instanceof IndexedContainer) { // if it is just created
            newDataSource = new NullGroupTableContainer(new IndexedContainer());
        } else if (!(newDataSource instanceof GroupTableContainer)) {
            throw new IllegalArgumentException("CubaGroupTable supports only GroupTableContainer");
        }

        super.setContainerDataSource(newDataSource);
    }

    @Override
    public void paintContent(PaintTarget target) throws PaintException {
        super.paintContent(target);

        if (hasGroups()) {
            Collection groupProperties = getGroupProperties();
            String[] groupColumns = new String[groupProperties.size()];

            int index = 0;
            for (Object groupColumnId : groupProperties) {
                groupColumns[index++] = _columnIdMap().key(groupColumnId);
            }
            target.addVariable(this, "groupColumns", groupColumns);
        }
    }

    @Override
    public boolean isColumnReorderingAllowed() {
        return requestColumnReorderingAllowed && super.isColumnReorderingAllowed();
    }

    @Override
    public void changeVariables(Object source, Map<String, Object> variables) {
        Object[] newGroupProperties = null;

        if (variables.containsKey("columnorder") && !variables.containsKey("groupedcolumns")) {
            newGroupProperties = new Object[0];
        } else if (variables.containsKey("groupedcolumns")) {
            focus();

            Object[] ids = (Object[]) variables.get("groupedcolumns");
            Object[] groupProperties = new Object[ids.length];
            for (int i = 0; i < ids.length; i++) {
                groupProperties[i] = _columnIdMap().get(ids[i].toString());
            }
            newGroupProperties = groupProperties;
            // Deny group by generated columns
            if (!_columnGenerators().isEmpty()) {
                List<Object> notGeneratedProperties = new ArrayList<>();
                for (Object id : newGroupProperties) {
                    // todo support grouping by generated columns with Printable
                    if (!_columnGenerators().containsKey(id) || isNonGeneratedProperty(id)) {
                        notGeneratedProperties.add(id);
                    }
                }
                newGroupProperties = notGeneratedProperties.toArray();
            }
        }

        if (variables.containsKey("collapsedcolumns")) {
            Object[] ids = (Object[]) variables.get("collapsedcolumns");

            Set<Object> idSet = ids.length > 0 ? new HashSet<>() : Collections.emptySet();

            for (Object id : ids) {
                idSet.add(_columnIdMap().get(id.toString()));
            }

            boolean needToRegroup = false;
            List<Object> groupProperties = new ArrayList<>(getGroupProperties());
            for (int index = 0; index < groupProperties.size(); index++) {
                final Object propertyId = groupProperties.get(index);
                if (idSet.contains(propertyId)) {
                    groupProperties.subList(index, groupProperties.size()).clear();
                    needToRegroup = true;
                    break;
                }
            }
            if (needToRegroup) {
                newGroupProperties = groupProperties.toArray();
            }
        }

        if ((hasGroupDisallowedProperties(newGroupProperties) || fixedGrouping)
                && isGroupsChanged(newGroupProperties)) {
            requestColumnReorderingAllowed = false;
            markAsDirty();
        }

        super.changeVariables(source, variables);

        if (!(hasGroupDisallowedProperties(newGroupProperties) || fixedGrouping) && newGroupProperties != null
                && isGroupsChanged(newGroupProperties)) {
            groupBy(newGroupProperties, true);
        }

        requestColumnReorderingAllowed = true;
    }

    protected boolean hasGroupDisallowedProperties(Object[] newGroupProperties) {
        if (newGroupProperties == null) {
            return false;
        }

        if (CollectionUtils.isEmpty(groupDisallowedProperties)) {
            return false;
        }

        for (Object property : newGroupProperties) {
            if (groupDisallowedProperties.contains(property)) {
                return true;
            }
        }
        return false;
    }

    protected boolean isGroupsChanged(Object[] newGroupProperties) {
        Collection<?> oldGroupProperties = getGroupProperties();
        if (newGroupProperties == null && oldGroupProperties == null)
            return false;

        if (newGroupProperties == null)
            return true;

        if (oldGroupProperties == null)
            return true;

        if (oldGroupProperties.size() != newGroupProperties.length)
            return true;

        int i = 0;
        for (Object oldGroupProperty : oldGroupProperties) {
            if (!Objects.equals(oldGroupProperty, newGroupProperties[i]))
                return true;
            i++;
        }

        return false;
    }

    @Override
    protected boolean changeVariables(Map<String, Object> variables) {
        boolean clientNeedsContentRefresh = super.changeVariables(variables);

        boolean needsResetPageBuffer = false;

        if (variables.containsKey("expandAllInGroup")) {
            focus();

            Object groupId = groupIdMap.get((String) variables.get("expandAllInGroup"));
            expandAllInGroup(groupId, false);
            clientNeedsContentRefresh = true;
            needsResetPageBuffer = true;
        }

        if (variables.containsKey("expand")) {
            focus();

            Object groupId = groupIdMap.get((String) variables.get("expand"));
            expand(groupId, false);
            clientNeedsContentRefresh = true;
            needsResetPageBuffer = true;
        }

        if (variables.containsKey("collapse")) {
            focus();

            Object groupId = groupIdMap.get((String) variables.get("collapse"));
            collapse(groupId, false);
            clientNeedsContentRefresh = true;
            needsResetPageBuffer = true;
        }

        if (needsResetPageBuffer) {
            resetPageBuffer();
        }

        return clientNeedsContentRefresh;
    }

    @Override
    protected boolean isCellPaintingNeeded(Object itemId, Object columnId) {
        return !isGroup(itemId);
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void paintRowAttributes(PaintTarget target, Object itemId) throws PaintException {
        super.paintRowAttributes(target, itemId);

        boolean hasAggregation = items instanceof AggregationContainer && isAggregatable()
                && !((AggregationContainer) items).getAggregationPropertyIds().isEmpty();

        boolean hasGroups = hasGroups();
        if (hasGroups) {
            if (isGroup(itemId)) {
                target.addAttribute("colKey", _columnIdMap().key(getGroupProperty(itemId)));
                target.addAttribute("groupKey", groupIdMap.key(itemId));

                if (isExpanded(itemId)) {
                    target.addAttribute("expanded", true);
                }

                Object propertyValue = getGroupPropertyValue(itemId);
                String formattedValue = formatGroupPropertyValue(itemId, propertyValue);
                target.addAttribute("groupCaption", formattedValue);

                if (hasAggregation) {
                    paintGroupAggregation(target, itemId,
                            ((AggregationContainer) items).aggregate(new GroupAggregationContext(this, itemId)));
                }
            }
        }
    }

    @Override
    protected Collection<?> getAggregationItemIds() {
        if (hasGroups()) {
            return rootGroups().stream().flatMap(groupId -> getGroupItemIds(groupId).stream())
                    .collect(Collectors.toList());
        } else {
            return items.getItemIds();
        }
    }

    protected void paintGroupAggregation(PaintTarget target, Object groupId, Map<Object, Object> aggregations)
            throws PaintException {
        boolean paintGroupProperty = false;

        Collection groupProperties = getGroupProperties();
        Object groupProperty = getGroupProperty(groupId);

        for (Object columnId : _visibleColumns()) {
            if (columnId == null || isColumnCollapsed(columnId)) {
                continue;
            }

            if (groupProperties.contains(columnId) && !paintGroupProperty) {
                if (columnId.equals(groupProperty)) {
                    paintGroupProperty = true;
                }
                continue;
            }

            if (getCellStyleGenerator() != null) {
                String cellStyle = getCellStyleGenerator().getStyle(this, null, columnId);
                if (cellStyle != null && !cellStyle.isEmpty()) {
                    target.addAttribute("style-" + _columnIdMap().key(columnId), cellStyle + "-ag");
                }
            }

            String value = (String) aggregations.get(columnId);
            if (value != null) {
                target.addText(value);
            } else {
                target.addText("");
            }
        }
    }

    @Override
    protected LinkedHashSet<Object> getItemIdsInRange(Object startItemId, final int length) {
        Set<Object> rootIds = super.getItemIdsInRange(startItemId, length);
        // actual implementation moved to WebGroupTable
        return new LinkedHashSet<>(rootIds);
    }

    @Override
    protected boolean isColumnNeedsToRefreshRendered(Object colId) {
        GroupTableContainer items = (GroupTableContainer) this.items;
        boolean groupped = items.hasGroups();

        return !groupped || !getGroupProperties().contains(colId);
    }

    @Override
    protected boolean isItemNeedsToRefreshRendered(Object itemId) {
        GroupTableContainer items = (GroupTableContainer) this.items;
        boolean groupped = items.hasGroups();

        return !groupped || !items.isGroup(itemId);
    }

    protected String formatGroupPropertyValue(Object groupId, Object groupValue) {
        return groupPropertyValueFormatter != null ? groupPropertyValueFormatter.format(groupId, groupValue)
                : (groupValue == null ? "" : groupValue.toString());
    }

    protected void expandAllInGroup(Object id, boolean rerender) {
        int pageIndex = getCurrentPageFirstItemIndex();
        expandAllInGroup(id);
        if (isMultiSelect()) {
            selectAllInGroup(id);
        }
        setCurrentPageFirstItemIndex(pageIndex, false);
        if (rerender) {
            resetPageBuffer();
            refreshRenderedCells();
            markAsDirty();
        }
    }

    protected void expandAllInGroup(Object id) {
        ((GroupTableContainer) items).expand(id);
        if (hasChildren(id)) {
            for (Object childId : getChildren(id)) {
                expandAllInGroup(childId);
            }
        }
    }

    protected void selectAllInGroup(Object id) {
        for (Object itemId : getGroupItemIds(id)) {
            select(itemId);
        }
    }

    protected void expand(Object id, boolean rerender) {
        int pageIndex = getCurrentPageFirstItemIndex();
        ((GroupTableContainer) items).expand(id);
        setCurrentPageFirstItemIndex(pageIndex, false);
        if (rerender) {
            resetPageBuffer();
            refreshRenderedCells();
            markAsDirty();
        }
    }

    protected void collapse(Object id, boolean rerender) {
        int pageIndex = getCurrentPageFirstItemIndex();
        ((GroupTableContainer) items).collapse(id);
        setCurrentPageFirstItemIndex(pageIndex, false);
        if (rerender) {
            resetPageBuffer();
            refreshRenderedCells();
            markAsDirty();
        }
    }

    protected void groupBy(Object[] properties, boolean rerender) {
        GroupTableContainer groupTableContainer = (GroupTableContainer) items;
        if (groupTableContainer.getGroupProperties().isEmpty() && properties.length == 0) {
            // no need to regroup and refreshRenderedCells
            return;
        }

        groupTableContainer.groupBy(properties);
        if (rerender) {
            resetPageBuffer();
            setCurrentPageFirstItemIndex(0, false);
            refreshRenderedCells();
            markAsDirty();
        }
    }

    // hook to implement in Web impl
    protected boolean isNonGeneratedProperty(Object id) {
        return false;
    }

    @Override
    public Collection<?> getGroupProperties() {
        Collection<?> groupProperties = ((GroupTableContainer) items).getGroupProperties();
        // Deny group by generated columns
        if (!_columnGenerators().isEmpty()) {
            List<Object> notGeneratedGroupProps = new ArrayList<>();
            for (Object id : groupProperties) {
                if (!_columnGenerators().containsKey(id) || isNonGeneratedProperty(id)) {
                    notGeneratedGroupProps.add(id);
                }
            }
            return notGeneratedGroupProps;
        } else {
            return groupProperties;
        }
    }

    @Override
    public void expandAll() {
        int pageIndex = getCurrentPageFirstItemIndex();
        ((GroupTableContainer) items).expandAll();
        setCurrentPageFirstItemIndex(pageIndex, false);
        resetPageBuffer();
        refreshRenderedCells();
        markAsDirty();
    }

    @Override
    public void expand(Object id) {
        expand(id, true);
    }

    @Override
    public void collapseAll() {
        int pageIndex = getCurrentPageFirstItemIndex();
        ((GroupTableContainer) items).collapseAll();
        setCurrentPageFirstItemIndex(pageIndex, false);
        resetPageBuffer();
        refreshRenderedCells();
        markAsDirty();
    }

    @Override
    public void collapse(Object id) {
        collapse(id, true);
    }

    @Override
    public boolean hasGroups() {
        return ((GroupTableContainer) items).hasGroups();
    }

    @Override
    public void groupBy(Object[] properties) {
        groupBy(properties, true);
    }

    public boolean getColumnGroupAllowed(Object id) {
        if (groupDisallowedProperties == null) {
            return true;
        }
        return !groupDisallowedProperties.contains(id);
    }

    public void setColumnGroupAllowed(Object id, boolean allowed) {
        if (groupDisallowedProperties == null) {
            groupDisallowedProperties = new ArrayList<>();
        }
        if (allowed) {
            groupDisallowedProperties.remove(id);
        } else {
            groupDisallowedProperties.add(id);
        }
    }

    @Override
    public boolean isGroup(Object itemId) {
        return ((GroupTableContainer) items).isGroup(itemId);
    }

    @Override
    public Collection<?> rootGroups() {
        return ((GroupTableContainer) items).rootGroups();
    }

    @Override
    public boolean hasChildren(Object id) {
        return ((GroupTableContainer) items).hasChildren(id);
    }

    @Override
    public Collection<?> getChildren(Object id) {
        return ((GroupTableContainer) items).getChildren(id);
    }

    @Override
    public Object getGroupProperty(Object itemId) {
        return ((GroupTableContainer) items).getGroupProperty(itemId);
    }

    @Override
    public Object getGroupPropertyValue(Object itemId) {
        return ((GroupTableContainer) items).getGroupPropertyValue(itemId);
    }

    @Override
    public Collection<?> getGroupItemIds(Object itemId) {
        return ((GroupTableContainer) items).getGroupItemIds(itemId);
    }

    @Override
    public int getGroupItemsCount(Object itemId) {
        return ((GroupTableContainer) items).getGroupItemsCount(itemId);
    }

    @Override
    public boolean isExpanded(Object id) {
        return ((GroupTableContainer) items).isExpanded(id);
    }

    public boolean isFixedGrouping() {
        return fixedGrouping;
    }

    public void setFixedGrouping(boolean fixedGrouping) {
        this.fixedGrouping = fixedGrouping;
        markAsDirty();
    }

    @Override
    protected void updateClickableColumnKeys() {
        if (cellClickListeners != null) {
            Collection<?> groupProperties = getGroupProperties();
            String[] clickableColumnKeys = new String[cellClickListeners.size()];
            int i = 0;
            for (Object columnId : cellClickListeners.keySet()) {
                if (!groupProperties.contains(columnId)) {
                    clickableColumnKeys[i] = _columnIdMap().key(columnId);
                    i++;
                }
            }

            getState().clickableColumnKeys = clickableColumnKeys;
        }
    }

    public GroupPropertyValueFormatter getGroupPropertyValueFormatter() {
        return groupPropertyValueFormatter;
    }

    public void setGroupPropertyValueFormatter(GroupPropertyValueFormatter groupPropertyValueFormatter) {
        this.groupPropertyValueFormatter = groupPropertyValueFormatter;
    }

    public interface GroupPropertyValueFormatter {
        String format(Object groupId, @Nullable Object value);
    }

    public static class GroupAggregationContext extends Context {
        private Object groupId;

        public GroupAggregationContext(GroupTableContainer datasource, Object groupId) {
            super(datasource.getGroupItemIds(groupId));
            this.groupId = groupId;
        }

        public Object getGroupId() {
            return groupId;
        }
    }
}