blob: aa7ed280d4931267a207952a719353c575d9cfe8 [file] [log] [blame]
<!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>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- -*- xhtml -*- -->
<title>针对 NetBeans 平台 6.0 的 NetBeans Java 语言基础结构教程</title>
<link rel="stylesheet" type="text/css" href="../../../netbeans.css">
<meta name="AUDIENCE" content="NBUSER">
<meta name="TYPE" content="ARTICLE">
<meta name="EXPIRES" content="N">
<meta name="developer" content="geertjan.wielenga@sun.com">
<meta name="indexed" content="y">
<meta name="description"
content="A walk-through of the Retouche approach.">
<!-- Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved. -->
<!-- Use is subject to license terms.-->
</head>
<body>
<h1>NetBeans Java 语言基础结构教程</h1>
<p>本教程将向您介绍新的 NetBeans 6.0 &quot;Retouche&quot; API 的各个方面,通过此 API 可以访问 NetBeans Java 编辑器。
</p><p><b>目录</b></p>
<img src="../../images/articles/60/netbeans-stamp60-61.gif" class="stamp" width="114" height="114" alt="本页上的内容适用于 NetBeans IDE 6.1" title="">本页上的内容适用于 NetBeans IDE 6.1 </p>
<ul class="toc">
<li><a href="#intro">Java 语言基础结构简介</a></li>
<li><a href="#setting-up-the-module">设置模块</a></li>
<li><a href="#creating-a-context-sensitive-toolbar-button">创建上下文相关的工具栏按钮</a></li>
<li><a href="#identifying-java-source-files">识别 Java 源文件</a></li>
<li><a href="#determining-open-state">确定打开状态</a></li>
<li><a href="#detecting-the-element-under-the-caret">检测插入记号下的元素</a></li>
<li><a href="#doing-something-useful">执行有用的操作</a></li>
</ul>
<p><b>要学习本教程,您需要具备下表中列出的软件和资源。</b></p>
<table>
<tbody>
<tr>
<th class="tblheader" scope="col">软件或资源</th>
<th class="tblheader" scope="col">要求的版本</th>
</tr>
<tr>
<td class="tbltd1">NetBeans IDE</td>
<td class="tbltd1"><a href="http://download.netbeans.org/netbeans/6.1/final/">版本 6.1</a> 或<br>
版本 6.0</td>
</tr>
<tr>
<td class="tbltd1">Java Developer Kit (JDK)</td>
<td class="tbltd1"><a href="http://java.sun.com/javase/downloads/index.jsp">版本 6</a> 或<br>
版本 5</td>
</tr>
</tbody>
</table>
<p class="tips">(可选)要解决疑难问题,可以<a href="http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=2753">下载完整的样例</a>并查看源代码。
</p><p></p><h2><a name="intro"></a>Java 语言基础结构简介</h2>
<p>在 NetBeans IDE 6.0 之前,支持 Java 编辑器的 Java 语言基础结构(包括 Java 代码生成和 Java 重构)基于一种名为 &quot;JMI for Java&quot;(也称为 &quot;MDR&quot;)的技术。JMI for Java 存在几个体系结构问题,例如单读锁。也就是说,即使仅从其模型中读取信息也必须获取锁,而这通常只有在执行写操作时才是必需的。经过几年的时间,JMI 在性能方面经过了调试并获得了改进。但另一个问题是它使用自己的 Java 解析器内部副本,这意味着它通过自己的方法来解释 Java 源代码,此方法不同于 JDK 的 Java 编译器所使用的方法。随着 JDK 5 中泛型的引入(引入了许多更复杂、更微妙的 Java 结构),这种差异变得更难以管理。因此,由于 JMI 的性能问题,并且由于开发了正式的语言模型而使 JDK Java 编译器变成了一个可通过编程来访问的工具,JMI for Java 被一种基于 JDK Java 编译器的新方法所取代。
</p><p>Retouche(法语中的一个词,表示“润色”)就是在这个时候诞生的。Retouche 是 NetBeans IDE 中新型快速的 Java 语言基础结构,能够支持已在 NetBeans 6.0 中实现的所有出色的 Java 编辑器功能。在其核心位置,Retouche 包装了 JDK Java 编译器的一个实例并使用它的工件,例如,抽象语法树(也称为 &quot;AST&quot;)以及在解析的各个阶段发布的符号分辨率。使用 Retouche 时,需要处理其中一些工件。例如,<a href="http://java.sun.com/javase/6/docs/jdk/api/javac/tree/index.html">Compiler Tree API</a>(编译器树 API)就是其中一个工件。编译器树 API 中类的包命名为 <tt>com.sun.*</tt>。因此,从技术上讲,这是一个非 JDK API,但它确实来自 JDK Java 编译器。对于 JDK Java 编译器的工件,您可以处理的与 Retouche 有关的另一个示例是 JDK API 中的正式语言模型,它由 <tt>javax.language.model.*</tt> 包提供。
</p><p></p><h2><a name="setting-up-the-module"></a>设置模块</h2>
<p>在本节中,我们使用向导来创建一个模块项目并设置与相关 NetBeans 模块的依赖关系。
</p><ol>
<li>选择“文件”&gt;“新建项目”。在“新建项目”向导中的“类别”下选择“NetBeans 模块”,在“项目”下选择“模块项目”,然后单击“下一步”。
</li><li>在“项目名称”中键入 <tt>CopyFQN</tt>,将“项目位置”设置为硬盘上的相应文件夹。选择“独立模块”和“设置为主项目”(如果未选择这两个选项)。单击“下一步”。
</li><li>在“代码名称基”中键入 <tt>org.netbeans.modules.copyfqn</tt>,在“模块显示名称”中键入 <tt>CopyFQN</tt>。单击“完成”。
</li><li>右键单击项目,选择“属性”,单击“项目属性”对话框中的“库”,然后声明与以下 API 的依赖关系:
<p></p><ul>
<li>数据系统 API
</li><li>编辑器库 2
</li><li>文件系统 API
</li><li>Javac API 包装器
</li><li>Java 源代码
</li><li>节点 API
</li><li>文本 API
</li><li>UI 实用程序 API
</li><li>实用程序 API
</li><li>窗口系统 API
</li></ul>
<p>您现在应该看到下面的内容:
</p><p align="left"><img border="1" src="../../images/tutorials/copyfqn/proj-props-copyfqn-60.png" alt="复制 fqn">
</p><p>单击“确定”。
</p></li>
<p></p><h2><a name="creating-a-context-sensitive-toolbar-button"></a>创建上下文相关的工具栏按钮</h2>
<p>在本节中,我们在工具栏中创建一个上下文相关按钮。这与新的 Retouche API 没有任何关系,但为我们提供了一个用户界面元素,用于与本教程后面实现的 Retouche API 进行交互。
</p><ol>
<li>右键单击模块项目,选择“新建”&gt;“其他”,并从“模块开发”类别中选择“操作”。单击“下一步”。
</li><li>选择“有条件地启用”并保留所有缺省设置(如下所示),以便此操作将与 <tt>DataObject</tt> 相关,并且仅在选定了一个 <tt>DataObject</tt> 时才启用。</p>
<p align="left"><img border="1" src="../../images/tutorials/copyfqn/cookie-action-60.png" alt="Cookie 操作">
</p><p>单击“下一步”。
</p></li><li>在“类别”中选择“编辑”,在“工具栏”中也选择“编辑”。 </p>
<p>您现在应该看到下面的内容:
</p><p align="left"><img border="1" src="../../images/tutorials/copyfqn/gui-registration-60.png" alt="GUI 注册">
</p><p>单击“下一步”。</p>
</li><li>在“类名”中键入 <tt>CopyFQNAction</tt>,在“显示名称”中键入 <tt>CopyFQN</tt>。
</li><li>浏览到将显示在此工具栏按钮中的图标。例如,使用将在本教程中使用的图标:</p>
<p align="left"><img border="1" src="../../images/tutorials/copyfqn/icon.png" alt="图标">
</p><p>单击“完成”。</p>
<p>您现在应该在新的 <tt>CopyFQNAction.java</tt> 类中看到以下代码:
</p><pre class="examplecode">public final class CopyFQNAction extends CookieAction {
protected void performAction(Node[] activatedNodes) {
DataObject dataObject = activatedNodes[0].getLookup().lookup(org.openide.loaders.DataObject.class);
// TODO use dataObject
}
protected int mode() {
return CookieAction.MODE_EXACTLY_ONE;
}
public String getName() {
return NbBundle.getMessage(CopyFQNAction.class, &quot;CTL_CopyFQNAction&quot;);
}
protected Class[] cookieClasses() {
return new Class[] {
DataObject.class
};
}
protected String iconResource() {
return &quot;org/netbeans/modules/copyfqn/icon.png&quot;;
}
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
protected boolean asynchronous() {
return false;
}
}</pre>
<p><b class="notes">注意:</b>我们在本教程其余部分执行的所有工作将集中于上面的 <tt>performAction()</tt> 方法。</p>
<p>现在您已创建一个与数据对象相关的操作。下面我们来看一看这意味着什么。
</p></li><li>右键单击模块并选择“安装”。</p>
<p>安装此模块后,您应该在工具栏中看到一个新按钮。
</p></li><li>在“项目”窗口中选择一个节点,然后在工具栏中查看此按钮。如果选择一个表示文件或文件夹(包括包)的节点,此按钮将启用,如下所示:</p>
<p align="left"><img border="1" src="../../images/tutorials/copyfqn/ctx-sensitive-on.png" alt="图标">
</p><p>但是,如果选择一个表示项目的节点,此按钮将被禁用,如下所示:</p>
<p align="left"><img border="1" src="../../images/tutorials/copyfqn/ctx-sensitive-off.png" alt="图标">
</p></li>
</ol>
<p>在下一节,我们不仅将区分项目节点和文件/文件夹节点,还将区分 Java 类的文件节点和其他所有类型的文件节点。
</p><p></p><h2><a name="identifying-java-source-files"></a>识别 Java 源文件</h2>
<p>在本节中,我们开始使用一个新的 &quot;Retouche&quot; API,此 API 称为 <a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-java-source/overview-summary.html">Java Source</a>(Java 源代码)。在此,我们使用 <a href="http://www.netbeans.org/download/dev/javadoc/org-netbeans-modules-java-source/org/netbeans/api/java/source/JavaSource.html">JavaSource</a> 类,它表示一个 Java 源文件。我们为与数据对象关联的文件对象返回此类的一个实例。如果返回 null,则说明此文件对象不是 Java 源文件。在选定某个文件的情况下单击此按钮时,将在状态栏中显示结果。
</p><ol>
<li>通过添加下面突出显示的行来填写 <tt>performAction()</tt> 方法:
<pre class="examplecode">protected void performAction(Node[] activatedNodes) {
DataObject dataObject = activatedNodes[0].getLookup().lookup(org.openide.loaders.DataObject.class);
// TODO use dataObject
<b>FileObject fileObject = dataObject.getPrimaryFile();
<a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-java-source/org/netbeans/api/java/source/JavaSource.html">JavaSource</a> javaSource = <a href="http://www.netbeans.org/download/dev/javadoc/org-netbeans-modules-java-source/org/netbeans/api/java/source/JavaSource.html#forFileObject(org.openide.filesystems.FileObject)">JavaSource.forFileObject(fileObject)</a>;
if (javaSource == null) {
StatusDisplayer.getDefault().setStatusText(&quot;Not a Java file: &quot; + fileObject.getPath());
} else {
StatusDisplayer.getDefault().setStatusText(&quot;Hurray! A Java file: &quot; + fileObject.getPath());
}</b>
}</pre>
</li><li>检查 import 语句是否如下所示:
<pre class="examplecode">import org.netbeans.api.java.source.JavaSource;
import org.openide.awt.StatusDisplayer;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.nodes.Node;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CookieAction;</pre>
</li><li>再次安装此模块。
</li><li>选择一个文件节点并按此按钮。</p>
<p>请注意,只有选择了 Java 文件时,才会显示 &quot;Hurray!&quot; 消息,如下所示:
</p><p align="left"><img border="1" src="../../images/tutorials/copyfqn/message-java-file-60.png" alt="message-java-file-60">
</p><p>另一种方法是<i>仅当选定了 Java 文件时才启用此按钮</i>。要实现此功能,请重写 <tt>CookieAction.enable()</tt> 方法,如下所示:
</p><pre class="examplecode">@Override
protected boolean enable(Node[] activatedNodes) {
if (super.enable(activatedNodes)) {
DataObject dataObject = activatedNodes[0].getLookup().lookup(org.openide.loaders.DataObject.class);
FileObject fileObject = dataObject.getPrimaryFile();
JavaSource javaSource = JavaSource.forFileObject(fileObject);
if (javaSource == null) {
return false;
}
return true;
}
return false;
}</pre>
<p>上面的方法过滤掉了<i>非</i> Java 文件的所有文件。因此,只有在当前文件是 Java 文件时,此按钮才启用。
</p></li>
<p></p><h2><a name="determining-open-state"></a>确定打开状态</h2>
<p>在本节中,我们将开始第一个显式调用的 &quot;Retouche&quot; 任务。此类任务是由 JavaSource 类的 <tt>runUserActionTask</tt> 方法提供的。使用此类任务可以控制解析过程的各个阶段,当您要立即响应用户的输入时,解析过程才适用。在此任务内执行的所有工作作为一个单元完成。在本例中,我们希望在调用自己的操作(由工具栏中的一个按钮表示)后,立即在状态栏中显示相应的文本。
</p><ol>
<li>将 <tt>performAction()</tt> 方法中的 &quot;Hurray!&quot; 消息替换为下面的代码行:
<pre class="examplecode"><a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-java-source/org/netbeans/api/java/source/JavaSource.html#runUserActionTask(org.netbeans.api.java.source.Task,%20boolean)">javaSource.runUserActionTask</a>(new <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-java-source/org/netbeans/api/java/source/Task.html">Task</a>&lt;<a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-java-source/org/netbeans/api/java/source/CompilationController.html">CompilationController</a>&gt;());</pre>
<p>您现在应该在编辑器的左边栏中看到一个灯泡,如下所示:
</p><p align="left"><img border="1" src="../../images/tutorials/copyfqn/runuserasactiontask-60.png" alt="图标">
</p></li><li>单击此灯泡。或者,在该行中放入插入记号,再按 Alt-Enter 组合键。然后让 IDE 实现此方法。
</li><li>稍微调整一下此方法,方法是:在方法的末尾添加一个 <tt>true</tt> 布尔值,并让 IDE 将此代码片段包装在一个 try/catch 块中。最后,结果应如下所示:
<pre class="examplecode">protected void performAction(Node[] activatedNodes) {
DataObject dataObject = activatedNodes[0].getLookup().lookup(org.openide.loaders.DataObject.class);
// TODO use dataObject
FileObject fileObject = dataObject.getPrimaryFile();
JavaSource javaSource = JavaSource.forFileObject(fileObject);
if (javaSource == null) {
StatusDisplayer.getDefault().setStatusText(&quot;Not a Java file: &quot; + fileObject.getPath());
} else {
<b>try {
javaSource.runUserActionTask(new Task&lt;CompilationController&gt;() {
public void run(CompilationController arg0) throws Exception {
throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);
}
}, true);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}</b>
}
}</pre>
</li><li>如下所示实现 <tt>run()</tt> 方法:
<pre class="examplecode">public void run(CompilationController compilationController) throws Exception {
<a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-java-source/org/netbeans/api/java/source/CompilationController.html#toPhase(org.netbeans.api.java.source.JavaSource.Phase)">compilationController.toPhase(Phase.ELEMENTS_RESOLVED)</a>;
<a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/text/Document.html">Document</a> document = <a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-java-source/org/netbeans/api/java/source/CompilationController.html#getDocument()">compilationController.getDocument()</a>;
if (document != null) {
StatusDisplayer.getDefault().setStatusText(&quot;Hurray, the Java file is open!&quot;);
} else {
StatusDisplayer.getDefault().setStatusText(&quot;The Java file is closed!&quot;);
}
}</pre>
</li><li>确保 import 语句如下所示:
<pre class="examplecode">import java.io.IOException;
import javax.swing.text.Document;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.JavaSource.Phase;
import org.netbeans.api.java.source.Task;
import org.openide.awt.StatusDisplayer;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CookieAction;</pre>
</li><li>再次安装此模块。
</li><li>选择一个文件节点并按此按钮。</p>
<p>请注意,只有选择了在 Java 编辑器中处于打开状态的 Java 文件时,才会显示 &quot;Hurray!&quot; 消息,如下所示:
</p><p align="left"><img border="1" src="../../images/tutorials/copyfqn/message-java-file-open-60.png" alt="message-java-file-open-60">
</p></li>
<p></p><h2><a name="detecting-the-element-under-the-caret"></a>检测插入记号下的元素</h2>
<p>至此我们已经了解到要处理 Java 文件并且文件处于打开状态,在本节中,可以随时开始检测插入记号下的元素的类型。
</p><ol>
<li>首先声明与 I/O API 的依赖关系,这样就可以将结果显示在“输出”窗口中。
</li><li>将 <tt>run()</tt> 方法中的 &quot;Hurray!&quot; 消息替换为下面突出显示的代码行:
<pre class="examplecode">public void run(CompilationController compilationController) throws Exception {
compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
Document document = compilationController.getDocument();
if (document != null) {
<b>new MemberVisitor(compilationController).scan(compilationController.getCompilationUnit(), null);</b>
} else {
StatusDisplayer.getDefault().setStatusText(&quot;The Java file is closed!&quot;);
}
}</pre>
<p></p></li><li>以下是 <tt>MemberVisitor</tt> 类,将其定义为 <tt>CopyFQNAction</tt> 类的内部类:
<pre class="examplecode">private static class MemberVisitor extends TreePathScanner&lt;Void, Void&gt; {
private CompilationInfo info;
public MemberVisitor(CompilationInfo info) {
this.info = info;
}
@Override
public Void visitClass(ClassTree t, Void v) {
Element el = info.getTrees().getElement(getCurrentPath());
if (el == null) {
StatusDisplayer.getDefault().setStatusText(&quot;Cannot resolve class!&quot;);
} else {
TypeElement te = (TypeElement) el;
List<? extends Element> enclosedElements = te.getEnclosedElements();
InputOutput io = IOProvider.getDefault().getIO(&quot;Analysis of &quot;
+ info.getFileObject().getName(), true);
for (int i = 0; i &lt; enclosedElements.size(); i++) {
Element enclosedElement = (Element) enclosedElements.get(i);
if (enclosedElement.getKind() == ElementKind.CONSTRUCTOR) {
io.getOut().println(&quot;Constructor: &quot;
+ enclosedElement.getSimpleName());
} else if (enclosedElement.getKind() == ElementKind.METHOD) {
io.getOut().println(&quot;Method: &quot;
+ enclosedElement.getSimpleName());
} else if (enclosedElement.getKind() == ElementKind.FIELD) {
io.getOut().println(&quot;Field: &quot;
+ enclosedElement.getSimpleName());
} else {
io.getOut().println(&quot;Other: &quot;
+ enclosedElement.getSimpleName());
}
}
io.getOut().close();
}
return null;
}
}</pre>
</li><li>再次安装此模块,并打开一个 Java 类。然后单击此按钮,并注意构造函数、方法以及字段已写入“输出”窗口中,如下所示:</p>
<p align="left"><img border="1" src="../../images/tutorials/copyfqn/output-window-60.png" alt="message-constructor-60">
</p></li><li>接下来,不在“输出”窗口中显示所有元素,而仅显示插入记号下的元素。只需将 <tt>visitClass</tt> 方法替换为下面突出显示的代码:
<pre class="examplecode">private static class MemberVisitor extends TreePathScanner&lt;Void, Void&gt; {
private CompilationInfo info;
public MemberVisitor(CompilationInfo info) {
this.info = info;
}
<b>@Override
public Void visitClass(ClassTree t, Void v) {
try {
JTextComponent editor = EditorRegistry.lastFocusedComponent();
if (editor.getDocument() == info.getDocument()) {
int dot = editor.getCaret().getDot();
TreePath tp = info.getTreeUtilities().pathFor(dot);
Element el = info.getTrees().getElement(tp);
if (el == null) {
StatusDisplayer.getDefault().setStatusText(&quot;Cannot resolve class!&quot;);
} else {
InputOutput io = IOProvider.getDefault().getIO(&quot;Analysis of &quot;
+ info.getFileObject().getName(), true);
if (el.getKind() == ElementKind.CONSTRUCTOR) {
io.getOut().println(&quot;Hurray, this is a constructor: &quot;
+ el.getSimpleName());
} else if (el.getKind() == ElementKind.METHOD) {
io.getOut().println(&quot;Hurray, this is a method: &quot;
+ el.getSimpleName());
} else if (el.getKind() == ElementKind.FIELD) {
io.getOut().println(&quot;Hurray, this is a field: &quot;
+ el.getSimpleName());
} else {
io.getOut().println(&quot;Hurray, this is something else: &quot;
+ el.getSimpleName());
}
io.getOut().close();
}
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
return null;
}</b>
}</pre>
</li><li>安装此模块。
</li><li>将插入记号放在 Java 代码中的某处,然后按此按钮。“输出”窗口显示有关插入记号下的代码的信息(如果适用)。例如,如果将插入记号放入某个方法中后按此按钮,“输出”窗口将通知您插入记号位于方法中,如下所示:</p>
<p align="left"><img border="1" src="../../images/tutorials/copyfqn/message-constructor-60.png" alt="message-constructor-60">
</p></li><li>但是,除了检测到插入记号下的元素的名称外,还可以检测到其他许多信息。在 <tt>visitClass</tt> 方法中,替换下面以粗体显示的代码行:
<pre class="examplecode">@Override
public Void visitClass(ClassTree t, Void v) {
try {
JTextComponent editor = EditorRegistry.lastFocusedComponent();
if (editor.getDocument() == info.getDocument()) {
int dot = editor.getCaret().getDot();
TreePath tp = info.getTreeUtilities().pathFor(dot);
Element el = info.getTrees().getElement(tp);
if (el == null) {
StatusDisplayer.getDefault().setStatusText(&quot;Cannot resolve class!&quot;);
} else {
InputOutput io = IOProvider.getDefault().getIO(&quot;Analysis of &quot;
+ info.getFileObject().getName(), true);
<b>String te = null;
if (el.getKind() == ElementKind.CONSTRUCTOR) {
te = ((TypeElement) ((ExecutableElement) el).getEnclosingElement()).getQualifiedName().toString();
io.getOut().println(&quot;Hurray, this is a constructor's qualified name: &quot; + te);
} else if (el.getKind() == ElementKind.METHOD) {
te = ((ExecutableElement) el).getReturnType().toString();
io.getOut().println(&quot;Hurray, this is a method's return type: &quot; + te);
} else if (el.getKind() == ElementKind.FIELD) {
te = ((VariableElement) el).asType().toString();
io.getOut().println(&quot;Hurray, this is a field's type: &quot; + te);
}</b> else {
io.getOut().println(&quot;Hurray, this is something else: &quot;
+ el.getSimpleName());
}
io.getOut().close();
}
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
return null;
}</pre>
</li><li>再次安装此模块。这一次,如果您在插入记号位于某个构造函数、方法或字段上时单击此按钮,将在“输出”窗口中显示有关此元素的更多详细信息。
</li></ol>
<p>在此阶段,我们能够检测到是否正在处理 Java 文件、文档是否已打开以及插入记号下的元素的类型。但是,我们可以根据这些信息执行什么操作?在下一节,将介绍一个简单的方案,在此方案中,可以证明我们新获得的知识是非常有用的。</p>
<p></p><h2><a name="doing-something-useful"></a>执行有用的操作</h2>
<p>在本节,我们基于插入记号下的元素设置 <tt>java.awt.datatransfer.Clipboard</tt> 提供的剪贴板的内容。当您按此按钮时,插入记号下的元素将放入剪贴板中,这样您就可以将这些内容粘贴到代码中的其他位置。
</p><ol>
<li>首先声明剪贴板并定义一个构造函数:
<pre class="examplecode">private Clipboard clipboard;
public CopyFQNAction() {
clipboard = Lookup.getDefault().lookup(ExClipboard.class);
if (clipboard == null) {
clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
}
}</pre>
</li><li>接下来,将代码中的每个 &quot;Hurray!&quot; 行替换为这样一行代码,该代码行将此元素作为一个字符串发送到将在下一步定义的方法。我们将此方法称为 <tt>setClipboardContents</tt>。因此,将第一个 &quot;Hurray!&quot; 行替换为下面的代码行:
<pre class="examplecode">setClipboardContents(te);</pre>
<p>对其他 &quot;Hurray!&quot; 行执行同样的操作,并确保向此方法传递正确的字符串。
</p><p><b class="notes">注意:</b>因为您尚未定义 <tt>setClipboardContents</tt> 方法,所以在此步骤中添加的每一行都带有红色的下划线。在下一步中添加新方法。
</p></li><li>最后,将以下代码添加到类的末尾。此方法接收字符串并将它放入剪贴板中:
<pre class="examplecode">private void setClipboardContents(String content) {
if (clipboard != null) {
if (content == null) {
StatusDisplayer.getDefault().setStatusText(&quot;&quot;);
clipboard.setContents(null, null);
} else {
StatusDisplayer.getDefault().setStatusText(&quot;Clipboard: &quot; + content);
clipboard.setContents(new StringSelection(content), null);
}
}
}</pre>
</li></ol>
<br>
<div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&amp;subject=Feedback:%20Java%20Language%20Infrastructure%20Tutorial%20Part%201">请将您的意见和建议发送给我们</a></div>
<br style="clear:both;" />
<!-- ======================================================================================== -->
<h2><a name="nextsteps"></a>另请参见</h2>
<p>有关创建和开发 NetBeans 模块的详细信息,请参见以下资源:
</p><ul>
<li><a href="http://wiki.netbeans.org/Java_DevelopersGuide">Java Developer's Guide</a>(Java 开发人员指南)</li>
<li><a href="http://wiki.netbeans.org/RetoucheDeveloperFAQ">Retouche Developer FAQ</a>(Retouche 开发人员常见问题解答)</li>
<li><a href="https://netbeans.org/kb/trails/platform.html">其他相关教程</a></li>
<li><a href="https://netbeans.org/download/dev/javadoc/">NetBeans API Javadoc</a></li>
</ul>
<hr>
<!-- ======================================================================================== -->
</ol></ol></ol></body>
</html>