Extending the JDT

A couple of weeks ago I had a look at the Motodevstudio for Android developers and I think it has some quite nice features like code snippets or wizards for Android activities, services and more. Actually it’s is quite easy to extend the eclipse JDT and provide such useful plugins. This blog post is about how to extend the NewElementWizard in JDT using eclipse Galileo.

We start with creating a new eclipse plugin project and create our wizard class. The NewElementWizard we want to extend resides in the org.eclipse.jdt.ui bundle. So we add both org.eclipse.jdt.ui and org.eclipse.jdt.core to our plugin manifest:

Manifest

Then we create a new class “NewActivityWizard”:

package newwizard;

import org.eclipse.jdt.internal.ui.wizards.NewElementWizard;

public class NewActivityWizard extends NewElementWizard {

}

First thing we get is a warning
“Discouraged access: The type NewElementWizard is not accessible due to restriction on required library /Applications/eclipse/plugins/org.eclipse.jdt.ui_3.5.1.r351_v20090821-0800.jar”.

The NewElementWizard belongs to an internal package and is only exported (using the ‘xfriends’ directive) to some chosen bundles. So we have the choice to have a look at the code of NewElementWizard and implement our wizard by extending org.eclipse.jface.wizard.Wizard instead, or we could make our plugin a fragment. Fragments are part of the OSGi R4 release and according to the specification a fragment is “a bundle that is attached to a host bundle”, adding content to the target bundle. For now we choose the later alternative, just because it requires less work.

Next thing we have to do is to override some methods of the superclass and of course we want to add our own wizard page. Our wizardpage will extend the org.eclipse.jdt.ui.wizards.NewTypeWizardPage. The following code is pretty much the same as in the wizards used internally in JDT as for example org.eclipse.jdt.internal.ui.wizards.NewClassCreationWizard.

public class NewActivityWizard extends NewElementWizard {

	private NewActivityWizardPage newActivityPage = null;

	public NewActivityWizard() {
        setWindowTitle("My new Android Activity");
        newActivityPage = new NewActivityWizardPage();
    }

	@Override
	public void addPages() {
        addPage(newActivityPage);
    }

    @Override
	 public void init(IWorkbench workbench, IStructuredSelection selection) {
    	newActivityPage.init(selection);
    }

    @Override
	public boolean canFinish() {
       return super.canFinish() &&
                   getContainer().getCurrentPage() == newActivityPage;
    }

	@Override
	protected void finishPage(IProgressMonitor monitor)	throws InterruptedException, CoreException {
		newActivityPage.createType(monitor);
	}

	@Override
	public IJavaElement getCreatedElement() {
		return newActivityPage.getCreatedType();
	}
}

Next we have to define the extension in the fragment.xml:

 
      
      
   

The most interesting part of the wizard page is the code generation part. We want to generate a onCreate method that takes a android.os.Bundle as argument:

private void generateOnCreate(IType type, ImportsManager imports) throws CoreException, JavaModelException {
	StringBuilder buf = new StringBuilder();
	final String delim = "n";
	buf.append("@Override");
	buf.append(lineDelim);
	buf.append("public void onCreate(");
	buf.append(imports.addImport("android.os.Bundle"));
	buf.append(" savedInstanceState) {");
	buf.append(lineDelim);
	buf.append("super.onCreate(savedInstanceState);");
	buf.append(lineDelim);
	final String content =
            CodeGeneration.getMethodBodyContent(type.getCompilationUnit(),
               type.getTypeQualifiedName('.'), "onCreate", false, "", delim);
	if (content != null && content.length() != 0)
		buf.append(content);
	buf.append(lineDelim);
	buf.append("}"); //$NON-NLS-1$
	type.createMethod(buf.toString(), null, false, null);
}

This is just the basic version of the new wizard, we also want to add intent actions and categories and update the Android manifest file. The complete source is available at http://github.com/kobmic/adt-extensions.

So how do we update the android manifest? As a Java programmer dom4j and XStream are some of my favourite XML frameworks – but I’m quite enthusiastic about Scala and Scala provides XML support baked into the language. The XML generation code for a new Android activtiy with a given list of intent actions and categories in Scala looks like this:

def createXML(activityName: String, intentActions: List[String], intentCategories: List[String]) : Node = {
  val xml =
    
         {for(action <- intentActions)
             yield {}}
	 {for(category <- intentCategories)
             yield {}}
      
    
   xml
}

Putting it all together

I chose to do the XML manipulation (written in Scala) and the Wizard (written in Java) in different OSGi bundles. If you don’t have the eclipse Scala IDE installed you even need the Scala library 2.7.7 as OSGi bundle. You can download the wizard with or without Scala dependencies from http://github.com/kobmic/adt-extensions/downloads.

The complete wizard looks like this:

At time of writing there’s a new version of MOTODEV studio available, now even as a set of plugins to eclipse:
http://developer.motorola.com/docstools/library/Installing_MOTODEV_Studio_for_Android/#installingStudioPlugins

This Post Has 3 Comments

  1. Aha, cool stuff. It must be liberating to be able to mix Scala and Java in OSGi.

  2. Great! You do just I wanted to do.
    And I found intent action and intent category list was visiable like a line when in small window screen

Leave a Reply

Close Menu