This article demonstrates how to add new files to an existing Eclipse projects using a wizard. Eclipse wizards are a great way to define repeatable templates for file types when the built-in template functionality won't suffice. After working through it, you should be able to implement your own wizards in Eclipse to create new files.
To get the most out of this, you should feel comfortable building Java™ programming language classes, and you should also be comfortable with inheritance and using interfaces. You should be able to start up Eclipse, but this article doesn't assume that you're an Eclipse master.
To run the examples, you need:
- Eclipse V3.2 or later
- Although you may have some success with earlier versions, the code here was tested with Eclipse V3.2.2, which was the latest official release at the time of this writing.
- IBM or Sun JDK V1.5 or later
- This was created with Java V1.5.0_07 on Mac OS® X V10.4.8. The operating system you use isn't important, although the version of the Java environment installed on your machine is. I recommend using a version of Java 5.
I like many features of the Eclipse IDE. One of them is its extensibility. It's easy to customize the IDE by adding plug-ins that provide functionality, including wizards that automate the creation of classes, interfaces, projects, other resources. This is great for a large enterprise, where building and distributing plug-ins based on the Eclipse IDE can become a streamlined process that allows many people to take advantage of the functionality automatically.
In any enterprise, using a process that enables teams to build applications in the same manner provides many benefits. Applications are much easier to maintain if they're built in a consistent manner. Consistency can help reduce mistakes. Also, because applications are built in the same manner, it's easier for team members to move from project to project.
The ability to create custom wizards for the Eclipse framework lets enterprises build enterprise-specific wizards that give teams the opportunity to create applications using consistent best practices.
The custom wizard you build here is in a plug-in project in Eclipse. Starting the custom wizards is easy, thanks to other wizards that begin the code for you. In the next steps, you use the Plug-in Project Wizard to create the beginnings of your plug-in.
To build the new plug-in project:
- Choose File > New > Project to select a project wizard.
- Under Plug-in Development, select Plug-in Project, as shown below.
- Click Next to advance to the next step.
Figure 1. Select a plug-in project
- Add a project name (in this article, the name is the not-so-creative ExampleWizard,
as shown in Figure 2). Use the default location unless you have a good reason to change it. Click Next.
Figure 2. New plug-in project
- Put your version number into Plug-in Version and add the name of the plug-in and name of the plug-in provider (probably you,
or the team you're working with). Be sure to update the package name of the Activator, which defaults to a lowercase version of the
project name. It's better to use a package name that fits inline with your company standards: for instance,
com.example.eclipse.wizards. After you have filled in the information as shown below, click Next.
Figure 3. Select a plug-in project
- Choose Custom plug-in wizard because this option offers you a chance to fine-tune the components that are included. If you haven't created new plug-in projects before, now is a perfect time to look at the descriptions of the other templates to see what is available. Click Next.
- In the Template Selection window, click Deselect All to unselect all the
options. Then, choose New File Wizard, as shown below. Click Next.
Figure 4. Choose a template
- The Eclipse wizard prompts you for some information about the new wizard you're creating (see Figure 5). Make sure you update the package
name, ideally to the same name that you used for the Activator (com.example.eclipse.wizards). Update the Wizard Category Name to the
name of the folder for the new wizard. This value is used like the Plug-in Development category you saw in Figure 1.
The Wizard Class Name is a Java class name for the class that inherits from
Wizardand implements theINewWizardinterface. The Wizard Page Class Name extends theWizardPageclass.
Figure 5. New Wizard Options window
- Click Finish. Eclipse adds your new project with the necessary classes and libraries.
You aren't finished, but you have a good start and are ready to begin adding some implementation behind the wizard.
The Wizard class and INewWizard interfaces
You now have three classes in your project: NewXHTMLFileWizard,
NewXHTMLFileWizardPage, and Activator. The following sections deal with the
NewXHTMLFileWizard class. The class is shown in Listing 1 without all the code in the methods.
Listing 1. NewXHTMLFileWizard class
public class NewXHTMLFileWizard extends Wizard implements INewWizard {
private NewXHTMLFileWizardPage page;
private ISelection selection;
public NewXHTMLFileWizard() {
// snipped...
}
public void addPages() {
// snipped...
}
public boolean performFinish() {
// snipped...
}
private void doFinish(
// snipped...
}
private InputStream openContentStream() {
// snipped...
}
private void throwCoreException(String message) throws CoreException {
// snipped...
}
public void init(IWorkbench workbench, IStructuredSelection selection) {
// snipped...
}
}
|
The last method, init(), is needed to implement the INewWizard interface. Next, this article covers this and the rest of the methods that are automatically included in this template.
The addPages() method adds pages to the wizard. The method, shown in Listing 2, adds a single page to
the wizard: NewXHTMLFileWizardPage.
Listing 2.
addPages() method adds pages to the wizard
/**
* Adding the page to the wizard.
*/
public void addPages() {
page = new NewXHTMLFileWizardPage(selection);
// You can add more pages here...
addPage(page);
}
|
The NewXHTMLFileWizardPage class contains the controls that provide the user with the ability to specify a
page name. You can add controls to the page later to let the end user enter more information.
The performFinish() method is called when the user clicks the Finish button on the wizard. After doing
some checking, it calls the doFinish() method using the IRunnableWithProgress
interface. Using this interface means you don't have to write all the UI elements that show progress bars while the
doFinish() method is being executed (in case it takes a long
time to run). It's listed in its entirety below.
Listing 3. performFinish() method
/**
* This method is called when 'Finish' button is pressed in
* the wizard. We will create an operation and run it
* using wizard as execution context.
*/
public boolean performFinish() {
final String containerName = page.getContainerName();
final String fileName = page.getFileName();
IRunnableWithProgress op = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException {
try {
doFinish(containerName, fileName, monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
} finally {
monitor.done();
}
}
};
try {
getContainer().run(true, false, op);
} catch (InterruptedException e) {
return false;
} catch (InvocationTargetException e) {
Throwable realException = e.getTargetException();
MessageDialog.openError(getShell(), "Error", realException.getMessage());
return false;
}
return true;
}
|
The doFinish() method, shown below, creates the new file and opens an editor in the IDE with the new file in
it. The openContentStream() method is called to get a stream from which to fill the new file with content.
Listing 4. Initial doFinish() method
/**
* The worker method. It will find the container, create the
* file if missing or just replace its contents, and open
* the editor on the newly created file.
*/
private void doFinish(
String containerName,
String fileName,
IProgressMonitor monitor)
throws CoreException {
// create a sample file
monitor.beginTask("Creating " + fileName, 2);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IResource resource = root.findMember(new Path(containerName));
if (!resource.exists() || !(resource instanceof IContainer)) {
throwCoreException("Container \"" + containerName + "\" does not exist.");
}
IContainer container = (IContainer) resource;
final IFile file = container.getFile(new Path(fileName));
try {
InputStream stream = openContentStream();
if (file.exists()) {
file.setContents(stream, true, true, monitor);
} else {
file.create(stream, true, monitor);
}
stream.close();
} catch (IOException e) {
}
monitor.worked(1);
monitor.setTaskName("Opening file for editing...");
getShell().getDisplay().asyncExec(new Runnable() {
public void run() {
IWorkbenchPage page =
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
try {
IDE.openEditor(page, file, true);
} catch (PartInitException e) {
}
}
});
monitor.worked(1);
}
|
The openContentStream() method
The openContentStream() method, shown below, returns a ByteArrayInputStream that
contains a static string generated as part of the template. For this article, the string is replaced with the contents of a template file.
The code in this method is the first thing to change in order to allow more usable content to be added to the new file when it's created.
Listing 5. openContentStream() method
/**
* Initialize file contents with a sample text.
*/
private InputStream openContentStream() {
String contents =
"This is the initial file contents for *.html " +
"file that should be word-sorted in the Preview " +
"page of the multi-page editor";
return new ByteArrayInputStream(contents.getBytes());
}
|
Instead of using a static string value for the content of the new file, you can use the getResourceAsStream()
method to load the contents of a file into an InputStreamcode> the doFinish() method can use to populate the new file.
The modifications are shown below.
Listing 6. Get the stream from a resource
/**
* Initialize the file contents to contents of the
* given resource.
*/
private InputStream openContentStream() {
return this.getClass()
.getResourceAsStream("templates/index-xhtml-template.resource");
}
|
In the index-xhtml-template.resource file is a valid Extensible Hypertext Markup
Language (XHTML) V1.0 Strict Web page. It has some basic tags and points to a pretend set of enterprise stylesheets and JavaScript files.
The file is listed in Listing 7. This file is located in the same package as the NewXHTMLFileWizard class, so in
this article the file is located in the com.example.eclipse.wizards package. If you want to put the file in a different
package, access it as if it's a file inside a directory (that is, com.example.resources is
/com/example/resources).
Listing 7. index-xhtml-template.resource file
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>This is an Example.com Web page</title>
<link rel="stylesheet" href=
"http://www.example.com/enterprise/styles/main.css" type=
"text/css" />
<script type="text/javascript" language="JavaScript" src=
"http://www.example.com/scripts/main.js">
</script>
</head>
<body>
<div id="search">
<form id="searchForm" name="searchForm" action=
"http://www.example.com/search.jsp">
<input type="text" name="searchText" /> <input type="button"
value="Search Example.com" />
</form>
</div>
<div id="mainMenu">
<p class="menu">Main menu</p>
<ul class="mainMenu">
<li><a href="#home">Home</a></li>
<li><a href="#item1">Item 1</a></li>
</ul>
</div><!-- Put the body of your page here -->
<div id="body"></div>
</body>
</html>
|
At this point, you can run the Eclipse plug-in to see what it looks like.
At any time during this article, after Eclipse creates the three classes used in the wizard, you can use launch another instance of Eclipse in which to run and test your plug-in. To launch your plug-in project, right-click the project, and select Run As > Eclipse Application as shown in Figure 6. A new instance of Eclipse starts up.
Figure 6. Run the project as an Eclipse application
Now you need to create a temporary project that contains your new file. The project's name doesn't matter — something like "temp" will work. Once the new project is in your workspace, add your new template by choosing File > New > Other.
If everything is working as expected, a new category is listed in the Select a Wizard window under the category you defined for the wizard. I used Example.com Enterprise Templates, as shown below.
Figure 7. Use the new template
When you complete the rest of the wizard, Eclipse creates a new file that contains the contents you defined. If this simple functionality is all your template requires, you can stop here. However, you may want to prompt the user for some input that you can use when you're putting together the content for the file.
The original NewXHTMLFileWizardPage has only two controls on the form: one for the name of the container
(a project or folder) and the other the name for the new file when it's created. The createControl() method (shown
in its entirety in Listing 8) is responsible for creating these controls and adding them to the dialog.
Listing 8. createControl() method
/**
* @see IDialogPage#createControl(Composite)
*/
public void createControl(Composite parent) {
Composite container = new Composite(parent, SWT.NULL);
GridLayout layout = new GridLayout();
container.setLayout(layout);
layout.numColumns = 3;
layout.verticalSpacing = 9;
Label label = new Label(container, SWT.NULL);
label.setText("&Container:");
containerText = new Text(container, SWT.BORDER | SWT.SINGLE);
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
containerText.setLayoutData(gd);
containerText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
dialogChanged();
}
});
Button button = new Button(container, SWT.PUSH);
button.setText("Browse...");
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
handleBrowse();
}
});
label = new Label(container, SWT.NULL);
label.setText("&File name:");
fileText = new Text(container, SWT.BORDER | SWT.SINGLE);
gd = new GridData(GridData.FILL_HORIZONTAL);
fileText.setLayoutData(gd);
fileText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
dialogChanged();
}
});
initialize();
dialogChanged();
setControl(container);
}
|
Before you can add a new control to this method, it needs to be declared at the top of the file along with the other controls.
Listing 9. Declare the new control
public class NewXHTMLFileWizardPage extends WizardPage {
/* Newly added for the page title */
private Text titleText;
// the rest of the class...
}
|
Now add a getter for the text. The NewXHTMLFileWizard class uses this getter when building the new file.
The getTitle() method is shown below.
Listing 10. getTitle() method
/**
* Gets the HTML title for the new file
*/
public String getTitle() {
return titleText.getText();
}
|
Make the modifications to the createControl() method to add the new input control and label for the title. The new
code is shown below.
Listing 11. Modified createControl() method
/**
* @see IDialogPage#createControl(Composite)
*/
public void createControl(Composite parent) {
Composite container = new Composite(parent, SWT.NULL);
GridLayout layout = new GridLayout();
container.setLayout(layout);
layout.numColumns = 3;
layout.verticalSpacing = 9;
Label label = new Label(container, SWT.NULL);
label.setText("&Container:");
containerText = new Text(container, SWT.BORDER | SWT.SINGLE);
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
containerText.setLayoutData(gd);
containerText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
dialogChanged();
}
});
Button button = new Button(container, SWT.PUSH);
button.setText("Browse...");
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
handleBrowse();
}
});
label = new Label(container, SWT.NULL);
label.setText("&File name:");
fileText = new Text(container, SWT.BORDER | SWT.SINGLE);
gd = new GridData(GridData.FILL_HORIZONTAL);
fileText.setLayoutData(gd);
fileText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
dialogChanged();
}
});
/* Need to add empty label so the next two controls
* are pushed to the next line in the grid. */
label = new Label(container, SWT.NULL);
label.setText("");
/* Adding the custom control here */
label = new Label(container, SWT.NULL);
label.setText("&Title:");
titleText = new Text(container, SWT.BORDER | SWT.SINGLE);
gd = new GridData(GridData.FILL_HORIZONTAL);
titleText.setLayoutData(gd);
titleText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
dialogChanged();
}
});
/* Finished adding the custom control */
initialize();
dialogChanged();
setControl(container);
}
|
Before you add the code to the NewXHTMLFileWizard class, test the changes so far by going through the steps
outlined earlier.
The text the user enters in the Title field in the new wizard will be used as the text in the <title>
element in the new HTML file. For the purpose of this article, that value is required, so you need to add code that checks the input to make sure
the user has entered something.
The modifications to the dialogChanged() method are shown
below.
Listing 12. Verify input in dialogChanged()
/**
* Ensures that both text fields are set.
*/
private void dialogChanged() {
IResource container = ResourcesPlugin.getWorkspace().getRoot()
.findMember(new Path(getContainerName()));
String fileName = getFileName();
String titleText = getTitle();
if (getContainerName().length() == 0) {
updateStatus("File container must be specified");
return;
}
if (container == null
|| (container.getType() & (IResource.PROJECT | IResource.FOLDER)) == 0) {
updateStatus("File container must exist");
return;
}
if (!container.isAccessible()) {
updateStatus("Project must be writable");
return;
}
if (fileName.length() == 0) {
updateStatus("File name must be specified");
return;
}
if (fileName.replace('\\', '/').indexOf('/', 1) > 0) {
updateStatus("File name must be valid");
return;
}
int dotLoc = fileName.lastIndexOf('.');
if (dotLoc != -1) {
String ext = fileName.substring(dotLoc + 1);
if (ext.equalsIgnoreCase("html") == false) {
updateStatus("File extension must be \"html\"");
return;
}
}
if (titleText.length() ==0 )
{
updateStatus("Title must be specified");
return;
}
updateStatus(null);
}
|
After these changes have been made, the wizard provides an error message if a Title value isn't entered. In addition, the Finish button is disabled until a value is specified for Title. Check out this functionality by running the plug-in project by using the steps outlined earlier.
The wizard page NewXHTMLFileWizardPage now captures a value for the HTML title from the user, but it doesn't
yet incorporate that value into the file. To begin adding the value to the file, first edit the index-xhtml-template.resource file to contain a placeholder
for the value. You can change the <title> element to <title>${title}</title>
to make the replacement easier.
You modify the performFinish() method to get the title from the wizard page and to pass it to the
doFinish() method along with the rest of the values.
Listing 13. Final performFinish() method
/**
* This method is called when 'Finish' button is pressed in the wizard. We
* will create an operation and run it using wizard as execution context.
*/
public boolean performFinish() {
final String containerName = page.getContainerName();
final String fileName = page.getFileName();
final String title = page.getTitle();
IRunnableWithProgress op = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor)
throws InvocationTargetException {
try {
doFinish(containerName, fileName, title, monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
} finally {
monitor.done();
}
}
};
try {
getContainer().run(true, false, op);
} catch (InterruptedException e) {
return false;
} catch (InvocationTargetException e) {
Throwable realException = e.getTargetException();
MessageDialog.openError(getShell(), "Error", realException
.getMessage());
return false;
}
return true;
}
|
Next, modify the doFinish() method slightly to accept the title as a parameter and pass it to the
openContentStream() method.
Listing 14. Final doFinish() method, accepting title as parameter
/**
* The worker method. It will find the container, create the file if missing
* or just replace its contents, and open the editor on the newly created
* file.
*/
private void doFinish(String containerName, String fileName, String title,
IProgressMonitor monitor) throws CoreException {
monitor.beginTask("Creating " + fileName, 2);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IResource resource = root.findMember(new Path(containerName));
if (!resource.exists() || !(resource instanceof IContainer)) {
throwCoreException("Container \"" + containerName
+ "\" does not exist.");
}
IContainer container = (IContainer) resource;
final IFile file = container.getFile(new Path(fileName));
try {
InputStream stream = openContentStream(title);
try {
if (file.exists()) {
file.setContents(stream, true, true, monitor);
} else {
file.create(stream, true, monitor);
}
} finally {
stream.close();
}
} catch (IOException e) {
}
monitor.worked(1);
monitor.setTaskName("Opening file for editing...");
getShell().getDisplay().asyncExec(new Runnable() {
public void run() {
IWorkbenchPage page = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getActivePage();
try {
IDE.openEditor(page, file, true);
} catch (PartInitException e) {
}
}
});
monitor.worked(1);
}
|
Finally, you need to modify the openContentStream() method
quite a bit to be able to replace the $title value
found in the file with the value supplied by the user (see Listing 15). In a template with a bunch of different values, you can use a more elegant
solution, such as a new class that extends FilterInputStream and replaces a whole set of different values.
Listing 15. Final openContentStream() method
/**
* Initialize the file contents to contents of the given resource.
*/
private InputStream openContentStream(String title) throws CoreException {
final String newline = "\n"; // System.getProperty("line.separator");
String line;
StringBuffer sb = new StringBuffer();
try {
InputStream input = this.getClass().getResourceAsStream(
"index-xhtml-template.resource");
BufferedReader reader = new BufferedReader(new InputStreamReader(
input));
try {
while ((line = reader.readLine()) != null) {
line = line.replaceAll("\\$\\{title\\}", title);
sb.append(line);
sb.append(newline);
}
} finally {
reader.close();
}
} catch (IOException ioe) {
IStatus status = new Status(IStatus.ERROR, "ExampleWizard", IStatus.OK,
ioe.getLocalizedMessage(), null);
throw new CoreException(status);
}
return new ByteArrayInputStream(sb.toString().getBytes());
}
|
The openContentStream() method does much more now than load the contents of a resource file and return
them as an InputStream. The new code iterates through the stream, reads it using an
InputStreamReader, and replaces the value $title in each line. The result is returned as a ByteArrayInputStream, which is the same
stream object used when the NewXHTMLFileWizard class was first generated.
If you've followed along, you should have a wizard that creates a new file in an existing project. But why stop there? Just as enterprises may have conventions they like to follow in resources like XHTML files, they probably have conventions for how projects are laid out.
With relatively minor additions to the existing project, you can build a wizard that adds an entire project to the workspace along with
folders and some initial files. The wizard builds a new folder for Example.com that is a Web site and creates the images and styles folders.
Inside the styles folder, the wizard creates a Cascading Style Sheets (CSS) file called site.css. The wizard wraps up by reusing a
method from the NewXHTMLFileWizard class to add a new XHTML file with the title initialized to the name of the
new project plus some new text.
Build the new NewSiteProjectWizard
Because you already have a plug-in project set up and working, you don't need to use a wizard to build the new class. Instead, you can
build the new wizard yourself by creating a new class that extends from Wizard and implements two interfaces:
INewWizard and IExecutableExtension.
Add the new NewSiteProjectWizard class into the same package as the
NewXHTMLFileWizard class. Look at Listing 16 for the
NewSiteProjectWizard class declaration, and make sure you extend the
Wizard class. Also add the INewWizard and
IExecutableExtension interfaces.
Because the NewSiteProjectWizard class extends the same class and implements one of the interfaces that
the NewXHTMLFileWizard class implements, you'll notice common methods if you compare the two.
The NewSiteProjectWizard appears in Listing 16 with the contents of the methods snipped for brevity (you'll see
them exposed later in this article ).
Listing 16. NewSiteProjectWizard class
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExecutableExtension;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard;
public class NewSiteProjectWizard extends Wizard implements INewWizard,
IExecutableExtension {
/*
* Use the WizardNewProjectCreationPage, which is provided by the Eclipse
* framework.
*/
private WizardNewProjectCreationPage wizardPage;
private IConfigurationElement config;
private IWorkbench workbench;
private IStructuredSelection selection;
private IProject project;
/**
* Constructor
*/
public NewSiteProjectWizard() {
super();
}
public void addPages() {
// snipped...
}
@Override
public boolean performFinish() {
// snipped...
}
/**
* This creates the project in the workspace.
*
* @param description
* @param projectHandle
* @param monitor
* @throws CoreException
* @throws OperationCanceledException
*/
void createProject(IProjectDescription description, IProject proj,
IProgressMonitor monitor) throws CoreException,
OperationCanceledException {
// snipped...
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench,
* org.eclipse.jface.viewers.IStructuredSelection)
*/
public void init(IWorkbench workbench, IStructuredSelection selection) {
// snipped...
}
/**
* Sets the initialization data for the wizard.
*/
public void setInitializationData(IConfigurationElement config,
String propertyName, Object data) throws CoreException {
// snipped...
}
/**
* Adds a new file to the project.
*
* @param container
* @param path
* @param contentStream
* @param monitor
* @throws CoreException
*/
private void addFileToProject(IContainer container, Path path,
InputStream contentStream, IProgressMonitor monitor)
throws CoreException {
// snipped
}
} |
After you add the new class and before you can execute it in Eclipse as a wizard, you need to make some changes to the plugin.xml file located in the base of the project.
Listing 17. The plugin.xml file
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
<extension
point="org.eclipse.ui.newWizards">
<category
name="Example.com Enterprise Templates"
id="ExampleWizard">
</category>
<wizard
name="Example.com Static Web Page"
icon="icons/sample.gif"
category="ExampleWizard"
class="com.example.eclipse.wizards.NewXHTMLFileWizard"
id="com.example.eclipse.wizards.NewXHTMLFileWizard">
</wizard>
<wizard
category="ExampleWizard"
class="com.example.eclipse.wizards.NewSiteProjectWizard"
icon="icons/sample.gif"
id="com.example.eclipse.wizards.NewSiteProjectWizard"
name="Example.com Static Web Site"
project="true">
</wizard>
</extension>
</plugin>
|
The changes to plugin.xml let Eclipse know that the NewSiteProjectWizard class is a wizard that can be called
by Eclipse. It's also organized under the same category as the NewXHTMLFileWizard class discussed earlier.
The attribute project="true" tells Eclipse it's a project, so it's displayed in the appropriate contexts.
The Eclipse API includes a few wizard classes and wizard-page classes that are useful if you're performing basic functionality and have no
reason to build in a bunch of customization. Technically speaking, NewSiteProjectWizard could extend
BasicNewProjectResourceWizard
— an existing
project wizard used to create basic projects — although the designers note in the JavaDoc for the class that it wasn't
intended to be subclassed. To get basic project information, like the project name, you can use the same wizard page used by
BasicNewProjectResourceWizard
— the WizardNewProjectCreationPage
class, as shown below.
Listing 18. addPages() method
public void addPages() {
/*
* Unlike the custom new wizard, we just add the pre-defined one and
* don't necessarily define our own.
*/
wizardPage = new WizardNewProjectCreationPage(
"NewExampleComSiteProject");
wizardPage.setDescription("Create a new Example.com Site Project.");
wizardPage.setTitle("New Example.com Site Project");
addPage(wizardPage);
}
|
This method creates a new instance of the page class, sets the description and title, and then adds it as a wizard page.
Like the NewXHTMLFileWizard class, NewSiteProjectWizard has a
performFinish() method (shown in Listing 19) that is executed when the user has completed the steps in the
wizard and clicked Finish. This method calls a process that executes the createProject() method that
does most of the heavy lifting, like creating the project, folder, and files.
Listing 19. performFinish() method
@Override
public boolean performFinish() {
if (project != null) {
return true;
}
final IProject projectHandle = wizardPage.getProjectHandle();
URI projectURI = (!wizardPage.useDefaults()) ? wizardPage
.getLocationURI() : null;
IWorkspace workspace = ResourcesPlugin.getWorkspace();
final IProjectDescription desc = workspace
.newProjectDescription(projectHandle.getName());
desc.setLocationURI(projectURI);
/*
* Just like the ExampleWizard, but this time with an operation object
* that modifies workspaces.
*/
WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
protected void execute(IProgressMonitor monitor)
throws CoreException {
createProject(desc, projectHandle, monitor);
}
};
/*
* This isn't as robust as the code in the BasicNewProjectResourceWizard
* class. Consider beefing this up to improve error handling.
*/
try {
getContainer().run(true, true, op);
} catch (InterruptedException e) {
return false;
} catch (InvocationTargetException e) {
Throwable realException = e.getTargetException();
MessageDialog.openError(getShell(), "Error", realException
.getMessage());
return false;
}
project = projectHandle;
if (project == null) {
return false;
}
BasicNewProjectResourceWizard.updatePerspective(config);
BasicNewProjectResourceWizard.selectAndReveal(project, workbench
.getActiveWorkbenchWindow());
return true;
}
|
The performFinish() method, after calling createProject() to create the files and
folders, calls two static methods to update the current perspective and to select the newly created project in the IDE.
The createProject() method shown in Listing 20 creates, then opens the new project. Then, the method
adds two new files and two new folders to the project. The files are added by a private method called
addFileToProject(), which was written to keep createProject() somewhat clean.
Listing 20. The createProject() method
/**
* This creates the project in the workspace.
*
* @param description
* @param projectHandle
* @param monitor
* @throws CoreException
* @throws OperationCanceledException
*/
void createProject(IProjectDescription description, IProject proj,
IProgressMonitor monitor) throws CoreException,
OperationCanceledException {
try {
monitor.beginTask("", 2000);
proj.create(description, new SubProgressMonitor(monitor, 1000));
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
proj.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(
monitor, 1000));
/*
* Okay, now we have the project and we can do more things with it
* before updating the perspective.
*/
IContainer container = (IContainer) proj;
/* Add an XHTML file */
addFileToProject(container, new Path("index.html"),
NewXHTMLFileWizard.openContentStream("Welcome to "
+ proj.getName()), monitor);
/* Add the style folder and the site.css file to it */
final IFolder styleFolder = container.getFolder(new Path("styles"));
styleFolder.create(true, true, monitor);
InputStream resourceStream = this.getClass().getResourceAsStream(
"templates/site-css-template.resource");
addFileToProject(container, new Path(styleFolder.getName()
+ Path.SEPARATOR + "style.css"),
resourceStream, monitor);
resourceStream.close();
/*
* Add the images folder, which is an official Exmample.com standard
* for static web projects.
*/
IFolder imageFolder = container.getFolder(new Path("images"));
imageFolder.create(true, true, monitor);
} catch (IOException ioe) {
IStatus status = new Status(IStatus.ERROR, "ExampleWizard", IStatus.OK,
ioe.getLocalizedMessage(), null);
throw new CoreException(status);
} finally {
monitor.done();
}
}
|
You may recognize most of the code in the addFileToProject() method as being essentially the same as that in
the doFinish() method shown in Listing 14. The signature of the method is quite different: It's been modified to accept parameters to make it more reusable within the context of the
createProject() method.
Listing 21. addFileToProject() method
/**
* Adds a new file to the project.
*
* @param container
* @param path
* @param contentStream
* @param monitor
* @throws CoreException
*/
private void addFileToProject(IContainer container, Path path,
InputStream contentStream, IProgressMonitor monitor)
throws CoreException {
final IFile file = container.getFile(path);
if (file.exists()) {
file.setContents(contentStream, true, true, monitor);
} else {
file.create(contentStream, true, monitor);
}
}
|
If the file already exists, the contents of the file are set to the contents of the contentStream passed into the
method. If the file doesn't already exist, it's created with the contents in it.
The complete NewSiteProjectWizard class is included in the download with this article. After you add the method
implementations shown here and the implementation for the INewWizard and
IExecutableExtension interfaces, you can run the project as an Eclipse application as shown earlier. This time, in
addition to creating a new file with the NewXHTMLFileWizard, you can create a new project.
When deploying the wizard .jar file (called NewFileWizard_1.0.0.jar here), place it in your plugin folder. Create the .jar by going to the plugin.xml file, double-clicking it in Eclipse, navigating to the Overview tab, and clicking on the Export Wizard link to the right. Then move the .jar to your Eclipse plugin folder, and do not unpack it.
If you get "Plugin does not have a valid identifier" or "Plugin does not have a valid version" errors when starting Eclipse, try starting Eclipse
on a command line with the -clean parameter.
If you run into errors, you can run the project as an Eclipse plug-in while debugging. I like using the Debug Perspective in the Eclipse IDE. Choose Run > Debug Last Launched to run start the plug-in in Eclipse while stepping through the code.
It's likely that you'll set a breakpoint at the first line of the performFinish() method because that's where the
action starts. The debugger should break at your breakpoint (as long as the error doesn't occur before the breakpoint) once you click
Finish on your wizard.
One of the greatest features of the Eclipse IDE is the ease of providing extended functionality by creating plug-ins that add new wizards for creating new files. Using enterprise-specific wizards for the creation of files enables consistent, rapid development of applications.
| Description | Name | Size | Download method |
|---|---|---|---|
| Code | os-eclipse-custwiz_NewFileWizard.zip | 12KB | HTTP |
Information about download methods
Learn
-
Read "Developing Eclipse
plug-ins," by David Gallardo.
-
Check out some tips and tricks for creating and
deploying Eclipse plug-ins.
-
Find resources for Eclipse developers at Eclipse.org's Development Resources.
-
Check out the "Recommended Eclipse reading list."
-
Browse all the Eclipse content on developerWorks.
-
Users new to Eclipse should check out Eclipse project resources' Start Here.
-
Expand your Eclipse skills by checking out IBM developerWorks' Eclipse project resources.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
For an introduction to the Eclipse platform, see "Getting started with the Eclipse Platform."
-
Stay current with developerWorks' Technical events and webcasts.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
Get products and technologies
-
Visit Eclipse.org to download Eclipse.
-
Find more plug-ins for Eclipse at Eclipse Plug-in Central.
-
Check out the latest Eclipse technology downloads at IBM alphaWorks.
-
Download IBM product evaluation versions, and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
-
Innovate your next open source development project with IBM trial software, available for download or on DVD.
Discuss
-
The Eclipse Platform newsgroups should be your first stop to discuss questions regarding Eclipse. (Selecting this will launch your default Usenet news reader application and open eclipse.platform.)
-
The Eclipse newsgroups has many resources for people interested in using and extending Eclipse.
-
Participate in developerWorks blogs and get involved in the developerWorks community.
Nathan A. Good lives in the Twin Cities area in Minnesota. Professionally, he does software development, software architecture, and systems administration. When he's not writing software, he enjoys building PCs and servers, reading about and working with new technologies, and trying to get his friends to make the move to open source software. He's written and co-written many books and articles, including Professional Red Hat Enterprise Linux 3, Regular Expression Recipes: A Problem-Solution Approach, Regular Expression Recipes for Windows Developers: A Problem-Solution Approach, PHP 5 Recipes: A Problem-Solution Approach, and his latest: Foundations of PEAR: Rapid PHP Development.



