com.haulmont.cuba.gui.data.impl.GroupDelegate.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.gui.data.impl.GroupDelegate.java

Source

/*
 * Copyright (c) 2008-2016 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.gui.data.impl;

import com.haulmont.bali.util.Preconditions;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.data.GroupInfo;
import com.haulmont.cuba.gui.data.PropertyDatasource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.map.LinkedMap;
import org.apache.commons.lang.ArrayUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public abstract class GroupDelegate<T extends Entity<K>, K> {

    protected Object[] groupProperties = null;

    protected Map<GroupInfo, GroupInfo> parents;

    protected Map<GroupInfo, List<GroupInfo>> children;

    protected List<GroupInfo> roots;

    protected Map<GroupInfo, List<K>> groupItems;
    // reversed relations from groupItems
    protected Map<K, GroupInfo> itemGroups;

    protected boolean isGrouping;

    protected CollectionDatasource<T, K> datasource;

    protected GroupDelegate(CollectionDatasource<T, K> datasource) {
        this.datasource = datasource;
    }

    public void groupBy(Object[] properties, CollectionDatasource.Sortable.SortInfo<MetaPropertyPath>[] sortInfos) {
        if (isGrouping) {
            return;
        }
        isGrouping = true;
        try {
            if (properties != null) {
                CollectionDsHelper.autoRefreshInvalid(datasource, true);

                groupProperties = properties;

                if (!ArrayUtils.isEmpty(groupProperties)) {
                    if (datasource.getState() == Datasource.State.VALID) {
                        doGroup();
                    } else if (datasource instanceof PropertyDatasource) {
                        datasource.addStateChangeListener(new Datasource.StateChangeListener<T>() {
                            @Override
                            public void stateChanged(Datasource.StateChangeEvent<T> e) {
                                if (e.getState() == Datasource.State.VALID) {
                                    doGroup();
                                    datasource.removeStateChangeListener(this);
                                }
                            }
                        });
                    }
                } else {
                    roots = null;
                    parents = null;
                    children = null;
                    groupItems = null;
                    itemGroups = null;
                }
            }
        } finally {
            isGrouping = false;

            if (!ArrayUtils.isEmpty(sortInfos)) {
                if (hasGroups()) {
                    doGroupSort(sortInfos);
                } else {
                    doSort(sortInfos);
                }
            }
        }
    }

    protected void doGroup() {
        roots = new LinkedList<>();
        parents = new LinkedHashMap<>();
        children = new HashMap<>();
        groupItems = new HashMap<>();
        itemGroups = new HashMap<>();

        final Collection<K> itemIds = datasource.getItemIds();
        for (final K id : itemIds) {
            final T item = datasource.getItem(id);
            GroupInfo<MetaPropertyPath> groupInfo = groupItems(0, null, roots, item, new LinkedMap());

            if (groupInfo == null) {
                throw new IllegalStateException("Item group cannot be NULL");
            }

            List<K> itemsIds = groupItems.computeIfAbsent(groupInfo, k -> new ArrayList<>());
            itemsIds.add(id);
        }
    }

    protected GroupInfo<MetaPropertyPath> groupItems(int propertyIndex, GroupInfo parent, List<GroupInfo> children,
            T item, final LinkedMap groupValues) {
        final MetaPropertyPath property = (MetaPropertyPath) groupProperties[propertyIndex++];
        Object itemValue = getValueByProperty(item, property);
        groupValues.put(property, itemValue);

        GroupInfo<MetaPropertyPath> groupInfo = new GroupInfo<>(groupValues);
        itemGroups.put(item.getId(), groupInfo);

        if (!parents.containsKey(groupInfo)) {
            parents.put(groupInfo, parent);
        }

        if (!children.contains(groupInfo)) {
            children.add(groupInfo);
        }

        List<GroupInfo> groupChildren = this.children.computeIfAbsent(groupInfo, k -> new ArrayList<>());

        if (propertyIndex < groupProperties.length) {
            groupInfo = groupItems(propertyIndex, groupInfo, groupChildren, item, groupValues);
        }

        return groupInfo;
    }

    protected abstract void doSort(CollectionDatasource.Sortable.SortInfo<MetaPropertyPath>[] sortInfo);

    protected void doGroupSort(CollectionDatasource.Sortable.SortInfo<MetaPropertyPath>[] sortInfo) {
        if (hasGroups()) {
            final MetaPropertyPath propertyPath = sortInfo[0].getPropertyPath();
            final boolean asc = CollectionDatasource.Sortable.Order.ASC.equals(sortInfo[0].getOrder());

            final int index = Arrays.asList(groupProperties).indexOf(propertyPath);
            if (index > -1) {
                if (index == 0) { // Sort roots
                    roots.sort(new GroupInfoComparator(asc));
                } else {
                    final Object parentProperty = groupProperties[index - 1];
                    for (final Map.Entry<GroupInfo, List<GroupInfo>> entry : children.entrySet()) {
                        Object property = entry.getKey().getProperty();
                        if (property.equals(parentProperty)) {
                            entry.getValue().sort(new GroupInfoComparator(asc));
                        }
                    }
                }
            } else {
                final Set<GroupInfo> groups = parents.keySet();
                for (final GroupInfo groupInfo : groups) {
                    List<K> items = groupItems.get(groupInfo);
                    if (items != null) {
                        items.sort(new EntityByIdComparator<>(propertyPath, datasource, asc));
                    }
                }
            }
        }
    }

    public List<GroupInfo> rootGroups() {
        if (hasGroups()) {
            return Collections.unmodifiableList(roots);
        }
        return Collections.emptyList();
    }

    public boolean hasChildren(GroupInfo group) {
        boolean groupExists = containsGroup(group);
        List<GroupInfo> groupChildren = this.children.get(group);
        return groupExists && CollectionUtils.isNotEmpty(groupChildren);
    }

    public List<GroupInfo> getChildren(GroupInfo groupId) {
        if (hasChildren(groupId)) {
            return Collections.unmodifiableList(children.get(groupId));
        }
        return Collections.emptyList();
    }

    public Object getGroupProperty(GroupInfo groupId) {
        if (containsGroup(groupId)) {
            return groupId.getProperty();
        }
        return null;
    }

    public Object getGroupPropertyValue(GroupInfo groupId) {
        if (containsGroup(groupId)) {
            return groupId.getValue();
        }
        return null;
    }

    public List<K> getGroupItemIds(GroupInfo group) {
        if (containsGroup(group)) {
            List<K> itemIds;
            if ((itemIds = groupItems.get(group)) == null) {
                itemIds = new LinkedList<>();
                final List<GroupInfo> children = getChildren(group);
                for (final GroupInfo child : children) {
                    itemIds.addAll(getGroupItemIds(child));
                }
            }
            return Collections.unmodifiableList(itemIds);
        }
        return Collections.emptyList();
    }

    public int getGroupItemsCount(GroupInfo groupId) {
        if (containsGroup(groupId)) {
            List<K> itemIds;
            if ((itemIds = groupItems.get(groupId)) == null) {
                int count = 0;
                final List<GroupInfo> children = getChildren(groupId);
                for (final GroupInfo child : children) {
                    count += getGroupItemsCount(child);
                }
                return count;
            } else {
                return itemIds.size();
            }
        }
        return 0;
    }

    public boolean hasGroups() {
        return roots != null;
    }

    public Collection<?> getGroupProperties() {
        if (groupProperties == null) {
            return Collections.emptyList();
        }

        return Arrays.asList(groupProperties);
    }

    public boolean containsGroup(GroupInfo groupInfo) {
        return hasGroups() && parents.keySet().contains(groupInfo);
    }

    protected Object getValueByProperty(T item, MetaPropertyPath property) {
        Preconditions.checkNotNullArgument(item);

        if (property.getMetaProperties().length == 1) {
            return item.getValue(property.getMetaProperty().getName());
        } else {
            return item.getValueEx(property.toString());
        }
    }

    public List<Entity> getOwnChildItems(GroupInfo groupId) {
        if (groupItems == null) {
            return Collections.emptyList();
        }

        List<K> idsList = groupItems.get(groupId);
        if (containsGroup(groupId) && CollectionUtils.isNotEmpty(idsList)) {
            return idsList.stream().map(id -> datasource.getItem(id)).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    public List<Entity> getChildItems(GroupInfo groupId) {
        if (groupItems == null) {
            return Collections.emptyList();
        }

        if (containsGroup(groupId)) {
            List<Entity> entities = new ArrayList<>();

            // if current group contains other groups
            if (hasChildren(groupId)) {
                List<GroupInfo> children = getChildren(groupId);
                for (GroupInfo childGroup : children) {
                    entities.addAll(getChildItems(childGroup));
                }
            }

            // if current group contains only items
            List<K> idsList = groupItems.get(groupId);
            if (CollectionUtils.isNotEmpty(idsList)) {
                entities.addAll(idsList.stream().map(id -> datasource.getItem(id)).collect(Collectors.toList()));
            }

            return entities;
        }
        return Collections.emptyList();
    }

    @SuppressWarnings({ "SuspiciousMethodCalls", "unchecked" })
    public GroupInfo getParentGroup(Entity entity) {
        K id = (K) entity.getId();
        if (!datasource.containsItem(id)) {
            throw new IllegalArgumentException("Datasource doesn't contain passed entity");
        }

        if (itemGroups == null) {
            return null;
        }
        return itemGroups.get(entity.getId());
    }

    @SuppressWarnings({ "SuspiciousMethodCalls", "unchecked" })
    public List<GroupInfo> getGroupPath(Entity entity) {
        K id = (K) entity.getId();
        if (!datasource.containsItem(id)) {
            throw new IllegalArgumentException("Datasource doesn't contain passed entity");
        }

        if (itemGroups == null) {
            return Collections.emptyList();
        }

        GroupInfo groupInfo = itemGroups.get(entity.getId());
        if (groupInfo == null) {
            return Collections.emptyList();
        }
        List<GroupInfo> parentGroups = new LinkedList<>();
        parentGroups.add(groupInfo);

        GroupInfo parent = parents.get(groupInfo);
        while (parent != null) {
            parentGroups.add(0, parent);
            parent = parents.get(parent);
        }

        return parentGroups;
    }
}