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.
NoClassDefFoundError
s?
ExplorerPanel
is disabled when I open it inside another component
TemplateWizard.Iterator
?
SystemOption
via layer, but changes are not persisted...?
NoClassDefFoundError
s?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.
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
FileObject
s.
Besides the fact that virtually everything else in the APIs that
works with files expects FileObject
s, these have a
number of advantages:
However FileObject
s must always exist, unlike File
s.
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
File
s, unless you need to interface to external
tools. However, in case translation from one to the other is necessary:
To look for a FileObject
representing a
File
, use
FileUtil.toFileObject(File)
.
To look for a File
from a
FileObject
, you may use
FileUtil.toFile(FileObject)
.
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 File
s 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 FileObject
s 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.
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 }
*.class
file) it came from?
A: Use the Classpath API, documented separately from the Open APIs.
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
TopComponent
s
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.
A: Yes, you can do all of these things. Refer to the Actions API for details.
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... } } }
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.
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.
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.
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)
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.
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.
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.
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.
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.
CallbackSystemAction
s such as Find or
Delete can easily have different bindings in each component,
using ActionMap
.
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.
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.
ExplorerPanel
is disabled when I open it inside another componentIf you have an ExplorerPanel
with some nodes in it,
and these nodes have context menus containing some
NodeAction
s (including CookieAction
s), 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:
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.
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();
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.