Thursday, June 10, 2010

Leveraging org.eclipse.ui.intro.samples extension point

Eclipse provides excellent samples framework that is part of 'Welcome' experience, unfortunately docs in the extension point state that This extension point is currently provided for internal Eclipse SDK usage only. It should not be used for anything else except Eclipse SDK examples. What this means is that:
  • Projects are downloaded from eclipse.org so you can't easily define alternate location of the zip files
  • The whole UI revolves around assumption that your project is an Eclipse plug-in or a project that contains some kind of code
Fortunately there is a way to manipulate all these features for your own needs.

Define location
As I stated above default url location for samples locations is eclipse.org which is defined as ShowSampleAction.UPDATE_SITE in org.eclipse.pde.internal.ui.samples. ShowSampleAction class is responsible for downloading zip file that is associated with the sample entry. All that is needed to make downloading sample from you own repository is to copy and modify ShowSampleAction with your own definitions of feature id, feature version and update site. All these constants are defined at the top of the class.

public class ShowSampleAction extends Action implements IIntroAction {
private static final String SAMPLE_FEATURE_ID = "org.eclipse.sdk.samples"; //$NON-NLS-1$
private static final String SAMPLE_FEATURE_VERSION = "3.3.0"; //$NON-NLS-1$
private static final String UPDATE_SITE = "http://dev.eclipse.org/viewcvs/index.cgi/%7Echeckout%7E/pde-ui-home/samples/"; //$NON-NLS-1$


Customize download message
In addition you might want to change the message that appears in the dialog when user clicks on the sample link. This is done in ShowSampleAction.ensureSampleFeaturePresent() method


private boolean ensureSampleFeaturePresent() {
if (checkFeature())
return true;
// the feature is not present - ask to download
if (MessageDialog.openQuestion(PDEPlugin.getActiveWorkbenchShell(), PDEUIMessages.ShowSampleAction_msgTitle, PDEUIMessages.ShowSampleAction_msgDesc)) {
return downloadFeature();
}
return false;
}


Plugin' it in
So now that we have our custom action defined how do we tell framework about it? When defining your samples in org.eclipse.ui.intro extension point the xml file that creates samples configExtension is the place we are interested in. I am not going to go into detail of how to implement the extension point here as this is covered vastly elsewhere. So here are the contents of my samples xml file:






description







The part we are interested in is the url attribute of the link element, this is where our custom action is defined:


url="http://org.eclipse.ui.intro/runAction?pluginId=com.iwaysoftware.integration.tools.samples&class=com.iwaysoftware.integration.tools.samples.actions.ShowSampleAction&id=com.iwaysoftware.samples.scifi"


Notice definition of the pluginId and class, this is what makes your custom action execute when link in the samples page is clicked.

This takes care of downloading and importing the project part.

Tweaking UI
After project is imported into your workspace from samples page there are number of things that can happen in the framework which is controlled in the IntroURL.doExecute() method: a welcome view can be closed, browser opened or another action ran. Set a break point in the method and interact with samples page to see what happens. By default IntroURL.SHOW_STANDBY is executed, but how and who controls what action is executed? Why it's our friend ShowSampleAction! Take a look at ShowSampleAction.switchToSampleStandby() method.

private void switchToSampleStandby(SampleWizard wizard) {
StringBuffer url = new StringBuffer();
url.append("http://org.eclipse.ui.intro/showStandby?"); //$NON-NLS-1$
url.append("pluginId=com.iwaysoftware.integration.tools.samples"); //$NON-NLS-1$
url.append("&"); //$NON-NLS-1$
url.append("partId=com.iwaysoftware.samples.SamplesStandbyPart"); //$NON-NLS-1$
url.append("&"); //$NON-NLS-1$
url.append("input="); //$NON-NLS-1$
url.append(sampleId);
IIntroURL introURL = IntroURLFactory.createIntroURL(url.toString());
if (introURL != null) {
introURL.execute();
ensureProperContext(wizard);
}
}


This is where url is constructed and passed to IntroURL. So if you change http://org.eclipse.ui.intro/showStandby to http://org.eclipse.ui.intro/close after sample is imported no welcome view will be docked to the left of your Eclipse instance. As I mentioned you can explore all available actions in IntroURL class.

So why is this important? Because samples extension point pre-wired to work with java projects, which might not be the case as it is in our product. Our product is a collection of xml files that are manipulated by user via GEF editors. So when a project is imported into workspace a java-project-centric welcome view appears that is not necessarily what you want to happen in your case because there is no source to browser for example. One obvious thing you can do is to call close action in the ShowSampleAction and be done with it, but you can also customize the view for your own needs.

Customizing the view
The content of the welcome view page are loaded from a class that implements IStandbyContentPart, particularly org.eclipse.pde.internal.ui.samples.SampleStandbyContent. The content of the page is defined in the createPartControl() method which could be as simple as name and description of the sample or as complicated as launching run shortcut with predefined parameters. What is nice here is you are in total control of what happens in the view.
Once you define the view, how do you integrate your changes into the framework? It took some debugging time to find the answer - org.eclipse.ui.internal.intro.impl.parts.StandbyPart.showContentPart() is the place, specifically


IntroStandbyContentPart standbyPartContent = ExtensionPointManager
.getInst().getSharedConfigExtensionsManager()
.getStandbyPart(partId);


this is the extension point from which implementer of IStandbyContentPart is loaded


String standbyContentClassName = standbyPartContent.getClassName();
String pluginId = standbyPartContent.getPluginId();

Object standbyContentObject = ModelLoaderUtil.createClassInstance(
pluginId, standbyContentClassName);


And the place where magic happens is in extension point world, where sample defining xml is set:


point="org.eclipse.ui.intro.configExtension">
configId="org.eclipse.ui.intro.universalConfig"
content="intro/samplesExtensionContent.xml">

id="com.iwaysoftware.samples.SamplesStandbyPart"
class="com.iwaysoftware.integration.tools.samples.content.SamplesStandbyContent"
pluginId="com.iwaysoftware.integration.tools.samples"/>



As you can see our custom SamplesStandbyContent is defined and will be loaded by the extension point mechanism when it's time to display the view.

Disclaimer :)
Any or all information mentioned above could become useless since samples extension point and its supporting classes are internal, so if something changes there this can tumble like a house of cards, I hope it will not. Fingers crossed.

No comments:

Post a Comment

Blogger Syntax Highliter