blob: 4fe6ec3dcd1a9756aa01fe2e1e654838a499fda9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2013 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
*******************************************************************************/
package org.eclipse.debug.internal.ui.commands.actions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.commands.IDebugCommandHandler;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.contexts.DebugContextEvent;
import org.eclipse.debug.ui.contexts.IDebugContextListener;
import org.eclipse.debug.ui.contexts.IDebugContextService;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
/**
* Updates commands for a window. Coalesces update requests by command type.
*
* @since 3.3
*/
public class DebugCommandService implements IDebugContextListener {
/**
* Maps command types to actions to update
*/
private Map<Class<?>, List<IEnabledTarget>> fCommandUpdates = new HashMap<>();
/**
* Window this service is for.
*/
private IWorkbenchWindow fWindow = null;
/**
* The context service for this command service.
*/
private IDebugContextService fContextService = null;
/**
* Service per window
*/
private static Map<IWorkbenchWindow, DebugCommandService> fgServices = new HashMap<>();
/**
* Returns the service for a window.
*
* @param window the window
* @return service
*/
public synchronized static DebugCommandService getService(IWorkbenchWindow window) {
DebugCommandService service = fgServices.get(window);
if (service == null) {
service = new DebugCommandService(window);
fgServices.put(window, service);
}
return service;
}
public DebugCommandService(IWorkbenchWindow window) {
fWindow = window;
fContextService = DebugUITools.getDebugContextManager().getContextService(window);
fContextService.addPostDebugContextListener(this);
PlatformUI.getWorkbench().addWindowListener(new IWindowListener() {
@Override
public void windowOpened(IWorkbenchWindow w) {
}
@Override
public void windowDeactivated(IWorkbenchWindow w) {
}
@Override
public void windowClosed(IWorkbenchWindow w) {
if (fWindow == w) {
dispose();
}
}
@Override
public void windowActivated(IWorkbenchWindow w) {
}
});
}
private void dispose() {
fContextService.removeDebugContextListener(this);
fgServices.remove(fWindow);
fCommandUpdates.clear();
fWindow = null;
}
/**
* Updates the given command type after the next context change.
*
* @param commandType the command class
* @param action the action to add to the update list
*/
public void postUpdateCommand(Class<?> commandType, IEnabledTarget action) {
synchronized (fCommandUpdates) {
Job.getJobManager().cancel(commandType);
List<IEnabledTarget> actions = fCommandUpdates.get(commandType);
if (actions == null) {
actions = new ArrayList<>();
fCommandUpdates.put(commandType, actions);
}
actions.add(action);
}
}
/**
* Updates the given command type based on the active context.
*
* @param commandType the command class
* @param action the action to update
*/
public void updateCommand(Class<?> commandType, IEnabledTarget action) {
ISelection context = fContextService.getActiveContext();
if (context instanceof IStructuredSelection && !context.isEmpty()) {
Object[] elements = ((IStructuredSelection)context).toArray();
updateCommand(commandType, elements, new IEnabledTarget[]{action});
} else {
action.setEnabled(false);
}
}
private void postUpdate(ISelection context) {
Map<Class<?>, List<IEnabledTarget>> commands = null;
synchronized (fCommandUpdates) {
commands = fCommandUpdates;
fCommandUpdates = new HashMap<>(commands.size());
}
if (context instanceof IStructuredSelection && !context.isEmpty()) {
Object[] elements = ((IStructuredSelection)context).toArray();
for (Entry<Class<?>, List<IEnabledTarget>> entry : commands.entrySet()) {
List<IEnabledTarget> actions = entry.getValue();
updateCommand(entry.getKey(), elements, actions.toArray(new IEnabledTarget[actions.size()]));
}
} else {
for (List<IEnabledTarget> actionList : commands.values()) {
for (IEnabledTarget target : actionList) {
target.setEnabled(false);
}
}
}
commands.clear();
}
/**
* Updates the given command type for the specified elements.
* @param handlerType the handle type class
* @param elements elements to update for
* @param actions the actions to update
*/
private void updateCommand(Class<?> handlerType, Object[] elements, IEnabledTarget[] actions) {
if (elements.length == 1) {
// usual case - one element
Object element = elements[0];
IDebugCommandHandler handler = getHandler(element, handlerType);
if (handler != null) {
UpdateActionsRequest request = new UpdateActionsRequest(elements, actions);
handler.canExecute(request);
return;
}
} else {
Map<IDebugCommandHandler, List<Object>> map = collate(elements, handlerType);
if (map != null) {
ActionsUpdater updater = new ActionsUpdater(actions, map.size());
for (Entry<IDebugCommandHandler, List<Object>> entry : map.entrySet()) {
UpdateHandlerRequest request = new UpdateHandlerRequest(entry.getValue().toArray(), updater);
entry.getKey().canExecute(request);
}
return;
}
}
// ABORT - no command processors
for (int i = 0; i < actions.length; i++) {
actions[i].setEnabled(false);
}
}
/**
* Updates the given command type for the specified elements.
* @param handlerType the handler type class
* @param elements elements to update for
* @param participant the participant
* @return if the command stays enabled while the command executes
*/
public boolean executeCommand(Class<?> handlerType, Object[] elements, ICommandParticipant participant) {
if (elements.length == 1) {
// usual case - one element
Object element = elements[0];
IDebugCommandHandler handler = getHandler(element, handlerType);
if (handler != null) {
ExecuteActionRequest request = new ExecuteActionRequest(elements);
request.setCommandParticipant(participant);
return handler.execute(request);
}
} else {
Map<IDebugCommandHandler, List<Object>> map = collate(elements, handlerType);
if (map != null) {
boolean enabled = true;
for (Entry<IDebugCommandHandler, List<Object>> entry : map.entrySet()) {
ExecuteActionRequest request = new ExecuteActionRequest(entry.getValue().toArray());
request.setCommandParticipant(participant);
// specifically use & so handler is executed
enabled = enabled & entry.getKey().execute(request);
}
return enabled;
}
}
// ABORT - no command processors
return false;
}
@Override
public void debugContextChanged(DebugContextEvent event) {
postUpdate(event.getContext());
}
/**
* Returns a map of command handlers to associated elements, or <code>null</code> if
* one is missing.
*
* @param elements the elements
* @param handlerType the handler type class
* @return map of command handlers to associated elements or <code>null</code>
*/
private Map<IDebugCommandHandler, List<Object>> collate(Object[] elements, Class<?> handlerType) {
Map<IDebugCommandHandler, List<Object>> map = new HashMap<>();
for (int i = 0; i < elements.length; i++) {
Object element = elements[i];
IDebugCommandHandler handler = getHandler(element, handlerType);
if (handler == null) {
return null;
} else {
List<Object> list = map.get(handler);
if (list == null) {
list = new ArrayList<>();
map.put(handler, list);
}
list.add(element);
}
}
return map;
}
private IDebugCommandHandler getHandler(Object element, Class<?> handlerType) {
return (IDebugCommandHandler)DebugPlugin.getAdapter(element, handlerType);
}
}