org.eclipse.sirius.common.ui.tools.api.navigator.GroupingContentProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.sirius.common.ui.tools.api.navigator.GroupingContentProvider.java

Source

/*******************************************************************************
 * Copyright (c) 2013 THALES GLOBAL SERVICES.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Obeo - initial API and implementation
 *******************************************************************************/
package org.eclipse.sirius.common.ui.tools.api.navigator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.sirius.common.tools.api.constant.CommonPreferencesConstants;
import org.eclipse.sirius.common.tools.api.util.TreeItemWrapper;
import org.eclipse.sirius.common.ui.SiriusTransPlugin;
import org.eclipse.sirius.common.ui.tools.internal.preference.DynamicConfigurationHelper;

import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Lists;

/**
 * An implementation of ITreeContentProvider that surround another
 * ITreeContentProvider to group its children with an intermediary tree level.
 * 
 * This class depends of three preferences that force a plugin execution context
 * to work properly. these preferences can be edited by the user directly in the
 * Eclipse UI preference in the Sirius page.
 * 
 * Actually, we haven't any listener on the preference. An user need to refresh
 * manually the GroupingContentProvider if he updates a preference.
 * 
 * For details about related preferences:
 * 
 * @see CommonPreferencesConstants.PREF_GROUP_ENABLE
 * @see CommonPreferencesConstants.PREF_GROUP_BY_CONTAINING_FEATURE
 * @see CommonPreferencesConstants.PREF_GROUP_SIZE
 * @see CommonPreferencesConstants.PREF_GROUP_TRIGGER
 * 
 */
public class GroupingContentProvider implements ITreeContentProvider {

    /**
     * Encapsulates the configurable parameters of the grouping content
     * provider, and keeps it in sync with possible changes in the preferences.
     */
    private static class Configuration extends DynamicConfigurationHelper {
        int groupSize;

        int groupTrigger;

        boolean groupEnabled;

        boolean groupByContainingFeature;

        Configuration(IPreferenceStore store) {
            super(store);
            bindInt(CommonPreferencesConstants.PREF_GROUP_SIZE, "groupSize"); //$NON-NLS-1$
            bindInt(CommonPreferencesConstants.PREF_GROUP_TRIGGER, "groupTrigger"); //$NON-NLS-1$
            bindBoolean(CommonPreferencesConstants.PREF_GROUP_ENABLE, "groupEnabled"); //$NON-NLS-1$
            bindBoolean(CommonPreferencesConstants.PREF_GROUP_BY_CONTAINING_FEATURE, "groupByContainingFeature"); //$NON-NLS-1$
        }
    }

    private final ITreeContentProvider delegateTreeContentProvider;

    private final Configuration config;

    /**
     * The constructor that required another ITreeContentProvider to surround.
     * 
     * @param delegateTreeContentProvider
     *            The delegated tree content provider
     */
    public GroupingContentProvider(ITreeContentProvider delegateTreeContentProvider) {
        this.delegateTreeContentProvider = delegateTreeContentProvider;
        this.config = new Configuration(SiriusTransPlugin.getPlugin().getPreferenceStore());
    }

    /**
     * Group size getter. The size of children that can be contained in each
     * intermediary tree level.
     * 
     * @see CommonPreferencesConstants.PREF_GROUP_SIZE
     * @return the group size
     */
    public int getGroupSize() {
        return config.groupSize;
    }

    /**
     * Group enable getter. Check if the GroupingContentProvider is enable.
     * 
     * @see CommonPreferencesConstants.PREF_GROUP_ENABLE
     * @return true if the group is enable
     */
    public boolean isGroupEnabled() {
        return config.groupEnabled;
    }

    /**
     * Group by containing feature getter. Check if the GroupByContainingFeature
     * is enable. This value changes the implementation used for the group
     * children. If it set at true, the grouping strategy use the containing
     * feature instead of the basic hierarchy.
     * 
     * @see CommonPreferencesConstants.PREF_GROUP_BY_CONTAINING_FEATURE
     * @return true if the group is enable
     */
    public boolean isGroupByContainingFeature() {
        return config.groupByContainingFeature;
    }

    /**
     * Trigger size getter. The trigger size of children that triggers the
     * display of intermediary tree levels.
     * 
     * @see CommonPreferencesConstants.PREF_GROUP_TRIGGER
     * @return the trigger size if groupSize >= groupTrigger, else the group
     *         size.
     */
    public int getTriggerSize() {
        if (config.groupTrigger >= config.groupSize) {
            return config.groupTrigger;
        } else {
            return config.groupSize;
        }
    }

    @Override
    public void dispose() {
        config.dispose();
        delegateTreeContentProvider.dispose();
    }

    @Override
    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        delegateTreeContentProvider.inputChanged(viewer, oldInput, newInput);

    }

    @Override
    public Object[] getElements(Object parentElement) {
        if (parentElement instanceof GroupingItem) {
            return getChildren(parentElement);
        } else {
            Object[] elements = delegateTreeContentProvider.getElements(parentElement);
            return groupChildren(parentElement, elements);
        }
    }

    @Override
    public Object[] getChildren(Object parentElement) {
        if (parentElement instanceof GroupingItem) {
            return ((GroupingItem) parentElement).getChildren().toArray();
        } else {
            Object[] children = delegateTreeContentProvider.getChildren(parentElement);
            return groupChildren(parentElement, children);
        }
    }

    @Override
    public Object getParent(Object element) {
        if (element instanceof GroupingItem) {
            GroupingItem groupItem = (GroupingItem) element;
            return groupItem.getParent();
        } else {
            return delegateTreeContentProvider.getParent(element);
        }
    }

    @Override
    public boolean hasChildren(Object element) {
        return element instanceof GroupingItem || delegateTreeContentProvider.hasChildren(element);
    }

    /**
     * This function help to group children.
     * 
     * @param parent
     *            the parent
     * @param children
     *            children
     * @return grouped children
     */
    protected Object[] groupChildren(Object parent, Object[] children) {

        Object[] result = children;
        if (isGroupEnabled()) {
            if (isGroupByContainingFeature()) {
                result = groupChildrenByContainingFeature(parent, children);
            } else {
                result = defaultGroupChildren(parent, children);
            }
        }
        return result;
    }

    private Object[] groupChildrenByContainingFeature(Object parent, Object[] children) {
        LinkedListMultimap<Object, Object> childrenContainingMapping = buildChildrenContainerMapping(children);
        List<Object> result = new ArrayList<Object>();
        for (Object structuralFeature : childrenContainingMapping.keySet()) {
            int currentOffset = 0;
            List<Object> indexedChildren = childrenContainingMapping.get(structuralFeature);
            if (indexedChildren.size() > getTriggerSize()) {
                List<List<Object>> partition = Lists.partition(indexedChildren, config.groupSize);
                if (partition.size() > 0) {
                    if (partition.size() > 1) {
                        for (List<Object> partItem : partition) {
                            GroupingItem currentGroup;
                            if (structuralFeature instanceof EStructuralFeature) {
                                currentGroup = new GroupingItem(currentOffset, parent,
                                        new ArrayList<Object>(partItem),
                                        " " + ((EStructuralFeature) structuralFeature).getName()); //$NON-NLS-1$
                            } else {
                                currentGroup = new GroupingItem(currentOffset, parent,
                                        new ArrayList<Object>(partItem));
                            }
                            result.add(currentGroup);
                            currentOffset = currentOffset + partItem.size();
                        }
                    } else {
                        for (List<Object> partItem : partition) {
                            result.addAll(partItem);
                        }
                    }
                }
            } else {
                result.addAll(indexedChildren);
            }
        }
        return result.toArray();

    }

    private LinkedListMultimap<Object, Object> buildChildrenContainerMapping(Object[] children) {
        LinkedListMultimap<Object, Object> childrenContainingMapping = LinkedListMultimap.create();
        Object noContainingFeature = new Object();
        for (Object child : children) {
            Object containingFeature;
            if (child instanceof EObject) {
                containingFeature = ((EObject) child).eContainingFeature();
            } else if (child instanceof TreeItemWrapper) {
                Object wrappedObject = ((TreeItemWrapper) child).getWrappedObject();
                if (wrappedObject instanceof EObject) {
                    containingFeature = ((EObject) wrappedObject).eContainingFeature();
                } else {
                    containingFeature = noContainingFeature;
                }
            } else {
                containingFeature = noContainingFeature;
            }
            childrenContainingMapping.put(containingFeature, child);
        }
        return childrenContainingMapping;
    }

    private Object[] defaultGroupChildren(Object parent, Object[] children) {
        if (children.length > getTriggerSize()) {
            List<List<Object>> partition = Lists.partition(Arrays.asList(children), config.groupSize);
            Object[] result = new Object[partition.size()];

            int indexOfResult = 0;
            for (List<Object> indexedChildren : partition) {
                int currentOffset = indexOfResult * config.groupSize;
                GroupingItem currentGroup = new GroupingItem(currentOffset, parent, indexedChildren);
                result[indexOfResult] = currentGroup;
                indexOfResult++;
            }

            return result;
        } else {
            return children;
        }
    }

    /**
     * Returns the base TreeContentProvider used to delegate calls if no
     * grouping is needed.
     * 
     * @return the base TreeContentProvider used to delegate calls if no
     *         grouping is needed
     */
    public ITreeContentProvider getDelegateTreeContentProvider() {
        return delegateTreeContentProvider;
    }
}