`
alex8946
  • 浏览: 359106 次
  • 性别: 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_Swt_Jface_核心应用_部分19

    1.3 Eclipse的诞生 3 1.4 Eclipse贡献SWT工具包 5 1.4.1 SWT的结构 6 1.4.2 SWT所支持的操作系统 6 1.5 Sun AWT/Swing与Eclipse SWT 7 1.5.1 Swing与SWT的比较 7 1.5.2 SWT的优势和不足 8 1.6 SWT与...

    Eclipse RCP详细教程

    9.3.用户自定义对话框 74 9.3.1.概述 74 9.3.2.创建工程 74 9.3.3.声明action 74 9.3.4.声明action 74 9.3.5.创建对话框 75 10.向导(wizard) 77 10.1.概述 77 10.2.例子 77 11.首选项 85 ...

    org.eclipse.ponyatov:针对我的编程游戏的自定义Eclipse

    org.eclipse.ponyatov针对我的编程游戏的自定义Eclipse组件: “ Ponyatov”透视图(个人适应的工作区布局) Makefile的性质生成文件生成器设置了新的向导和新的Makefile项目必需的: Eclipse PDE经过测试: 面向C /...

    Eclipse RCP Plugin 开发自学入门指南(CEC首发)

    5.2 添加自定义视图 ......................................................................................................................... 47 5.3 向VIEW里添加ACTION ....................................

    anyedittools:Eclipse的“瑞士刀”

    AnyEdit提供了“导入/导出”工作集向导。 AnyEdit在编辑器和文件中的“ Compare With”和“ Replace With”菜单中添加了四个新的上下文菜单操作。 AnyEdit可以在编辑器中显示空格(制表符与空格),并且可以将...

    Eclipse RCP Plug-in开发自学教程(Eclipse3.6)

    5.2 添加自定义视图 .........................................................................................................................47 5.3 向VIEW里添加ACTION .....................................

    Eclipse Java Project Creation Customizer-开源

    这个eclipse插件和一个“项目创建向导”,用于使用一些自定义的预定义文件和文件夹(版本0.0.1的文件夹)来创建Java项目。

    FLEX从入门到精通.pdf

     5.2.7 创建自定义透视图  5.3 FlexBuilder3中的新内容  5.3.1 新向导  5.3.2 设训,模式的增强  5.3.3 开发功能的增强  5.3.4 CS3套件的整合  5.4 本章小结 第Ⅱ部分 使用FIex3开发应用程序  第6章...

    GrammarWithEntityAndAnyValue

    如果还没有Eclipse Application启动配置,请创建一个并启动它 使用“常规”&gt;“现有项目”将其导入到Workspace向导中,但仅选择no.hal.bdd.xtext.examples项目 在src文件夹中的no.hal.bdd.xtext.examples包中

    java技术员必下的最强大的插件,搜索JSmart_1.3.1.jar(寻觅插件)然后下载

    文件太大,无法上传,请联系... 2008年6月修正了生成自定义MVC框架时的包名不能自定义的bug,修正了MYECLIPSE5.5开发struts1.3的bug 自定义MVC框架时加入了批量生成功能,支持多选文件  加入了运行前的提示框

    java开发者必用的插件,搜索JSmart_1.3.1.jar(寻觅插件)然后下载

    文件太大,无法上传,请联系... 2008年6月修正了生成自定义MVC框架时的包名不能自定义的bug,修正了MYECLIPSE5.5开发struts1.3的bug 自定义MVC框架时加入了批量生成功能,支持多选文件  加入了运行前的提示框

    Code Blocks 8.02 带mingw

    对于追求完美的C++程序员,再也不必忍受Eclipse的缓慢或VS.NET的庞大体积及高昂的价格。Code::Blocks从一开始就以跨平台目标,无论是在Windows或是Linux系统下,您都可以使用到相同的开发界面环境,而不必为系统去...

    graphics-procedural-modeling

    Eclipse 插件,用于使用自定义脚本语言对程序图形纹理进行建模。 这个项目的目的是演示 XText 框架,它应该被视为一个实验项目。 插件特点: 基本的 XText 功能(语法着色、内容辅助、大纲等) 打字时更新渲染...

    Maven权威指南 很精典的学习教程,比ANT更好用

    创建Simple Weather项目 4.4. 定制项目信息 4.5. 添加新的依赖 4.6. Simple Weather源码 4.7. 添加资源 4.8. 运行Simple Weather项目 4.8.1. Maven Exec 插件 4.8.2. 浏览你的项目依赖 4.9. 编写单元测试...

    Neuro4jWorkflows:开源轻量级java工作流引擎-开源

    使用特殊的向导,您可以生成新的流程、新的自定义块或 Web 应用程序。 从 GitHub (https://github.com/neuro4j) 下载源代码 Studio 可以从 (http://neuro4j.org/f/welcome-downloads) 下载。如何开始? 阅读如何...

    Code Blocks

    对于追求完美的C++程序员,再也不必忍受Eclipse的缓慢,再也不必忍受VS.NET的庞大和高昂的价格。  虽然Code::Blocks从一开始就追求跨平台目标,但是最初的开发重点是Windows系统下的版本,从06年3月21日版本:1.0 ...

    Code::Blocks 8.02 GCC 4.40 汉化包

    对于追求完美的C++程序员,再也不必忍受Eclipse的缓慢,再也不必忍受VS.NET的庞大和高昂的价格。  虽然Code::Blocks从一开始就追求跨平台目标,但是最初的开发重点是Windows 系统下的版本,从06年3月21日版本:1.0 ...

    RSS Owl | RSS / RDF / Atom Feed Reader:RSS Owl是一个功能强大的应用程序,用于组织,搜索和阅读提要。-开源

    其中一些独特的亮点是选项卡式阅读,可以保存的强大搜索功能,具有自动操作的新闻过滤器,嵌入式浏览器和报纸布局,进纸匣通知,清理向导以及强大的用户自定义设置。 RSSOwl无需安装,只需双击就可以启动它,并找到...

Global site tag (gtag.js) - Google Analytics