Overview

Open APIs FAQ

This Frequently Asked Questions list attempts to address some questions that many people have about how to perform common tasks with the APIs. In most cases there is a straightforward solution but you might not think of it immediately.

If these things are straightforward to do, why isn't there just a utility method to do them? In these cases the desired functionality is really a multi-stage process that the APIs require you to logically break up into separate pieces, each of which the APIs can handle cleanly. That way you have more control over the details.

  1. Files vs. file objects
  2. Files from nodes
  3. Finding classes by name
  4. Windows & dialogs
  5. Installing items to Main Window's menus & toolbars
  6. Accessing the cursor/selection in the Editor
  7. Using submenus
  8. Adding items to popup menus
  9. Why did a file system not find my resource?
  10. Are there APIs to standard NetBeans modules?
  11. Changing source code in the Editor
  12. Providing documentation for a module
  13. Sanity-checking module manifests
  14. Tracking selections in the Explorer
  15. Binding one key to more than one action
  16. Adding a library JAR from outside the IDE installation
  17. How can I run jar, serialver, etc. from within my module?
  18. Compile a bunch of sources in a batch?
  19. What should go into ModuleInstall.installed and what into .restored?
  20. Create a new file and set its contents to something?
  21. I added a bunch of cookies to my data object, but still things like Open are not on the context menu.
  22. Pop up a dialog prompting for a directory to save to?
  23. Launch internal web browser?
  24. Listen to renames, deletes, creations, modifies, saves of files?
  25. Have legacy preferences storage; can I integrate with a SystemOption?
  26. My top components come up blank/bogus after an IDE restart - what to do??
  27. Should I use NodeAction or CookieAction?
  28. What is a "data object representation class" anyway?
  29. How to capture double-click on a node to do something else? What about when a template is instantiated?
  30. Why do my nodes in the Explorer always have an expand-box by them, even though they have no children?
  31. How do I tell when a piece of source code has been modified by the user?
  32. Determine which method etc. the cursor is on at the moment?
  33. Where the heck is the classpath?
  34. Can I add templates from my module? What is the easiest way to do this?
  35. Can I add syntax coloring for my own data object/MIME type?
  36. What is the relationship between EditorCookie, StyledDocument, and NbDocument & EditorSupport?
  37. Save, rename a file?
  38. What's the diff between a source file, a source element, and a class element?
  39. I'm having trouble implementing this file system....
  40. Pre-/post-processors on Java compiler?
  41. How do I add specific instances of a class to a folder (e.g. menus())?

Files vs. file objects

Q: What exactly is the difference between a filename on disk and a FileObject? How do I convert them?

A: Raw files on disk are generally represented in Java using java.io.File. These correspond directly to what the operating system thinks of as a file.

In the Open APIs, raw files are very rarely manipulated directly. Rather, you should almost always be using FileObjects. Besides the fact that virtually everything else in the APIs that works with files expects FileObjects, these have a number of advantages:

  1. The file system they come from need not correspond to physical files on disk, but could be JAR file entries, or potentially database entries, FTP downloads, etc.
  2. The rest of the IDE can interact with them in an object-oriented fashion, including locking and change notification.
  3. File objects "know" where they are within a file system, and thus their package name (alternately, resource path) is clearly defined. This is very important for the consistency of the Repository, and for the proper functioning of services such as debugging.
Even such things as explicitly invoking external compilation on a file should not require manual conversion to a file object. Most likely there is a way to do what you want without using Files, unless you need to interface to external tools. However, in case translation from one to the other is really needed:

Files from nodes

Q: I have a node from somewhere (e.g. a NodeAction). I think it corresponds to a file in the Repository; how can I get that file?

A: Just get the DataObject as a cookie and go from there:

Node n = ...;
DataObject dob = (DataObject) n.getCookie (DataObject.class);
if (dob == null) {
    // not a file node
} else {
    // could also get all files in the data object, if desired:
    FileObject fo = dob.getPrimaryFile ();
    // do something with fo
}

Finding classes by name

Q: How do I find a Java class by name in the Repository? What about the source file (or *.class file) it came from?

A: Use ClassElement.forName(String). This will give you the class element, if one can be found by that name.

You can try to look for the file it came from, too. However, be aware that elements do not need to come from any physical source: e.g. ClassElement.forName("java.util.Vector") will probably give you an element that is not based on any file, but rather the live Vector class in the Java VM. With that caveat:

ClassElement ce = ClassElement.forName (myClassName);
if (ce != null) {
    SourceElement se = ce.getSource ();
    if (se != null) {
        DataObject dob = (DataObject) se.getCookie (DataObject.class);
        if (dob != null) {
            FileObject fo = dob.getPrimaryFile ();
            // check if desired that fo.getExt ().equals ("java"), etc.
        } else {
            // didn't specify where it came from
        }
    } else {
        // no containing source, probably a class found only in the VM
    }
} else {
    // not found at all
}

Windows & dialogs

Q: Can I just open up my own frames and dialogs?

A: Yes, you could; but please instead use the standard windowing system to do these sorts of things. Then you will properly handle workspace switching, docking, context help, various keyboard shortcuts, and many other things contributing to the IDE's appearance and functionality.

The Window System API describes the general steps you should use to open up new function windows, if you need such a thing. Specifically, you should use TopComponents for such purposes.

You can also use TopManager.notify(NotifyDescriptor) to show dialogs that interact well with the IDE's window system, and have a number of bits of prebuilt UI. You can use various standard subclasses of NotifyDescriptor to represent simple messages; exceptions; general-purpose dialogs with content panels; or even multi-stage wizards.

Installing items to Main Window's menus & toolbars

Q: Can I add my own items to the menus in the Main Window? What about the toolbars? Can I add complex things to the toolbars like custom palettes? Keyboard shortcuts?

A: Yes, you can do all of these things. Refer to the Actions API for details.

Accessing the cursor/selection in the Editor

Q: Can I find out where the cursor (or selection) is in the Editor?

A: Yes; you need to first get the selected node (which if the Editor is selected, should correspond to the file being edited); get the most recent editor pane open on it; and then access the caret:

Node[] n = TopComponent.getActiveComponent ().getActivatedNodes ();
if (n.length == 1) {
    EditorCookie ec = (EditorCookie) n[0].getCookie (EditorCookie.class);
    if (ec != null) {
        JEditorPane[] panes = ec.getOpenedPanes ();
        if (panes.length > 0) {
            int cursor = panes[0].getCaret ().getDot ();
            String selection = panes[0].getSelectedText ();
            // use this info somehow...
        }
    }
}

Using submenus

Q: Can I install submenus into popups or other menus, instead of a regular action?

A: Yes, any place where the APIs expect to have an item installed into a popup or regular menu, you can provide a submenu instead. Usually this is done with a dummy action whose popup and menu presenters is a submenu. See the Actions API for details.

Adding items to popup menus

Q: Is it possible to add my own items to the popup menus of (someone else's) data object nodes?

A: This is possible using code such as:

DataLoader loader = DataLoaderPool.firstProducerOf (SomeDataObject.class);
if (loader != null) {
    SystemAction[] actions = loader.getActions ();
    SystemAction[] newactions = new SystemAction[actions.length + 2];
    System.arraycopy (actions, 0, newactions, 0, actions.length);
    // Really, take more care that it is not a duplicate,
    // place into a specific position, etc.:
    newactions[actions.length] = null;
    newactions[actions.length + 1] = SystemAction.get (SomeAction.class);
    loader.setActions (newactions);
}
But you should avoid doing this unless it is really critical to usability. Generally service actions should be used in such situations - then the new action will be in the popup under the submenu Tools..., on most nodes. This is much easier to do and safer.

Why did a file system not find my resource?

Q: I tried to find a resource on a file system (or get children of a folder, etc.) and it did not find a file which I know was there. What is happening?

A: If the file was added to a containing directory or otherwise modified very soon before you tried to use the FileSystems API to access it, it is possible the change was not yet visible to the IDE. This can happen if the change is made by use of java.io.File or an external process; file systems which use AbstractFileSystem.refreshTime to implement auto-refreshing caches do not automatically rescan the disk every time a request is made for information about file objects. Rather, when the next refresh occurs, or FileObject.refresh() is called on the containing folder, the cache is updated (and events fired to inform other code). If you know that some sort of external modification of files is possible immediately before you are doing an access, please use refresh() explicitly to make sure the caches are synchronized.

Are there APIs to standard NetBeans modules?

Q: I have looked through the Open APIs for what I want to do, and I realize that actually what I need is not specified in them, because it is a part of a specific NetBeans module - for example, the Form Editor. Are there any APIs available for these?

A: Yes and no. Yes, because all NetBeans modules are Java code, which is written as cleanly as possible and often provides fairly well-defined interfaces to its functionality that could be reusable, and which NetBeans programmers do use to communicate between modules in some case. No, because currently none of these interfaces are properly documented.

For example, the Java Sources Module (which provides a loader for *.java files, and most of their associated functionality such as parsing, compile, source editing, etc.) does have an API, i.e. a set of documented (as Javadoc) conventions as to how other modules may use its services - typically, other modules will provide a DataObject type which subclasses it. NetBeans programmers use this internal API to implement Java-based types such as forms, as well as to provide plug-in functionality such as JavaBeans support.

The reason these calls are not part of the APIs is because no API to them has been carefully thought out and documented. The details of the calls could change greatly from one version to the next, and nobody is tracking such changes. So you can use these module services, but you are on your own if there is an incompatibility. Naturally, it is preferable to use only the Open APIs if that is possible; in some cases it is unreasonable to make this restriction.

Note that any module, not just a standard NetBeans module, can provide an API to other modules. That is, you may decide to release a module which has a published API. Then other modules can access that API, providing they specify dependencies on it using manifest versioning according to the Modules API.

No module should use APIs from the IDE core (i.e. API implementation), however, and no APIs for core classes will be published. The only NetBeans module which connects directly to the core (partially bypassing the Open APIs) is the Auto Update module, since the APIs do not specify any mechanism for querying or modifying the set of installed modules (by design); so, this module depends on specifics of the NetBeans core implementation. Similarly, the API Support and Scripting modules use one call in the core to install test modules.

Changing source code in the Editor

Q: How can I get access to the source code for a class in the Repository? I want to change some methods, etc.

A: You do not need to do this! Using the Java Hierarchy API, you can find class elements (see above), which are automatically parsed from source code, examine their contents such as methods, and then change any of these contents - the code in the Editor will change automatically. Just remember to save the file afterwards if necessary, using a SaveCookie (gotten from the data object gotten from the source element). The bodies of methods and so on are not parsed, but you can still set them without needing to find the method in the source code. Properties such as access modifiers can be adjusted without any need for parsing or generating Java source - the element implementation handles this for you.

Note that modification, and saving, is possible even if the file is not visually open in an Editor window. To make sure the file is visible, use OpenCookie.open().

If you really want direct access to the text of a file, you can use EditorCookie.getDocument() and then use the Swing API to manipulate it. (But please use NbDocument.runAtomicAsUser(...)!) The Java Hierarchy API should normally synchronize with this document automatically, according to the parser timeout.

Providing documentation for a module

Q: Is there any standard way of providing documentation for my new module?

A: Yes. See the Modules API which describes how to include JavaHelp documentation in a module under Help | Contents; and you can provide rich context help rather easily, linking into the same documentation.

Sanity-checking module manifests

Q: My module does not appear to be loading correctly, and I suspect that the manifest file is not in the correct format. How do I make sure?

A: First of all, manifest file processing tools are according to the JAR specification required to accept and ignore unrecognized attributes - so if you mistyped the name of an attribute (they should be case-insensitive) the IDE will not inform you of this. If you supplied an invalid value, however, or some sort of runtime problem occurred (e.g. a specified instance in the JAR did not implement a required interface), then the IDE should produce an error message - if it does not, that is a bug which should be reported through the normal channels.

Using the API Support module, you may browse to module manifests in the Explorer and they should be recognized as such. Select the node and check its properties and subnodes in the Explorer; you can see at a glance how the IDE is parsing your manifest, and if there are any errors.

The most common error is forgetting that there is a distinction between global attributes (specified in the head of the manifest before any blank lines) and manifest sections with their own attributes (specified in individual blocks after the header, with blank lines separating them), which are only treated specially by the IDE if they contain the attribute OpenIDE-Module-Section. Please see the Modules API for details and example manifests.

Tracking selections in the Explorer

Q: How should I keep track of what the current node selection in the Explorer window is? Should I use the ExplorerManager?

A: You can do that. If you can somehow find a class implementing ExplorerManager.Provider then you can get the Explorer manager. This provider might in fact be a TopComponent in the TopComponent.Registry, if for example it was actually a ExplorerPanel. But this is bad style - for example, if someone wrote a TopComponent that included an ExplorerPanel only as a subcomponent, and manually managed the node selection, this trick would fail.

Rather, if you know which top component you care about, you can just call TopComponent.getActivatedNodes() and this will work correctly even for non-Explorer components with a node selection, such as Editor panes open on Java sources.

Better still is to be agnostic about which top component should be providing the activated nodes, and just listen to changes in the TopComponent.Registry.PROP_ACTIVATED_NODES (or TopComponent.Registry.PROP_CURRENT_NODES as appropriate).

But best of all is not to have to ever directly pay attention to the node selection. If you only need to know the node selection in order to make some user action enabled or not, you should simply extend NodeAction; this class does all the dirty work for you of listening to changes in the node selection and updating its state automatically.

Not yet written

Sorry - this question and its answer have not been written yet, though it is planned. Please ask this question on the Open APIs mailing list if you are curious.

Binding one key to more than one action

Q: Is it possible to bind one shortcut key to more than one action, so they will all be run? What about binding a key differently in different windows?

A: No, the TopManager.getGlobalKeymap() is a master keymap for the whole IDE, and like all keymaps accepts only one action per binding. If you want multiple actions to be run, you must create a "wrapper" action that runs them all in turn (or in parallel).

You may bind a key differently in different windows, by using the normal Swing techniques of binding keystrokes to components. In fact, some work when into implementing the global map so that it would work across arbitrary components; it is overridden by local bindings, such as navigation keys on dialogs or Explorer trees, or various editing keys in the Editor.

Before you bind a key performing a high-level specific action, such as F9 for CompileAction, to a different action in a local component (e.g. window), think carefully whether this is really the right approach. In many cases the UI of your extension and the IDE as a whole will be better served by leaving the key binding alone, and instead providing an appropriate cookie, action performer, or other callback associated with your component, so that the action (and potentially other code unknown to you) will function naturally. If you must rebind a global key, consider whether it is appropriate to determine the current key binding for the action (if any) in the global keymap, and use this keystroke to rebind - so user customizations will remain intact.

Adding a library JAR from outside the IDE installation

Q: Can my module add a library JAR to the classpath from outside the IDE installation? For example, I have an application called Etch-a-Kvetch for modeling customer call response systems, and I want to build a module to integrate it into the IDE. The user may already have Etch-a-Kvetch installed on their disk, and I want to reuse the eak.jar main library JAR in my module. It is not present in the IDE's installation directory. Can I add it to the IDE's classpath so my module can use it?

A: Not easily. You have a few options:

  1. Add an entry to ide.cfg. For example:

    -cp:a c:\eak\lib\eak.jar
    

    This startup file, on Windows installations, provides the ability to add classpath entries to the IDE's Java invocation. On Unix, such a file is not read so other techniques might be needed.

    Pros: Adds the library as desired. Cons: Very fragile. Assumes that the ide.cfg is actually read, which is not always true (it is not read by the *.bat scripts or the Unix scripts, for example). Easy to clobber existing user customizations. Dependent on the exact structure of the IDE's bin/ directory, which has changed quite a bit in the past and could change again. Places library in startup classpath, whereas it is preferred to have it in the module classloader only. Must be done before the IDE starts, requiring some kind of manual installation step and making updating your module very difficult.

  2. Duplicate eak.jar in modules/ext/. That is, ship a copy of the required JAR file inside the IDE installation, and continue to refer to dynamic resources in the desired external location.

    Pros: Simple to implement and should be reliable. Version of the library shipped can be controlled to match that which the module was compiled against, avoiding potential version skew. Cons: Impractical when the library is physically very large, or there are licensing issues involved in redistributing it. Bugfix upgrades to the master library may not be reflected in the IDE's copy.

  3. Partition your module and use a new classloader. In other words: logically divide your module into two halves. The first half will form a compilation unit depending on the Open APIs and will contain all of the classes referred to directly in the module manifest (the installer, data loaders, or whatever you have). It should contain one or more interfaces or similar constructs which describe what it expects the second half to implement. The second half will form a separate compilation unit, depending on the first half, possibly the Open APIs, and also eak.jar. It should contain implementations of the interfaces specified in the first half. The first half, at install/restore time (or lazily when any functionality is needed) should create a new URLClassLoader whose parent should be the first half's classloader, and including as URLs the locations of both the second half as a JAR and the library JAR; using this classloader, look up the implementation classes by name; create new instances of them; cast them to the interface types; and begin using them. The second half should probably be placed in a separate JAR file; if not, it will be necessary to subclass the dynamic classloader to not delegate class loads for the implementation packages to its parent. Either way, it is strongly recommended that the build process enforce that the first half compile without reference to the second.

    Pros: Compliant with the Open APIs and reliable. The library JAR may be located anywhere at runtime and could even be moved or replaced at runtime. Cons: Potentially complex to implement. Some use of reflection required, though it should be safe. More complex build and test procedure for the module. The partition between the two halves must be carefully chosen, especially for large modules, to minimize the complexity of the interfaces and push as much implementation as possible to one side or the other.

Launch internal web browser?

Just call TopManager.showUrl(URL) to open the IDE's default web browser (internal or external) on some page. If you want to make a menu item to do this, just create a *.url file containing the URL and place it in a menu folder (this only works with the User Utilities module installed, note). HtmlBrowser and its inner classes provide more powerful options.


Built on December 12 2001.  |  Portions Copyright 1997-2001 Sun Microsystems, Inc. All rights reserved.