diff --git a/docs/Eclipse4_Commands.md b/docs/Eclipse4_Commands.md new file mode 100644 index 00000000000..c1acd4ccd20 --- /dev/null +++ b/docs/Eclipse4_Commands.md @@ -0,0 +1,449 @@ +# Eclipse 4 Commands + +## Introduction + +In Eclipse 4 (E4), commands are executed using the **ECommandService** and **EHandlerService** which are part of the E4 dependency injection framework. This document explains how to programmatically call commands in E4 applications. + +Commands in Eclipse provide a semantic abstraction for user actions. A command represents what should be done (e.g., "Save File"), while handlers contain the actual implementation code that executes when the command is invoked. + +## Key Services + +### ECommandService + +The **ECommandService** is used to: +- Define commands programmatically +- Create `ParameterizedCommand` instances for execution +- Query defined commands +- Define command categories and parameters + +### EHandlerService + +The **EHandlerService** is used to: +- Execute commands +- Activate and deactivate handlers +- Check if a handler can execute (enabled state) +- Execute commands with specific contexts + +## Calling Commands in E4 + +### Basic Command Execution + +To execute a command without parameters: + +```java +import jakarta.inject.Inject; +import org.eclipse.e4.core.commands.ECommandService; +import org.eclipse.e4.core.commands.EHandlerService; +import org.eclipse.core.commands.ParameterizedCommand; + +public class MyPart { + + @Inject + private ECommandService commandService; + + @Inject + private EHandlerService handlerService; + + public void executeCommand() { + // Create the command + ParameterizedCommand command = commandService.createCommand( + "com.example.mycommand", + null // no parameters + ); + + // Execute the command + handlerService.executeHandler(command); + } +} +``` + +### Command Execution with Parameters + +To execute a command with parameters, provide a `Map` containing the parameter values: + +```java +import java.util.HashMap; +import java.util.Map; +import jakarta.inject.Inject; +import org.eclipse.e4.core.commands.ECommandService; +import org.eclipse.e4.core.commands.EHandlerService; +import org.eclipse.core.commands.ParameterizedCommand; + +public class MyPart { + + @Inject + private ECommandService commandService; + + @Inject + private EHandlerService handlerService; + + public void showSpecificView(String viewId) { + // Create parameter map + Map parameters = new HashMap<>(); + parameters.put("org.eclipse.ui.views.showView.viewId", viewId); + + // Create the parameterized command + ParameterizedCommand command = commandService.createCommand( + "org.eclipse.ui.views.showView", + parameters + ); + + // Execute the command + handlerService.executeHandler(command); + } +} +``` + +### Using Collections.emptyMap() for Commands Without Parameters + +For commands without parameters, you can use `Collections.emptyMap()` instead of `null`: + +```java +import java.util.Collections; +import jakarta.inject.Inject; +import org.eclipse.e4.core.commands.ECommandService; +import org.eclipse.e4.core.commands.EHandlerService; +import org.eclipse.core.commands.ParameterizedCommand; + +public class MyPart { + + @Inject + private ECommandService commandService; + + @Inject + private EHandlerService handlerService; + + public void executeCommand() { + ParameterizedCommand command = commandService.createCommand( + "com.example.mycommand", + Collections.emptyMap() + ); + + handlerService.executeHandler(command); + } +} +``` + +### Executing Commands with Return Values + +Commands can return values. The `executeHandler` method returns an `Object`: + +```java +public Object executeCommandWithReturnValue() { + ParameterizedCommand command = commandService.createCommand( + "com.example.mycommand", + null + ); + + // Execute and capture the return value + Object result = handlerService.executeHandler(command); + return result; +} +``` + +### Executing Commands in a Specific Context + +You can execute a command in a specific Eclipse context by passing an `IEclipseContext`: + +```java +import org.eclipse.e4.core.contexts.IEclipseContext; +import jakarta.inject.Inject; + +public class MyPart { + + @Inject + private ECommandService commandService; + + @Inject + private EHandlerService handlerService; + + @Inject + private IEclipseContext context; + + public void executeInContext() { + ParameterizedCommand command = commandService.createCommand( + "com.example.mycommand", + null + ); + + // Execute in specific context + handlerService.executeHandler(command, context); + } +} +``` + +## Dependency Injection Patterns + +### Injecting Services in a Part + +E4 parts receive services through dependency injection using the `@Inject` annotation: + +```java +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import org.eclipse.e4.core.commands.ECommandService; +import org.eclipse.e4.core.commands.EHandlerService; +import org.eclipse.swt.widgets.Composite; + +public class MyView { + + @Inject + private ECommandService commandService; + + @Inject + private EHandlerService handlerService; + + @PostConstruct + public void createPartControl(Composite parent) { + // Create UI + // Services are automatically injected + } +} +``` + +### Injecting Services in a Handler + +Handlers also receive services through dependency injection in their `@Execute` method: + +```java +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.core.commands.ECommandService; +import org.eclipse.e4.core.commands.EHandlerService; + +public class MyHandler { + + @Execute + public void execute(ECommandService commandService, + EHandlerService handlerService) { + // Use services to execute another command + ParameterizedCommand command = commandService.createCommand( + "com.example.othercommand", + null + ); + handlerService.executeHandler(command); + } +} +``` + +## Defining Commands Programmatically + +While commands are typically defined in the application model or plugin.xml, you can also define them programmatically: + +```java +import org.eclipse.core.commands.Category; +import org.eclipse.core.commands.Command; +import jakarta.inject.Inject; +import org.eclipse.e4.core.commands.ECommandService; + +public class CommandDefiner { + + @Inject + private ECommandService commandService; + + public void defineCommand() { + // Define a category + Category category = commandService.defineCategory( + "com.example.category", + "My Commands", + "Description of my commands" + ); + + // Define a command + Command command = commandService.defineCommand( + "com.example.mycommand", + "My Command", + "Description of my command", + category, + null // no parameters + ); + } +} +``` + +## Complete Example + +Here's a complete example showing how to execute the "Show View" command from a part: + +```java +package com.example.parts; + +import java.util.HashMap; +import java.util.Map; +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import org.eclipse.core.commands.ParameterizedCommand; +import org.eclipse.e4.core.commands.ECommandService; +import org.eclipse.e4.core.commands.EHandlerService; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; + +public class MyPart { + + @Inject + private ECommandService commandService; + + @Inject + private EHandlerService handlerService; + + @PostConstruct + public void createPartControl(Composite parent) { + parent.setLayout(new GridLayout()); + + Button button = new Button(parent, SWT.PUSH); + button.setText("Open Problem View"); + + button.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + openView("org.eclipse.ui.views.ProblemView"); + } + }); + } + + private void openView(String viewId) { + // Create parameter map + Map parameters = new HashMap<>(); + parameters.put("org.eclipse.ui.views.showView.viewId", viewId); + + // Create the parameterized command + ParameterizedCommand command = commandService.createCommand( + "org.eclipse.ui.views.showView", + parameters + ); + + // Execute the command + handlerService.executeHandler(command); + } +} +``` + +## Error Handling + +When executing commands, you may want to handle exceptions: + +```java +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.NotEnabledException; +import org.eclipse.core.commands.NotHandledException; +import org.eclipse.core.commands.common.NotDefinedException; + +public void executeCommandSafely() { + try { + ParameterizedCommand command = commandService.createCommand( + "com.example.mycommand", + null + ); + + handlerService.executeHandler(command); + + } catch (ExecutionException e) { + // Handler threw an exception + e.printStackTrace(); + } catch (NotDefinedException e) { + // Command is not defined + e.printStackTrace(); + } catch (NotEnabledException e) { + // Command is not enabled + e.printStackTrace(); + } catch (NotHandledException e) { + // No handler available + e.printStackTrace(); + } +} +``` + +Note: In many cases, `executeHandler` wraps exceptions and stores them in the context, so you may need to check the context for exceptions after execution. + +## Common Use Cases + +### Opening a Perspective + +```java +public void openPerspective(String perspectiveId) { + Map parameters = new HashMap<>(); + parameters.put("org.eclipse.ui.perspectives.showPerspective.perspectiveId", perspectiveId); + + ParameterizedCommand command = commandService.createCommand( + "org.eclipse.ui.perspectives.showPerspective", + parameters + ); + + handlerService.executeHandler(command); +} +``` + +### Saving the Active Editor + +```java +public void saveActiveEditor() { + ParameterizedCommand command = commandService.createCommand( + "org.eclipse.ui.file.save", + null + ); + + handlerService.executeHandler(command); +} +``` + +### Refreshing a Resource + +```java +public void refreshResource() { + ParameterizedCommand command = commandService.createCommand( + "org.eclipse.ui.file.refresh", + null + ); + + handlerService.executeHandler(command); +} +``` + +## Required Dependencies + +To use E4 command services, ensure your `META-INF/MANIFEST.MF` includes: + +``` +Require-Bundle: org.eclipse.e4.core.commands, + org.eclipse.e4.core.contexts, + org.eclipse.e4.core.di, + org.eclipse.core.commands +``` + +And import the injection packages: + +``` +Import-Package: jakarta.inject;version="1.0.0", + jakarta.annotation;version="1.1.0" +``` + +## Differences from E3 ICommandService and IHandlerService + +Key differences from Eclipse 3.x: + +1. **Service Names**: `ECommandService` and `EHandlerService` (E4) vs. `ICommandService` and `IHandlerService` (E3) + +2. **Method Names**: + - E4: `commandService.createCommand()` → `handlerService.executeHandler()` + - E3: `commandService.getCommand()` → `handlerService.executeCommand()` + +3. **Dependency Injection**: E4 services are injected via `@Inject`, E3 services obtained via `getSite().getService()` + +4. **Context Handling**: E4 uses `IEclipseContext`, E3 uses `IEvaluationContext` + +For migrating from E3 to E4, see the [Eclipse4 Migration Guide](Eclipse4_Migration.md). + +## Additional Resources + +- [Eclipse4 Migration Guide](Eclipse4_Migration.md) - Migrating from E3 to E4 +- [Platform Command Framework](PlatformCommandFramework.md) - E3 command framework +- [Eclipse4 RCP FAQ](Eclipse4_RCP_FAQ.md) - Common E4 questions +- [Eclipse4 RCP Dependency Injection](Eclipse4_RCP_Dependency_Injection.md) - DI details +- [Vogella Tutorial on Commands and Handlers](https://www.vogella.com/tutorials/Eclipse4Services/article.html#command-and-handler-service) + +--- + +*This guide is maintained as part of the Eclipse Platform UI project.* diff --git a/docs/Eclipse4_Migration.md b/docs/Eclipse4_Migration.md index 1b879c2c307..15788b9964b 100644 --- a/docs/Eclipse4_Migration.md +++ b/docs/Eclipse4_Migration.md @@ -795,6 +795,247 @@ After migrating components, test thoroughly: - Execute commands multiple times - Check resource disposal (no memory leaks) +## Migrate Programmatic Command Execution + +Commands can be executed programmatically in both E3 and E4, but the approach and services differ. + +### E3 Approach + +In E3, you use `ICommandService` to get a command and `IHandlerService` to execute it: + +```java +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.ParameterizedCommand; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.commands.ICommandService; +import org.eclipse.ui.handlers.IHandlerService; + +public class MyView extends ViewPart { + + public void executeCommand() { + ICommandService commandService = (ICommandService) getSite() + .getService(ICommandService.class); + IHandlerService handlerService = (IHandlerService) getSite() + .getService(IHandlerService.class); + + try { + // Execute command without parameters + handlerService.executeCommand("com.example.mycommand", null); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } +} +``` + +**With Parameters (E3):** + +```java +import java.util.HashMap; +import java.util.Map; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.IParameter; +import org.eclipse.core.commands.Parameterization; +import org.eclipse.core.commands.ParameterizedCommand; + +public void executeCommandWithParameters() { + ICommandService commandService = (ICommandService) getSite() + .getService(ICommandService.class); + IHandlerService handlerService = (IHandlerService) getSite() + .getService(IHandlerService.class); + + // Get the command + Command showView = commandService.getCommand("org.eclipse.ui.views.showView"); + + // Create parameter + IParameter viewIdParam = showView.getParameter("org.eclipse.ui.views.showView.viewId"); + Parameterization parm = new Parameterization(viewIdParam, "org.eclipse.ui.views.ProblemView"); + + // Create parameterized command + ParameterizedCommand parmCommand = new ParameterizedCommand( + showView, + new Parameterization[] { parm } + ); + + try { + handlerService.executeCommand(parmCommand, null); + } catch (Exception e) { + e.printStackTrace(); + } +} +``` + +### E4 Approach + +In E4, you use `ECommandService` to create commands and `EHandlerService` to execute them: + +```java +import jakarta.inject.Inject; +import org.eclipse.e4.core.commands.ECommandService; +import org.eclipse.e4.core.commands.EHandlerService; +import org.eclipse.core.commands.ParameterizedCommand; + +public class MyView { + + @Inject + private ECommandService commandService; + + @Inject + private EHandlerService handlerService; + + public void executeCommand() { + // Execute command without parameters + ParameterizedCommand command = commandService.createCommand( + "com.example.mycommand", + null + ); + + handlerService.executeHandler(command); + } +} +``` + +**With Parameters (E4):** + +```java +import java.util.HashMap; +import java.util.Map; +import jakarta.inject.Inject; +import org.eclipse.e4.core.commands.ECommandService; +import org.eclipse.e4.core.commands.EHandlerService; +import org.eclipse.core.commands.ParameterizedCommand; + +public class MyView { + + @Inject + private ECommandService commandService; + + @Inject + private EHandlerService handlerService; + + public void executeCommandWithParameters() { + // Create parameter map + Map parameters = new HashMap<>(); + parameters.put("org.eclipse.ui.views.showView.viewId", + "org.eclipse.ui.views.ProblemView"); + + // Create parameterized command + ParameterizedCommand command = commandService.createCommand( + "org.eclipse.ui.views.showView", + parameters + ); + + // Execute the command + handlerService.executeHandler(command); + } +} +``` + +### Migration Steps + +1. **Replace service retrieval**: + ```java + // E3: + ICommandService commandService = (ICommandService) getSite() + .getService(ICommandService.class); + IHandlerService handlerService = (IHandlerService) getSite() + .getService(IHandlerService.class); + + // E4: + @Inject + private ECommandService commandService; + + @Inject + private EHandlerService handlerService; + ``` + +2. **Update command creation**: + ```java + // E3: + Command cmd = commandService.getCommand("com.example.mycommand"); + ParameterizedCommand parmCmd = new ParameterizedCommand(cmd, parameterizations); + + // E4: + ParameterizedCommand command = commandService.createCommand( + "com.example.mycommand", + parametersMap + ); + ``` + +3. **Update command execution**: + ```java + // E3: + handlerService.executeCommand("com.example.mycommand", null); + // or + handlerService.executeCommand(parameterizedCommand, null); + + // E4: + handlerService.executeHandler(command); + // or with context + handlerService.executeHandler(command, context); + ``` + +4. **Update parameter handling**: + ```java + // E3: Create Parameterization objects + IParameter viewIdParam = showView.getParameter("viewId"); + Parameterization parm = new Parameterization(viewIdParam, "myViewId"); + ParameterizedCommand parmCmd = new ParameterizedCommand( + showView, + new Parameterization[] { parm } + ); + + // E4: Use a Map + Map parameters = new HashMap<>(); + parameters.put("viewId", "myViewId"); + ParameterizedCommand command = commandService.createCommand( + "com.example.showView", + parameters + ); + ``` + +### Key Differences + +| Aspect | E3 | E4 | +|--------|----|----| +| **Command Service** | `ICommandService` | `ECommandService` | +| **Handler Service** | `IHandlerService` | `EHandlerService` | +| **Service Access** | `getSite().getService()` | `@Inject` annotation | +| **Command Creation** | `getCommand()` + `new ParameterizedCommand()` | `createCommand()` | +| **Parameter Format** | `Parameterization[]` array | `Map` | +| **Execution Method** | `executeCommand()` | `executeHandler()` | +| **Return Value** | Returns `Object` | Returns `Object` | + +### Common Migration Patterns + +**Opening a View:** + +```java +// E3: +handlerService.executeCommand("org.eclipse.ui.views.showView", null); + +// E4: +Map params = new HashMap<>(); +params.put("org.eclipse.ui.views.showView.viewId", "com.example.myview"); +ParameterizedCommand cmd = commandService.createCommand( + "org.eclipse.ui.views.showView", params); +handlerService.executeHandler(cmd); +``` + +**Saving:** + +```java +// E3: +handlerService.executeCommand("org.eclipse.ui.file.save", null); + +// E4: +ParameterizedCommand cmd = commandService.createCommand( + "org.eclipse.ui.file.save", null); +handlerService.executeHandler(cmd); +``` + +For more details on E4 command execution, see [Eclipse4 Commands](Eclipse4_Commands.md). + ## Common Migration Pitfalls 1. **Forgot @PostConstruct**: Methods won't be called without the annotation @@ -803,9 +1044,12 @@ After migrating components, test thoroughly: 4. **Persisted state**: Use `-clearPersistedState` when testing model changes 5. **Class not injectable**: Handler/view class must have a public no-arg constructor or an @Inject constructor 6. **Shell auto-generation**: Always use `@Named(IServiceConstants.ACTIVE_SHELL)` to avoid getting a new Shell +7. **Service name confusion**: Use `ECommandService` and `EHandlerService` (not `ICommandService` and `IHandlerService`) +8. **Wrong method names**: Use `createCommand()` and `executeHandler()` in E4 (not `getCommand()` and `executeCommand()`) ## Additional Resources +- [Eclipse4 Commands](Eclipse4_Commands.md) - How to call commands in E4 - [Eclipse4 RCP FAQ](Eclipse4_RCP_FAQ.md) - Common questions and answers - [Eclipse4 RCP Dependency Injection](Eclipse4_RCP_Dependency_Injection.md) - DI details - [Eclipse4 RCP Contexts](Eclipse4_RCP_Contexts.md) - Context hierarchy diff --git a/docs/Eclipse4_RCP_EAS_List_of_All_Provided_Services.md b/docs/Eclipse4_RCP_EAS_List_of_All_Provided_Services.md index 9230e44f9a6..d066175966c 100644 --- a/docs/Eclipse4_RCP_EAS_List_of_All_Provided_Services.md +++ b/docs/Eclipse4_RCP_EAS_List_of_All_Provided_Services.md @@ -3,6 +3,8 @@ Eclipse4/RCP/EAS/List of All Provided Services This page provides a listing of the services and other data values that can be injected or obtained from a [context](Eclipse4_RCP_Contexts.md). +> **For detailed information on using ECommandService and EHandlerService**, see [Eclipse4 Commands](Eclipse4_Commands.md). + Application Context ------------------- diff --git a/docs/Eclipse4_RCP_FAQ.md b/docs/Eclipse4_RCP_FAQ.md index 98e402042f6..2eb253dbad0 100644 --- a/docs/Eclipse4_RCP_FAQ.md +++ b/docs/Eclipse4_RCP_FAQ.md @@ -334,6 +334,8 @@ This problem usually occurs in testing, where an object is created explictly via Commands and Handlers --------------------- +> **Need to execute commands programmatically?** See [Eclipse4 Commands](Eclipse4_Commands.md) for detailed instructions on calling commands using ECommandService and EHandlerService in E4 applications. + ### Why aren't my handler fields being re-injected? Handler instances are singleton-ish — that is, only a single instance is created within the Eclipse-4 workbench — and the handler may actually be invoked by different threads in parallel. So the handler instance can’t be re-injected since that may result in field clobbering. Only the method arguments of the @CanExecute and @Execute arguments are injected, since they can't be clobbered with parallel invocations.