站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Netbeans 专题NetBeans Copy Class Refactoring 模块教程

NetBeans Copy Class Refactoring 模块教程

摘要: NetBeans Copy Class Refactoring 模块教程 反馈 请注意, 并不是本教程中介绍的所有 API 都是当前最新的 。在您自己的磁盘上使用它们。不保证本教程中使用的 NetBe...
NetBeans Copy Class Refactoring 模块教程

反馈

请注意,并不是本教程中介绍的所有 API 都是当前最新的。在您自己的磁盘上使用它们。不保证本教程中使用的 NetBeans 与最终版本或下一个版本的 NetBeans 兼容。

重构是使用一些小变换重新设计代码,但不会改变任何程序行为。就像您构造一个表达式使它更容易理解和修改一样,您重构代码可以使其更容易读,更容易理解,更快更新。就像重构的表达式必须产生相同的效果一样,重构的程序必须与原始源具有相同的功能。

重构代码的一些常见目的包括:

本教程向您介绍如何通过添加附加的重构功能即 Copy Class 来扩展 IDE。Copy Class 功能允许您向相同或不同的包复制类。当您复制类时,您可以更改名称,也可以更改构造方法的名称。如果该类被复制到一个新包,则自动更新包声明。

本教程将介绍以下主题:

安装完软件之后,本教程可以在 60 分钟之内完成。

有关创建 NetBeans 插件模块的更多信息,请参见 NetBeans 网站上的 NetBeans Development Project 主页。如果有问题,请访问 NetBeans Developer FAQ 或使用本页顶部的反馈链接。


了解示例

开始编写插件模块之前,必须确保您具备所有必要的软件。此外,您可能想在亲自构建之前使用示例。

安装软件

开始之前,需要在您的计算机上安装以下软件:

  • NetBeans IDE 5.0(下载
  • NetBeans IDE 5.0 源文件(下载
  • Java Standard Development Kit (JDK!") version 1.4.2(下载)或 5.0(下载

安装示例

采用以下步骤来安装示例:

  1. 解压缩附加的文件
  2. 在 IDE 中,选择 File > Open Project,然后浏览到包含已解压缩文件的文件夹。打开模块项目。其形式如下:

  3. 右键单击项目节点并在 Target Platform 中选择 Install/Reload。将打开目标平台并安装该模块。

介绍示例

  1. 创建或打开应用程序并确保它具有一个 Java 源文件。
  2. 选择该 Java 源文件并在 Refactor 窗口中,选择新的 Copy Class 菜单项,如下所示:

    将打开 Copy Class 对话框,如下所示:

    也可以使用 To Package 下拉菜单选择将 Java 源文件复制到的包。如果没有选择包,则将该文件复制到包含原始文件的包。

  3. 单击 Next。将打开 Refactoring 窗口,如下所示:

  4. 单击 Do Refactoring。该类将被复制到您指定的文件夹中。

现在您了解了 Copy Class Refactoring 插件模块的用户界面,让我们从头开始创建该模块。

设置模块项目

开始编写模块前,必须确保正确地设置了项目。

访问 NetBeans IDE 源

当您使 IDE 的源可用于 NetBeans Platform Manager 时,您可以从 Source Editor 中访问 IDE 的源文件和 Javadoc。这样便简化了插件模块的开发,原因是它使您能够非常快速地查找有关执行的类和方法的信息。整个教程中都提到源和 Javadoc,因此应该在进行下一步前先使它们可用。

  1. 如果您尚未这样做,请从此处下载源。
  2. 选择 Tools > NetBeans Platform Manager。
  3. 在 Sources 选项卡中,单击 Add ZIP/Folder,并浏览到包含 NetBeans IDE 源的 ZIP 文件,如下所示:

  4. 单击 Close。

创建模块项目

  1. 选择 File > New Project。在 Categories 下,选择 NetBeans Plug-in Modules。在 Projects 下,选择 Module Project 并单击 Next。
  2. 在 Name and Location 面板中,在 Project Name 中键入 CopyClassRefactoring。将 Project Location 更改为您计算机上的任何目录,如 c:\mymodules。选择 Standalone Module 单选按钮和 Set as Main Project 复选框。单击 Next。
  3. 在 Basic Module Configuration 面板中,将 Code Name Base 中的 yourorghere 替换为 netbeans.modules,于是全名为 org.netbeans.modules.copyclassrefactoring。在 Module Display Name 中填入 CopyClassRefactoring。留出本地化资源包和 XML 层的位置,以便将它们存储在具有名称 org.netbeans.modules.copyclassrefactoring 的包中。单击 Finish。

IDE 创建 CopyClassRefactoring 项目。该项目包含所有资源和项目元数据,如该项目的 Ant 构建脚本。该项目在 IDE 中打开。您可以在 Projects 窗口 (Ctrl-1) 中查看其逻辑结构,在 Files 窗口 (Ctrl-2) 中查看其文件结构。

指定模块的依存关系

您需要将几个属于 NetBeans API 的类设为子类。每个类都将被声明为 Module 依存关系。使用 Project Properties 对话框来执行此操作。

  1. 在 Projects 窗口中,右键单击 CopyClassRefactoring 项目节点并选择 Properties。在 Project Properties 对话框中单击 Libraries。
  2. 对于以下每个 API,单击“Add...”,从 Module 列表中选择名称,然后单击 OK 确认:

  3. 单击 OK,退出 Project Properties 对话框。
  4. 在 Projects 窗口中,展开 Important Files 节点,双击 Project Metadata 节点,并注意查看您选择的 API 是否按模块依存关系进行了声明:
  5. <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://www.netbeans.org/ns/project/1">
        <type>org.netbeans.modules.apisupport.project</type>
        <configuration>
            <data xmlns="http://www.netbeans.org/ns/nb-module-project/2">
                <code-name-base>org.netbeans.modules.refactoring.copyclass</code-name-base>
                <standalone/>
                <module-dependencies>
                    <dependency>
                        <code-name-base>javax.jmi.reflect</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <release-version>1</release-version>
                            <specification-version>1.6</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.netbeans.api.mdr</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <release-version>1</release-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.netbeans.jmi.javamodel</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <release-version>2</release-version>
                            <specification-version>1.19.0</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.netbeans.modules.java</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <release-version>1</release-version>
                            <specification-version>1.24.0.2.2.2</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.netbeans.modules.java.project</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <release-version>1</release-version>
                            <specification-version>1.7</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.netbeans.modules.javacore</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <release-version>1</release-version>
                            <specification-version>1.16.0.2.2</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.netbeans.modules.jmiutils</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <release-version>1</release-version>
                            <specification-version>1.4.0.2</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.netbeans.modules.projectapi</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <release-version>1</release-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.netbeans.modules.projectuiapi</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <release-version>1</release-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.netbeans.modules.refactoring</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <release-version>1</release-version>
                            <implementation-version/>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.openide.awt</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <specification-version>6.6</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.openide.filesystems</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <specification-version>6.2</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.openide.loaders</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <specification-version>5.4</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.openide.modules</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <specification-version>6.2</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.openide.nodes</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <specification-version>6.2</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.openide.text</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <specification-version>6.2</specification-version>
                        </run-dependency>
                    </dependency>
                    <dependency>
                        <code-name-base>org.openide.util</code-name-base>
                        <build-prerequisite/>
                        <compile-dependency/>
                        <run-dependency>
                            <specification-version>6.5</specification-version>
                        </run-dependency>
                    </dependency>
                </module-dependencies>
                <public-packages/>
            </data>
        </configuration>
    </project>

创建重构逻辑

重构的非可视部分执行重构逻辑。基本上它由两个类组成,即“refactoring”类和“refactoring plugin”类。

创建 Refactoring 类

refactoring 类作为用于调用重构的 API。此外,refactoring plugin 类使用它来确定重构参数。refactoring 类本身几乎不执行任何工作,所有工作都由 refactoring plugin 类来完成。refactoring 类通常只包含重构参数的获取方法和设置方法。参数为 newNametargetFoldertargetPackageName。它们在 Copy Class 对话框中使用:

执行以下操作:

  1. 创建文件。 右键单击 CopyClassRefactoring 项目节点,选择 New > Java Class,在 Class Name 中键入 CopyClassRefactoring,然后从 Package 下拉列表中选择 org.netbeans.modules.copyclassrefactoring。单击 Finish。新 Java 类将在 Source Editor 中打开。用以下内容替换默认代码:
    package org.netbeans.modules.refactoring.copyclass;
    
    import org.netbeans.jmi.javamodel.Resource;
    import org.netbeans.modules.refactoring.api.AbstractRefactoring;
    import org.netbeans.modules.refactoring.classpath.Util;
    import org.openide.filesystems.FileObject;
    
    public final class CopyClassRefactoring extends AbstractRefactoring {
       
        private Resource resource;
        private FileObject targetFolder;
        private String targetPackageName;
        private String newName;
       
        /** Creates a new instance of CopyClassRefactoring
         *
         */
        public CopyClassRefactoring(Resource resource) {
            this.resource = resource;
        }
       
        protected void setClassPath() {
            // leave the complete classpath
            Util.setDefaultClassPath();
        }
    
        public Resource getResource() {
            return resource;
        }
    
        // --- START PARAMETERS ----------------------------------------------------------
    
        public FileObject getTargetClassPathRoot() {
            return targetFolder;
        }
    
        public void setTargetClassPathRoot(FileObject targetFolder) {
            this.targetFolder = targetFolder;
        }
       
        public String getTargetPackageName() {
            return targetPackageName;
        }
       
        public void setTargetPackageName(String newName) {
            this.targetPackageName = newName;
        }
       
        public String getNewName() {
            return newName;
        }
       
        public void setNewName(String newName) {
            this.newName = newName;
        }
    
        // --- END PARAMETERS ----------------------------------------------------------
    
    }
  2. 读取源。 按下 Ctrl 键,将鼠标移动到 AbstractRefactoring 标识符上,并注意到出现一个超链接:

    单击该链接。AbstractRefactoring 类在 Source Editor 中打开。熟悉一下源文件并理解它如何与其 CopyClassRefactoring 实现相关。

创建 Refactoring Plugin 类

每个重构都应该至少有一个执行所有工作的插件类。重构模块本身应该提供进行 J2SE 重构的基本插件类。其他模块可以添加其他插件,这允许它们参与重构。例如,当重命名一个方法时,J2EE 插件类需要重命名 EJB(如有必要)的其他界面中的相关方法,并且更改部署描述符。下面介绍的插件类是执行所有基本 J2SE 工作的基本插件类。

插件类需要实现的四个方法为:

  • preCheck()。检查前提条件。
  • fastCheckParameters()。检查参数的有效性,只检查能快速执行且不需要复杂计算的检查。
  • checkParameters()。对所有 fastCheckParameters() 方法未涉及的其他参数进行有效性检查。
  • prepare()。负责创建重构所做更改的描述符(RefactoringElement 实现的实例)。实际上该方法负责告诉重构如何执行。通过创建 RefactoringElement 的特定实现的实例来实现,每个实例代表重构应该做的更改。因此,如果是 Rename Field 重构,则每次访问字段时进行的重命名将具有相应的 RefactoringElement,用来表示发生了一次重命名。因此,除了执行 prepare() 方法本身之外,您还需要创建将用于执行更改的 RefactoringElement 接口的实现。重构元素通常作为 SimpleRefactoringElementImpl 的子类被创建。以下是需要执行的方法:
    • performChange()。执行重构元素所代表的更改。
    • getText()。返回重构元素的文本。这通常包含重构元素将更改的代码片段或描述重构元素将执行功能的文本。
    • getDisplayText()。通常与 getText() 相同,但是可能包含 HTML 标记。例如,如果更改包含代码的返回行,则可以使用 HTML 标记来用粗体显示将要更改的代码片段。
    • getParentFile()。将受重构元素所代表的更改影响的文件。
    • getJavaElement()。与此更改相关的 Java 元素。
    • getPosition()。相关代码的文档位置。

    prepare() 方法实例化重构元素并将其添加到作为输出参数的 RefactoringElementsBag 的实例。

所有上面的方法都返回名为 Problem 的类的实例。该类的实例代表对于执行重构来说可能是致命的或非致命的问题。这些方法执行各种检查(如给定重构的规范中所述)并返回它们发现的问题作为提到的 Problem 类的实例。可以将这些问题链接起来(使用 Problem.setNext() 方法),这有可能返回单次操作中的几个问题。注意致命问题必须位于链首。使用 JavaRefactoringPlugin.createProblem() 方法创建问题将自动确保此要求。所有方法(除 fastCheckParameters() 之外)都应该激发进度事件,因为它们正在执行潜在的耗费时间的操作。

执行以下操作:

  1. 创建文件。 右键单击 CopyClassRefactoring 项目节点,选择 New > Java Class,在 Class Name 中键入 CopyClassRefactoringPlugin,然后从 Package 下拉列表中选择 org.netbeans.modules.copyclassrefactoring。单击 Finish。新 Java 类将在 Source Editor 中打开。用以下内容替换默认代码:
    package org.netbeans.modules.refactoring.copyclass;
    
    import java.io.IOException;
    import java.text.MessageFormat;
    import java.util.Iterator;
    import java.util.StringTokenizer;
    import org.netbeans.jmi.javamodel.Element;
    import org.netbeans.jmi.javamodel.Import;
    import org.netbeans.jmi.javamodel.ImportClass;
    import org.netbeans.jmi.javamodel.JavaClass;
    import org.netbeans.jmi.javamodel.JavaModelPackage;
    import org.netbeans.jmi.javamodel.Resource;
    import org.netbeans.modules.javacore.JMManager;
    import org.netbeans.modules.javacore.api.JavaModel;
    import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
    import org.netbeans.modules.javacore.internalapi.JavaModelUtil;
    import org.netbeans.modules.refactoring.CheckUtils;
    import org.netbeans.modules.refactoring.api.AbstractRefactoring;
    import org.netbeans.modules.refactoring.api.MoveClassRefactoring;
    import org.netbeans.modules.refactoring.api.Problem;
    import org.netbeans.modules.refactoring.api.RenameRefactoring;
    import org.netbeans.modules.refactoring.plugins.JavaRefactoringPlugin;
    import org.netbeans.modules.refactoring.plugins.MoveClassRefactoringPlugin;
    import org.netbeans.modules.refactoring.spi.SimpleRefactoringElementImpl;
    import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation;
    import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
    import org.openide.ErrorManager;
    import org.openide.filesystems.FileObject;
    import org.openide.filesystems.FileUtil;
    import org.openide.loaders.DataFolder;
    import org.openide.loaders.DataObject;
    import org.openide.text.PositionBounds;
    import org.openide.util.NbBundle;
    import org.openide.util.Utilities;
    
    /** Plugin that implements the core functionality of Copy Class Refactoring.
     */
    public class CopyClassRefactoringPlugin extends JavaRefactoringPlugin {
       
        /** Reference to the parent refactoring instance */
        private final CopyClassRefactoring refactoring;
       
        /** Creates a new instance of PullUpRefactoringPlugin
         * @param refactoring Parent refactoring instance.
         */
        CopyClassRefactoringPlugin(CopyClassRefactoring refactoring) {
            this.refactoring = refactoring;
        }
       
        /** Checks pre-conditions of the refactoring.
         * @return Problems found or null.
         */
        public Problem preCheck() {
            fireProgressListenerStart(AbstractRefactoring.PRE_CHECK, 4);
            try {
                Resource resource = refactoring.getResource();
               
                // check whether the element is valid
                Problem result = isElementAvail(resource);
                if (result != null) {
                    // fatal error -> don't continue with further checks
                    return result;
                }
               
                if (!CheckUtils.isElementInOpenProject(resource)) {
                    return new Problem(true, NbBundle.getMessage(JavaRefactoringPlugin.class, "ERR_ProjectNotOpened"));
                }
               
                // increase progress (step 1)
                fireProgressListenerStep();
               
                // increase progress (step 2)
                fireProgressListenerStep();
               
                // increase progress (step 3)
                fireProgressListenerStep();
               
                // all checks passed -> return null
                return null;
            } finally {
                // fire operation end on the registered progress listeners
                fireProgressListenerStop();
            }
        }
       
        public Problem fastCheckParameters() {
            if (!Utilities.isJavaIdentifier(refactoring.getNewName())) {
                String msg = new MessageFormat(NbBundle.getMessage(RenameRefactoring.class, "ERR_InvalidIdentifier")).format(
                    new Object[] {refactoring.getNewName()}
                );
                return createProblem(null, true, msg);
            }
            if (!isValidPackageName(refactoring.getTargetPackageName())) {
                String msg = new MessageFormat(NbBundle.getMessage(RenameRefactoring.class, "ERR_InvalidPackage")).format(
                    new Object[] {refactoring.getTargetPackageName()}
                );
                return createProblem(null, true, msg);
            }
            String name = refactoring.getTargetPackageName().replace('.','/') + '/' + refactoring.getNewName() + ".java";
            if (refactoring.getTargetClassPathRoot().getFileObject(name) != null)
        return createProblem(null, true,
    new MessageFormat(NbBundle.getMessage(MoveClassRefactoring.class,
    "ERR_ClassToMoveClashes")).format(new Object[]{refactoring.getNewName()}));
            return null;
        }
       
        private static boolean isValidPackageName(String name) {
            StringTokenizer tokenizer = new StringTokenizer(name, "."); // NOI18N
            while (tokenizer.hasMoreTokens()) {
                if (!Utilities.isJavaIdentifier(tokenizer.nextToken())) {
                    return false;
                }
            }
            return true;
        }
    
        public Problem checkParameters() {
            return null;
        }
    
        public Problem prepare(RefactoringElementsBag refactoringElements) {
            refactoringElements.add(refactoring,
                    new CopyClass(
                        refactoring.getResource(),
                        refactoring.getTargetClassPathRoot(),
                        refactoring.getTargetPackageName(),
                        refactoring.getNewName()
                    ));
            return null;
        }
    }
  2. 读取源。 按下 Ctrl 键,将鼠标移动到 JavaRefactoringPlugin 标识符上,并注意到出现一个超链接:

    单击该链接。JavaRefactoringPlugin 类在 Source Editor 中打开。熟悉一下源文件并理解它如何与其 CopyClassRefactoringPlugin 实现相关。

  3. 接下来,您创建将用来执行更改的 RefactoringElement 接口的实现。通过向上面文件末尾添加以下内部类来完成:
        private static class CopyClass extends SimpleRefactoringElementImpl implements RefactoringElementImplementation{
           
            private DataObject source;
            private FileObject targetRoot;
            private String targetPackageName;
            private String newName;
            private Resource resource;
           
            public CopyClass (Resource resource, FileObject targetRoot, String packageName, String newName) {
                this.source = ((JMManager) JMManager.getManager()).getDataObject(resource);
                this.resource = resource;
                this.targetRoot = targetRoot;
                this.targetPackageName = packageName;
                this.newName = newName;
            }
           
    public String getText() {
                return getDisplayText ();
            }
       
    public String getDisplayText() {
                return new MessageFormat (NbBundle.getMessage(CopyClassRefactoringPlugin.class, "TXT_CopyClassToPackage")).format ( // NOI18N
                    new Object[] {newName, targetPackageName, resource.getName()}
                );
            }
    
    public Element getJavaElement() {
                return null;
            }
    
    public PositionBounds getPosition() {
                return null;
            }
    
    public void performChange() {
                String nameAfterCopy = null;
                try {
                    FileObject fo = FileUtil.createFolder(targetRoot, targetPackageName.replace('.','/'));
                    DataFolder folder = DataFolder.findFolder(fo);
                    objectToDelete = source.copy(folder);
                    nameAfterCopy = objectToDelete.getName();
                    objectToDelete.rename(newName);
                } catch (IOException ioe) {
                    ErrorManager.getDefault().notify(ioe);
        }
                Resource r = JavaModel.getResource(objectToDelete.getPrimaryFile());
                String name = resource.getPackageName();
                ImportClass proxy = ((JavaModelPackage) r.refOutermostPackage()).getImport();
                Import addedImport = proxy.createImport(name, null, false, true);
                r.addImport(addedImport);
                for (Iterator i = r.getClassifiers().iterator(); i.hasNext(); ) {
                    JavaClass c = (JavaClass) i.next();
                    if (c.getSimpleName().equals(nameAfterCopy)) {
                        c.setSimpleName(newName);
                    }
                }
               
            }
    
            private DataObject objectToDelete = null;
    
    public FileObject getParentFile() {
                return source.getPrimaryFile();
            }
        }
  4. 读取源。 按下 Ctrl 键,将鼠标移动到 RefactoringElementImplementation 标识符上,并注意到出现一个超链接:

    单击该链接。RefactoringElementImplementation 类在 Source Editor 中打开。熟悉一下源文件并理解它如何与其内部 CopyClass 实现相关。

创建 Refactoring Plugin 工厂

插件类的实例化由插件工厂执行。通过重构调用调用的 META-INF/services 文件夹中的一个条目,插件工厂在 NetBeans 查询中注册。为了确保调用给定重构时实例化插件类,您需要在工厂的 createInstance() 方法的开始部分为您的插件类添加以下实例化代码:

if (refactoring instanceof YourRefactoring) {
     return new YourRefactoringPlugin((YourRefactoring) refactoring);
}

从上面可以看出,createInstance() 方法将父重构视为参数。每个插件类应该参考父重构以便能够从中获得重构参数。这就是插件类通常将重构作为构造方法参数的原因。

执行以下操作:

  1. 创建文件。 右键单击 Unit Test Packages 节点,选择 New > Java Class,在 Class Name 中键入 PluginsFactory,然后从 Package 下拉列表中选择 org.netbeans.modules.copyclassrefactoring。单击 Finish。新 Java 类将在 Source Editor 中打开。用以下内容替换默认代码:
    package org.netbeans.modules.refactoring.copyclass;
    
    import org.netbeans.modules.refactoring.api.AbstractRefactoring;
    import org.netbeans.modules.refactoring.spi.RefactoringPlugin;
    import org.netbeans.modules.refactoring.spi.RefactoringPluginFactory;
    
    public class PluginsFactory implements RefactoringPluginFactory {
        /** Factory method called by a refactoring. Creates and returns a new plugin
         * instance for a given refactoring. If no plugin for a given refactoring
         * is present, this method returns null.
         * @param refactoring Parent refactoring for which a plugin should be created.
         * @return New instance of a refactoring plugin for the provided refactoring
         * or null.
         */
        public RefactoringPlugin createInstance(AbstractRefactoring refactoring) {
            if (refactoring instanceof CopyClassRefactoring) {
                return new CopyClassRefactoringPlugin((CopyClassRefactoring) refactoring);
            }
            return null;
        }
    }
  2. 读取源。 按下 Ctrl 键,将鼠标移动到 RefactoringPluginFactory 标识符上,并注意到出现一个超链接:

    单击该链接。RefactoringPluginFactory 类在 Source Editor 中打开。熟悉一下源文件并理解它如何与其 PluginsFactory 实现相关。

  3. 在 NetBeans 查询中注册该工厂。 创建一个名为 META-INF/services 的文件夹。添加名为 org.netbeans.modules.refactoring.spi.RefactoringPluginFactory 的空文件,内容如下:
    org.netbeans.modules.refactoring.copyclass.PluginsFactory

此时,Projects 窗口应该如下所示:

创建用户界面

用户界面由三个组件组成:

创建 CopyClassAction

要实现重构操作,您需要创建 org.netbeans.modules.refactoring.spi.ui.AbstractRefactoringAction 的子类。感兴趣的部分是 enabled()createRefactoringUI() 方法:

  • enabled()。确定在 IDE 中基于当前活动的(选中的)节点应该启用操作的时间。通常情况下,该方法的实现不应该执行任何昂贵的操作,它最好不要接触 Java 元数据,完全由是否有 JavaDataObjects 位于选中的节点之后以及选中的节点数决定(当在 Pull Up 重构时某些操作可能同时适用于几个节点,您可以从中选择拉出几个成员,某些操作可能只能在一个节点上操作)。由于性能的原因,enable() 方法不能获得有关编辑器中 caret 位置的信息,这就是为什么应该减弱该方法中的检查。大部分其他检查应该在重构 preCheck() 方法中执行,该方法可以向用户提供一个描述性消息,说明为什么不在选定对象上执行重构的原因以及用户如何解决该问题。
  • createRefactoringUI()。用户调用操作时调用。方法接收活动的节点以及表示光标下文本的元素。基于这个原因,该方法应该构造重构应该执行的一组元素(或一个元素),并将其传递到应该从该方法中返回的重构 UI 对象的新实例中。

执行以下操作:

  1. 创建文件。 右键单击 CopyClassRefactoring 项目节点,选择 New > Java Class,在 Class Name 中键入 CopyClassAction,在 Package 中键入 org.netbeans.modules.copyclassrefactoring.options。单击 Finish。新 Java 类将在 Source Editor 中打开。用以下内容替换默认代码:
    package org.netbeans.modules.refactoring.copyclass.ui;
    
    import org.netbeans.jmi.javamodel.Element;
    import org.netbeans.modules.java.JavaDataObject;
    import org.netbeans.modules.javacore.JMManager;
    import org.netbeans.modules.javacore.api.JavaModel;
    import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
    import org.netbeans.modules.refactoring.spi.ui.AbstractRefactoringAction;
    import org.netbeans.modules.refactoring.spi.ui.RefactoringUI;
    import org.openide.loaders.DataObject;
    import org.openide.nodes.Node;
    import org.openide.util.NbBundle;
    
    public class CopyClassAction extends AbstractRefactoringAction {
       
        /** Creates a new instance of PullUpAction
         */
        public CopyClassAction() {
            super(NbBundle.getMessage(CopyClassAction.class, "LBL_CopyClass_Action"), null); // NOI18N
            putValue("noIconInMenu", Boolean.TRUE); // NOI18N
        }
       
        /** Method responsible for creating RefactoringUI object.
         * @param nodes Active nodes to perform the refactoring on.
         * @param selectedElement Element to perform the refactoring on or null if the action
         *      was not invoked from the editor - in that case the active nodes take
         *      the precedence.
         * @return RefactoringUI object for Copy Class refactoring.
         */
        protected RefactoringUI createRefactoringUI(Node[] nodes, Element selectedElement) {
    
    if (selectedElement == null) {
                // selected element is null -> action was invoked on nodes
                JavaDataObject ob = (JavaDataObject) nodes[0].getCookie(JavaDataObject.class);
               
                selectedElement =  JavaModel.getResource(ob.getPrimaryFile());
            }
            return new CopyClassRefactoringUI(selectedElement.getResource());
        }
       
        /** Method that determines whether this action is enabled for the active nodes.
         * @param activatedNodes Active nodes.
         * @return Boolean indicating whether the action is enabled.
         */
        protected boolean enabled(Node[] activatedNodes) {
          
    // if no nodes are active, the action should be disabled
            if (activatedNodes.length != 1) return false;
           
            // the action should be enabled only if all selected nodes are associated
            // with the same JavaDataObject (i.e. they are all declared in the same Java file)
            // so, let's get dataobject from the first activated node
            DataObject dobj = (DataObject) activatedNodes[0].getCookie(DataObject.class);
    
            // check if the dataobject is instance of JavaDataObejct and that it represents a file
            // that is on the IDE classpath (belongs to one of open projects)
            if ((dobj instanceof JavaDataObject) && ((JMManager) JavaMetamodel.getManager()).mergedCPContains(dobj.getPrimaryFile())) {
                return true;
            } else {
                return false;
    }
    
        }
       
        protected String iconResource () {
            return "org/netbeans/modules/refactoring/resources/refactoring.gif"; // NOI18N
        }
    }
  2. 读取源。 按下 Ctrl 键,将鼠标移动到 AbstractRefactoringAction 标识符上,并注意到出现一个超链接:

    单击该链接。AbstractRefactoringAction 类在 Source Editor 中打开。熟悉一下源文件并理解它如何与其 CopyClassAction 实现相关。

创建 CopyClassRefactoringUI

要将该模块插入到重构框架中,您需要创建 org.netbeans.modules.refactoring.spi.ui.RefactoringUI 的子类。感兴趣的部分是 getPanel()checkParameters() 方法:

  • getPanel()。返回包含重构参数的输入字段的特定于重构的面板。该方法由负责显示重构参数对话框的 ParametersPanel 调用。从该方法中返回的面板的名称将用作对话框的名称。该面板可以使用传递 ParametersPanelsetPreviewEnabled 方法来启用和禁用重构参数对话框的 Preview 按钮。
  • checkParameters()。检查用户在重构参数面板中输入的参数并设置值。

执行以下操作:

  1. 创建文件。 创建 CopyClassRefactoringUI 文件并将其添加到 org.netbeans.modules.copyclassrefactoring.options 包中。用以下内容替换默认代码:
    package org.netbeans.modules.refactoring.copyclass.ui;
    
    import org.netbeans.jmi.javamodel.JavaClass;
    import org.netbeans.jmi.javamodel.Resource;
    import org.netbeans.modules.javacore.api.JavaModel;
    import org.netbeans.modules.refactoring.api.AbstractRefactoring;
    import org.netbeans.modules.refactoring.api.Problem;
    import org.netbeans.modules.refactoring.copyclass.CopyClassRefactoring;
    import org.netbeans.modules.refactoring.spi.ui.CustomRefactoringPanel;
    import org.netbeans.modules.refactoring.spi.ui.ParametersPanel;
    import org.netbeans.modules.refactoring.spi.ui.RefactoringUI;
    import org.netbeans.modules.refactoring.ui.PullUpRefactoringUI;
    import org.openide.util.HelpCtx;
    import org.openide.util.NbBundle;
    
    public class CopyClassRefactoringUI implements RefactoringUI {
        // reference to pull up refactoring this UI object corresponds to
        private final CopyClassRefactoring refactoring;
        // UI panel for collecting parameters
        private CopyClassPanel panel;
       
        public CopyClassRefactoringUI(Resource resource) {
            refactoring = new CopyClassRefactoring(resource);
        }
       
        // --- IMPLEMENTATION OF RefactoringUI INTERFACE ---------------------------
       
        public boolean isQuery() {
            return false;
        }
    
        public CustomRefactoringPanel getPanel(ParametersPanel parent) {
            if (panel == null) {
        panel = new CopyClassPanel(parent, getName()
    + " - " + ((JavaClass) refactoring.getResource().getClassifiers().get(0)).getName(),
    refactoring.getResource().getPackageName(), JavaModel.getFileObject(refactoring.getResource()));
            }
            return panel;
        }
    
        public Problem setParameters() {
            setupRefactoring();
            return refactoring.checkParameters();
        }
       
        public Problem checkParameters() {
            if (panel==null)
                return null;
            setupRefactoring();
            return refactoring.fastCheckParameters();
        }
       
        private void setupRefactoring() {
            refactoring.setTargetClassPathRoot(panel.getRootFolder());
            refactoring.setTargetPackageName(panel.getPackageName().replace('/',  '.'));
            refactoring.setNewName(panel.getNewName());
        }
    
        public AbstractRefactoring getRefactoring() {
            return refactoring;
        }
    
        public String getDescription() {
            return NbBundle.getMessage(CopyClassAction.class, "DSC_CopyClass", refactoring.getNewName()); // NOI18N
        }
    
        public String getName() {
            return NbBundle.getMessage(CopyClassAction.class, "LBL_CopyClass"); // NOI18N
        }
    
        public boolean hasParameters() {
            return true;
        }
    
        public HelpCtx getHelpCtx() {
            return new HelpCtx(PullUpRefactoringUI.class.getName());
        }
    }
  2. 读取源。 按下 Ctrl 键,将鼠标移动到 RefactoringUI 标识符上,并注意到出现一个超链接:

    单击该链接。RefactoringUI 类在 Source Editor 中打开。熟悉一下源文件并理解它如何与其内部 CopyClassRefactoringUI 实现相关。

创建 CopyClassPanel

执行以下操作:

  1. 创建文件。 创建 CopyClassPanel JPanel 并将其添加到 org.netbeans.modules.copyclassrefactoring.ui 包中。
  2. 设计面板。 向 JPanel 中添加一个 JTextfield、三个 JComboBoxes 和四个 JLabel,如下所示:

  3. 添加代码。在 Source 视图中,用以下内容替换默认的代码:
    package org.netbeans.modules.refactoring.copyclass.ui;
    
    import java.awt.Component;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.File;
    import javax.swing.DefaultComboBoxModel;
    import javax.swing.DefaultListCellRenderer;
    import javax.swing.JList;
    import javax.swing.JTextField;
    import javax.swing.ListCellRenderer;
    import javax.swing.event.DocumentEvent;
    import javax.swing.event.DocumentListener;
    import org.netbeans.api.java.project.JavaProjectConstants;
    import org.netbeans.api.project.FileOwnerQuery;
    import org.netbeans.api.project.Project;
    import org.netbeans.api.project.ProjectInformation;
    import org.netbeans.api.project.ProjectUtils;
    import org.netbeans.api.project.SourceGroup;
    import org.netbeans.api.project.Sources;
    import org.netbeans.api.project.ui.OpenProjects;
    import org.netbeans.modules.refactoring.spi.ui.CustomRefactoringPanel;
    import org.netbeans.modules.refactoring.spi.ui.ParametersPanel;
    import org.netbeans.spi.java.project.support.ui.PackageView;
    import org.openide.filesystems.FileObject;
    import org.openide.filesystems.FileUtil;
    
    public class CopyClassPanel extends CustomRefactoringPanel implements ActionListener, DocumentListener {
     
        private static final ListCellRenderer GROUP_CELL_RENDERER = new GroupCellRenderer();
        private static final ListCellRenderer PROJECT_CELL_RENDERER = new ProjectCellRenderer();
       
        private Project project;
        private ParametersPanel parent;
        private FileObject fo;
        private SourceGroup[] groups;
       
        public CopyClassPanel(final ParametersPanel parent, String title, String startPackage, FileObject f) {
            setName(title);
            this.fo = f;
            this.parent = parent;
            initComponents();
            setCombosEnabled(true);
            setThisClassVisible(true);
           
            rootComboBox.setRenderer(GROUP_CELL_RENDERER);
            packageComboBox.setRenderer(PackageView.listRenderer());
            projectsComboBox.setRenderer( PROJECT_CELL_RENDERER );
                   
            rootComboBox.addActionListener( this );
            packageComboBox.addActionListener( this );
            projectsComboBox.addActionListener( this );
           
            Object textField = packageComboBox.getEditor().getEditorComponent();
            if (textField instanceof JTextField) {
                ((JTextField) textField).getDocument().addDocumentListener(this);
            }
            newNameTextField.getDocument().addDocumentListener(this);
            newNameTextField.setSelectionStart(0);
            newNameTextField.setSelectionEnd(newNameTextField.getText().length());
           
            project = fo != null ? FileOwnerQuery.getOwner(fo):OpenProjects.getDefault().getOpenProjects()[0];
            initValues(startPackage);
        }
       
        private boolean initialized = false;
        public void initialize() {
            if (initialized)
                return ;
            //put initialization code here
            initialized = true;
        }
       
        public void initValues(String preselectedFolder ) {
           
            Project openProjects[] = OpenProjects.getDefault().getOpenProjects();
            DefaultComboBoxModel projectsModel = new DefaultComboBoxModel( openProjects );
            projectsComboBox.setModel( projectsModel );               
            projectsComboBox.setSelectedItem( project );
           
            updateRoots();
            updatePackages();
            if (preselectedFolder != null) {
                packageComboBox.setSelectedItem(preselectedFolder);
            }
            // Determine the extension
        }
       
        public void requestFocus() {
            newNameTextField.requestFocus();
        }
       
        public FileObject getRootFolder() {
            return ((SourceGroup) rootComboBox.getSelectedItem()).getRootFolder();
        }
       
        public String getPackageName() {
            String packageName = packageComboBox.getEditor().getItem().toString();
            return packageName.replace('.', '/'); // NOI18N
        }
       
        private void fireChange() {
            parent.stateChanged(null);
        }
       
        /** This method is called from within the constructor to
         * initialize the form.
         * WARNING: Do NOT modify this code. The content of this method is
         * always regenerated by the Form Editor.
         */
        //                          
        private void initComponents() {
            java.awt.GridBagConstraints gridBagConstraints;
    
            labelProject = new javax.swing.JLabel();
            projectsComboBox = new javax.swing.JComboBox();
            labelLocation = new javax.swing.JLabel();
            rootComboBox = new javax.swing.JComboBox();
            labelPackage = new javax.swing.JLabel();
            packageComboBox = new javax.swing.JComboBox();
            bottomPanel = new javax.swing.JPanel();
            newNameLabel = new javax.swing.JLabel();
            newNameTextField = new javax.swing.JTextField();
    
            setLayout(new java.awt.GridBagLayout());
    
            labelProject.setLabelFor(projectsComboBox);
    org.openide.awt.Mnemonics.setLocalizedText(labelProject,
    org.openide.util.NbBundle.getMessage(CopyClassPanel.class, "LBL_Project"));
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 2;
            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
            gridBagConstraints.insets = new java.awt.Insets(0, 0, 6, 0);
            add(labelProject, gridBagConstraints);
    
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 1;
            gridBagConstraints.gridy = 2;
            gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
            gridBagConstraints.weightx = 1.0;
            gridBagConstraints.insets = new java.awt.Insets(0, 6, 6, 0);
            add(projectsComboBox, gridBagConstraints);
    projectsComboBox.getAccessibleContext().setAccessibleDescription
    (java.util.ResourceBundle.getBundle("org/netbeans/modules/refactoring/ui/Bundle").
    getString("ACSD_projectsCombo"));
            labelLocation.setLabelFor(rootComboBox);
    org.openide.awt.Mnemonics.setLocalizedText(labelLocation,
    org.openide.util.NbBundle.getMessage(CopyClassPanel.class, "LBL_Location"));
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 3;
            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
            gridBagConstraints.insets = new java.awt.Insets(0, 0, 6, 0);
            add(labelLocation, gridBagConstraints);
    
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 1;
            gridBagConstraints.gridy = 3;
            gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
            gridBagConstraints.weightx = 1.0;
            gridBagConstraints.insets = new java.awt.Insets(0, 6, 6, 0);
            add(rootComboBox, gridBagConstraints);
    rootComboBox.getAccessibleContext().setAccessibleDescription
    (java.util.ResourceBundle.getBundle("org/netbeans/modules/refactoring/ui/Bundle").getString("ACSD_rootCombo"));
            labelPackage.setLabelFor(packageComboBox);
    org.openide.awt.Mnemonics.setLocalizedText(labelPackage,
    org.openide.util.NbBundle.getMessage(CopyClassPanel.class, "LBL_ToPackage"));
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 4;
            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
            gridBagConstraints.insets = new java.awt.Insets(0, 0, 12, 0);
            add(labelPackage, gridBagConstraints);
    
            packageComboBox.setEditable(true);
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 1;
            gridBagConstraints.gridy = 4;
            gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
            gridBagConstraints.weightx = 1.0;
            gridBagConstraints.insets = new java.awt.Insets(0, 6, 12, 0);
            add(packageComboBox, gridBagConstraints);
    
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 5;
            gridBagConstraints.gridwidth = 2;
            gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
            gridBagConstraints.weightx = 1.0;
            gridBagConstraints.weighty = 1.0;
            add(bottomPanel, gridBagConstraints);
    
    org.openide.awt.Mnemonics.setLocalizedText(newNameLabel,
    org.openide.util.NbBundle.getMessage(CopyClassPanel.class, "LBL_NewName"));
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 1;
            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
            gridBagConstraints.insets = new java.awt.Insets(0, 0, 6, 0);
            add(newNameLabel, gridBagConstraints);
    
            newNameTextField.setText("NewClass");
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 1;
            gridBagConstraints.gridy = 1;
            gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
            gridBagConstraints.weightx = 1.0;
            gridBagConstraints.insets = new java.awt.Insets(0, 6, 6, 0);
            add(newNameTextField, gridBagConstraints);
    
        }
        //                          
       
        // Variables declaration - do not modify                    
        protected javax.swing.JPanel bottomPanel;
        private javax.swing.JLabel labelLocation;
        private javax.swing.JLabel labelPackage;
        private javax.swing.JLabel labelProject;
        private javax.swing.JLabel newNameLabel;
        private javax.swing.JTextField newNameTextField;
        private javax.swing.JComboBox packageComboBox;
        private javax.swing.JComboBox projectsComboBox;
        private javax.swing.JComboBox rootComboBox;
        // End of variables declaration                  
    
        // ActionListener implementation -------------------------------------------
           
        public void actionPerformed(ActionEvent e) {
            if (projectsComboBox == e.getSource()) {
                project = (Project) projectsComboBox.getSelectedItem();
                updateRoots();
                updatePackages();
            } else
            if ( rootComboBox == e.getSource() ) {           
                updatePackages();
            }
            else if ( packageComboBox == e.getSource() ) {
            }
        }   
       
        // DocumentListener implementation -----------------------------------------
       
        public void changedUpdate(DocumentEvent e) {               
            fireChange();       
        }   
       
        public void insertUpdate(DocumentEvent e) {
            fireChange();       
        }
       
        public void removeUpdate(DocumentEvent e) {
            fireChange();       
        }
       
        // Private methods ---------------------------------------------------------
           
        private void updatePackages() {
            SourceGroup g = (SourceGroup) rootComboBox.getSelectedItem();
            packageComboBox.setModel(PackageView.createListView(g));
        }
       
        void setCombosEnabled(boolean enabled) {
            packageComboBox.setEnabled(enabled);
            rootComboBox.setEnabled(enabled);
            projectsComboBox.setEnabled(enabled);
        }
       
        void setThisClassVisible(boolean visible) {
            newNameLabel.setVisible(visible);
            newNameTextField.setVisible(visible);
        }
       
        public String getNewName() {
            return newNameTextField.getText();
        }
       
        private void updateRoots() {
            Sources sources = ProjectUtils.getSources(project);
            groups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
            if (groups.length == 0) {
                // XXX why?? This is probably wrong. If the project has no Java groups,
                // you cannot move anything into it.
                groups = sources.getSourceGroups( Sources.TYPE_GENERIC );
            }
    
            int preselectedItem = 0;
            for( int i = 0; i < groups.length; i++ ) {
                if (fo!=null) {
                    try {
                        if (groups[i].contains(fo)) {
                            preselectedItem = i;
                        }
                    } catch (IllegalArgumentException e) {
                        // XXX this is a poor abuse of exception handling
                    }
                }
            }
                   
            // Setup comboboxes
            rootComboBox.setModel(new DefaultComboBoxModel(groups));
            rootComboBox.setSelectedIndex(preselectedItem);
        }
       
        private static class GroupCellRenderer extends DefaultListCellRenderer/**/ {
           
            public Component getListCellRendererComponent(
                JList list,
                Object value,
                int index,
                boolean isSelected,
                boolean cellHasFocus) {
           
        DefaultListCellRenderer cbr =
    (DefaultListCellRenderer)super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );  
                SourceGroup g = (SourceGroup) value;
                cbr.setText(g.getDisplayName());
                cbr.setIcon(g.getIcon(false));
                return cbr;
            }
        }
       
        private static class ProjectCellRenderer extends DefaultListCellRenderer {
           
            public Component getListCellRendererComponent(
                JList list,
                Object value,
                int index,
                boolean isSelected,
                boolean cellHasFocus) {
           
        DefaultListCellRenderer cbr =
    (DefaultListCellRenderer)super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );       
                if ( value != null ) {
                    ProjectInformation pi = ProjectUtils.getInformation((Project)value);
                    cbr.setText(pi.getDisplayName());
                    cbr.setIcon(pi.getIcon());
                }
                return cbr;
            }
        }
    }

在 NetBeans System Filesystem 中注册 Refactoring

IDE 使用 Ant 构建脚本来构建和安装您的模块。构建脚本是创建模块项目时为您创建的。

要在 Options 窗口中注册模块,您必须在 layer.xml 文件中执行以下操作:

  1. 更新 layer.xml 文件。将以下条目添加到 layer.xml 文件:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.0//EN" "http://www.netbeans.org/dtds/filesystem-1_0.dtd">
    
    <filesystem>
       
        <folder name="Menu">   
            <folder name="Refactoring">
                <attr name="LastSeparator.instance/IntroduceVariableAction.instance" boolvalue="true"/>         
                <attr name="copyclassSeparator.instance/UndoAction.instance" boolvalue="true"/>
                <attr name="MoveClassAction.instance/CopyClassAction.instance" boolvalue="true"/>        
                <file name="CopyClassAction.instance">
                    <attr name="instanceClass" stringvalue="org.netbeans.modules.refactoring.copyclass.ui.CopyClassAction"/>
                </file>        
                <attr name="CopyClassAction.instance/CleanUpAction.instance" boolvalue="true"/>
                <attr name="CopyClassAction.instance/InnerToOuterAction.instance" boolvalue="true"/>
            </folder>
        </folder>
     
        <folder name="Actions">
            <folder name="Refactoring">
                <file name="org-netbeans-modules-refactoring-copyclass-ui-CopyClassAction"/>
            </folder>
        </folder>
    
        <folder name="Services">
            <folder name="org-netbeans-modules-refactoring">
                <file name="options">
                    <attr name="previewAll.org.netbeans.modules.refactoring.copyclass.CopyClass" boolvalue="false"/>
                </file>
            </folder>
        </folder>
       
    </filesystem>
  2. 本地化标签。layer.xml 文件所在的包中,向 Bundle.properties 文件中添加以下条目:
    LBL_CopyClass_Action=Copy Class...
    LBL_CopyClass=Copy Class
    DSC_CopyClass=Copy Class {0}
    LBL_NewName=&New Name\:
    LBL_Project=P&roject
    
    LBL_Location=&Location
    
    LBL_ToPackage=&To Package

构建和安装模块

IDE 使用 Ant 构建脚本来构建和安装您的模块。构建脚本是创建模块项目时为您创建的。

安装和测试 NetBeans 模块

  1. 在 Projects 窗口,右键单击 CopyClassRefactoring 项目并在 Target Platform 中选择 Install/Reload。

    模块即在目标 IDE 或平台中构建和安装。目标 IDE 或平台打开,您可以试用新的模块。默认目标 IDE 或平台是由开发 IDE 的当前实例使用的安装平台。

  2. 创建新的 Java 应用程序项目,选择 Java 源文件并选择 Refactor > Copy Class,如下所示:

    有关该模块的其他方面,请参阅介绍示例部分。

创建可共享的模块二进制文件(NBM 文件)

NBM 文件是一个 NetBeans 模块,该模块已被打包以便通过 Web 发送。NBM 文件和模块 JAR 文件之间的主要差别为:

  • NBM 文件是压缩的文件。
  • NBM 文件可以包含多个 JAR 文件,即模块可以将它们使用的任何库打包到它们的 NBM 文件中。
  • NBM 文件包含 NetBeans 将用来在 Update Center 显示有关它信息的元数据,如 manifest 内容、许可等。
  • 出于安全考虑,NBM 文件通常都有签名。

NBM 文件只是具有特殊扩展名的 ZIP 文件。它们使用 JDK 机理给 JAR 文件签名。除非您正在执行一些不同寻常的操作,否则不需要担心 NBM 文件的内容,只需让 NBM 创建的标准 Ant 构建脚本为您完成所有工作。IDE 根据在 Project Properties 对话框的项目中输入的选项生成构建脚本。您可以在 Project Properties 对话框中设置模块的依存关系、版本控制和打包信息。通过为项目编辑 Ant 脚本和 Ant 属性,您可以进一步自定义程序执行。

  1. 在 Projects 窗口中,右键单击 CopyClassRefactoring 项目节点,并选择 Create NBM。

    将创建 NBM 文件并且可以在 Files 窗口中查看它 (Ctrl-2):

  2. 通过电子邮件使其他人也可以使用。
  3. 使用 Update Center 来安装 NBM 文件。

向 NetBeans 捐献重构

以下是当对 NetBeans 应用新的重构时需要采取的步骤。


下一步

有关创建和开发 NetBeans 模块的更多信息,请参见以下资源:


版本控制

版本
日期
更改
1 2005 年 10 月 28 日
  • 初始版本。
  • 待办事项:
    • 在 CopyClassAction 中添加图标和更改位置。
    • 替换工程师和文档 CopyClassPanel 的位置。
    • 让重构团队检查所有事项。
    • 添加用于对 NetBeans 进行自己重构的步骤。
    • 添加 Experimental 模块中其他重构的变量。
    • 添加并描述模块的 JUnit 测试。
    • 并不是使用的所有 API 都位于 NetBeans API List 中;为什么?
    • 谈论有关“重构框架”的详细信息
    • 谈论有关“查询”的详细信息
    • 在项目的“CopyClassRefactoring”中没有参数;为什么?
    • 为什么 DataObject 名为“objectToDelete”?
    • 检查此处是否涉及重构开发者指南中的所有事项。
    • 提供本教程的开始部分源的简要概述,包括变量。




↑返回目录
前一篇: NetBeans Project Template 模块教程
后一篇: NetBeans Editor Extension 模块教程