Posts Tagged ‘JDT’

Eclipse Papercut #6 – Modifying Mylyn Context

Monday, October 5th, 2009

In this episode of Eclipse Papercuts I will demonstrate how to modify the Mylyn tasks context. Many thanks to David Green for providing sample code to access the Mylyn context.

Mylyn makes certain assumptions how the developer works. If you start a new task the Mylyn context is empty and fills up based on the selected files.

Which is not always the way I work. Frequently I know that for my task a whole package (or project) is relevant. I would like to include all classes in this package / project in my Mylyn task.

Ok, lets see how we can solve this papercut. I describe how to add Java classes from a package to an active task.

Create an command which extends the package explorer with a new popup commands as described here Extend the package explorer.

Create the command handler with the following coding:


package de.vogella.mylyn.tasksmodify.handlers;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.mylyn.context.core.ContextCore;
import org.eclipse.mylyn.context.core.IInteractionContext;
import org.eclipse.mylyn.internal.context.core.ContextCorePlugin;
import org.eclipse.mylyn.monitor.core.InteractionEvent;
import org.eclipse.mylyn.monitor.core.InteractionEvent.Kind;
import org.eclipse.ui.handlers.HandlerUtil;

public class SampleHandler extends AbstractHandler {
	public Object execute(ExecutionEvent event) throws ExecutionException {

		IStructuredSelection selection = (IStructuredSelection) HandlerUtil
				.getActiveMenuSelection(event);
		if (selection ==null){
			return null;
		}

		Object firstElement = selection.getFirstElement();

		if (firstElement instanceof IPackageFragment) {
			IPackageFragment mypackage = (IPackageFragment) firstElement;
			try {
				if (mypackage.getKind() == IPackageFragmentRoot.K_SOURCE){
					 ICompilationUnit[] compilationUnits = mypackage.getCompilationUnits();
					 for (ICompilationUnit iCompilationUnit : compilationUnits) {
						 IInteractionContext activeContext = ContextCore.getContextManager()
							.getActiveContext();
						 ContextCorePlugin.getContextManager().processInteractionEvent(iCompilationUnit,
									Kind.PROPAGATION, InteractionEvent.ID_UNKNOWN, activeContext);
					}
				}
			} catch (JavaModelException e) {
				e.printStackTrace();
			}
		}

		return null;
	}
}

Launch your plugin. Create a Mylyn task and select a package in the package explorer. Select your commands and all files of this package will be added to the task.

Further reading: If you want to access other Java Elements, e.g. Project and add them to the Mylyn task this tutorial might help you: Eclipse JDT.

Please note that ContextCorePlugin is supposed to be internal API but I did not find another way of accessing the Mylyn tasks.

 

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 #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: Filter / Exclude certain packages during “Organize Imports”

Wednesday, April 1st, 2009

The Eclipse “Organize Import” Feature (Cntr+Shift + O) allows to automatically import the packages which are used in a program. If there are several options Eclipse suggests all available classes and the user has to select the right one.

organizeImport

If you are working mainly with SWT the fact that Eclipse does also suggest java.awt and javax.swing classes can be annoying.

To prevent this you can add a “Type Filter” under Windows -> Preferences -> Java -> Appearance -> Type Filters. Then these classes are not suggested anymore.

Type Filter

Type Filter