NetBeans Java Language Infrastructure Tutorial (Part 2)

In the previous tutorial, you created a button in the toolbar which, when clicked, explicitly invoked the underlying Java language infrastructure. In this tutorial, you will make implicit calls to the same infrastructure, by registering factory classes in the META-INF/services folder. At the end of this tutorial, the Projects window will look as follows:

Projects window

After installation of the module, information about the Element under the caret is displayed in a read-only textfield in the IDE's status bar. For example, if a package is identified under the caret, the textfield shows the following information (look in the status bar at the bottom of the screenshot below):

Package found

However, if a field is under the caret, the textfield shows different content:

Variable found

Similarly, if the caret is placed on a class declaration or a method, the underlying Java language infrastructure is called implicitly, i.e., without the user needing to do anything explicit, and the status bar displays information relevant to the current Element.

Contents

Content on this page applies to NetBeans IDE 6.0

The following resources have been provided specifically to help you get acquainted with the Retouche APIs:

For more information on creating NetBeans modules, see the NetBeans Development Project home on the NetBeans website. If you have questions, visit the NetBeans Developer FAQ or use the feedback link at the bottom of this page.


Installing the Software

Before you begin, you need to install the following software on your computer:

Setting Up the Module

In this section, we use wizards to create a module project and to set dependencies on relevant NetBeans modules.

  1. Choose File > New Project. In the New Project wizard, choose NetBeans Modules under Categories and Module under Projects. Click Next.
  2. Type WhichElement in Project Name and set Project Location to an appropriate folder on your disk. If they are not selected, select Standalone Module and Set as Main Project. Click Next.
  3. Type org.netbeans.modules.whichelement in Code Name Base and WhichElement in Module Display Name. Click Finish.
  4. Right-click the project, choose Properties, click Libraries in the Project Properties dialog box and declare a dependency on the following APIs:

    Click OK.

Creating a Caret-Aware Java Source Task Factory

In this section, we create a task factory for Java source files that is caret-aware. We then register it in the META-INF/services folder.

  1. Right-click the module project, choose New > Java Class. Click Next.
  2. Type WhichElementJavaSourceTaskFactory in Class Name. Click Finish.
  3. Change the default code to the following:
    public class WhichElementJavaSourceTaskFactory extends CaretAwareJavaSourceTaskFactory {
        
        public WhichElementJavaSourceTaskFactory() {
            super(Phase.RESOLVED, Priority.LOW);
        }
    
        public CancellableTask<CompilationInfo> createTask(FileObject fileObject) {
            return new WhichElementTask(this, fileObject);
        }
        
    }
  4. Create a skeleton implementation of CancellableTask, which we will fill out later:
    public class WhichElementTask implements CancellableTask<CompilationInfo> {
    
        private WhichElementJavaSourceTaskFactory whichElementJavaSourceTaskFactory;
        private FileObject fileObject;
    
        WhichElementTask(WhichElementJavaSourceTaskFactory whichElementJavaSourceTaskFactory, FileObject fileObject) {
            this.whichElementJavaSourceTaskFactory = whichElementJavaSourceTaskFactory;
            this.fileObject = fileObject;
        }
    
        public void cancel() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    
        public void run(CompilationInfo compilationInfo) {
            String fileName = compilationInfo.getFileObject().getName();
            StatusDisplayer.getDefault().setStatusText("File name: " + fileName);
        }
        
    }
  5. Register the factory in the META-INF/services folder. To do so, first find the "all services" node, in the Important Files node, shown below:

    caretaware-metainf2

    Expand it and look for the org.netbeans.api.java.source.JavaSourceTaskFactory. Then right-click it, as shown here:

    caretaware-metainf1

    Now you can browse to your factory class...

    caretaware-metainf3

    ...and once you click OK, new nodes are added to your project:

    caretaware-metainf4

  6. Right-click the module and choose Install.

    Once the module is installed, open a Java file and notice that, automatically, the file name appears in the status bar, as shown below:

    first open

Creating a Status Element Provider

In this section...

  1. Create a Java class called WhichElementStatusElementProvider, and fill it out as follows:
    public class WhichElementStatusElementProvider implements StatusLineElementProvider {
        
        private WhichElementPanel whichElementPanel;
        public WhichElementStatusElementProvider() {
            whichElementPanel = new WhichElementPanel();
        }
        
        public Component getStatusLineElement() {
            return whichElementPanel;
        }
        
        static class WhichElementPanel extends JPanel {
            private JLabel iconLabel;
            
            private JTextField whichElementTextField;
            
            WhichElementPanel() {
                super(new FlowLayout(FlowLayout.LEADING, 0,0));
                
                iconLabel = new JLabel(){
                    Point tooltipLocation;
                    
                    // Consider the font's size to compute the location of the
                    // tooltip:
                    public void addNotify() {
                        super.addNotify();
                        tooltipLocation = new Point(0, -2 * getFont().getSize());
                    }
                    
                    public Point getToolTipLocation(MouseEvent event) {
                        return tooltipLocation;
                    }
                };
                
                add(iconLabel, BorderLayout.WEST);
                
                // Create the text field:
                whichElementTextField = new JTextField(40) {
                    Point tooltipLocation;
                    
                    // Consider the font's size to compute the location of the
                    // tooltip:
                    public void addNotify() {
                        super.addNotify();
                        tooltipLocation = new Point(0, -2 * getFont().getSize());
                    }
                    
                    public Point getToolTipLocation(MouseEvent event) {
                        return tooltipLocation;
                    }
                };
                
                // Set the text field to read-only:
                whichElementTextField.setEditable(false);
                
                add(whichElementTextField, BorderLayout.CENTER);
            }
            
            void setIcon(Icon icon) {
                iconLabel.setIcon(icon);
            }
            
            void setIconToolTip(String text) {
                iconLabel.setToolTipText(text);
            }
            
            void setText(String text) {
                whichElementTextField.setText(text);
            }
            
            public void setToolTipText(String text) {
                whichElementTextField.setToolTipText(text);
            }
        }
    }
  2. Check that your import statements are as follows:
    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.FlowLayout;
    import java.awt.Point;
    import java.awt.event.MouseEvent;
    import javax.swing.Icon;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import org.openide.awt.StatusLineElementProvider;
  3. As in the previous section, register your new class in the META-INF/services folder, this time in org.openide.awt.StatusDisplayer, as shown below:

    metainf

  4. Finally...
    public class WhichElementTask implements CancellableTask<CompilationInfo> {
        
        private WhichElementJavaSourceTaskFactory whichElementJavaSourceTaskFactory;
        private FileObject fileObject;
        private boolean canceled;
        WhichElementStatusElementProvider.WhichElementPanel whichElementPanel;
        
        WhichElementTask(WhichElementJavaSourceTaskFactory whichElementJavaSourceTaskFactory,FileObject fileObject) {
            this.whichElementJavaSourceTaskFactory = whichElementJavaSourceTaskFactory;
            this.fileObject = fileObject;
        }
        
        private static final Collection NO_MODIFIERS = Collections.emptySet();
        
        public void run(CompilationInfo compilationInfo) {
            // Find the TreePath for the caret position:
            TreePath tp =
                    compilationInfo.getTreeUtilities().pathFor(whichElementJavaSourceTaskFactory.getLastPosition(fileObject));
            
            // if cancelled, return:
            if (isCancelled()) {
                return;
            }
            
            // Get Element:
            Element element = compilationInfo.getTrees().getElement(tp);
            
            // if cancelled, return:
            if (isCancelled()) {
                return;
            }
            
            String status = "";
            String iconToolTip = "";
            Icon icon = UiUtils.getElementIcon(ElementKind.PARAMETER, NO_MODIFIERS);
            
            if (element != null) {
                String modifiers = element.getModifiers().toString();
                if (modifiers.startsWith("[") && modifiers.endsWith("]")) {
                    modifiers = modifiers.substring(1, modifiers.length() -1).replaceAll(",", "").trim();
                }
                iconToolTip =  modifiers + (modifiers.length() > 0 ? " " : "");            
                icon = UiUtils.getElementIcon(element.getKind(), element.getModifiers());
                
                if (element instanceof PackageElement) {
                    PackageElement packageElement = (PackageElement) element;
                    status = packageElement.toString();
                    iconToolTip += element.getKind().name().toLowerCase();
                } else if (element instanceof TypeElement) {
                    TypeElement typeElement = (TypeElement) element;
                    status = typeElement.getQualifiedName().toString();
                    iconToolTip += element.getKind().name().toLowerCase();
                } else if (element instanceof VariableElement) {
                    VariableElement variableElement = (VariableElement) element;
                    status = variableElement.toString() + ":" + variableElement.asType().toString();
                    iconToolTip += element.getKind().name().toLowerCase();
                } else if (element instanceof ExecutableElement) {
                    ExecutableElement executableElement = (ExecutableElement) element;
                    // Method
                    if (element.getKind() == ElementKind.METHOD) {
                        status = executableElement.getEnclosingElement().toString()
                                + "."
                                + executableElement.toString()
                                + ":"
                                + executableElement.getReturnType().toString();
                        iconToolTip += element.getKind().name().toLowerCase();
                    } else if (element.getKind() == ElementKind.CONSTRUCTOR) { // CTOR - use enclosing class name
                        status = executableElement.getEnclosingElement().toString()
                                + "."
                                + executableElement.toString();
                        iconToolTip += element.getKind().name().toLowerCase();
                    }
                }
            }
            
            WhichElementStatusElementProvider.WhichElementPanel localWhichElementPanel = getWhichElementPanel();
            
            // Set the info:
            if (localWhichElementPanel != null) {
                localWhichElementPanel.setIcon(icon);
                localWhichElementPanel.setIconToolTip(iconToolTip);
                localWhichElementPanel.setText(status);
                localWhichElementPanel.setToolTipText(status);
            }
        }
        
        private WhichElementStatusElementProvider.WhichElementPanel getWhichElementPanel() {
            if (whichElementPanel == null) {
                StatusLineElementProvider statusLineElementProvider = (StatusLineElementProvider) Lookup.getDefault().lookup(WhichElementStatusElementProvider.class);
                if (statusLineElementProvider != null) {
                    whichElementPanel = (WhichElementStatusElementProvider.WhichElementPanel) statusLineElementProvider.getStatusLineElement();
                }
            }
            return whichElementPanel;
        }
        
        /**
         * After this method is called the task if running should exit the run
         * method immediately.
         */
        public final synchronized void cancel() {
            canceled = true;
        }
        
        protected final synchronized boolean isCancelled() {
            return canceled;
        }
    }
  5. Install the module again.

Send Us Your Feedback

Next Steps

For more information about creating and developing NetBeans Module, see the following resources: