// 
//     Licensed to the Apache Software Foundation (ASF) under one
//     or more contributor license agreements.  See the NOTICE file
//     distributed with this work for additional information
//     regarding copyright ownership.  The ASF licenses this file
//     to you under the Apache License, Version 2.0 (the
//     "License"); you may not use this file except in compliance
//     with the License.  You may obtain a copy of the License at
// 
//       http://www.apache.org/licenses/LICENSE-2.0
// 
//     Unless required by applicable law or agreed to in writing,
//     software distributed under the License is distributed on an
//     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//     KIND, either express or implied.  See the License for the
//     specific language governing permissions and limitations
//     under the License.
//

= 代码生成器集成教程
:jbake-type: platform_tutorial
:jbake-tags: tutorials 
:jbake-status: published
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:icons: font
:experimental:
:description: 代码生成器集成教程 - Apache NetBeans
:keywords: Apache NetBeans Platform, Platform Tutorials, 代码生成器集成教程

本教程介绍如何编写在 NetBeans 代码生成功能中集成新项目的模块，您可以通过在编辑器中按 Alt-Insert 组合键来调用该模块。







（可选）要解决疑难问题，可以 link:http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=11179[ 下载完整的样例 ]并查看其源代码。


== 代码生成器集成简介

NetBeans IDE 6.5 中引入的代码生成器功能由一系列项目组成，您可以按 Alt-Insert 组合键来调用它们。每个项目都会将代码生成到编辑器中。


image::images/code-generator_code-generator-5.png[]


== 创建模块项目

在此部分，我们将利用向导创建每个 NetBeans 模块所需的源代码结构。源代码结构包含位于特定位置的某些文件夹以及一组始终需要的文件。例如，每个 NetBeans 模块都需要一个  ``nbproject``  文件夹和一个  ``layer.xml``  文件，前者用于存放项目的 meta 数据，后者用于工具栏按钮和窗口等项的声明性注册。


[start=1]
1. 选择“文件”>“新建项目”(Ctrl-Shift-N)。在“类别”下选择“NetBeans 模块”。在“项目”下面，选择“模块”并单击“下一步”。

[start=2]
1. 在“名称和位置”面板的“项目名称”中键入  ``DemoCodeGenerator`` 。将项目位置更改为计算机上的任意目录，例如  ``c:\mymodules`` 。将“独立模块”单选按钮保留为选中状态。此面板现在应如下所示：


image::images/code-generator_code-generator-1.png[]

单击“下一步”。


[start=3]
1. 在“基本模块配置”面板中，键入  ``org.netbeans.modules.demo``  作为代码名称基。选中“生成 XML 层”复选框，并保留本地化绑定和 XML 层的位置不变，这样它们将存储在名称为  ``org/netbeans/modules/demo``  的包中。此面板现在应如下所示：


image::images/code-generator_code-generator-2.png[]


[start=4]
1. 单击“完成”。

IDE 将创建  ``DemoCodeGenerator``  项目。此项目包含所有源代码和项目 meta 数据，例如项目的 Ant 生成脚本。此项目在 IDE 中打开。您可以在“项目”窗口 (Ctrl-1) 中查看其逻辑结构，在“文件”窗口 (Ctrl-2) 中查看其文件结构。


== [[使用“代码生成器提供器”向导]] 

在此部分，我们将通过向导来创建实现生成器功能集成所需的桩模块类和层条目。


[start=1]
1. 右键单击项目节点，然后选择“新建”>“其他”。在“新建文件”对话框中，选择“模块开发”>“代码生成器”，如下所示：


image::images/code-generator_code-generator-3.png[]


[start=2]
1. 在“新建代码生成器”面板中，设置以下内容：
* *类名。*指定向导将生成的桩模块的类名。在该字段中键入 "DemoCodeGenerator"。
* *包。*指定将在其中生成桩模块类的包。从下拉列表中选择 "org.netbeans.modules.demo"。
* *Mime 类型。*指定代码生成器集成将应用的 Mime 类型。在该字段中键入 "x-java"。
* *生成代码生成器上下文提供器。*在代码生成器的查找中添加一些额外的对象。将此复选框保留为取消选中状态。

您现在应该看到下面的屏幕：


image::images/code-generator_code-generator-4.png[]


[start=3]
1. 单击“完成”。

“项目”窗口现在应如下所示：


image::images/code-generator_code-generator-6.png[]

在  ``layer.xml``  文件中，您应该能看到以下内容：


[source,xml]
----

<filesystem>
    <folder name="Editors">
        <folder name="text">
            <folder name="x-java">
                <folder name="CodeGenerators">
                    <file name="org-netbeans-modules-demo-DemoCodeGenerator$Factory.instance"/>
                </folder>
            </folder>
        </folder>
    </folder>
</filesystem>
----

生成的类应如下所示：


[source,java]
----

public class DemoCodeGenerator implements CodeGenerator {

    JTextComponent textComp;

    /**
     * 
     * @param context containing JTextComponent and possibly other items 
     * registered by {@link CodeGeneratorContextProvider}
     */
    private DemoCodeGenerator(Lookup context) { 
    // Good practice is not to save Lookup outside ctor
        textComp = context.lookup(JTextComponent.class);
    }

    public static class Factory implements CodeGenerator.Factory {

        public List create(Lookup context) {
            return Collections.singletonList(new DemoCodeGenerator(context));
        }
    }

    /**
     * The name which will be inserted inside Insert Code dialog
     */
    public String getDisplayName() {
        return "Sample Generator";
    }

    /**
     * This will be invoked when user chooses this Generator from Insert Code
     * dialog
     */
    public void invoke() {
    }
    
}
----


== [[实现代码生成器集成]] 

接下来，我们将实现 API。API 的类如下所示：

|===
|类 |描述 

|JavaSource |待完成 

|CancellableTask |待完成 

|WorkingCopy |待完成 

|CompilationUnitTree |待完成 

|TreeMaker |待完成 

|ClassTree |待完成 

|ModifiersTree |待完成 

|VariableTree |待完成 

|TypeElement |待完成 

|ExpressionTree |待完成 

|MethodTree |待完成 
|===

下面，我们将设置所需模块的依赖关系，然后在我们自己的模块中实现它们。


[start=1]
1. 右键单击项目，选择“属性”，在“库”面板中设置以下 4 个依赖关系。


image::images/code-generator_code-generator-7.png[]

*注意：*您会注意到，“代码生成器”向导已经自动设置了“编辑器库 2”和“实用程序 API”。另外两个依赖关系是“Javac API 包装器”和“Java 源代码”，您需要能够通过新的代码生成器集成生成新 Java 代码片段。


[start=2]
1. 打开生成的类，然后按如下所示修改  ``invoke()``  方法：

[source,java]
----

public void invoke() {
    try {
        Document doc = textComp.getDocument();
        JavaSource javaSource = JavaSource.forDocument(doc);
        CancellableTask task = new CancellableTask<WorkingCopy>() {
            public void run(WorkingCopy workingCopy) throws IOException {
                workingCopy.toPhase(Phase.RESOLVED);
                CompilationUnitTree cut = workingCopy.getCompilationUnit();
                TreeMaker make = workingCopy.getTreeMaker();
                for (Tree typeDecl : cut.getTypeDecls()) {
                    if (Tree.Kind.CLASS == typeDecl.getKind()) {
                        ClassTree clazz = (ClassTree) typeDecl;
                        ModifiersTree methodModifiers = 
                                make.Modifiers(Collections.<Modifier>singleton(Modifier.PUBLIC), 
                                Collections.<AnnotationTree>emptyList());
                        VariableTree parameter = 
                                make.Variable(make.Modifiers(Collections.<Modifier>singleton(Modifier.FINAL), 
                                Collections.<AnnotationTree>emptyList()), 
                                "arg0", 
                                make.Identifier("Object"), 
                                null);
                        TypeElement element = workingCopy.getElements().getTypeElement("java.io.IOException");
                        ExpressionTree throwsClause = make.QualIdent(element);
                        MethodTree newMethod = 
                                make.Method(methodModifiers, 
                                "writeExternal", 
                                make.PrimitiveType(TypeKind.VOID), 
                                Collections.<TypeParameterTree>emptyList(), 
                                Collections.singletonList(parameter), 
                                Collections.<ExpressionTree>singletonList(throwsClause), 
                                "{ throw new UnsupportedOperationException(\"Not supported yet.\") }", 
                                null);
                        ClassTree modifiedClazz = make.addClassMember(clazz, newMethod);
                        workingCopy.rewrite(clazz, modifiedClazz);
                    }
                }
            }
            public void cancel() {
            }
        };
        ModificationResult result = javaSource.runModificationTask(task);
        result.commit();
    } catch (Exception ex) {
        Exceptions.printStackTrace(ex);
    }
}
----


[start=3]
1. 确保声明了以下导入数据：

[source,java]
----

import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeParameterTree;
import com.sun.source.tree.VariableTree;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.netbeans.api.java.source.CancellableTask;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.JavaSource.Phase;
import org.netbeans.api.java.source.ModificationResult;
import org.netbeans.api.java.source.TreeMaker;
import org.netbeans.api.java.source.WorkingCopy;
import org.netbeans.spi.editor.codegen.CodeGenerator;
import org.netbeans.spi.editor.codegen.CodeGeneratorContextProvider;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
----


== 安装并试用功能

现在，安装模块并使用代码生成器功能集成。IDE 使用 Ant 生成脚本来生成和安装模块。此生成脚本是在创建项目时创建的。


[start=1]
1. 在“项目”窗口中，右键单击项目并选择“运行”。

此时将启动一个新的 IDE 实例，并安装代码生成器集成模块。


[start=2]
1. 按 Alt-Insert 组合键，您将看到其中包括的新项目：


image::images/code-generator_code-generator-5.png[]


[start=3]
1. 单击某个项目，其中将插入代码。


== 创建可共享的模块二进制文件

该模块现已完成，您可以将其交给其他用户使用了。为此，您需要创建一个 "NBM"（NetBeans 模块）二进制文件并分发它。


[start=1]
1. 在“项目”窗口中，右键单击项目，然后选择“创建 NBM”。

此时将创建 NBM 文件，您可以在“文件”窗口 (Ctrl-2) 中查看它：


[start=2]
1. 将它分发给其他人，比如说通过  link:http://plugins.netbeans.org/PluginPortal/[NetBeans 插件门户]。接收者应使用插件管理器（“工具”>“插件”）来安装它。


link:http://netbeans.apache.org/community/mailing-lists.html[请将您的意见和建议发送给我们]



== 后续步骤

有关创建和开发 NetBeans 模块的详细信息，请参见以下资源：

*  link:https://netbeans.apache.org/platform/index.html[NetBeans 平台主页]
*  link:https://bits.netbeans.org/dev/javadoc/[NetBeans API 列表（当前开发版本）]
*  link:https://netbeans.apache.org/kb/docs/platform_zh_CN.html[其他相关教程]
