Version 1.5
Copyright © 2007 - 2010 Lars Vogel
21.06.2010
| Revision History | ||
|---|---|---|
| Revision 0.1 | 01.04.2008 | Lars Vogel |
| Created | ||
| Revision 0.2 - 0.9 | 14.04.2009 - 20.04.2009 | Lars Vogel |
| bugfixes and enhancements | ||
| Revision 1.0 | 31.05.2009 | Lars Vogel |
| Update to Eclipse 3.5 | ||
| Revision 1.1 - 1.4 | 12.07.2009 - 21.04.2010 | Lars Vogel |
| bugfixes and enhancements | ||
| Revision 1.5 | 21.06.2010 | Lars Vogel |
| Update to Eclipse 3.6 (Helios) | ||
Table of Contents
Eclipse is a powerful, extensible IDE for building general purpose applications. The Eclipse IDE allows the developer to extend the IDE functionality via so-called plugins. You can add new functionality for example menus, toolbar items, views, editors and perspectives to the Eclipse IDE via these plugins. This tutorial gives several examples how to do this.
Eclipse is build upon the OSGI framework (Equinox) . The OSGi framework provides a dynamic modular architecture in which so-called bundles can be deployed. Out of historical reasons Eclipse uses the term plugins but Eclipse plugins are the same as OSGi bundles. Via these plugins / bundles you can extend the Eclipse IDE.
This tutorial assumes that you are already familiar with using the Eclipse IDE . You may also want to have a look at the Eclipse RCP Tutorial .
The following will create a plugin which will contribute a menu entry to the standard Eclipse menu. Create a plugin project "de.vogella.plugin.first" via File -> New -> Project -> "Plug-in Development" -> "Plug-in Project".


Select the "Hello, World Command!" template and press Finish. This should automatically open the "Plug-in Development perspective".

Select the file "MANIFEST.MF", right click it, select Run-As-> Eclipse Application

A new workbench starts with a new menu entry. If you select this menu entry a message box will be displayed.

This chapter explains how to extend existing UI elements using the example of the package explorer. The context menu is displayed if the user select a file in the package explorer via a right mouse click. We will offer the option to create a HTML page for a Java source file. file.
To contribute to an existing menu or toolbar you need to know the corresponding ID. This ID can be found via the "Menu Spy". See Eclipse Source Code Guide for details.
The following uses Eclipse Commands. See Eclipse Commands to learn how to work with commands.
Create a new plugin project "de.vogella.plugin.htmlconverter". Do not use a template. Select the tab "Dependencies" of your "plugin.xml". Add the dependencies to "org.eclipse.jdt.core" "org.eclipse.core.resources".
Add a command with the ID "de.vogella.plugin.htmlconverter.convert" and the default handler "de.vogella.plugin.htmlconverter.handler.Convert" to your plugin. Add this command to the menu via the extension point "org.eclipse.ui.menus" and use as the "locationURI" "popup:org.eclipse.jdt.ui.PackageExplorer". Set the label to "Create HTML" for this contribution. The resulting "plugin.xml" should look like the following.
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.eclipse.ui.menus">
<menuContribution
locationURI="popup:org.eclipse.jdt.ui.PackageExplorer">
<command
commandId="de.vogella.plugin.htmlconverter.convert"
label="Create HTML"
style="push">
</command>
</menuContribution>
</extension>
<extension
point="org.eclipse.ui.commands">
<command
defaultHandler="de.vogella.plugin.htmlconverter.handler.Convert"
id="de.vogella.plugin.htmlconverter.convert"
name="Convert">
</command>
</extension>
</plugin>
Create the class "de.vogella.plugin.htmlconverter.handler.Convert" Eclipse allows to save additional information for each file. You can use the interface "IResource" and the methods "setPersistentProperty()" and "getPersistentProperty()". With these functions you can save Strings on files. We use these functions to save a directory for Java source files which already were exported via HTML.
package de.vogella.plugin.htmlconverter.handler;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.ui.handlers.HandlerUtil;
public class Convert extends AbstractHandler {
private QualifiedName path = new QualifiedName("html", "path");
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
IStructuredSelection selection = (IStructuredSelection) HandlerUtil
.getActiveMenuSelection(event);
DirectoryDialog fileDialog = new DirectoryDialog(HandlerUtil
.getActiveShell(event));
String directory = "";
Object firstElement = selection.getFirstElement();
if (firstElement instanceof ICompilationUnit) {
ICompilationUnit cu = (ICompilationUnit) firstElement;
IResource res = cu.getResource();
boolean newDirectory = true;
directory = getPersistentProperty(res, path);
if (directory != null && directory.length() > 0) {
newDirectory = !(MessageDialog.openQuestion(HandlerUtil
.getActiveShell(event), "Question",
"Use the previous output directory?"));
}
if (newDirectory) {
directory = fileDialog.open();
}
if (directory != null && directory.length() > 0) {
analyze(cu);
setPersistentProperty(res, path, directory);
write(directory, cu);
}
} else {
MessageDialog.openInformation(HandlerUtil.getActiveShell(event),
"Information", "Please select a Java source file");
}
// iterator.next();
// }
return null;
}
protected String getPersistentProperty(IResource res, QualifiedName qn) {
try {
return (String) res.getPersistentProperty(qn);
} catch (CoreException e) {
return "";
}
}
// TODO: Include this in the HTML output
private void analyze(ICompilationUnit cu) {
// Cool JDT allows you to analyze the code easily
// I don't see really a use case here but I just wanted to do this here
// as I consider this as cool and
// what to have a place where I can store the data
try {
IType type = null;
IType[] allTypes;
allTypes = cu.getAllTypes();
/**
* Search the public class
*/
for (int t = 0; t < allTypes.length; t++) {
if (Flags.isPublic((allTypes[t].getFlags()))) {
type = allTypes[t];
break;
}
}
String classname = type.getFullyQualifiedName();
IMethod[] methods = type.getMethods();
} catch (JavaModelException e) {
e.printStackTrace();
}
}
protected void setPersistentProperty(IResource res, QualifiedName qn,
String value) {
try {
res.setPersistentProperty(qn, value);
} catch (CoreException e) {
e.printStackTrace();
}
}
private void write(String dir, ICompilationUnit cu) {
try {
cu.getCorrespondingResource().getName();
String test = cu.getCorrespondingResource().getName();
// Need
String[] name = test.split("\\.");
System.out.println(test);
System.out.println(name.length);
String htmlFile = dir + "\\" + name[0] + ".html";
System.out.println(htmlFile);
FileWriter output = new FileWriter(htmlFile);
BufferedWriter writer = new BufferedWriter(output);
writer.write("<html>");
writer.write("<head>");
writer.write("</head>");
writer.write("<body>");
writer.write("<pre>");
writer.write(cu.getSource());
writer.write("</pre>");
writer.write("</body>");
writer.write("</html>");
writer.flush();
} catch (JavaModelException e) {
} catch (IOException e) {
e.printStackTrace();
}
}
}
Finished. If you run your plugin you should be able to create HTML output from a Java source code file.

It would be better if the command if only display if a Source file is selected. Add the dependency "org.eclipse.core.expressions" to your plugin. Select your menu contribution. Using the right mouse add the condition to the command that it should only be visible if a file is selected which represents a "org.eclipse.jdt.core.ICompilationUnit". This is a class which can be compiled.





This will result in the following "plugin.xml".
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.eclipse.ui.menus">
<menuContribution
locationURI="popup:org.eclipse.jdt.ui.PackageExplorer">
<command
commandId="de.vogella.plugin.htmlconverter.convert"
label="Create HTML"
style="push">
<visibleWhen>
<with
variable="activeMenuSelection">
<iterate>
<adapt
type="org.eclipse.jdt.core.ICompilationUnit">
</adapt></iterate>
</with>
</visibleWhen>
</command>
</menuContribution>
</extension>
<extension
point="org.eclipse.ui.commands">
<command
defaultHandler="de.vogella.plugin.htmlconverter.handler.Convert"
id="de.vogella.plugin.htmlconverter.convert"
name="Convert">
</command>
</extension>
</plugin>
Eclipse represents Resources like Projects, Files, Folders, Packages as IResource.
Marker represent additional informations for resources, e.g. an error marker. Every marker can have attributes (key / value combination). Markers can be displayed in the standard view, e.g. the Task, Bookmark or the problems view. To be displayed in these views you have to use predefined attributes.
The following will demonstrate how to create marker for a selected resource.
Create a plug-in project "de.vogella.plugin.markers". Add the dependency to org.eclipse.core.resources", "org.eclipse.jdt.core" and "org.eclipse.jdt.ui". Create the command "de.vogella.plugin.markers.AddMarker" with the default handler "de.vogella.plugin.markers.handler.AddMarker" and add this command to the menu.
Create the following code.
package de.vogella.plugin.markers.handler;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.handlers.HandlerUtil;
public class AddMarker extends AbstractHandler {
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
IStructuredSelection selection = (IStructuredSelection) HandlerUtil
.getActiveSite(event).getSelectionProvider().getSelection();
if (selection == null) {
return null;
}
Object firstElement = selection.getFirstElement();
if (firstElement instanceof IJavaProject) {
IJavaProject type = (IJavaProject) firstElement;
try {
IResource resource = type.getUnderlyingResource();
IMarker marker = resource.createMarker(IMarker.TASK);
marker.setAttribute(IMarker.MESSAGE, "This a a task");
marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
If you run you can create a marker in the TODO list if you select a Java project and click your menu entry.

Adapters help to display information about objects in view without having to adjust the existing views. In this example we will create a small view which allows to select objects and use the properties view to display them.
Adapters are used on several places for example you can use an adapter to display your data in the outline view. See Outline View Example for an example how to do this.
We will simple use an adapter to show our data in the property view. Create a new plugin project "de.vogella.plugin.adapter". Use the "Plug-in with a view" template with the following settings.

Add the dependency "org.eclipse.ui.views" in tab dependencies of plugin.xml.
Create the following data model.
package de.vogella.plugin.adapter.model;
public class Todo {
private String summary;
private String description;
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Change the code of SampleView.java to the following. After this change you should be able to run your project, open your view and see your todo items.
package de.vogella.plugin.adapter.views;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import de.vogella.plugin.adapter.model.Todo;
public class SampleView extends ViewPart {
public static final String ID = "de.vogella.plugin.adapter.views.SampleView";
private TableViewer viewer;
class ViewContentProvider implements IStructuredContentProvider {
private Todo[] todos;
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
todos = (Todo[]) newInput;
}
public void dispose() {
}
@Override
public Object[] getElements(Object inputElement) {
return todos;
}
}
class ViewLabelProvider extends LabelProvider implements
ITableLabelProvider {
public String getColumnText(Object obj, int index) {
Todo todo = (Todo) obj;
return todo.getSummary();
}
public Image getColumnImage(Object obj, int index) {
return getImage(obj);
}
public Image getImage(Object obj) {
return PlatformUI.getWorkbench().getSharedImages().getImage(
ISharedImages.IMG_OBJ_ELEMENT);
}
}
/**
* This is a callback that will allow us to create the viewer and initialize
* it.
*/
public void createPartControl(Composite parent) {
viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
| SWT.V_SCROLL);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
getSite().setSelectionProvider(viewer);
viewer.setInput(getElements());
}
/**
* Passing the focus request to the viewer's control.
*/
public void setFocus() {
viewer.getControl().setFocus();
}
// Build up a simple data model
private Todo[] getElements() {
Todo[] todos = new Todo[2];
Todo todo = new Todo();
todo.setSummary("First Todo");
todo.setDescription("A very good description");
todos[0] = todo;
todo = new Todo();
todo.setSummary("Second Todo");
todo.setDescription("Second super description");
todos[1] = todo;
return todos;
}
}
To displays its values in the property view, add the extension point "org.eclipse.core.runtime.adapters" to your project. The data of the extension point should be like the following.
<extension
point="org.eclipse.core.runtime.adapters">
<factory
adaptableType="de.vogella.plugin.adapter.model.Todo"
class="de.vogella.plugin.adapter.TodoAdapterFactory">
<adapter
type="org.eclipse.ui.views.properties.IPropertySource">
</adapter>
</factory>
</extension>
Implement the factory and the new class "TodoPropertySource" which implements "IPropertySource".
package de.vogella.plugin.adapter;
import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.ui.views.properties.IPropertySource;
import de.vogella.plugin.adapter.model.Todo;
public class TodoAdapterFactory implements IAdapterFactory {
@Override
public Object getAdapter(Object adaptableObject, Class adapterType) {
if (adapterType== IPropertySource.class && adaptableObject instanceof Todo){
return new TodoPropertySource((Todo) adaptableObject);
}
return null;
}
@Override
public Class[] getAdapterList() {
return new Class[] { IPropertySource.class };
}
}
package de.vogella.plugin.adapter;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.properties.TextPropertyDescriptor;
import de.vogella.plugin.adapter.model.Todo;
public class TodoPropertySource implements IPropertySource {
private final Todo todo;
public TodoPropertySource(Todo todo) {
this.todo = todo;
}
@Override
public boolean isPropertySet(Object id) {
return false;
}
@Override
public Object getEditableValue() {
return this;
}
@Override
public IPropertyDescriptor[] getPropertyDescriptors() {
return new IPropertyDescriptor[] {
new TextPropertyDescriptor("summary", "Summary"),
new TextPropertyDescriptor("description", "Description") };
}
@Override
public Object getPropertyValue(Object id) {
if (id.equals("summary")) {
return todo.getSummary();
}
if (id.equals("description")) {
return todo.getDescription();
}
return null;
}
@Override
public void resetPropertyValue(Object id) {
}
@Override
public void setPropertyValue(Object id, Object value) {
String s = (String) value;
if (id.equals("summary")) {
todo.setSummary(s);
}
if (id.equals("description")) {
todo.setDescription(s);
}
}
}
If you run your workbench and open your View via Windows -> Show View -> Others -> Sample Category -> Sample View and the property view you should be able to view your data.

To use your plugins in your standard Eclipse installation you can export your plugin and put it into the Eclipse "dropin" folder.
Select your plugin.xml and select the tab overview. Press the hyperlink "Export Wizard".

Select the plugin you want to export.

This will create a jar in the directory plugin. Copy this jar to the "dropin" directory in your Eclipse installation directory and re-start Eclipse. Your plugin should now be available.

Thank you for practicing with this tutorial.
I maintain this tutorial in my private time. If you like the information please help me by using flattr or donating or by
|
Before posting questions, please see the vogella FAQ . If you have questions or find an error in this article please use the www.vogella.de Google Group . I have created a short list how to create good questions which might also help you. .
http://www.eclipse.org/articles/article.php?file=Article-Adapters/index.html Adapters in Eclipse
http://www.eclipse.org/articles/Article-Resource-deltas/resource-deltas.html How to react to Eclipse resource deltas