blob: d8e16f52384d93fc607e69c162ecc3b8b331b3ff [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Mickael Istria (Red Hat Inc.) - [266030] Allow "others" working set
*******************************************************************************/
package org.eclipse.ui.internal.navigator.workingsets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.IAggregateWorkingSet;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.IWorkingSetManager;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.navigator.NavigatorContentService;
import org.eclipse.ui.navigator.CommonNavigator;
import org.eclipse.ui.navigator.CommonViewer;
import org.eclipse.ui.navigator.ICommonContentExtensionSite;
import org.eclipse.ui.navigator.ICommonContentProvider;
import org.eclipse.ui.navigator.IExtensionStateModel;
import org.eclipse.ui.navigator.resources.ProjectExplorer;
/**
* Provides children and parents for IWorkingSets.
*
* @since 3.2.1
*
*/
public class WorkingSetsContentProvider implements ICommonContentProvider {
/**
* The extension id for the WorkingSet extension.
*/
public static final String EXTENSION_ID = "org.eclipse.ui.navigator.resources.workingSets"; //$NON-NLS-1$
/**
* A key used by the Extension State Model to keep track of whether top level Working Sets or
* Projects should be shown in the viewer.
*/
public static final String SHOW_TOP_LEVEL_WORKING_SETS = EXTENSION_ID + ".showTopLevelWorkingSets"; //$NON-NLS-1$
private static final Object[] NO_CHILDREN = new Object[0];
/**
* An object representing the "Others" working set, showing unassigned
* content
*/
public static final Object OTHERS_WORKING_SET = new Object();
private WorkingSetHelper helper;
private IAggregateWorkingSet workingSetRoot;
private IExtensionStateModel extensionStateModel;
private IWorkingSetManager workingSetManager;
private CommonNavigator projectExplorer;
private CommonViewer viewer;
private IPropertyChangeListener rootModeListener = event -> {
if(SHOW_TOP_LEVEL_WORKING_SETS.equals(event.getProperty())) {
updateRootMode();
}
};
private IPropertyChangeListener workingSetManagerListener = event -> {
if (helper != null) {
helper.refreshWorkingSetTreeState();
}
};
@Override
public void init(ICommonContentExtensionSite aConfig) {
NavigatorContentService cs = (NavigatorContentService) aConfig.getService();
viewer = (CommonViewer) cs.getViewer();
projectExplorer = viewer.getCommonNavigator();
extensionStateModel = aConfig.getExtensionStateModel();
extensionStateModel.addPropertyChangeListener(rootModeListener);
updateRootMode();
workingSetManager = PlatformUI.getWorkbench().getWorkingSetManager();
workingSetManager.addPropertyChangeListener(workingSetManagerListener);
}
@Override
public void restoreState(IMemento aMemento) {
}
@Override
public void saveState(IMemento aMemento) {
}
@Override
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof IWorkingSet) {
IWorkingSet workingSet = (IWorkingSet) parentElement;
if (workingSet.isAggregateWorkingSet() && projectExplorer != null) {
switch (projectExplorer.getRootMode()) {
case ProjectExplorer.WORKING_SETS :
IWorkingSet[] activeWorkingSets = ((IAggregateWorkingSet) workingSet).getComponents();
if (helper.getUnassignedProjects().isEmpty()) {
return activeWorkingSets;
}
Object[] res;
res = new Object[activeWorkingSets.length + 1];
System.arraycopy(activeWorkingSets, 0, res, 0, activeWorkingSets.length);
res[activeWorkingSets.length] = OTHERS_WORKING_SET;
return res;
case ProjectExplorer.PROJECTS:
return getWorkingSetElements(workingSet);
}
}
return getWorkingSetElements(workingSet);
} else if (parentElement == OTHERS_WORKING_SET) {
Set<IProject> res = helper.getUnassignedProjects();
return res.toArray(new Object[res.size()]);
}
return NO_CHILDREN;
}
private IAdaptable[] getWorkingSetElements(IWorkingSet workingSet) {
IAdaptable[] children = workingSet.getElements();
for (int i = 0; i < children.length; i++) {
IResource resource = Adapters.adapt(children[i], IResource.class);
if (resource instanceof IProject)
children[i] = resource;
}
return children;
}
@Override
public Object getParent(Object element) {
if (helper != null && projectExplorer.getRootMode() == ProjectExplorer.WORKING_SETS)
return helper.getParent(element);
return null;
}
@Override
public boolean hasChildren(Object parentElement) {
// since getChildren is a low-cost operation, we can use it
// to compute hasChildren. That should prevent the expand arrow
// to be shown when there's no content.
return getChildren(parentElement).length > 0;
}
@Override
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
@Override
public void dispose() {
helper = null;
extensionStateModel.removePropertyChangeListener(rootModeListener);
workingSetManager.removePropertyChangeListener(workingSetManagerListener);
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (newInput instanceof IWorkingSet) {
IWorkingSet rootSet = (IWorkingSet) newInput;
helper = new WorkingSetHelper(rootSet);
}
}
private void updateRootMode() {
if (projectExplorer == null) {
return;
}
if( extensionStateModel.getBooleanProperty(SHOW_TOP_LEVEL_WORKING_SETS) )
projectExplorer.setRootMode(ProjectExplorer.WORKING_SETS);
else
projectExplorer.setRootMode(ProjectExplorer.PROJECTS);
}
protected class WorkingSetHelper {
private final IWorkingSet workingSet;
private Map<IAdaptable, IAdaptable> parents;
private Set<IProject> unassignedProjects;
/**
* Create a Helper class for the given working set
*
* @param set
* The set to use to build the item to parent map.
*/
public WorkingSetHelper(IWorkingSet set) {
workingSet = set;
refreshWorkingSetTreeState();
}
void refreshWorkingSetTreeState() {
parents = new WeakHashMap<>();
if (workingSet.isAggregateWorkingSet()) {
IAggregateWorkingSet aggregateSet = (IAggregateWorkingSet) workingSet;
if (workingSetRoot == null)
workingSetRoot = aggregateSet;
IWorkingSet[] components = aggregateSet.getComponents();
for (IWorkingSet component : components) {
IAdaptable[] elements = getWorkingSetElements(component);
for (IAdaptable element : elements) {
parents.put(element, component);
}
parents.put(component, aggregateSet);
}
} else {
IAdaptable[] elements = getWorkingSetElements(workingSet);
for (IAdaptable element : elements) {
parents.put(element, workingSet);
}
}
unassignedProjects = new HashSet<>(Arrays.asList(ResourcesPlugin.getWorkspace().getRoot().getProjects()));
for (IAdaptable node : parents.keySet()) {
unassignedProjects.remove(node.getAdapter(IProject.class));
}
}
/**
* @return projects that aren't part of a selected working set
*/
public Set<IProject> getUnassignedProjects() {
return unassignedProjects;
}
/**
*
* @param element
* An element from the viewer
* @return The parent associated with the element, if any.
*/
public Object getParent(Object element) {
if (element instanceof IWorkingSet && element != workingSetRoot)
return workingSetRoot;
Object res = parents.get(element);
if (res != null) {
return res;
}
if (unassignedProjects.contains(element)) {
return OTHERS_WORKING_SET;
}
return null;
}
}
}