| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation and others. |
| * 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 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.ui.workingsets; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.IdentityHashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.IElementComparer; |
| import org.eclipse.ui.ILocalWorkingSetManager; |
| import org.eclipse.ui.IMemento; |
| import org.eclipse.ui.IWorkingSet; |
| import org.eclipse.ui.IWorkingSetManager; |
| import org.eclipse.ui.IWorkingSetUpdater; |
| import org.eclipse.ui.PlatformUI; |
| |
| public class WorkingSetModel { |
| |
| public static final String CHANGE_WORKING_SET_MODEL_CONTENT = "workingSetModelChanged"; //$NON-NLS-1$ |
| |
| public static final IElementComparer COMPARER = new WorkingSetComparar(); |
| |
| private static final String TAG_LOCAL_WORKING_SET_MANAGER = "localWorkingSetManager"; //$NON-NLS-1$ |
| private static final String TAG_ACTIVE_WORKING_SET = "activeWorkingSet"; //$NON-NLS-1$ |
| private static final String TAG_WORKING_SET_NAME = "workingSetName"; //$NON-NLS-1$ |
| private static final String TAG_CONFIGURED = "configured"; //$NON-NLS-1$ |
| |
| private ILocalWorkingSetManager fLocalWorkingSetManager; |
| private List<IWorkingSet> fActiveWorkingSets; |
| private ListenerList<IPropertyChangeListener> fListeners; |
| private IPropertyChangeListener fWorkingSetManagerListener; |
| private OthersWorkingSetUpdater fOthersWorkingSetUpdater; |
| |
| private ElementMapper fElementMapper = new ElementMapper(); |
| |
| private boolean fConfigured; |
| |
| private static class WorkingSetComparar implements IElementComparer { |
| @Override |
| public boolean equals(Object o1, Object o2) { |
| IWorkingSet w1 = o1 instanceof IWorkingSet ? (IWorkingSet) o1 |
| : null; |
| IWorkingSet w2 = o2 instanceof IWorkingSet ? (IWorkingSet) o2 |
| : null; |
| if (w1 == null || w2 == null) |
| return o1.equals(o2); |
| return w1 == w2; |
| } |
| |
| @Override |
| public int hashCode(Object element) { |
| if (element instanceof IWorkingSet) |
| return System.identityHashCode(element); |
| return element.hashCode(); |
| } |
| } |
| |
| private static class ElementMapper { |
| private Map fElementToWorkingSet = new HashMap(); |
| private Map fWorkingSetToElement = new IdentityHashMap(); |
| |
| private Map fResourceToWorkingSet = new HashMap(); |
| private List fNonProjectTopLevelElements = new ArrayList(); |
| |
| public void clear() { |
| fElementToWorkingSet.clear(); |
| fWorkingSetToElement.clear(); |
| fResourceToWorkingSet.clear(); |
| fNonProjectTopLevelElements.clear(); |
| } |
| |
| public void rebuild(IWorkingSet[] workingSets) { |
| clear(); |
| for (int i = 0; i < workingSets.length; i++) { |
| put(workingSets[i]); |
| } |
| } |
| |
| public IAdaptable[] refresh(IWorkingSet ws) { |
| IAdaptable[] oldElements = (IAdaptable[]) fWorkingSetToElement |
| .get(ws); |
| if (oldElements == null) |
| return null; |
| IAdaptable[] newElements = ws.getElements(); |
| List toRemove = new ArrayList(Arrays.asList(oldElements)); |
| List toAdd = new ArrayList(Arrays.asList(newElements)); |
| computeDelta(toRemove, toAdd, oldElements, newElements); |
| for (Iterator iter = toAdd.iterator(); iter.hasNext();) { |
| addElement((IAdaptable) iter.next(), ws); |
| } |
| for (Iterator iter = toRemove.iterator(); iter.hasNext();) { |
| removeElement((IAdaptable) iter.next(), ws); |
| } |
| if (toRemove.size() > 0 || toAdd.size() > 0) |
| fWorkingSetToElement.put(ws, newElements); |
| return oldElements; |
| } |
| |
| private void computeDelta(List toRemove, List toAdd, |
| IAdaptable[] oldElements, IAdaptable[] newElements) { |
| for (int i = 0; i < oldElements.length; i++) { |
| toAdd.remove(oldElements[i]); |
| } |
| for (int i = 0; i < newElements.length; i++) { |
| toRemove.remove(newElements[i]); |
| } |
| |
| } |
| |
| public IWorkingSet getFirstWorkingSet(Object element) { |
| return (IWorkingSet) getFirstElement(fElementToWorkingSet, element); |
| } |
| |
| public List getAllWorkingSets(Object element) { |
| return getAllElements(fElementToWorkingSet, element); |
| } |
| |
| public List getAllWorkingSetsForResource(IResource resource) { |
| return getAllElements(fResourceToWorkingSet, resource); |
| } |
| |
| public List getNonProjectTopLevelElements() { |
| return fNonProjectTopLevelElements; |
| } |
| |
| private void put(IWorkingSet ws) { |
| if (fWorkingSetToElement.containsKey(ws)) |
| return; |
| IAdaptable[] elements = ws.getElements(); |
| fWorkingSetToElement.put(ws, elements); |
| for (int i = 0; i < elements.length; i++) { |
| IAdaptable element = elements[i]; |
| addElement(element, ws); |
| if (!(element instanceof IProject) |
| && !(element instanceof IScriptProject)) { |
| fNonProjectTopLevelElements.add(element); |
| } |
| } |
| } |
| |
| private void addElement(IAdaptable element, IWorkingSet ws) { |
| addToMap(fElementToWorkingSet, element, ws); |
| IResource resource = element.getAdapter(IResource.class); |
| if (resource != null) { |
| addToMap(fResourceToWorkingSet, resource, ws); |
| } |
| } |
| |
| private void removeElement(IAdaptable element, IWorkingSet ws) { |
| removeFromMap(fElementToWorkingSet, element, ws); |
| IResource resource = element.getAdapter(IResource.class); |
| if (resource != null) { |
| removeFromMap(fResourceToWorkingSet, resource, ws); |
| } |
| } |
| |
| private void addToMap(Map map, IAdaptable key, IWorkingSet value) { |
| Object obj = map.get(key); |
| if (obj == null) { |
| map.put(key, value); |
| } else if (obj instanceof IWorkingSet) { |
| List l = new ArrayList(2); |
| l.add(obj); |
| l.add(value); |
| map.put(key, l); |
| } else if (obj instanceof List) { |
| ((List) obj).add(value); |
| } |
| } |
| |
| private void removeFromMap(Map map, IAdaptable key, IWorkingSet value) { |
| Object current = map.get(key); |
| if (current == null) { |
| return; |
| } else if (current instanceof List) { |
| List list = (List) current; |
| list.remove(value); |
| switch (list.size()) { |
| case 0: |
| map.remove(key); |
| break; |
| case 1: |
| map.put(key, list.get(0)); |
| break; |
| } |
| } else if (current == value) { |
| map.remove(key); |
| } |
| } |
| |
| private Object getFirstElement(Map map, Object key) { |
| Object obj = map.get(key); |
| if (obj instanceof List) |
| return ((List) obj).get(0); |
| return obj; |
| } |
| |
| private List getAllElements(Map map, Object key) { |
| Object obj = map.get(key); |
| if (obj instanceof List) |
| return (List) obj; |
| if (obj == null) |
| return Collections.EMPTY_LIST; |
| List result = new ArrayList(1); |
| result.add(obj); |
| return result; |
| } |
| } |
| |
| public WorkingSetModel(IMemento memento) { |
| fLocalWorkingSetManager = PlatformUI.getWorkbench() |
| .createLocalWorkingSetManager(); |
| addListenersToWorkingSetManagers(); |
| fActiveWorkingSets = new ArrayList<IWorkingSet>(2); |
| |
| if (memento == null || !restoreState(memento)) { |
| IWorkingSet others = fLocalWorkingSetManager.createWorkingSet( |
| WorkingSetMessages.WorkingSetModel_others_name, |
| new IAdaptable[0]); |
| others.setId(WorkingSetIDs.OTHERS); |
| fLocalWorkingSetManager.addWorkingSet(others); |
| fActiveWorkingSets.add(others); |
| } |
| Assert.isNotNull(fOthersWorkingSetUpdater); |
| |
| fElementMapper.rebuild(getActiveWorkingSets()); |
| fOthersWorkingSetUpdater.updateElements(); |
| } |
| |
| private void addListenersToWorkingSetManagers() { |
| fListeners = new ListenerList<>(ListenerList.IDENTITY); |
| fWorkingSetManagerListener = event -> workingSetManagerChanged(event); |
| PlatformUI.getWorkbench().getWorkingSetManager() |
| .addPropertyChangeListener(fWorkingSetManagerListener); |
| fLocalWorkingSetManager |
| .addPropertyChangeListener(fWorkingSetManagerListener); |
| } |
| |
| public void dispose() { |
| if (fWorkingSetManagerListener != null) { |
| PlatformUI.getWorkbench().getWorkingSetManager() |
| .removePropertyChangeListener(fWorkingSetManagerListener); |
| fLocalWorkingSetManager |
| .removePropertyChangeListener(fWorkingSetManagerListener); |
| fLocalWorkingSetManager.dispose(); |
| fWorkingSetManagerListener = null; |
| } |
| } |
| |
| // ---- model relationships --------------------------------------- |
| |
| public IAdaptable[] getChildren(IWorkingSet workingSet) { |
| return workingSet.getElements(); |
| } |
| |
| public Object getParent(Object element) { |
| if (element instanceof IWorkingSet |
| && fActiveWorkingSets.contains(element)) |
| return this; |
| return fElementMapper.getFirstWorkingSet(element); |
| } |
| |
| public Object[] getAllParents(Object element) { |
| if (element instanceof IWorkingSet |
| && fActiveWorkingSets.contains(element)) |
| return new Object[] { this }; |
| return fElementMapper.getAllWorkingSets(element).toArray(); |
| } |
| |
| public Object[] addWorkingSets(Object[] elements) { |
| List result = null; |
| for (int i = 0; i < elements.length; i++) { |
| Object element = elements[i]; |
| List sets = null; |
| if (element instanceof IResource) { |
| sets = fElementMapper |
| .getAllWorkingSetsForResource((IResource) element); |
| } else { |
| sets = fElementMapper.getAllWorkingSets(element); |
| } |
| if (sets != null && sets.size() > 0) { |
| if (result == null) |
| result = new ArrayList(Arrays.asList(elements)); |
| result.addAll(sets); |
| } |
| } |
| if (result == null) |
| return elements; |
| return result.toArray(); |
| } |
| |
| public boolean needsConfiguration() { |
| return !fConfigured && fActiveWorkingSets.size() == 1 |
| && WorkingSetIDs.OTHERS |
| .equals(fActiveWorkingSets.get(0).getId()); |
| } |
| |
| public void configured() { |
| fConfigured = true; |
| } |
| |
| // ---- working set management ----------------------------------- |
| |
| /** |
| * Adds a property change listener. |
| * |
| * @param listener |
| * the property change listener to add |
| */ |
| public void addPropertyChangeListener(IPropertyChangeListener listener) { |
| fListeners.add(listener); |
| } |
| |
| /** |
| * Removes the property change listener. |
| * |
| * @param listener |
| * the property change listener to remove |
| */ |
| public void removePropertyChangeListener(IPropertyChangeListener listener) { |
| fListeners.remove(listener); |
| } |
| |
| public IWorkingSet[] getActiveWorkingSets() { |
| return fActiveWorkingSets |
| .toArray(new IWorkingSet[fActiveWorkingSets.size()]); |
| } |
| |
| public IWorkingSet[] getAllWorkingSets() { |
| List<IWorkingSet> result = new ArrayList<IWorkingSet>(); |
| result.addAll(fActiveWorkingSets); |
| IWorkingSet[] locals = fLocalWorkingSetManager.getWorkingSets(); |
| for (int i = 0; i < locals.length; i++) { |
| if (!result.contains(locals[i])) |
| result.add(locals[i]); |
| } |
| IWorkingSet[] globals = PlatformUI.getWorkbench().getWorkingSetManager() |
| .getWorkingSets(); |
| for (int i = 0; i < globals.length; i++) { |
| if (!result.contains(globals[i])) |
| result.add(globals[i]); |
| } |
| return result.toArray(new IWorkingSet[result.size()]); |
| } |
| |
| public void setActiveWorkingSets(IWorkingSet[] workingSets) { |
| fActiveWorkingSets = new ArrayList<IWorkingSet>( |
| Arrays.asList(workingSets)); |
| fElementMapper.rebuild(getActiveWorkingSets()); |
| fOthersWorkingSetUpdater.updateElements(); |
| fireEvent(new PropertyChangeEvent(this, |
| CHANGE_WORKING_SET_MODEL_CONTENT, null, null)); |
| } |
| |
| public void saveState(IMemento memento) { |
| memento.putString(TAG_CONFIGURED, Boolean.toString(fConfigured)); |
| fLocalWorkingSetManager |
| .saveState(memento.createChild(TAG_LOCAL_WORKING_SET_MANAGER)); |
| for (Iterator<IWorkingSet> iter = fActiveWorkingSets.iterator(); iter |
| .hasNext();) { |
| IMemento active = memento.createChild(TAG_ACTIVE_WORKING_SET); |
| IWorkingSet workingSet = iter.next(); |
| active.putString(TAG_WORKING_SET_NAME, workingSet.getName()); |
| } |
| } |
| |
| public List getNonProjectTopLevelElements() { |
| return fElementMapper.getNonProjectTopLevelElements(); |
| } |
| |
| private boolean restoreState(IMemento memento) { |
| String configured = memento.getString(TAG_CONFIGURED); |
| if (configured == null) { |
| return false; |
| } |
| fConfigured = Boolean.valueOf(configured).booleanValue(); |
| |
| final IMemento wsManagerMemento = memento |
| .getChild(TAG_LOCAL_WORKING_SET_MANAGER); |
| if (wsManagerMemento != null) { |
| fLocalWorkingSetManager.restoreState(wsManagerMemento); |
| } |
| |
| IMemento[] actives = memento.getChildren(TAG_ACTIVE_WORKING_SET); |
| for (int i = 0; i < actives.length; i++) { |
| String name = actives[i].getString(TAG_WORKING_SET_NAME); |
| if (name != null) { |
| IWorkingSet ws = fLocalWorkingSetManager.getWorkingSet(name); |
| if (ws == null) { |
| ws = PlatformUI.getWorkbench().getWorkingSetManager() |
| .getWorkingSet(name); |
| } |
| if (ws != null) { |
| fActiveWorkingSets.add(ws); |
| } |
| } |
| } |
| return true; |
| } |
| |
| private void workingSetManagerChanged(PropertyChangeEvent event) { |
| String property = event.getProperty(); |
| if (IWorkingSetManager.CHANGE_WORKING_SET_UPDATER_INSTALLED.equals( |
| property) && event.getSource() == fLocalWorkingSetManager) { |
| IWorkingSetUpdater updater = (IWorkingSetUpdater) event |
| .getNewValue(); |
| if (updater instanceof OthersWorkingSetUpdater) { |
| fOthersWorkingSetUpdater = (OthersWorkingSetUpdater) updater; |
| fOthersWorkingSetUpdater.init(this); |
| } |
| return; |
| } |
| // don't handle working sets not managed by the model |
| if (!isAffected(event)) |
| return; |
| |
| if (IWorkingSetManager.CHANGE_WORKING_SET_CONTENT_CHANGE |
| .equals(property)) { |
| IWorkingSet workingSet = (IWorkingSet) event.getNewValue(); |
| IAdaptable[] elements = fElementMapper.refresh(workingSet); |
| if (elements != null) { |
| fireEvent(event); |
| } |
| } else if (IWorkingSetManager.CHANGE_WORKING_SET_REMOVE |
| .equals(property)) { |
| IWorkingSet workingSet = (IWorkingSet) event.getOldValue(); |
| List<IWorkingSet> elements = new ArrayList<IWorkingSet>( |
| fActiveWorkingSets); |
| elements.remove(workingSet); |
| setActiveWorkingSets( |
| elements.toArray(new IWorkingSet[elements.size()])); |
| } else if (IWorkingSetManager.CHANGE_WORKING_SET_NAME_CHANGE |
| .equals(property)) { |
| fireEvent(event); |
| } |
| } |
| |
| private void fireEvent(PropertyChangeEvent event) { |
| for (IPropertyChangeListener listener : fListeners) { |
| listener.propertyChange(event); |
| } |
| } |
| |
| private boolean isAffected(PropertyChangeEvent event) { |
| if (fActiveWorkingSets == null) |
| return false; |
| Object oldValue = event.getOldValue(); |
| Object newValue = event.getNewValue(); |
| if ((oldValue != null && fActiveWorkingSets.contains(oldValue)) |
| || (newValue != null |
| && fActiveWorkingSets.contains(newValue))) { |
| return true; |
| } |
| return false; |
| } |
| |
| public boolean isActiveWorkingSet(IWorkingSet changedWorkingSet) { |
| return fActiveWorkingSets.contains(changedWorkingSet); |
| } |
| } |