| <!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> |
| <!-- -*- xhtml -*- --> |
| <title>NetBeans Java Language Infrastructure Tutorial for NetBeans Platform 7.2</title> |
| <link rel="stylesheet" type="text/css" href="https://netbeans.org/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) 2009, 2010, Oracle and/or its affiliates. All rights reserved. --> |
| <!-- Use is subject to license terms.--> |
| </head> |
| <body> |
| <h1>NetBeans Java Language Infrastructure Tutorial</h1> |
| |
| <p>In this tutorial, you will be introduced to aspects of the NetBeans "Retouche" APIs, |
| which give you access to the NetBeans Java editor.</p> |
| |
| <p><strong class="notes">Note: </strong>This document uses |
| NetBeans Platform 7.2 and NetBeans IDE 7.2. If you |
| are using an earlier version, see <a href="71/nbm-copyfqn.html">the previous version |
| of this document</a>.</p> |
| |
| <p><b>Contents</b></p> |
| |
| <p><img src="../../images/articles/72/netbeans-stamp.gif" class="stamp" width="114" height="114" alt="Content on this page applies to NetBeans IDE 7.2" title="Content on this page applies to NetBeans IDE 7.2"/></p> |
| <ul class="toc"> |
| <li><a href="#intro">Introduction to the Java Language Infrastructure</a></li> |
| <li><a href="#setting-up-the-module">Setting Up the Module</a></li> |
| <li><a href="#creating-a-context-sensitive-toolbar-button">Creating a Context-Sensitive Toolbar Button</a></li> |
| <li><a href="#identifying-java-source-files">Identifying Java Source Files</a></li> |
| <li><a href="#determining-open-state">Determining Open State</a></li> |
| <li><a href="#detecting-the-element-under-the-caret">Detecting the Element Under the Caret</a></li> |
| <li><a href="#doing-something-useful">Doing Something Useful</a></li> |
| </ul> |
| |
| <p><b>To follow this tutorial, you need the software and resources listed in the following |
| table.</b></p> |
| |
| <table> |
| <tbody> |
| <tr> |
| <th class="tblheader" scope="col">Software or Resource</th> |
| <th class="tblheader" scope="col">Version Required</th> |
| </tr> |
| <tr> |
| <td class="tbltd1"><a href="https://netbeans.org/downloads/index.html">NetBeans IDE</a></td> |
| <td class="tbltd1">version 7.2 or above</td> |
| </tr> |
| <tr> |
| <td class="tbltd1"><a href="http://java.sun.com/javase/downloads/index.jsp">Java Developer Kit (JDK)</a></td> |
| <td class="tbltd1">Version 6 or above</td> |
| </tr> |
| </tbody> |
| </table> |
| |
| <p class="tips">Optionally, for troubleshooting purposes, you can <a href="http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=2753">download the |
| completed sample</a> and inspect the sources.</p> |
| |
| <h2><a name="intro"></a>Introduction to the Java Language Infrastructure</h2> |
| |
| <p>Prior to NetBeans IDE 6, |
| the Java language infrastructure that supported the Java editor, including Java code generation and |
| Java refactoring, was based on something called "JMI for Java" (also known as "MDR"). JMI for Java |
| had a few architectural issues such as single-read lock. In other words, a lock had to be acquired just to read |
| information from its model, while this is normally required when doing write actions only. Over a period of a |
| few years, JMI was debugged and improved in terms of performance. But another issue was that it used its own internal |
| copy of the Java parser, which meant it had its own approach to interpreting Java source, which differed from |
| the JDK's Java compiler. With the introduction of generics in JDK 5, which introduced a lot more complicated |
| and subtle Java constructs, that divergence became harder to manage. Therefore, because of JMI's performance issues |
| and because a formal language model was developed to make the JDK Java compiler a programmatically accessible tool, |
| the JMI for Java was replaced with a new approach, based on the JDK Java compiler. </p> |
| |
| <p>That is when Retouche, a French word |
| meaning "touch up", was born. Retouche is the new and fast Java language infrastructure in NetBeans IDE, capable of |
| supporting all the cool Java editor features that have been realised in NetBeans 6.0. At the heart of it, Retouche |
| wraps an instance of the JDK Java compiler and consumes its artifacts, such as the abstract syntax tree (also known as "AST") |
| and the symbolic resolutions that are emitted in various phases of parsing. When working with Retouche, you need to deal |
| with some of these artifacts. The <a href="http://java.sun.com/javase/6/docs/jdk/api/javac/tree/index.html">Compiler Tree API</a> is one of these, for example. The package naming of |
| classes in the Compiler Tree API is <tt>com.sun.*</tt>. Therefore, technically, this is a non-JDK API, but it does come from the JDK Java compiler. |
| Another example of the JDK Java compiler's artifacts that you work with in relation to Retouche is the formal language model |
| in the JDK APIs, provided by the <tt>javax.language.model.*</tt> packages.</p> |
| |
| |
| <h2><a name="setting-up-the-module"></a>Setting Up the Module</h2> |
| |
| <p>In this section, we use wizards and dialogs |
| in NetBeans IDE to create a module project and |
| to set dependencies on relevant NetBeans modules. </p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li>Choose File > New Project. In the New Project wizard, |
| choose NetBeans Modules under Categories and Module |
| under Projects. Click Next. </li> |
| |
| <li>Type <tt>CopyFQN</tt> in Project Name |
| and set Project Location to an appropriate folder on your disk. Click Next.</li> |
| |
| <li>Type <tt>org.netbeans.modules.copyfqn</tt> in Code Name Base |
| and <tt>CopyFQN</tt> in Module Display Name. Click Finish.</li> |
| |
| <li>Right-click the project's Libraries node, choose Add Module Dependency, |
| and declare dependencies on the following APIs: |
| <br/> |
| <br/> |
| <ul> |
| <li>Datasystems API</li> |
| <li>Editor Library 2</li> |
| <li>File System API</li> |
| <li>Javac API Wrapper</li> |
| <li>Java Source</li> |
| <li>Lookup API</li> |
| <li>Nodes API</li> |
| <li>Text API</li> |
| <li>UI Utilities API</li> |
| <li>Utilities API</li> |
| <li>Window System API</li> |
| </ul> |
| |
| <p>You should now see this:</p> |
| <br/> |
| <p><img src="../../images/tutorials/copyfqn/72/pic1.png" alt="copy fqn"/></p> |
| |
| <p>Click OK.</p></li> |
| |
| </ol> |
| |
| </div> |
| |
| <h2><a name="creating-a-context-sensitive-toolbar-button"></a>Creating a Context-Sensitive Toolbar Button</h2> |
| |
| <p>In this section, we create a context-aware button |
| in the toolbar. This has nothing to do with the |
| Retouche APIs discussed in the introduction of this |
| tutorial, but gives us a user interface element |
| for interacting with our implementation of the Retouche APIs, later in this |
| tutorial.</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| <li>Right-click the CopyFQN module project, choose |
| New > Other and choose Action from the |
| Module Development category. |
| <br/> |
| <br/> |
| <p><img src="../../images/tutorials/copyfqn/72/pic2.png" alt="cookie action"/></p> |
| <br/> |
| <p>Click Next. </p> |
| </li> |
| |
| <li><p>Choose Conditionally Enabled, choose DataObject, and keep |
| all the defaults, as shown below, so that |
| the action will be sensitive to <tt>DataObjects</tt> |
| and so that it will only be enabled when one <tt>DataObject</tt> |
| is selected.</p> |
| <br/> |
| <p><img src="../../images/tutorials/copyfqn/72/pic3.png" alt="cookie action"/></p> |
| <br/> |
| <p>Click Next. </p> |
| </li> |
| |
| <li><p>Choose "File" in Category and "File" in Toolbar. You should now see the following:</p> |
| <br/> |
| <p><img src="../../images/tutorials/copyfqn/72/pic4.png" alt="gui registration"/></p> |
| <br/> |
| <p>Click Next.</p> |
| </li> |
| |
| <li><p>Type <tt>CopyFQNAction</tt> in Class Name |
| and <tt>CopyFQN</tt> in Display Name. Browse to an icon that will |
| be displayed in the toolbar button. For example, use the one that |
| will be used throughout this tutorial:</p> |
| <p><img src="../../images/tutorials/copyfqn/icon.png" alt="icon"/></p> |
| <p>You should now see the following:</p> |
| <br/> |
| <p><img src="../../images/tutorials/copyfqn/72/pic5.png" alt="gui registration"/></p> |
| <br/> |
| </li> |
| |
| <li> |
| <p>Click Finish. You should now see the following code in your new |
| <tt>CopyFQNAction.java</tt> class:</p> |
| |
| <pre class="examplecode">package org.netbeans.modules.copyfqn; |
| |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import org.openide.awt.ActionID; |
| import org.openide.awt.ActionReference; |
| import org.openide.awt.ActionRegistration; |
| import org.openide.loaders.DataObject; |
| import org.openide.util.NbBundle.Messages; |
| |
| @ActionID( |
| category = "File", |
| id = "org.netbeans.modules.copyfqn.CopyFQNAction") |
| @ActionRegistration( |
| iconBase = "org/netbeans/modules/copyfqn/icon.png", |
| displayName = "#CTL_CopyFQNAction") |
| @ActionReference( |
| path = "Toolbars/File", |
| position = 0) |
| @Messages("CTL_CopyFQNAction=CopyFQN") |
| public final class CopyFQNAction implements ActionListener { |
| |
| private final DataObject context; |
| |
| public CopyFQNAction(DataObject context) { |
| this.context = context; |
| } |
| |
| @Override |
| public void actionPerformed(ActionEvent ev) { |
| // TODO use context |
| } |
| |
| }</pre> |
| |
| <p><b class="notes">Note:</b> All the work we will do in the remainder |
| of this tutorial will focus on the <tt>actionPerformed</tt> |
| method above.</p> |
| <br/> |
| <p>You have now created an action that is sensitive to data objects. Let's see what that means right away.</p></li> |
| |
| <li><p>Right-click the module and choose Run. Once the new instance of the IDE is started up |
| and the module is installed, you should see a new button in the toolbar:</p> |
| <br/> |
| <p><img src="../../images/tutorials/copyfqn/72/first1.png" alt="icon"/></p> |
| <br/> |
| </li> |
| |
| <li><p>Select a node in the Projects window and then look at the button in the toolbar. If you select |
| a node representing a file or folder (including a package) the button is enabled, as shown here:</p> |
| <br/> |
| <p><img style="border:1px solid black" src="../../images/tutorials/copyfqn/72/first2.png" alt="icon"/></p> |
| <br/> |
| <p>However, if you select a node representing a project, |
| the button is disabled, as shown below:</p> |
| <br/> |
| <p><img style="border:1px solid black" src="../../images/tutorials/copyfqn/72/first3.png" alt="icon"/></p> |
| </li> |
| |
| </ol> |
| |
| </div> |
| |
| <p>In the next section, we will go further than distinguishing between project nodes and file/folder nodes, which is |
| what we're able to do so far—we will |
| distinguish between file nodes for Java classes versus all other kinds of file nodes.</p> |
| |
| <h2><a name="identifying-java-source-files"></a>Identifying Java Source Files</h2> |
| |
| <p>In this section, we begin using one of the |
| new "Retouche" APIs, called <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-java-source/overview-summary.html">Java Source</a>. |
| Here we use the <tt><a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-java-source/org/netbeans/api/java/source/JavaSource.html">JavaSource</a></tt> class, |
| which represents a Java source file. We return an instance of |
| this class for the file object associated with our data object. |
| If null is returned, the file object is not a Java source file. |
| We display the result in the status bar, when the button |
| is clicked while a file is selected.</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li>Fill out the <tt>actionPerformed</tt> method by |
| adding the lines highlighted below: |
| |
| <pre class="examplecode">public void actionPerformed(ActionEvent ev) { |
| |
| <b>FileObject fileObject = context.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("Not a Java file: " + fileObject.getPath()); |
| } else { |
| StatusDisplayer.getDefault().setStatusText("Hurray! A Java file: " + fileObject.getPath()); |
| }</b> |
| |
| }</pre> |
| |
| Check that your import statements are as follows: |
| <pre class="examplecode">import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import org.netbeans.api.java.source.JavaSource; |
| import org.openide.awt.*; |
| import org.openide.filesystems.FileObject; |
| import org.openide.loaders.DataObject; |
| import org.openide.util.NbBundle.Messages;</pre></li> |
| |
| <li><p>Run the module again. Select a file node and press the button. Notice that the "Hurray!" message only appears when |
| you select a Java file, as shown below:</p> |
| <br/> |
| <p><img style="border:1px solid black" src="../../images/tutorials/copyfqn/72/second1.png" alt="message-java-file-60"/></p> |
| <br/> |
| <p><img style="border:1px solid black" src="../../images/tutorials/copyfqn/72/second2.png" alt="message-java-file-60"/></p> |
| </li> |
| |
| </ol> |
| |
| </div> |
| |
| <h2><a name="determining-open-state"></a>Determining Open State</h2> |
| |
| <p>In this section, we are introduced to our first explicitly invoked "Retouche" task. |
| Such a task is provided by the JavaSource class's <tt>runUserActionTask</tt> method. |
| A task of this kind lets you control the phases of a parsing process, which is applicable |
| when you want to respond immediately to the user's input. Everything done within the |
| task is done as a single unit. In our case, we want the invocation of our action, represented |
| by a button in the toolbar, to be immediately followed by the display of a text in |
| the status bar.</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li>Replace the "Hurray!" message in the <tt>actionPerformed</tt> method with |
| this line: |
| |
| <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><<a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-java-source/org/netbeans/api/java/source/CompilationController.html">CompilationController</a>>());</pre> |
| |
| <p>You should now see a lightbulb in the editor's left sidebar, as shown here:</p> |
| |
| <p><img src="../../images/tutorials/copyfqn/71/runuserasactiontask.png" alt="icon"/></p> |
| <br/> |
| <p>Press Ctrl-Shift-I to import the necessary classes, choose <tt>org.netbeans.api.java.source.Task</tt>, shown |
| below, and |
| click OK in the dialog box. |
| </p> |
| <br/> |
| <p><img src="../../images/tutorials/copyfqn/72/second3.png" alt="message-java-file-60"/></p> |
| <br/> |
| |
| <p>Click the lightbulb in the editor. |
| Alternatively, put the caret in the line and press Alt-Enter. |
| Then let the IDE implement the method. |
| </p> |
| |
| </li> |
| |
| <li>Tweak the generated method slightly, by adding a <tt>true</tt> boolean to the end of the |
| method, and letting the IDE wrap the snippet in a try/catch block. At the end, the result should be as follows: |
| |
| <pre class="examplecode">public void actionPerformed(ActionEvent ev) { |
| |
| FileObject fileObject = context.getPrimaryFile(); |
| |
| JavaSource javaSource = JavaSource.forFileObject(fileObject); |
| if (javaSource == null) { |
| StatusDisplayer.getDefault().setStatusText("Not a Java file: " + fileObject.getPath()); |
| } else { |
| |
| <b>try { |
| javaSource.runUserActionTask(new Task<CompilationController>() { |
| public void run(CompilationController arg0) throws Exception { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| }, true); |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| }</b> |
| |
| } |
| |
| }</pre></li> |
| |
| <li>Implement the <tt>run()</tt> method as follows: |
| |
| <pre class="examplecode">@Override |
| 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("Hurray, the Java file is open!"); |
| } else { |
| StatusDisplayer.getDefault().setStatusText("The Java file is closed!"); |
| } |
| |
| }</pre> |
| |
| Make sure that your import statements are as follows: |
| |
| <pre class="examplecode">import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| 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.*; |
| import org.openide.filesystems.FileObject; |
| import org.openide.loaders.DataObject; |
| import org.openide.util.Exceptions; |
| import org.openide.util.NbBundle.Messages;</pre></li> |
| |
| <li><p>Run the module again. Select a file node and press the button. Notice that the "Hurray!" message only appears when |
| you select a Java file that is open in the Java editor, as shown here:</p> |
| <br/> |
| <p><img style="border:1px solid black" src="../../images/tutorials/copyfqn/72/third1.png" alt="message-java-file-open-60"/></p> |
| <br/> |
| <p><img style="border:1px solid black" src="../../images/tutorials/copyfqn/72/third2.png" alt="message-java-file-open-60"/></p> |
| </li> |
| |
| </ol> |
| |
| </div> |
| |
| <h2><a name="detecting-the-element-under-the-caret"></a>Detecting the Element Under the Caret</h2> |
| |
| <p>In this section, now that we know that we are dealing with a Java file and that it is |
| open, we can begin detecting the type of element that is under the caret at any given |
| time.</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li>Begin by declaring a dependency on the I/O APIs, so |
| that we can print our results to the Output window.</li> |
| |
| <li>Replace the "Hurray!" message in the <tt>run()</tt> method with |
| the lines highlighted below: |
| |
| <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("The Java file is closed!"); |
| } |
| |
| }</pre> </li> |
| |
| |
| <li>And here is the <tt>MemberVisitor</tt> class, which |
| is defined as an inner class of our <tt>CopyFQNAction</tt> class: |
| |
| |
| <pre class="examplecode">private class MemberVisitor extends TreePathScanner<Void, Void> { |
| |
| 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("Cannot resolve class!"); |
| } else { |
| TypeElement te = (TypeElement) el; |
| List<? extends Element> enclosedElements = te.getEnclosedElements(); |
| InputOutput io = IOProvider.getDefault().getIO("Analysis of " |
| + info.getFileObject().getName(), true); |
| for (int i = 0; i < enclosedElements.size(); i++) { |
| Element enclosedElement = (Element) enclosedElements.get(i); |
| if (enclosedElement.getKind() == ElementKind.CONSTRUCTOR) { |
| io.getOut().println("Constructor: " |
| + enclosedElement.getSimpleName()); |
| } else if (enclosedElement.getKind() == ElementKind.METHOD) { |
| io.getOut().println("Method: " |
| + enclosedElement.getSimpleName()); |
| } else if (enclosedElement.getKind() == ElementKind.FIELD) { |
| io.getOut().println("Field: " |
| + enclosedElement.getSimpleName()); |
| } else { |
| io.getOut().println("Other: " |
| + enclosedElement.getSimpleName()); |
| } |
| } |
| io.getOut().close(); |
| } |
| return null; |
| } |
| |
| }</pre> |
| <p><b class="notes">Note:</b> To use the "InputOutput" class above, you need |
| a new dependency, on the "I/O APIs".</p> |
| </li> |
| |
| <li><p>Run the module again, and open a Java class. |
| Then click the button and notice that the |
| constructors, methods, and fields are written |
| to the Output window, as shown below:</p> |
| <br/> |
| <p><img style="border:1px solid black" src="../../images/tutorials/copyfqn/72/four1.png" alt="message-constructor-60"/></p></li> |
| |
| <li>Next, instead of printing all the elements |
| to the Output window, we will only print |
| the element under the caret. Only replace the |
| <tt>visitClass</tt> method, with the code highlighted below: |
| |
| <pre class="examplecode">private class MemberVisitor extends TreePathScanner<Void, Void> { |
| |
| 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("Cannot resolve class!"); |
| } else { |
| InputOutput io = IOProvider.getDefault().getIO("Analysis of " |
| + info.getFileObject().getName(), true); |
| if (el.getKind() == ElementKind.CONSTRUCTOR) { |
| io.getOut().println("Hurray, this is a constructor: " |
| + el.getSimpleName()); |
| } else if (el.getKind() == ElementKind.METHOD) { |
| io.getOut().println("Hurray, this is a method: " |
| + el.getSimpleName()); |
| } else if (el.getKind() == ElementKind.FIELD) { |
| io.getOut().println("Hurray, this is a field: " |
| + el.getSimpleName()); |
| } else { |
| io.getOut().println("Hurray, this is something else: " |
| + el.getSimpleName()); |
| } |
| io.getOut().close(); |
| } |
| } |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| return null; |
| }</b> |
| |
| }</pre> </li> |
| |
| <li><p>Run the module. Put the caret somewhere within your Java code and press the button. The Output window |
| displays information about the code under the caret, if applicable. For example, if you |
| press the button after you put the caret in a method, as shown below, the Output window |
| tells you that the caret is in a method:</p> |
| <br/> |
| <p><img style="border:1px solid black" src="../../images/tutorials/copyfqn/72/four2.png" alt="message-constructor-60"/></p> |
| </li> |
| |
| <li>But we can detect a lot more than just the name |
| of the element under the caret. In the <tt>visitClass</tt> |
| method, replace the lines in bold below: |
| |
| <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("Cannot resolve class!"); |
| } else { |
| InputOutput io = IOProvider.getDefault().getIO("Analysis of " |
| + info.getFileObject().getName(), true); |
| <b>String te = null; |
| if (el.getKind() == ElementKind.CONSTRUCTOR) { |
| te = ((TypeElement) ((ExecutableElement) el).getEnclosingElement()).getQualifiedName().toString(); |
| io.getOut().println("Hurray, this is a constructor's qualified name: " + te); |
| } else if (el.getKind() == ElementKind.METHOD) { |
| te = ((ExecutableElement) el).getReturnType().toString(); |
| io.getOut().println("Hurray, this is a method's return type: " + te); |
| } else if (el.getKind() == ElementKind.FIELD) { |
| te = ((VariableElement) el).asType().toString(); |
| io.getOut().println("Hurray, this is a field's type: " + te); |
| }</b> else { |
| io.getOut().println("Hurray, this is something else: " |
| + el.getSimpleName()); |
| } |
| io.getOut().close(); |
| } |
| } |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| return null; |
| }</pre></li> |
| |
| <li>Run the module again. This time, when you click |
| the button while the caret is over a constructor, method, |
| or field, more detailed information about the element |
| is printed to the Output window. |
| <br/> |
| <br/> |
| <p><img style="border:1px solid black" src="../../images/tutorials/copyfqn/72/four3.png" alt="next"/></p> |
| </li> |
| |
| </ol> |
| |
| </div> |
| |
| <p>At this stage, we are able to detect whether we are dealing with a Java file, whether the |
| document is open, and the type of element that is under the caret. But what can we do with |
| this information? In the next section, a simple scenario is presented where our newly acquired |
| knowledge will prove useful.</p> |
| |
| |
| <h2><a name="doing-something-useful"></a>Doing Something Useful</h2> |
| |
| <p>In this section, we set the contents of the clipboard, provided by <tt>java.awt.datatransfer.Clipboard</tt>, |
| based on the element under the caret. When you press the button, the element under the caret will |
| be put in the clipboard, so that you can paste the content elsewhere in your code. </p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li>Begin by changing the constructor to declare the clipboard: |
| |
| <pre class="examplecode">private Clipboard clipboard; |
| |
| public CopyFQNAction(DataObject context) { |
| this.context = context; |
| clipboard = Lookup.getDefault().lookup(ExClipboard.class); |
| if (clipboard == null) { |
| clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); |
| } |
| }</pre></li> |
| |
| <li>Next, replace each "Hurray!" line in your code, with a line that sends the element as a string |
| to a method that we will define in the next step. We will call our method <tt>setClipboardContents</tt>. |
| Therefore, for example, replace the first "Hurray!" line with the following: |
| |
| <pre class="examplecode">setClipboardContents(te);</pre> |
| |
| <p>Do the same for the other "Hurray!" lines, making sure to pass the correct string to the method.</p> |
| <br/> |
| <p><b class="notes">Note:</b> Because you have not defined the <tt>setClipboardContents</tt> method yet, each of the |
| lines you add in this step is underlined in red. In the next step, we add the new method.</p></li> |
| |
| <li>Finally, add the following to the end of the class. This method receives the |
| string and puts it in the clipboard: |
| |
| <pre class="examplecode">private void setClipboardContents(String content) { |
| if (clipboard != null) { |
| if (content == null) { |
| StatusDisplayer.getDefault().setStatusText(""); |
| clipboard.setContents(null, null); |
| } else { |
| StatusDisplayer.getDefault().setStatusText("Clipboard: " + content); |
| clipboard.setContents(new StringSelection(content), null); |
| } |
| } |
| }</pre></li> |
| |
| </ol> |
| |
| </div> |
| |
| <div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&subject=Feedback:%20Java%20Language%20Infrastructure%20Tutorial%207.2">Send Us Your Feedback</a></div> |
| |
| <!-- ======================================================================================== --> |
| |
| <h2><a name="nextsteps"></a>See Also</h2> |
| |
| <p>For more information about creating and developing NetBeans Module, see the following resources: </p> |
| <ul> |
| <li><a href="http://wiki.netbeans.org/Java_DevelopersGuide">Java Developer's Guide</a></li> |
| <li><a href="http://wiki.netbeans.org/RetoucheDeveloperFAQ">Retouche Developer FAQ</a></li> |
| <li><a href="https://netbeans.org/kb/trails/platform.html">Other Related Tutorials</a></li> |
| <li><a href="https://netbeans.org/download/dev/javadoc/">NetBeans API Javadoc</a></li> |
| </ul> |
| |
| <!-- ======================================================================================== --> |
| |
| </body> |
| </html> |