Posts Tagged ‘Plugin’

Eclipse Papercut #5 – Getting external libraries as bundles

Monday, September 21st, 2009

In this episode of Eclipse Papercuts I explain how you get bundles for popolar Java libraries for your Eclipse plugin development.

I believe it has advantages to work with plugin projects instead of pure Java projects even if the plan is not to create Eclipse plugins / RCP applications.

The core strength of OSGi for this scenario is in my opinion the encapsalation and protection of your inner classes. With OSGi bundles you decide which packages of your project are exported and therefore visible to other projects (via the tab Runtime in the editor for the file plugin.xml).

This leaves only one problem: If you are using externally libraries you have to convert them into bundles / plugins.

I would be nicer to consume pre-packages bundles. As I created a bug iText should be OSGi and tweeted about it Chris Aniszczyk pointed me to Eclipse Orbit.

Eclipse Orbit provides lots of standard libraries already pre-packaged as bundles / plugins.

Lets see how you could get the iText version from Orbit. Check the Orbit FAQ to find out more.

You need to connect via cvs to the Eclipse cvs repository. CVS URL is :pserver:anonymous@dev.eclipse.org/cvsroot/tools

Navigate to “org.eclipse.orbit”. Select “com.lowagie.text” and check it out.

orbit10

Orbit keeps the different version of the library as cvs branches. Select your project and select Replace With -> Another Branch or Version…
orbit20

orbit30

After selecting the branch and pressing ok the system will download the library and you can start using the library.

In addition to Orbit you can also use the Springsource bundle repository. On this website you can search for bundles and download then directly. You can then import them as plugin projects into your workspace.

Create your first Eclipse e4 application

Wednesday, August 12th, 2009

Update This description has been updated in the following tutorial: Eclipse e4.

Eclipse released on the 29 July 2009 the tech preview version 0.9 of Eclipse e4, which can be downloaded here.

I wasn’t able to find a description how to create an new application based on E4. Starting from the CVS example is possible but I wanted to start from scratch. After several failed attempts to create an E4 application I believe I have found a way to create an E4 application. I assume others will have similar problems so I wanted to share my experience.

To follow this description you need to have knowledge how to develop Eclipse Plugins in Eclipse 3.5 or earlier. Check out this Eclipse Plugin Development Tutorial.

So lets get started with e4.

Download e4. Extract it and start it is similar to older Eclipse versions.

Currently there is no wizard which creates a working scaffold of an E4 application. We will therefore use the exsting plugin templates.

Create a new Plugin project “de.vogella.e4.first”. Do not select “Generate an activator”, nor “This plug-in will make contributions to the UI” nor “Would you like to create a rich client application”. Do not select a template. Press finish.

e410

This will open the Plugin Perspective, switch here to the Package Explorer to see your new project.

Create the package “de.vogella.e4.first”. The result should look like the following.

e412

Select your MANIFEST.MF and add the following dependencies to your plugin.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: First
Bundle-SymbolicName: de.vogella.e4.first;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Require-Bundle: org.eclipse.ui,
 org.eclipse.core.runtime,
 org.eclipse.e4.ui.workbench;bundle-version="0.9.0",
 org.eclipse.e4.ui.workbench.renderers.swt;bundle-version="0.9.0",
 org.eclipse.e4.ui.workbench.renderers.swt.contributions;bundle-version="0.9.0",
 org.eclipse.e4.ui.workbench.swt;bundle-version="0.9.0",
 org.eclipse.e4.ui.services;bundle-version="0.9.0",
 org.eclipse.e4.core.services;bundle-version="0.9.0"

You need (some of) these plugins for the usage of E4 features.

Add the following extension points. I noticed that for some of the features you have to modify the plugin.xml directly as the UI does not support them. But you have to create the plugin.xml via the MANIFEST.MF file; as I created it directly as a new file I received a strange system behavior.


<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.e4.workbench.parts">
          <part
            class="de.vogella.e4.first.views.View1"
            label="My first E4 View"
            parentId="org.eclipse.e4.ui.tags.navigation">
      </part>
   </extension>
   <extension
         id="product"
         point="org.eclipse.core.runtime.products">
      <product
            application="org.eclipse.e4.ui.workbench.swt.application"
            name="vogella">
             <property
               name="appName"
               value="vogella">
         </property>
         <property
               name="applicationXMI"
               value="de.vogella.e4.first/Application.xmi">
         </property>
      </product>

   </extension>

</plugin>

“org.eclipse.e4.workbench.parts” is a new extension point by which you can define your view parts, formally known as views and editors.

The extension point “org.eclipse.core.runtime.products” has a new parameter “applicationXMI”. This is a parameter to the model of your UI. We came to this later. You also do not define your own application; we use “org.eclipse.e4.ui.workbench.swt.application”.

Create a product configuration called “vogella.product”. This is similar to Eclipse 3.5; see here for a description: Eclipse Product Configuration.

e420

Make sure you select “org.eclipse.e4.ui.workbench.swt.application”.

e430

Add as dependency to your product the plugin “org.eclipse.e4.ui.workbench”. Add also the plugin “de.vogella.e4.first”.
Press “add required plugins”.

Create the following two classes:


package de.vogella.e4.first.handlers;

import org.eclipse.e4.workbench.ui.IWorkbench;

public class ExitHandler {
	public void execute(IWorkbench workbench) {
		workbench.close();
	}
}

and

package de.vogella.e4.first.views;

import org.eclipse.e4.core.services.context.IEclipseContext;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.services.IDisposable;

public class View1 implements IDisposable {

	public View1(Composite parent, final IEclipseContext outputContext) {
		Label label = new Label(parent, SWT.NONE);
		label.setText("E4 is new");
		GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
		gridData.horizontalIndent = 20;
		label.setLayoutData(gridData);
		Text text = new Text(parent, SWT.NONE);
		text.setText("and different");
		GridLayoutFactory.fillDefaults().generateLayout(parent);
	}

	public void dispose() {

	}
}

You need a model for your application. In the parameter “applicationXMI” we earlier defined that this model will be “de.vogella.e4.first/Application.xmi”. Create the file Application.xmi with the following content.


<?xml version="1.0" encoding="ASCII"?>
<application:MApplication xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:application="http://www.eclipse.org/ui/2008/Application">
  <windows name="Main" x="100" y="100" width="800" height="600">
    <menu>
      <items xsi:type="application:MMenuItem" name="File">
        <menu>
          <items xsi:type="application:MMenuItem" id="" name="Exit" command="//@command.0"/>
        </menu>
      </items>
    </menu>
    <children policy="VerticalComposite">
      <children xsi:type="application:MSashForm" policy="HorizontalSash">
        <children xsi:type="application:MStack">
          <children xsi:type="application:MContributedPart" iconURI="" name="My View" tooltip="My first View" URI="platform:/plugin/de.vogella.e4.first/de.vogella.e4.first.views.View1"/>
        </children>
        <weights>30</weights>
        <weights>70</weights>
      </children>
    </children>
    <handlers id="" URI="platform:/plugin/de.vogella.e4.first/de.vogella.e4.first.handlers.ExitHandler" persistedState="" command="//@command.0"/>
  </windows>
  <command id="application.exit" name="Exit"/>
</application:MApplication>

This is the EMF model which defines your UI. Eclipse should display this model in a EMF editor so that you can investigate it. In this model you find your view and command definition. The content can be viewed via the properities view.

e450

Now start your application from your vogella.product. Starting it directly from the plugin.xml will not work (easily).

If you receive the error “org.eclipse.core.runtime.AssertionFailedException: null argument:-applicationXMI argument missing” check you plugin.xml. E4 created in several cases twice the products entry.

You should be rewarded ;-) with this user interface:

e460

With this first application I hope you can started using the E4 advanced features, e.g. CSS styling, etc.

My final thoughts: Eclipse e4 has already amazing capabilities for a tech review release. The new modeled UI removes the need for a lot of boilerplate code.

Try it out!

TweetHub – Eclipse ECF Twitter client

Monday, August 10th, 2009

Eclipse ECF is a framework for supporting the development of distributed Eclipse-based tools and applications.

With the Galileo release Eclipse ECF provides also a nice Twitter client TweetHub. TweetHub is an Eclipse RCP application for communicating with Twitter.

It took me a while to gather the right information to make TwitterHub work. So I thought it might be helpful to blog about it so that this might be easier for others.

First you need to install the ECF SDK via the update manager from http://download.eclipse.org/rt/ecf/3.0/3.5/repo.

To install TwitterHub you need to download the code from cvs. The cvs connection string is: :pserver:anonymous@ecf1.osuosl.org:/ecf

To learn how to use cvs see Eclipse cvs access.

Check-out the following plugins from the cvs location:

org.eclipse.ecf.provider.twitter.ui.hub.product
org.eclipse.ecf.provider.twitter.ui.hub
org.eclipse.ecf.provider.twitter

TweetHub08

This should be sufficient. You should now be able to run the product.

TweetHub10

So now you can use TwitterHub to follow me on Twitter. ;-)

I would like to thank Marcelo Mayworm who helped on in the eclipse.technology.ecf newsgroup.

Additional links:
ECL New and Noteworthy
TweetHub wiki .

Deprecate certain extension points to guide developers?

Wednesday, July 29th, 2009

Given the discussion about E4 and the evolution of Eclipse I’m wondering if it would make sense to flag certain Eclipse extension points as deprecated and add a warning to the PDE tooling if someone uses these extension points. This would help developes to do “the right thing”.

This question came to me while watching The Myth of the Genius Programmer. Björn quoted from this presentation in this post Criticism is not evil . In this video it was explained that tools matter in the sense that they guide new developer in a certain direction.

For example I believe that actions could be marked as deprecated. I see still a lot of questions which relates to actions in the newsgroup and I believe that commands are superior to actions. I know that the description indicates that “org.eclipse.ui.menus” can be used but perhaps more guidance would help?

Any opinions on this?

Update I submitted Bug 285034 for the enhancement in PDE / UI for the warning.

Eclipse Papercut #3 – Plugin to find unused methods

Wednesday, July 15th, 2009

In this episode of Eclipse Papercuts we will look at how we can analyse our own code to find “dead” code in your projects. We will write a plug-in to find methods which are not called (within the workspace).

methodCalls

Of course if you are a framework developer all your exported public methods are API and you can hardly change it. But in a lot of cases the development team has all code in one workspace. In this case it should be easy to identify dead code easily. Currently you have to select each method and select “Open Call Hierachy”.

This calls of course for a simpler solution, lets solve a papercut.

Create a Plug-in Project “de.vogella.jdt.codeanalysis” .

Define the following model class which will store the results of the calucation.


package de.vogella.jdt.infoview.model;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IMethod;

public class MethodInformation {
private final String methodName;
private final int numberOfCalls;
private final ICompilationUnit cu;
private final IMethod method;

public MethodInformation(String methodName, int numberOfCalls,
ICompilationUnit cu, IMethod method) {
this.methodName = methodName;
this.numberOfCalls = numberOfCalls;
this.cu = cu;
this.method = method;
}

/**
* @return the method
*/
public String getMethodName() {
return methodName;
}

/**
* @return the numberOfCalls
*/
public int getNumberOfCalls() {
return numberOfCalls;
}

/**
* @return the cu
*/
public ICompilationUnit getResource() {
return cu;
}

/**
* @return the method
*/
public IMethod getMethod() {
return method;
}

}

Define a Eclipse command “de.vogella.jdt.codeanalysis.calculateUsage” with the default handler “de.vogella.jdt.codeanalysis.handler.CalculateUsage”.

Create these two helper classes with will search through a given Java project. The usage of the JDT functionality is explained in Eclipse JDT.


package de.vogella.jdt.codeanalysis.analysis;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;

import de.vogella.jdt.codeanalysis.model.MethodInformation;

public class CodeAnalysis {

	public static List<MethodInformation> calculate(IJavaProject project) {
		List<MethodInformation> list = new ArrayList<MethodInformation>();
			try {
				if (project.isOpen()) {

					IPackageFragment[] packages = project
							.getPackageFragments();
					// parse(JavaCore.create(project));
					for (IPackageFragment mypackage : packages) {
						if (mypackage.getKind() == IPackageFragmentRoot.K_SOURCE) {
							for (ICompilationUnit unit : mypackage
									.getCompilationUnits()) {
								IType[] types = unit.getTypes();
								for (int i = 0; i < types.length; i++) {
									IType type = types[i];
									IMethod[] methods = type.getMethods();
									for (int j = 0; j < methods.length; j++) {
										IMethod method = methods[j];
										if (!method.isMainMethod()) {
											int number = performIMethodSearch(method);

											if (number == 0) {
												MethodInformation metric = new MethodInformation(
														method.getElementName(),
														number, unit, method);
												list.add(metric);
											}

										}

									}

								}

							}
						}

					}
				}
			} catch (CoreException e) {
				e.printStackTrace();
			}
		return list;
	}

	private static int performIMethodSearch(IMethod method)
			throws CoreException {
		SearchPattern pattern = SearchPattern.createPattern(method,
				IJavaSearchConstants.REFERENCES);
		IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
		MySearchRequestor requestor = new MySearchRequestor();
		SearchEngine searchEngine = new SearchEngine();
		searchEngine.search(pattern, new SearchParticipant[] { SearchEngine
				.getDefaultSearchParticipant() }, scope, requestor, null);
		return requestor.getNumberOfCalls();

	}
}

package de.vogella.jdt.codeanalysis.analysis;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.search.MethodReferenceMatch;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchRequestor;

public class MySearchRequestor extends SearchRequestor {

	private int numberOfCalls = 0;

	@Override
	public void acceptSearchMatch(SearchMatch match) throws CoreException {
		if (match instanceof MethodReferenceMatch) {
//			MethodReferenceMatch methodMatch = (MethodReferenceMatch) match;
//			Object element = methodMatch.getElement();
			numberOfCalls++;
		}
	}

	/**
	 * @return the numberOfCall
	 */
	public int getNumberOfCalls() {
		return numberOfCalls;
	}

}

We create a little View with a Table (and its content and label provider)


package de.vogella.jdt.codeanalysis.views;

import java.util.List;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.ui.JavaUI;
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.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.part.ViewPart;

import de.vogella.jdt.codeanalysis.model.MethodInformation;

public class ResultView extends ViewPart {
	public static final String ID = "de.vogella.jdt.codeanalysis.ResultView";
	private TableViewer viewer;

	public void setInput(List<MethodInformation> list) {
		viewer.setInput(list);
	}

	@Override
	public void createPartControl(Composite parent) {
		viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
				| SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
		buildTableColumns(viewer);
		viewer.setLabelProvider(new AnalysisLabelProvider());
		viewer.setContentProvider(new AnalysisContentProvider());

		viewer.getTable().addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				Object data = e.item.getData();
				if (!(data instanceof MethodInformation))
					return;
				MethodInformation info = (MethodInformation) data;
				IMethod method = info.getMethod();
				ICompilationUnit cu = info.getResource();
				if (cu == null)
					return;
				try {
					IEditorPart part = JavaUI.openInEditor(cu);
					JavaUI.revealInEditor(part, (IJavaElement) method);
				} catch (CoreException ex) {
					// error handling
				}
			}
		});
	}

	@Override
	public void setFocus() {
	}

	private void buildTableColumns(TableViewer viewer) {
		String[] titles = { "File", "Method", "Number of Calls" };
		int[] bounds = { 100, 100, 100 };

		for (int i = 0; i < titles.length; i++) {
			TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
			column.getColumn().setText(titles[i]);
			column.getColumn().setWidth(bounds[i]);
			column.getColumn().setResizable(true);
			column.getColumn().setMoveable(true);
		}
		Table table = viewer.getTable();
		table.setHeaderVisible(true);
		table.setLinesVisible(true);
	}

	public class AnalysisLabelProvider extends LabelProvider implements
			ITableLabelProvider {

		@Override
		public Image getColumnImage(Object element, int columnIndex) {
			return null;
		}

		@Override
		public String getColumnText(Object element, int columnIndex) {
			MethodInformation metric = (MethodInformation) element;
			switch (columnIndex) {
			case 0:
				return metric.getResource().getElementName();
			case 1:
				return metric.getMethodName();
			default:
				return String.valueOf(metric.getNumberOfCalls());
			}

		}
	}

	public class AnalysisContentProvider implements IStructuredContentProvider {

		@Override
		public Object[] getElements(Object inputElement) {
			List<MethodInformation> list = (List<MethodInformation>) inputElement;
			return list.toArray();
		}

		@Override
		public void dispose() {
		}

		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}

	}

}

Finally we can create the handler:


package de.vogella.jdt.codeanalysis.handler;

import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.handlers.HandlerUtil;

import de.vogella.jdt.codeanalysis.analysis.CodeAnalysis;
import de.vogella.jdt.codeanalysis.model.MethodInformation;
import de.vogella.jdt.codeanalysis.views.ResultView;

public class CalculateUsage extends AbstractHandler {

	@Override
	public Object execute(final ExecutionEvent event) throws ExecutionException {

		IStructuredSelection selection = (IStructuredSelection) HandlerUtil
				.getActiveMenuSelection(event);
		if (selection == null || selection.getFirstElement() == null) {
			// Nothing selected, do nothing
			MessageDialog.openInformation(HandlerUtil.getActiveShell(event),
					"Information", "Please select a project");
			return null;
		}
		final Object firstElement = selection.getFirstElement();
		if (!(firstElement instanceof IJavaProject)) {
			return null;
		}

		final IJavaProject project = (IJavaProject) firstElement;

		try {
			if (!project.isOpen()
					|| !(project.getProject()
							.hasNature("org.eclipse.jdt.core.javanature"))) {
				MessageDialog.openInformation(
						HandlerUtil.getActiveShell(event), "Information",
						"Only works for open Java Projects");
				return null;
			}
		} catch (CoreException e1) {
			return null;
		}

		Job job = new Job("Calculate Usage of methods") {
			@Override
			protected IStatus run(IProgressMonitor monitor) {
				final List<MethodInformation> calculate = CodeAnalysis
						.calculate(project);
				// Open view in the UI thread
				Display.getDefault().asyncExec(new Runnable() {
					public void run() {
						try {
							final ResultView findView = (ResultView) HandlerUtil
									.getActiveWorkbenchWindow(event)
									.getActivePage().showView(ResultView.ID);
							findView.setInput(calculate);
						} catch (PartInitException e) {
							e.printStackTrace();
						}
					}

				});
				return Status.OK_STATUS;
			}

		};
		job.setUser(true);
		job.schedule();

		return null;
	}
}

As a last step add your command to the menu of the package explorer. This is also described Eclipse Plugin Development you results in the following plugin.xml


<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.ui.views">
      <view
            class="de.vogella.jdt.codeanalysis.views.ResultView"
            id="de.vogella.jdt.codeanalysis.ResultView"
            name="Analysis Result"
            restorable="true">
      </view>
   </extension>

   <extension
         point="org.eclipse.ui.commands">
      <command
            defaultHandler="de.vogella.jdt.codeanalysis.handler.CalculateUsage"
            id="de.vogella.jdt.codeanalysis.calculateUsage"
            name="Calculate method usage">
      </command>
   </extension>
   <extension
         point="org.eclipse.ui.menus">
      <menuContribution
            locationURI="popup:org.eclipse.jdt.ui.PackageExplorer">
         <command
               commandId="de.vogella.jdt.codeanalysis.calculateUsage"
               label="Calculate method usage"
               style="push">
         </command>
      </menuContribution>
   </extension>

</plugin>

If you now export your plugin into your Eclipse IDE you will have a new menu entry which allows you to start the usage calculation of your methods. Once finished your View should be opened / updated and the methods displayed which are not called. By double-clicking on them you can jump to the related method.

Note: project.isOpen() returns false, if you select an open project only with the right mouse click. If you click the project with the left mouse you should be fine.

Of course you can easily think of possible extensions to this approach:

  1. Calculate the usage of all methods and show then in the table
  2. Make is work for several projects
  3. Display in the table if a method has private, protected, default or public access
  4. Introduce filter
  5. Create marker in the editor for the identified methods. See Eclipse Plugin Development – Resource Markers

And here is the project for download.

de.vogella.jdt.codeanalysis.source_1.0.0.200907151800

Last but not least if would be nice if the Eclipse API Tools could help us with finding “dead” code. If have therefore opened Bug 283574.

Eclipse Papercut #2 – Changing Eclipse subversive default behavior

Wednesday, July 8th, 2009

Update: Igor Burilo (Eclipse Subversive developer) was kind enough to accept a modified version of patch developed in this episode. While I’m very happy about this, this unfortunately renders this example irrelevant . You still might want to read this to see how to checkout code from the Eclipse version control system.

In this second part of my 7 Paper Cuts in Eclipse I want to change some standard Eclipse code.

This second papercut is a bit more complex as the first papercut; I’m planning to return to simpler examples in the next papercut.

So lets define the papercut:

I frequently create example projects which I share in a svn repository. For related projects I use the mulitproject layout. The standard in subversion is “Simple Mode – One project for one repository”. Therefore everytime I share a project I have to switch in the subversion dialog from “Simple Mode” to “Advanced Mode”.

papercut1_10

Nothing big? I agree, hence the perfect example for a papercut.

To change the coding of this dialog we need to find the class which is responsible to display this page. Plug-in Spy make this easy via Alt + Shift + F1.

papercut1_20

Now you need to download the source code of the plug-in ”
org.eclipse.team.svn.ui”. This plug-in is stored in svn and can get access via subversion itself. The URL for the subversion repository view is: “http://dev.eclipse.org/svnroot/technology/org.eclipse.subversive”. See here to learn how to use svn and cvs to access the Eclipse code

Add this repository in the “SVN Repository” view and check-out (download) the plug-ins ”
org.eclipse.team.svn.ui” and “org.eclipse.team.svn.core”. You find them in “trunk”.

papercut1_30

Looking at the coding of SelectProjectNamePage.java changing the selection only requires a few lines to be changed.

I use Eclipse Preferences to remember the last user selection.

protected SelectProjectNamePageSimpleModeComposite simpleModeComposite;
	protected ShareProjectNameAdvancedModeComposite advancedModeComposite;
	private static String SELECTION_MODE = "isSimpleMode";

	public SelectProjectNamePage() {
		super(
			SelectProjectNamePage.class.getName(),
			"",  //$NON-NLS-1$
			SVNTeamUIPlugin.instance().getImageDescriptor("icons/wizards/newconnect.gif")); //$NON-NLS-1$
		Preferences preferences = new ConfigurationScope()
		.getNode("org.eclipse.team.svn.ui.wizard.shareproject");
		Preferences preference = preferences.node("note1");
		this.isSimpleMode = preference.getBoolean(SELECTION_MODE, true);;
	}

and I have to change a few more lines to use isSimpleMode everywhere:

this.simpleModeRadionButton.setSelection(this.isSimpleMode);
....
this.advancedModeRadionButton.setSelection(!this.isSimpleMode); // Either or
this.advancedModeRadionButton.addSelectionListener(modeListener);

And I have to set the preference in the ModeListener


	protected class ModeListener extends SelectionAdapter {
		public void widgetSelected(SelectionEvent e) {
			//change controls area mode
			Button modeButton = (Button) e.widget;
			if (SelectProjectNamePage.this.simpleModeRadionButton == modeButton && SelectProjectNamePage.this.isSimpleMode == false) {
				SelectProjectNamePage.this.isSimpleMode = true;
				preference.putBoolean(SELECTION_MODE, true);
				enableControlsArea();
			} else if (SelectProjectNamePage.this.advancedModeRadionButton == modeButton && SelectProjectNamePage.this.isSimpleMode == true) {
				SelectProjectNamePage.this.isSimpleMode = false;
				preference.putBoolean(SELECTION_MODE, false);
				enableControlsArea();
			}
		}
	}

The full source code is attached.

SelectProjectNamePage

If you now export these two plug-ins into your runnig Eclipse, this dialog will remember the last selection (Simple vrs. Advanced).

As I hope this behavior is useful to others I created a bug report (with a patch). See Bug . I hope the subversion programmers will consider this patch (or an improved version of it).

Remember sharing is good. Open Source does not only allow you to solve your issues but it makes it easy to contribute back.

See Eclipse Papercuts to get all posts in this series.

Eclipse Papercut #1 – The annoying creation of the project package

Monday, July 6th, 2009

This series is about simplifying the handling of Eclipse for simple but repetitive tasks. See 7 Paper Cuts in Eclipse.

Ok lets start with something simple.

By convension Eclipse projects are named based on reverse URL’s, e.g. “org.eclipse.jdt” or “de.vogella.test”. Also this convension suggests to create a package with the same name as the project. For this you have to select the src folder in the project, right click on it, select New – Package then copy the name of the project from the first line and paste it into the second line of the dialog.

papercut1_10

While you may argue that this is not to bad as the amount of project you are creating is limited; I personally find this annoying as I’m writting lots of example for my articles on http://www.vogella.de/.

Time to simplify (at least a little bit).

Create an Eclipse plug-in called “de.vogella.jdt.packageexplorer”. Add a Eclipse command to it with the following default handler.


package de.vogella.jdt.packageexplorer.handler;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFolder;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.handlers.HandlerUtil;

public class AddPackage extends AbstractHandler {

	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {

		IStructuredSelection selection = (IStructuredSelection) HandlerUtil
				.getActiveMenuSelection(event);
		Object firstElement = selection.getFirstElement();
		if (firstElement instanceof IJavaProject) {
			IJavaProject javaProject = (IJavaProject) firstElement;
			try {
				IFolder folder = javaProject.getProject().getFolder("src");
				// folder.create(true, true, null);
				IPackageFragmentRoot srcFolder = javaProject
						.getPackageFragmentRoot(folder);
				srcFolder.createPackageFragment(javaProject.getProject()
						.getName(), true, null);
			} catch (JavaModelException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
}

Add this command to the content menu in the package explorer as described in Eclipse plug-in Development.

The resulting plugin.xml looks 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.jdt.packageexplorer.AddPackage"
               label="Add Default Package"
               style="push">
         </command>
      </menuContribution>
   </extension>
   <extension
         point="org.eclipse.ui.commands">
      <command
            defaultHandler="de.vogella.jdt.packageexplorer.handler.AddPackage"
            id="de.vogella.jdt.packageexplorer.AddPackage"
            name="Add Default Package">
      </command>
   </extension>

</plugin>

Export your plug-in and put it into the dropin folder in your Eclipse installation. Restart Eclipse.

If you now create a new project you can right click on the project and directly create your project / default package.

papercut1_30

If you want to improve this further you can assign a keybinding to your command and use a shortcut to create the package. Or if you have more artifacts to create you can use the handler to create .java files, more packages, etc.

Here is the source bundle to download:
de.vogella.jdt.packageexplorer.source_1.0.0

Here is the exported plugin (you need you rename zip to jar before putting this into the Eclipse/dropin folder)

de.vogella.jdt.packageexplorer_1.0.0

See Eclipse Papercuts to get all posts in this series.

Eclipse RCP File browser

Tuesday, June 23rd, 2009

Eclipse RCP allow you to create easily utility programs.

Lets for example create a mini-file browser which will open a folder / file if you double-click the selection via the standard program.

To create a RCP based file browser create a new Eclipse RCP project, e.g. de.vogella.rcp.intro.filebrowser.

Create a package “de.vogella.rcp.intro.filebrowser.provider” and create the following label and content provider.


package de.vogella.rcp.intro.filebrowser.provider;

import java.io.File;

import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;

public class FileContentProvider implements ITreeContentProvider {

@Override
public Object[] getChildren(Object parent) {
File file = (File) parent;
return file.listFiles();
}

public Object[] getElements(Object inputElement) {
return (Object[]) inputElement;
}

@Override
public Object getParent(Object element) {
File file = (File) element;
return file.getParentFile();
}

@Override
public boolean hasChildren(Object parent) {
File file = (File) parent;
return file.isDirectory();
}

@Override
public void dispose() {

}

@Override
public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
}

}

package de.vogella.rcp.intro.filebrowser.provider;

import java.io.File;

import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.plugin.AbstractUIPlugin;

public class FileLabelProvider extends LabelProvider {
private static final Image folderImage = AbstractUIPlugin
.imageDescriptorFromPlugin("de.vogella.rcp.intro.filebrowser",
"icons/folder.gif").createImage();
private static final Image driveImage = AbstractUIPlugin
.imageDescriptorFromPlugin("de.vogella.rcp.intro.filebrowser",
"icons/filenav_nav.gif").createImage();
private static final Image fileImage = AbstractUIPlugin
.imageDescriptorFromPlugin("de.vogella.rcp.intro.filebrowser",
"icons/file_obj.gif").createImage();

@Override
public Image getImage(Object element) {
File file = (File) element;
if (file.isDirectory())
return file.getParent() != null ? folderImage : driveImage;
return fileImage;
}

@Override
public String getText(Object element) {
String fileName = ((File) element).getName();
if (fileName.length() > 0) {
return fileName;
}
return ((File) element).getPath();
}
}

For the label provider you need the referred icons in your folder icons. You can use the following download.

Download the Icons

Adjust then the view.

package de.vogella.rcp.intro.filebrowser;

import java.awt.Desktop;
import java.io.File;
import java.io.IOException;

import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;

import de.vogella.rcp.intro.filebrowser.provider.FileContentProvider;
import de.vogella.rcp.intro.filebrowser.provider.FileLabelProvider;

public class View extends ViewPart {
	public static final String ID = "de.vogella.rcp.intro.filebrowser.view";
	private TreeViewer viewer;

	public void createPartControl(Composite parent) {
		viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		viewer.setContentProvider(new FileContentProvider());
		viewer.setLabelProvider(new FileLabelProvider());
		viewer.setInput(File.listRoots());
		viewer.addOpenListener(new IOpenListener() {

			@Override
			public void open(OpenEvent event) {
				IStructuredSelection selection = (IStructuredSelection) event
						.getSelection();

				File file = (File) selection.getFirstElement();
				if (Desktop.isDesktopSupported()) {
					Desktop desktop = Desktop.getDesktop();
					if (desktop.isSupported(Desktop.Action.OPEN)) {
						try {
							desktop.open(file);
						} catch (IOException e) {
							// DO NOTHING
						}
					}
				}
			}
		});
	}

	public void setFocus() {
		viewer.getControl().setFocus();
	}
}

Eclipse Plug-in Spy in your Eclipse RCP application

Monday, June 22nd, 2009

To use the the Eclipse Plug-in Spy in your Eclipse RCP application include the plug-in org.eclipse.pde.runtime into your application.


Switch to our mobile site