`
alex8946
  • 浏览: 366527 次
  • 性别: Icon_minigender_1
  • 来自: 广东广州
社区版块
存档分类
最新评论

如何创建 Eclipse 自定义向导

阅读更多
Eclipse 框架和集成开发环境 (IDE) 的最优秀特性之一是可扩展性。在本文中,您将了解如何快速构建用于自动完成添加新文件过程的向导。由于可以预定义文件的内容,因此向导通过提供一致性和自动化使您可以更好地进行开发。

入门

本文将演示如何使用向导把新文件添加到已有 Eclipse 项目中。当内置模板功能不足时,Eclipse 向导是定义可重复文件类型模板的优秀方法。阅读完本文后,您应当能够在 Eclipse 中实现自己的向导以创建新文件。

为了发挥本文的最大功效,您必须熟悉如何构建 Java™ 编程语言类,并且还应当熟悉继承和使用接口。您应当能够启动 Eclipse,但是本文假定您并不精通 Eclipse。

要运行本文的示例,需要:

Eclipse V3.2 或更高版本
虽然您可能在使用早期版本时获得了一些成功,但是本文中的代码都是使用 Eclipse V3.2.2 测试的,这是撰写本文时的最新官方发行版。
IBMSun JDK V1.5 或更高版本
本文是在 Mac OS® X V10.4.8 上使用 Java V1.5.0_07 创建的。您使用哪个操作系统并不重要,但是计算机上安装的 Java 环境的版本十分重要。建议使用 Java 5 版。

Eclipse 向导概览

我喜欢 Eclipse IDE 的很多特性。其中之一就是可扩展性。通过添加提供功能的插件(包括自动创建类、接口、项目、其他资源的向导),可以轻松地自定义 IDE。此特性对于大型企业来说非常重要,在这类企业中可以基于 Eclipse IDE 流线化地构建和分发插件,使许多人都可以自动利用功能。

在任何企业中,让团队以相同的样式构建应用程序可以提供许多优点。如果应用程序是以一致的样式构建的,则应用程序将更易于维护。一致性可以帮助减少错误。此外,由于应用程序是以相同的样式构建的,因此团队成员可以更轻松地完成一个又一个项目。

能够为 Eclipse 框架创建自定义向导使企业可以构建企业专有的向导,这些向导将给团队提供使用一致的最佳实践创建应用程序的机会。





回页首


创建新向导

本文中构建的自定义向导位于 Eclipse 的插件项目中。开始创建自定义向导十分简单,这要感谢为您开始编写代码提供前期帮助的其他向导。在接下来的步骤中,将使用 Plug-in Project 向导创建插件的开头部分。

要构建新插件项目,请执行以下步骤:

  1. 选择 File > New > Project 以选择项目向导。
  2. Plug-in Development 下选择 Plug-in Project,如下所示:
  3. 单击 Next 前进到下一步。

    图 1. 选择插件项目
    选择插件项目

  4. 添加项目名称(在本文中,该名称为 ExampleWizard(不是那么有创造性),如图 2 所示)。如果没有特殊原因,请使用默认位置。单击 Next

    图 2. 新插件项目
    新插件项目

  5. Plug-in Version 中输入版本号,并添加插件名称和插件提供者的名称(可能是您,也可能是协作的团队)。请一定要更新 Activator 的包名称,它默认为小写版本的项目名称。最好使用符合您公司标准的包名称:例如 com.example.eclipse.wizards。当您填写完下图中所示的信息后,单击 Next

    图 3. 选择插件项目
    选择插件项目

  6. 选择 Custom plug-in wizard,因为使用此选项可以让您对所包括的组件进行微调。如果您以前从未创建过新插件项目,那么现在最好看看其他模板的描述以了解可用信息。单击 Next
  7. 在 Template Selection 窗口中,单击 Deselect All 取消选中所有选项。然后,选择 New File Wizard,如下所示。单击 Next

    图 4. 选择模板
    选择模板

  8. Eclipse 向导将给您提示一些关于正在创建的新向导的信息(参见图 5)。确保更新包名称,理想情况下将其更新为 Activator 所使用的相同名称 (com.example.eclipse.wizards)。将 Wizard Category Name 更新为新向导文件夹的名称。该值的使用方法与 图 1 中的 Plug-in Development 类别相同。Wizard Class Name 是从 Wizard 继承的类的 Java 类名,该类将实现 INewWizard 接口。Wizard Page Class Name 将扩展 WizardPage 类。

    图 5. New Wizard Options 窗口
    New Wizard Options 窗口

  9. 单击 Finish。Eclipse 将给新项目添加必要的类和库。

虽然还没完成,但是已经有了很好的开端并且准备好开始在向导背后添加一些实现。





回页首


Wizard 类和 INewWizard 接口

现在项目中有三个类:NewXHTMLFileWizardNewXHTMLFileWizardPageActivator。下面的部分将处理 NewXHTMLFileWizard 类。该类如清单 1 所示,不过没有显示方法中的所有代码。


清单 1. NewXHTMLFileWizard 类
                
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...
    }
}

实现 INewWizard 接口必须使用最后一个方法 init()。接下来,本文将介绍此方法以及此模板中自动包括的其余方法。

addPages() 方法

addPages() 方法将把页面添加到向导中。清单 2 中所示的方法将把单个页面添加到向导 NewXHTMLFileWizardPage 中。


清单 2. addPages() 方法将把页面添加到向导中
                
    /**
     * Adding the page to the wizard.
     */

    public void addPages() {
        page = new NewXHTMLFileWizardPage(selection);
        // You can add more pages here...
        addPage(page);
    }

NewXHTMLFileWizardPage 类包含为用户提供指定页面名称功能的控件。您可以稍后把控件添加到页面中,使最终用户可以输入更多信息。

performFinish() 方法

当用户单击向导中的 Finish 按钮时将调用 performFinish() 方法。在执行一些检查之后,它将使用 IRunnableWithProgress 接口调用 doFinish() 方法。使用此接口意味着在执行 doFinish() 方法时(在本例中需要花很长时间运行)不必编写显示进度条的所有 UI 元素。下面完整地列出了该方法。


清单 3. performFinish() 方法
                
    /**
     * 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;
    }

doFinish() 方法

如下所示,doFinish() 方法将创建新文件并通过 IDE 中的编辑器打开新文件。将调用 openContentStream() 方法以获得给新文件填充内容的输入流。


清单 4. 初始的 doFinish() 方法
                
    /**
     * 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);
    }

openContentStream() 方法

如下所示,openContentStream() 方法将返回包含生成的静态字符串作为模板一部分的 ByteArrayInputStream。对于本文,字符串将被替换为模板文件的内容。

此方法中的代码是首先必须更改的,这样才能在创建时允许把更多有用的内容添加到新文件中。


清单 5. openContentStream() 方法
                
    /**
     * 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());
    }





回页首


添加基本内容

新文件的内容不使用静态字符串值,您可以使用 getResourceAsStream() 方法把文件的内容载入到 InputStream 中,并且 doFinish() 方法可以用它来填充新文件。请做出如下所示的修改。


清单 6. 从资源获得输入流
                
    /**
     * Initialize the file contents to contents of the 
     * given resource.
     */
    private InputStream openContentStream() {
        return this.getClass()
                    .getResourceAsStream("templates/index-xhtml-template.resource");
    }

index-xhtml-template.resource 文件中是有效的可扩展超文本标记语言(Extensible Hypertext Markup Language,XHTML)V1.0 Strict Web 页面。它有针对一组模拟企业样式表和 JavaScript 文件的一些基本标记和点。该文件列于清单 7 中。此文件与 NewXHTMLFileWizard 类在同一个包中,因此在本文中此文件位于 com.example.eclipse.wizards 包中。如果需要将文件放在其他包中,则可以像访问目录内的文件一样访问它(即,com.example.resources/com/example/resources)。


清单 7. index-xhtml-template.resource 文件
                
<!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>

现在,您可以运行 Eclipse 插件来查看该插件的内容。





回页首


测试新向导

在 Eclipse 创建向导所使用的三个类之后,您可以在阅读本文的过程中随时启动另一个 Eclipse 实例来运行和测试插件。要启动插件项目,请在项目上右击,并选择 Run As > Eclipse Application,如图 6 所示。Eclipse 的新实例将启动。


图 6. 将项目作为 Eclipse 应用程序来运行
将项目作为 Eclipse 应用程序来运行

现在需要创建包含新文件的临时项目。项目的名称无关紧要 —— 诸如 “temp” 之类的名称即可。当新项目已在工作区中后,请通过选择 File > New > Other 来添加新模板。

如果一切按预期运行正常,则新类别将列于您为向导定义的类别下的 Select a Wizard 窗口中。我使用了 Example.com 企业模板,如下所示:


图 7. 使用新模板
使用新模板

当您完成向导的其余部分后,Eclipse 将创建包含定义内容的新文件。如果此简单功能就是模板所需的全部功能,则可以止于此处。但是,可能还需要提示用户提供一些用来整合文件内容的输入。





回页首


自定义向导页面

初始 NewXHTMLFileWizardPage 的表单中只有两个控件:一个用于容器(项目或文件夹)的名称,而另一个用于创建新文件时使用的名称。createControl() 方法(完整的方法代码如清单 8 所示)负责创建这些控件和将其添加到对话框中。


清单 8. createControl() 方法
                
    /**
     * @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);
    }

必须先在文件的顶部声明新控件和其他控件,然后才可以将新控件添加到此方法中。


清单 9. 声明新控件
                
public class NewXHTMLFileWizardPage extends WizardPage {
 /* Newly added for the page title */
    private Text titleText;

    // the rest of the class...	
}

现在添加文本的 getter。NewXHTMLFileWizard 类将在构建新文件时使用此 getter。getTitle() 方法如下所示:


清单 10. getTitle() 方法
                
    /**
     * Gets the HTML title for the new file
     */
    public String getTitle() {
        return titleText.getText();
    }

修改 createControl() 方法以添加标题的新输入控件和标签。新代码如下所示:


清单 11. 修改后的 createControl() 方法
                
    /**
     * @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);
    }

在将代码添加到 NewXHTMLFileWizard 类中之前,请通过执行先前概述的步骤测试到目前为止的更改。





回页首


验证用户输入

用户在新向导的 Title 字段中输入的文本将用作新 HTML 文件的 <title> 元素中的文本。出于本文的目的,该值是必需的,因此需要添加检查输入的代码以确保用户输入的内容是没有问题的。

dialogChanged() 方法的修改如下所示:


清单 12. 检验 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);
    }

完成这些更改后,如果不输入 Title 值,向导将提供错误消息。此外,Finish 按钮将被禁用,直至为 Title 指定了值为止。通过使用先前概述的步骤运行插件项目来检验此功能。





回页首


添加自定义内容

向导页面 NewXHTMLFileWizardPage 现在将捕捉用户输入的 HTML 标题的值,但是它尚未把该值合并到文件中。要开始将该值添加到文件中,首先需要编辑 index-xhtml-template.resource 文件使其包含该值的占位符。您可以将 <title> 元素更改为 <title>${title}</title>,这样可以更轻松地包含占位符。

修改 performFinish() 方法以从向导页面获得标题并将标题传递给 doFinish() 方法以及其余值。


清单 13. 最终的 performFinish() 方法
                
    /**
     * 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;
    }

接下来,略微修改 doFinish() 方法使其接受标题作为参数并将其传递给 openContentStream() 方法。


清单 14. 接受标题作为参数的最终 doFinish() 方法
                
    /**
     * 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);
	}

最后,需要修改 openContentStream() 方法的很大一部分才能够将文件中的 $title 值替换为用户提供的值(参见清单 15)。在配有大量不同值的模板中,您可以使用更精确的解决方案,例如扩展 FilterInputStream 并替换一整组不同值的新类。


清单 15. 最终的 openContentStream() 方法
                
    /**
     * 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());
        

    }

openContentStream() 方法的功能现在不仅限于装入资源文件的内容和将其作为 InputStream 返回。新代码将迭代流,使用 InputStreamReader 读取流,并替换每行中的值 $title。结果将以 ByteArrayInputStream 的形式返回,这与首次生成 NewXHTMLFileWizard 类时使用的流对象相同。

线程访问

我开始编写的代码在单击向导中的 Finish 时生成了 Invalid thread access 错误。我直接通过 openContentStream() 方法访问 page.getTitle(),而那样做是无效的。该值必须由 NewXHTMLFileWizard 来确定并传给 performFinish() 方法中的 op 对象。





回页首


创建新项目向导

如果您从头阅读了文本,则应当有一个在已有项目中创建新文件的向导。但是为什么不到此为止呢?因为在诸如 XHTML 文件之类的资源中企业可能需要遵循一些约定,项目布局可能也有相应的约定。

通过向已有项目中添加相对较少的内容,就可以构建将整个项目连同文件夹和一些初始文件添加到工作区中的向导。向导将为 Web 站点 Example.com 创建新文件夹并创建 images 文件夹和 styles 文件夹。在 styles 文件夹中,向导将创建名为 site.css 的层叠样式表(Cascading Style Sheet,CSS)文件。向导最后通过重用 NewXHTMLFileWizard 类中的方法来添加新的 XHTML 文件,该文件的初始名称是新项目的名称附带一些新文本。





回页首


构建新 NewSiteProjectWizard

由于有一个插件项目已经设置并正在运行,因此无需使用向导构建新类。相反,您自己可以通过创建扩展自 Wizard 并且实现两个接口 INewWizardIExecutableExtension 的新类来构建新向导。

NewSiteProjectWizard 新类添加到 NewXHTMLFileWizard 类所在的包中。查看清单 16 中的 NewSiteProjectWizard 类声明,并确保扩展 Wizard 类。同时添加 INewWizardIExecutableExtension 接口。

由于 NewSiteProjectWizard 类扩展同一个类并且实现由 NewXHTMLFileWizard 类实现的一个接口,因此如果将两者相比较,您会发现共有方法。清单 16 中显示了 NewSiteProjectWizard,这里为了简短起见省略了方法的内容(您将稍后在本文中看到这些内容)。


清单 16. NewSiteProjectWizard 类
                
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
    }
}

修改 plugin.xml

在添加新类之后在 Eclipse 中将其作为向导执行之前,需要对位于项目库中的 plugin.xml 文件进行一些更改。


清单 17. plugin.xml 文件
                
<?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>

更改 plugin.xml 的目的是让 Eclipse 知道 NewSiteProjectWizard 类是可以由 Eclipse 调用的向导。它也被归到先前讨论的 NewXHTMLFileWizard 类所在的类别下。project="true" 属性告诉 Eclipse 它是一个项目,因此它将被显示在相应的上下文中。

addPages() 方法

Eclipse API 包括一些向导类和向导页面类,如果您要执行基本功能并且不需要进行定制,则这些类将十分有用。从技术上讲,NewSiteProjectWizard 可以扩展 BasicNewProjectResourceWizard —— 用于创建基本项目的已有项目向导 —— 虽然设计者在 JavaDoc 中注明不适于创建该类的子类。要获得基本的项目信息(如项目名称),可以使用 BasicNewProjectResourceWizard 所使用的同一个向导页面 —— WizardNewProjectCreationPage 类,如下所示:


清单 18. addPages() 方法
                
   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);
   }

此方法将创建页面类的新实例,设定描述和标题,然后将其添加为向导页面。

performFinish() 方法

NewXHTMLFileWizard 类一样,NewSiteProjectWizard 也有 performFinish() 方法(如清单 19 所示),该方法将在用户完成向导中的步骤并单击了 Finish 时执行。此方法将调用执行 createProject() 方法的过程,该方法将执行大部分繁琐的创建工作,如创建项目、文件夹和文件。


清单 19. performFinish() 方法
                
    @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;
    }

performFinish() 方法将在调用 createProject() 创建文件和文件夹之后调用两个静态方法更新当前透视图并选择在 IDE 中新创建的项目。

createProject() 方法

清单 20 中所示的 createProject() 方法将创建并打开新项目。然后,方法将向项目中添加两个新文件和两个新文件夹。文件都是由名为 addFileToProject() 的私有方法添加的,该方法的作用是保持 createProject() 相对整洁。


清单 20. createProject() 方法
                
    /**
     * 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();
        }
    }

addFileToProject() 方法

您可能会发现 addFileToProject() 方法中的大部分代码基本上与清单 14 中所示的 doFinish() 方法中代码相同。方法的签名差别很大:它已被修改为接受参数以使其在 createProject() 方法的上下文内具有更好的可重用性。


<
分享到:
评论

相关推荐

    eclipse 中自定义向导(wizard)的实现

    eclipse 中自定义向导(wizard)的实现 eclipse 中自定义向导(wizard)的实现是结合实际例子进行讲解的,通过设计和实现自定义向导来描述任务。下面是关于自定义向导的设计和实现的详细信息。 概述 ---- 自定义...

    Eclipse向导入门

    本文将引导你了解如何利用Eclipse的向导功能,以及如何创建自定义向导以适应特定需求。\n\n**Eclipse向导的核心价值**\n\nEclipse向导的灵活性和可扩展性是其核心优势。它们可以通过插件的形式被添加到Eclipse中,...

    Eclipse Java 向导界面设计与实现需求说明

    使用场景及目标:本设计适用于配置特定 Eclipse 插件用以创建不同类型的 C/C++工程时的自定义流程管理。 其它说明:注意由于 CDTCommonProjectWizard 类型继承限制带来的困难挑战,需要考虑采用其他替代解决方案去...

    Eclipse 向导设计模式

    ### Eclipse向导设计模式详解 #### 一、引言 在Eclipse开发环境中,向导是一种常见的用户交互机制,用于引导用户完成复杂任务的过程。Eclipse中的向导设计模式不仅提高了用户界面的友好性,还简化了开发人员的编码...

    自己动手写开发工具--基于eclipse的工具开发

    PDE为开发者提供了图形化的开发界面和构建工具,使得创建、调试和打包Eclipse插件变得相对简单。首先,你需要安装Eclipse IDE for RCP and RAP Developers版本,这个版本包含了PDE工具集。 二、插件项目创建 1. ...

    Eclipse代码生成器插件开发

    在本节中,我们探讨了Eclipse代码生成器插件开发的相关知识点,包括插件开发基本概念、Wizard向导制作、plugin.xml文件、Wizard类、新建项目向导和自定义Wizard页面等。这些知识点对于开发Eclipse插件非常重要,了解...

    eclipse、myeclipse经典主题配色theme.epf

    而"eclipse、myeclipse经典主题配色theme.epf"是一个专门针对这两款IDE的主题配置文件,它允许用户自定义编辑器的颜色方案,以提升编程时的视觉体验和舒适度。 首先,我们要理解EPF文件的含义。EPF是"Eclipse ...

    自定义eclipce扩展点代码实例

    在这个例子中,我们定义了一个名为`myCustomWizard`的扩展点,用于创建自定义向导。 接下来,我们需要为这个扩展点编写对应的XML Schema(schema/myCustomWizard.xsd),它定义了扩展点接受的数据结构和属性。这...

    2016-11-1 ---2017.... eclipse设置python库,设置背景颜色等

    Eclipse允许用户自定义编辑器的主题和颜色方案,以提高代码可读性和舒适度。这里我们可以使用提供的`.epf`文件(Eclipse Preferences File),如“Eclipse黑色背景配置-songqi330.epf”。 1. 导入`.epf`文件:在...

    JSP_tag.rar_Eclipse_eclipse jsp t_jsp custom tag_自定义标签

    首先,创建一个新的动态Web项目,然后通过Lomboz的"New &gt; JSP Tag Library"向导创建标签库。向导会生成一个默认的TLD文件,我们需要在这个文件中定义我们的自定义标签。例如,我们可以定义一个名为`&lt;hello&gt;`的标签,...

    使用 Eclipse 插件开发环境构建模板

    这个扩展点允许开发者贡献自定义向导,为PDE插件项目添加额外的内容。向导的实现基于用户在向导中的选择,使用参数化的模板来生成内容。例如,清单1展示了SimpleViewTemplateWizard类,它是创建简单视图向导的基础。...

    Eclipse环境下创建AVD的两种方式.pdf

    - 在执行此命令后,系统会询问是否创建自定义硬件配置。默认情况下,选择"No"将快速创建一个基本配置。如果选择"Yes",则可以进一步定制硬件参数,如屏幕分辨率、内存大小等。 - AVD的存储位置依赖于环境变量`...

    在Eclipse中创建Struts2项目

    回到项目创建向导,选择`Apache Struts`、`Dynamic Web Module`和`Java`作为项目特性。对于Struts版本,选择2.0(或更高版本),对于Java版本,选择5.0或以上。完成这些配置后,点击`Finish`按钮,项目即被创建。 #...

    Eclipse插件开发实例

    本资源主要讲解Eclipse插件开发的实例,包括代码生成器插件的开发和Wizard向导的制作。通过本资源,读者可以了解Eclipse插件开发的基本步骤和Wizard向导的制作过程。 Eclipse插件开发 Eclipse插件开发是指在...

    eclipse 创建maven项目 选择 archetypes所用到的jar包

    当在Eclipse中创建一个Maven项目时,为了快速启动开发,我们可以选择使用Maven Archetypes。Archetypes是预定义的项目模板,它们可以自动生成一个具有基本结构的项目,从而简化初始化过程。 **什么是Maven ...

    eclipse插件中编程创建一个java项目

    在这里,我们可以添加自定义的初始化逻辑,例如创建Java项目。 4. **创建Java项目**:使用`org.eclipse.core.resources.IProject`接口和`org.eclipse.jdt.core.IJavaProject`接口来创建Java项目。首先,创建一个...

    Eclipse创建web项目

    在安装向导中选择 “Eclipse IDE for Java EE Developers” 版本进行安装。点击 “Install” 按钮开始安装过程,此过程可能需要较长时间,请耐心等待。安装完成后,通过点击 “Launch” 启动 Eclipse。 #### 二、...

    rcp自己通过扩展点新建java项目

    1. **创建插件项目**:在Eclipse中,首先创建一个新的Eclipse插件项目,这将作为我们的自定义向导的基础。 2. **定义扩展点**:在`plugin.xml`文件中,我们需要声明对`org.eclipse.ui.newWizards`扩展点的使用。...

    eclipse linux32安装包

    - 自定义工作流:每个开发者都可以根据自己的喜好自定义Eclipse的布局、快捷键和首选项。 - 代码风格:Eclipse可以配置代码格式化规则,保证团队间的一致性。 总之,"eclipse linux32安装包"是一个专为32位Linux...

    Eclipse入门Eclipse的使用简介及插件开发.rar

    Eclipse基于OSGi框架,允许开发者创建和集成自定义功能。如果你想要扩展Eclipse的功能,可以创建一个插件项目。在"File" -&gt; "New" -&gt; "Other"中,选择"Eclipse Plug-in Project",按照向导完成配置。 在插件开发中...

Global site tag (gtag.js) - Google Analytics