站内搜索: 请输入搜索关键词
当前页面: 在线文档首页 > NetBeans API Javadoc 4.1.0

Open APIs FAQ - NetBeans API Javadoc 4.1.0

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.

Also see NetBeans developer FAQs on the web site with more questions and answers.

  1. Help! What is my classpath? Why am I getting NoClassDefFoundErrors?
  2. Files vs. file objects
  3. URIs and URLs
  4. Files from nodes
  5. Finding classes by name
  6. Windows & dialogs
  7. Installing items to Main Window's menus & toolbars
  8. Accessing the cursor/selection in the Editor
  9. Using submenus
  10. Adding items to popup menus
  11. Why did a filesystem not find my resource?
  12. Are there APIs to standard NetBeans modules?
  13. Changing source code in the Editor
  14. Providing documentation for a module
  15. Sanity-checking module manifests
  16. Tracking selections in the Explorer
  17. Binding one key to more than one action
  18. My context menu on an ExplorerPanel is disabled when I open it inside another component
  19. Can I make a template that creates multiple files without writing a TemplateWizard.Iterator?
  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. Launch internal web browser?
  23. Listen to renames, deletes, creations, modifies, saves of files?
  24. Have legacy preferences storage; can I integrate with a SystemOption?
  25. My top components come up blank/bogus after an IDE restart - what to do??
  26. Should I use NodeAction or CookieAction?
  27. What is a "data object representation class" anyway?
  28. How to capture double-click on a node to do something else? What about when a template is instantiated?
  29. Why do my nodes in the Explorer always have an expand-box by them, even though they have no children?
  30. How do I tell when a piece of source code has been modified by the user?
  31. Determine which method etc. the cursor is on at the moment?
  32. Can I add templates from my module? What is the easiest way to do this?
  33. Can I add syntax coloring for my own data object/MIME type?
  34. What is the relationship between EditorCookie, StyledDocument, and NbDocument & EditorSupport?
  35. Save, rename a file?
  36. What's the diff between a source file, a source element, and a class element?
  37. I'm having trouble implementing this filesystem....
  38. How do I add specific instances of a class to a folder (e.g. menus())?
  39. I added a SystemOption via layer, but changes are not persisted...?

Help! What is my class path? Why am I getting NoClassDefFoundErrors?

You probably compiled your module with another module in the compile-time class path, but forgot to declare a dependency on that module in your manifest. This is the most common diagnosis, but there are other possible causes.

For help on working with class paths, please see the Class Path document.

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 not usually 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 filesystem 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.

However FileObjects must always exist, unlike Files.

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 necessary:

URIs and URLs

Q: What is the difference?

A: A URL is a kind of URI. URNs such as urn:oasis:foo are URIs but not URLs.

Q: Should I use java.net.URI or java.net.URL?

A: Whichever is more convenient. URLs must use a registered URL protocol and cannot handle URNs; there is slightly more overhead in making a URL than a URI, but not much. URI provides better methods for relativizing and canonicalizing URLs as well as other operations on the syntactic structure. To directly load content you need to use a URL.

Q: Can I interconvert URIs and URLs?

A: Yes, use uri.toURL() and URI.create(url.toExternalForm()).

Q: Can I interconvert Files and URIs?

A: Easily. Use file.toURI(). In the other direction, use new File(uri).

For URLs, go through URI. Never use file.toURL(); it does not handle unusual characters correctly.

Careful with file URLs/URIs denoting directories. NetBeans APIs generally expect these to end in a slash (/). However file.toURI() will not end in a slash if the file does not currently exist! Be sure to check if the URI ends in a slash and add one if not, if you in fact know that the File is intended to represent a directory.

Q: Can I interconvert FileObjects and URLs?

A: Use fileObject.getURL(), or URLMapper methods for more control over the kind of returned protocol; in the other direction, use URLMapper.findFileObject(url).

For URIs, go through URL.

Q: How do jar URLs work?

A: Unlike e.g. URLClassLoader, in the NetBeans APIs file:/tmp/foo.jar refers to the raw byte contents of foo.jar. To refer to the root entry of the JAR (e.g. for use as a classpath entry) you must use jar:file:/tmp/foo.jar!/. FileUtil has methods (getArchiveFile, getArchiveRoot, and isArchiveFile) to help you convert between these representations.

Q: Which URL protocols are used in NetBeans?

A: Several, including some custom protocols:

file

For representing files on disk.

jar

For representing entries inside JARs and ZIPs, including the root directory entry.

nbres

A resource loaded from a NetBeans module (or technically the cross-module class loader), e.g. nbres:/org/netbeans/modules/foo/resources/foo.dtd may load the same thing as jar:file:/opt/netbeans/ide4/modules/org-netbeans-modules-foo.jar!/org/netbeans/modules/foo/resources/foo.dtd.

nbresloc

Same, but transparently localized and branded according to the usual conventions, e.g. nbresloc:/org/netbeans/modules/foo/resources/foo.html may actually load the same thing as nbres:/org/netbeans/modules/foo/resources/foo_nb_ja.html.

nbdocs

Same as nbresloc but also searches in docs/ subfolders of installation directories, e.g. nbdocs:/org/netbeans/modules/usersguide/ide.css may work like file:/opt/netbeans/ide4/docs/org/netbeans/modules/usersguide/ide.css.

nbinst

Loads installation files using InstalledFileLocator in installation directories, e.g. nbinst:///modules/ext/some-lib.jar may load the same thing as file:/opt/netbeans/ide4/modules/ext/some-lib.jar.

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 the Classpath API, documented separately from the Open APIs.

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 DialogDisplayer.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.getRegistry ().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)Lookup.getDefault().lookup(DataLoaderPool.class)).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);
}
You need to know the implementation class of the foreign data object.

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 filesystem not find my resource?

Q: I tried to find a resource on a filesystem (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; filesystems normally 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.

The IDE may also automatically refresh all files at various times: when the main window receives focus; when you select File | Refresh All Files; after an Ant script finishes running.

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.

All published NetBeans APIs (development version)

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.

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 global keymap (get Keymap from Lookup) 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 went 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.

CallbackSystemActions such as Find or Delete can easily have different bindings in each component, using ActionMap.

Launch internal web browser?

Just call HtmlBrowser.URLDisplayer.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.

I added a SystemOption via layer, but changes are not persisted...?

To correctly register a SystemOption, you need to place a *.settings file somewhere beneath Services/ in your XML layer. Normally you will also create a .shadow somewhere in UI/Services/ to ensure it is displayed to the user in the Options window.

Sometimes people try to put the *.settings file directly in UI/Services/. This will not work correctly. SharedClassObject.findObject will create an instance of your option - but a default instance only, because it only looks in Services/ to find a default or customized version of the option bean. Changes will never be persisted to disk, because findObject does not know of a *.settings file associated with the option and will consider it to be a transient (in-memory) option only. In this situation you might see the symptom that changes are persisted only if you open the Options dialog and select your setting before first attempting to access it from code.

Check the Services API for the full scoop.

Also make sure that you are firing a property change from your setter methods—typically this is done using putProperty(PROP_FOO_BAR, newValue, true)—and that the property name matches the introspected JavaBeans property name, i.e. for setFooBar the property name should be fooBar. Remember as well that if newValue.equals(oldValue), no property will be fired (standard behavior of java.beans.PropertyChangeSupport)—this can happen if you have a complex structured value type and attempt to modify a portion of the structure without creating a fresh value object, in which case oldValue == newValue.

My context menu on an ExplorerPanel is disabled when I open it inside another component

If you have an ExplorerPanel with some nodes in it, and these nodes have context menus containing some NodeActions (including CookieActions), you need to be careful how you open this panel in NetBeans 3.4.x or earlier. If you call explorerPanel.open() to open it as a regular docked TopComponent, all should be well. But:

  1. If you open it inside another TopComponent, as a sub-panel, the node actions will generally be disabled. This is because the state of the actions is determined by the global TopComponent.Registry's notion of the activated nodes, which is dependent on the activatedNodes of the selected (active) TopComponent. The fix is simply to have the parent TopComponent listen to selection changes in the explorer panel's ExplorerManager and propagate them as the activated nodes for the outermost component as understood by the NetBeans window system. This is essentially what ExplorerPanel does for you automatically when it is opened directly.

  2. If you open the panel as a dialog by using it as the innerPane component in a DialogDescriptor, then the context menu should work. But if the panel is a subcomponent in the dialog, it will not work - you need to make the ExplorerPanel the actual top-level message component.

In NetBeans 3.5 the actions system is smarter (details) and does not depend on global state - the context menu for a node generated by explorer views is determined solely by the node itself, regardless of where or how the explorer view is being displayed. Therefore you can ignore this item in NetBeans 3.5 - it should just work as expected.

In NetBeans 3.5 you can also make a context-sensitive action work naturally as a toolbar button within a component with a node selection (such as an Explorer). In previous releases this would only be possible if the component had focus, and was a TopComponent with a correctly set node selection. Now you can be more flexible:

TopComponent tc = new TopComponent();
tc.setLayout(new BorderLayout());
ExplorerPanel p = new ExplorerPanel();
p.add(new BeanTreeView());
tc.add(p, BorderLayout.CENTER);
Node n = yourRootNodeHere();
p.getExplorerManager().setRootContext(n);
Action a = SystemAction.get(YourNodeActionHere.class);
// This will only follow the selection in p's ExplorerManager:
Action a2 = ((ContextAwareAction)a).createContextAwareInstance(p.getLookup());
JButton b = new JButton(a2);
tc.add(b, BorderLayout.SOUTH);
tc.open();

Can I make a template that creates multiple files without writing a TemplateWizard.Iterator?

Sure. First search the online help for group template and macro template to get the basic idea. Here is a sample *.group file from the Open APIs Support module, and here is the defining XML layer (look for Templates/Options_API/). When the user enters Foo as a name in the New wizard, this will create files like FooSettings.java, as well as Bundle.properties.

Note that only the group file is marked as a template in the XML layer, so the other files are hidden in the New wizard.

If you have a macro like __NAME__ in a *.java file included in a group template you can also use the syntax __NAME$Old$New$Fallback__ which has the effect of using most of __NAME__ while replacing the suffix Old with New if possible, or replacing the entire string with Fallback if the suffix match fails for some reason. Here is an example from the same group template.

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.
Built on May 4 2005.  |  Portions Copyright 1997-2005 Sun Microsystems, Inc. All rights reserved.