Adding an extra source directories is possible in case you need to create a separate output JARs (besides the module itself), generally with its own special classpath.
In your module's project.xml, add a declaration of the source root just before </data>:
<extra-compilation-unit>
<package-root>othersrc</package-root>
<classpath>...anything it might need to compile against...</classpath>
<built-to>build/otherclasses</built-to>
<built-to>${cluster}/modules/ext/other.jar</built-to>
</extra-compilation-unit>
This declaration has no effect on the build, but lets you work with the sources in the IDE's code editor.
You will separately need to add a target to your build.xml to compile and package these sources however you like. (You can name your target netbeans-extra and it will get run automatically toward the end of the module's build cycle.) If you define properties like a special classpath in project.properties, you can use the values in both build.xml and project.xml to minimize duplication.
You can also create a plain Java SE project in a subdirectory of your module and bundle its JAR. DevFaqWrapperModules describes a related technique.
Read the harness/README file under your Netbeans installation directory for information about issues like this one. The build harness has many capabilities not exposed through the GUI.
First, make sure you have Mercurial installed on your machine, along with its requirements such as python.
Then, from the command line, you run
hg clone http://hg.netbeans.org/main/to get the full Platform and IDE sources. If you also want the contrib/ and misc/ repositories, it is best to first install the Forest extension for Mercurial, and then run
hg fclone http://hg.netbeans.org/main/ nb_all
To build, simply run Ant in the root of your NetBeans sources. The build will appear in $NB_SRC/nbbuild/netbeans/.
(as of June, 2009)
1. Go to the nightly build download site:
http://bits.netbeans.org/dev/nightly/
2. Click the link for the build you want.
3. You will be shown an index page which makes no mention of how you can get a ZIP file or a source archive, so ignore it.
4. Add "zip/" to the end of the URL in your browser's address bar and hit enter. In other words, the complete URL might look like this:
http://bits.netbeans.org/dev/nightly/2008-06-24_02-01-08/zip/
5. There are about a dozen links on that page. The one you want is begins with "netbeans-trunk-nightly" and ends with "-src.zip" Click that link to download the source archive.
Override the public Action getActions(boolean context) method of your node (99% of the time you can ignore the boolean parameter).
If this node is really a DataNode for your own file type, instead see DevFaqActionAddFileMime.
The simplest way is to run New Action Wizard (File > New... > Module Development > Action) which creates an action for you and registers it in your layer.xml.
You can also declare your action in your layer manually:
<folder name="Editors">
<folder name="text">
<folder name="x-java">
<folder name="Popup">
<file name="org-mymodule-MyAction.instance"/>
</folder>
</folder>
</folder>
</folder>
You can also use Editors/Popup/ to add an action to all editor kits.
The simplest way is to run File > New... > Module Development > Action which creates an action for you and registers it in your layer.xml.
You can also declare your action in your layer manually:
<folder name="Loaders">
<folder name="text">
<folder name="html">
<folder name="Actions">
<file name="org-mymodule-MyAction.instance"/>
</folder>
</folder>
</folder>
</folder>
You can replace text/html with text/x-java, text/x-ant+xml, text/x-jsp, image/png, etc.
However, this still may not work depending on how the data loader for the type works. The DataLoader implementation has to override actionsContext() and return this path if it wants to load the Action instances from there. If the data loader you are interested in does not yet do this, please first file a bug report to make sure this is fixed in a future release; as an inferior workaround, you can use e.g.
DataLoader loader = DataLoaderPool.getDefault().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);
// More realistically: take 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 to implement this workaround. You should avoid doing this unless it is really critical to usability, and replace it with layer-based declarative actions as soon as that is available (you did file that bug report, right?).
Add to your layer:
<folder name="Loaders">
<folder name="folder">
<folder name="any">
<folder name="Actions">
<file name="org-mymodule-MyAction.instance"/>
</folder>
</folder>
</folder>
</folder>
The simplest way is to run New Action Wizard (File > New... > Module Development > Action) which creates an action for you and registers it in your layer.xml file.
Register the panel like this in the layer (here for Java SE projects):
<filesystem>
<folder name="Projects">
<folder name="org-netbeans-modules-java-j2seproject">
<folder name="Customizer">
<file name="Bla.instance">
<attr name="instanceCreate" methodvalue="org.bla.BlaPanelProvider.createBlaPanel"/>
</file>
</folder>
</folder>
</folder>
</filesystem>
Then create it:
public class BlaPanelProvider implements ProjectCustomizer.CompositeCategoryProvider {
public static BlaPanelProvider createBlaPanel() {
return new BlaPanelProvider();
}
@Override
public Category createCategory(Lookup arg0) {
ProjectCustomizer.Category toReturn = null;
toReturn = ProjectCustomizer.Category.create(
"Bla",
"Bla",
null,
null);
return toReturn;
}
@Override
public JComponent createComponent(Category arg0, Lookup arg1) {
return new BlaPanel();
}
}
See also Project Properties GUI for custom project templates
See also: Common Project Actions
Just register an instance of the action in your XML layer under Actions/SomeFolder and add shadow reference in Projects/Actions/. It should implement a ContextAwareAction interface, e.g. a CookieAction.
<filesystem>
<folder name="Actions">
<folder name="SomeFolder">
<file name="projectcontextmenudemo-DemoAction.instance"/>
</folder>
</folder>
<folder name="Projects">
<folder name="Actions">
<file name="projectcontextmenudemo-DemoAction.shadow">
<attr name="originalFile"
stringvalue="Actions/SomeFolder/projectcontextmenudemo-DemoAction.instance"/>
</file>
</folder>
</folder>
</filesystem>
See also How do I add an action to a project popup menu of a specific project type?
You can install an action into the context menu of all projects simply by adding to your layer under the folder Projects/Actions/. Your action should be context-sensitive, meaning it should be a placeholder which implements ContextAwareAction; the context-aware derived action will do the real work. Generally it will look for an instance of Project in the supplied Lookup (context).
If you just override isEnabled on the derived action based on the context, the menu item will always be present, though it will be greyed out in the case of inappropriate projects. If you want to hide the menu item for all but relevant projects, you will need to make the derived action implement Presenter.Popup and the resulting component should also implement DynamicMenuContext.
This is a lot of boilerplate (hopefully a future revision of the APIs will make it simpler and/or the module development support will include a wizard for it), so it is better to show an example you can copy and customize. The following trivial action shows the location of a project so long as its name comes in the first half of the alphabet:
package projectcontextmenudemo;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.DynamicMenuContent;
import org.openide.awt.Mnemonics;
import org.openide.filesystems.FileUtil;
import org.openide.util.ContextAwareAction;
import org.openide.util.Lookup;
import org.openide.util.actions.Presenter;
public class DemoAction extends AbstractAction implements ContextAwareAction {
public void actionPerformed(ActionEvent e) {assert false;}
public Action createContextAwareInstance(Lookup context) {
return new ContextAction(context);
}
private boolean enable(Project p) {
assert p != null;
// TODO state for which projects action should be enabled
char c = ProjectUtils.getInformation(p).getDisplayName().charAt(0);
return c >= 'A' && c <= 'M';
}
private String labelFor(Project p) {
assert p != null;
// TODO menu item label with optional mnemonics
return "&Info on " + ProjectUtils.getInformation(p).getDisplayName();
}
private void perform(Project p) {
assert p != null;
// TODO what to do when run
String msg = "Project location: " +
FileUtil.getFileDisplayName(p.getProjectDirectory());
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(msg));
}
private final class ContextAction extends AbstractAction implements Presenter.Popup {
private final Project p;
public ContextAction(Lookup context) {
Project _p = context.lookup(Project.class);
p = (_p != null && enable(_p)) ? _p : null;
}
public void actionPerformed(ActionEvent e) {
perform(p);
}
public JMenuItem getPopupPresenter() {
class Presenter extends JMenuItem implements DynamicMenuContent {
public Presenter() {
super(ContextAction.this);
}
public JComponent[] getMenuPresenters() {
if (p != null) {
Mnemonics.setLocalizedText(this, labelFor(p));
return new JComponent[] {this};
} else {
return new JComponent[0];
}
}
public JComponent[] synchMenuPresenters(JComponent[] items) {
return getMenuPresenters();
}
}
return new Presenter();
}
}
}
and here is how to register it:
<filesystem>
<folder name="Actions">
<folder name="SomeFolder">
<file name="projectcontextmenudemo-DemoAction.instance"/>
</folder>
</folder>
<folder name="Projects">
<folder name="Actions">
<file name="projectcontextmenudemo-DemoAction.shadow">
<attr name="originalFile"
stringvalue="Actions/SomeFolder/projectcontextmenudemo-DemoAction.instance"/>
</file>
</folder>
</folder>
</filesystem>
Applies to: NetBeans 5.0, 5.5, 6.x
While there is no official way to add a custom action to a foreign TopComponent's tab, you can achieve this by installing a hook into the Swing event dispatch thread and listening for the popup activation. Then when the popup is detected, copy the actions from the TopComponent, add your custom actions, and finally set a new popup menu for that particular TopComponent.
// Install hook into the AWT event dispatch thread so we can capture context popup
private void addGlobalContextAction() {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
MouseEvent mouseEvent = (MouseEvent) event;
if (mouseEvent.isPopupTrigger()) {
if (mouseEvent.getSource() instanceof TabDisplayer) {
// Find the TopComponent being wrapped
TabDisplayer tabDisplayer = (TabDisplayer) mouseEvent.getSource();
TabData tabData = tabDisplayer.getModel().getTab(
tabDisplayer.getSelectionModel().getSelectedIndex());
TopComponent tc = (TopComponent) tabData.getComponent();
// Copy existing popup-menu
mouseEvent.consume();
JPopupMenu popup = new JPopupMenu();
boolean separatorJustAdded = false;
for (Action action : tc.getActions()) {
if (action == null) {
if (!separatorJustAdded) {
popup.addSeparator();
separatorJustAdded = true;
}
} else if (action.isEnabled() &&
!(action instanceof FileSystemAction)) {
JMenuItem wrappedAction = new JMenuItem(action);
wrappedAction.setText(removeAmpersand(
action.getValue(Action.NAME)));
popup.add(wrappedAction);
separatorJustAdded = false;
}
}
// Add custom action
popup.add(new AbstractAction("Click me") {
public void actionPerformed(ActionEvent e) {
System.out.println("Yay, you clicked me!");
}
});
tabDisplayer.setComponentPopupMenu(popup);
}
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
}
private String removeAmpersand(Object actionName) {
return ((String) actionName).replaceAll("&", "");
}
Notice however, there are some ugly action filtering going on and keyboard accelerator indications have to be removed from the action text. Furthermore, it's relatively expensive to add such an application-wide hook. As such, this is to be considered a fragile hack until a better solution becomes available.
The simplest way is to run New Action Wizard (File > New... > Module Development > Action) which creates an action for you and registers it in your layer.xml file.
There are three ways to do this, depending on what exactly you need:
CookieAction is used to write actions that are sensitive to what is in the selected Node(s) Lookup. You can specify one or more classes that must be present in the selected Node's Lookup, and some other semantics about enablement.
Being an older class, under the hood it is using Node.getCookie(), so your action will only be sensitive to things actually returned by that method - in other words, only objects that implement the marker interface Node.Cookie can work here.
NodeAction is somewhat more flexible, but requires more code to implement. It is just passed the array of activated nodes whenever that changes, and can choose to enable or disable itself as it wishes. Essentially this is just an action that automagically tracks the global Node selection.
The following is relatively simple and affords a way to perform whatever enablement logic you like (NodeAction can do that too, but this might be a little more straightforward and your code doesn't have to worry about nodes at all: DevFaqWhatIsANode). To understand how this works, see DevFaqTrackGlobalSelection:
public class FooAction extends AbstractAction implements LookupListener, ContextAwareAction {
private Lookup context;
Lookup.Result<Whatever> lkpInfo;
public FooAction() {
this(Utilities.actionsGlobalContext());
}
private FooAction(Lookup context) {
putValue (Action.NAME, NbBundle.getMessage(FooAction.class,
"LBL_Action")); //NOI18N
this.context = context;
}
void init() {
assert SwingUtilities.isEventDispatchThread()
: "this shall be called just from AWT thread";
if (lkpInfo != null) {
return;
}
//The thing we want to listen for the presence or absence of
//on the global selection
lkpInfo = context.lookupResult(Whatever.class);
lkpInfo.addLookupListener(this);
resultChanged(null);
}
public boolean isEnabled() {
init();
return super.isEnabled();
}
public void actionPerformed(ActionEvent e) {
init();
Collection<? extends Whatever> c = lkpInfo.allItems();
//do something with them here
}
public void resultChanged(LookupEvent ev) {
setEnabled (lkpInfo.allItems().size() != 0);
}
public Action createContextAwareInstance(Lookup context) {
return new FooAction(context);
}
}
Applies to: NetBeans 6.5, 6.7
You may have noticed that the examples of adding actions typically look like this:
<folder name="Actions">
<folder name="Build">
<file name="com-foo-SomeAction.instance"/>
</folder>
</folder>
<folder name="Menu">
<folder name="Build">
<file name="pointerToComFooSomeAction.shadow">
<attr name="originalFile" stringvalue="Actions/Build/com-foo-SomeAction.instance"/>
</file>
</folder>
</folder>
And you may have noticed that actions are usually put, not directly into the Menu/ folders, but into subfolders of this Actions/ folder. Then we create .shadow files that act like symbolic links, pointing to the real .instance file . Why all this indirection?
Older versions of the NetBeans UI included the ability to rearrange, and even delete, whole menus or individual menu items, and future ones may again. (Many applications built on NetBeans will not want to expose such customizability, but some do.) The current UI does include a key binding editor; the Actions/ folder can be used from this editor to list available actions, even those which are not currently bound to any keystroke.
Additionally, for actions which are javax.swing.Action but not SystemAction, creating the action instance in only a single place ensures that it acts as a singleton. (While the action class likely has no declared instance fields, it does have some state, notably information about keyboard accelerators which should be displayed in menu presenters.)
Applies to: NetBeans 6.7
No.
Occasionally people want to do something like this, because they want to enhance, for example, the behavior or nodes for Java files or other nodes created by some other modules. However, this is a recipe for disaster - nobody writing a Node subclass does so expecting that random modules will change its internal structures without warning. It is possible to write code that does this sort of thing that looks like it works, but it is sheer luck and it will probably not work for long.
(there, did I say that strongly enough?).
Many modules are designed for extensibility - in fact, Nodes for Java files in the IDE do allow you to add children, actions, etc. They offer an API for doing this sort of thing (for example, adding Actions to Loaders/text/x-java/Actions declaratively); see the beans module for an example of adding sub-nodes to Java classes.
If you want to modify the children/properties/actions/etc. of a Node you did not create, look for an API that lets you do that in a supported way.
If one does not exist, file an enhancement request against the module that actually creates these nodes, asking for an appropriate API for doing what you want (and be clear about exactly what you want or why). If you really want to expedite it, write a patch that creates such an API (look at how other modules do this sort of thing and aim to follow a similar pattern) - it's hard to say no to working code.
Yes. Have your node subclass AbstractNode or whatever else you like.
NB 6 > m9 Specific: Implement ChildFactory. To create the Children object for your Node, pass it to Children.create(). When the child list needs updating, call refresh() on your ChildFactory. Its createKeys method will be called again and you can update the set of key objects as needed; Nodes for objects that remain in the list of keys will simply continue to exist; additions and removals will be handled.
NB 5 And Earlier: Have your Children object subclass Children.Keys . As needed, call setKeys() on the Children.Keys object. Just by passing a larger or smaller (or reordered) list of keys, you will be adding or removing (or reordering) children.
Do not ever try to add/remove children from a node you did not create (unless it has an API that explicitly gives you permission to do that); occasionally people try to add child nodes to nodes for things like Java files. If it works at all it's by accident.
Applies to: NetBeans 4.0, 4.1, 5.0
The most important thing is to know what you are trying to do clearly, so you can find what you need to get started quickly. Here are some basic NetBeans factoids which will either answer some questions or whet your appetite for more information:
A lot of things in NetBeans are based around file recognition and using files to provide Java objects. Even if your application has nothing to do with editing files, this may still be very useful to you, since the same mechanism that recognizes/displays a user's files on disk also recognizes/displays configuration data (which may not even be files in the traditional sense at all), and such "files" can actually be factories for whatever kind of object you want (and that way you get persistence of those files for free).
For example, the FeedReader tutorial simply serializes POJO Feed objects into the configuration filesystem , and its whole UI consists of aiming a standard tree component at a folder full of those objects, and providing a few actions to let the user create more of them. When the application shuts down, it does not need to any special code for persisting them, it is all automatic.
For more information about how that works, see the section on file recognition.
One of the most basic and important things to know about is how modules register objects - this is mainly done through a configuration file inside the module's jar file (if you are using NetBeans 5.0 or greater's module building support, you can usually avoid hand-editing this file). Most things a module does to influence the environment are declarative rather than programmatic - in other words, you put some text in an XML file, or an entry in a jar manifest, or a file in some specific place in the module jar, and your functionality will be discovered when the system starts up - as opposed to writing java code.
Two of the most common needs are opening custom Swing components in the UI, and installing actions in the main menu .
Other basic topics that are worth reading to get the lay of the land are:
There are various tutorials, and the canonical reference to NetBeans APIs is the API javadoc.
This is document currently has draft status
This tutorial will show you how easy is to create an application client on top of the NetBeans Platform. It will be demonstrated on the example of Database Reader.
Install all of the required products (installation guides are available on the product's websites). When it'll be done we have to set up a few things. First of all please start NetBeans IDE 5.5.1 and register GlassFish v2. Right click on the Servers node in the Runtime tab and select Add server (choose Sun Java Application Server).
Now we need to register NetBeans Platform into IDE. It's in fact almost same as to add a new server. In menu Tools -> NetBeans Platform Manager click on a Add Platform button and pass through the wizard (as a new platform select downloaded NetBeans Platform 5.5.1).
It's time to create all projects. We need NetBeans Module Suite project, NetBeans Module (added into your NetBeans Module Suite) project and Enterprise Application project with Application Client and EJB module included. Let's do it. First of all we create NetBeans Module Suite project. Call it dbreader. As used platform choose the new one what you registered before.
Then create NetBeans Module Project. Call it customers. And check that you want to add it into your dbreader suite. All other options leave as default.
Actually we have had NetBeans Modules created and now we have to create Java EE part. So let's create an Enterprise Application with Application Client and EJB module. Call it dbreader-ear. Include Application Client and EJB module. Exclude Web module. Also select Java EE 5 version and choose Sun Java Application Server as development server.
Great ! You have successfully created all required projects. Now you should see something like this in Projects tab.
We need to modify dbreader-ear build.xml script because the dbreader suite jnlp distro has to be packed into dbreader ear. Due to add these lines into dbreader-ear build.xml (instructions for 6.x are in the next part).
<property name="dbreader.home" value="../"/>
<target name="build-dbreader-jnlp">
<java classname="org.apache.tools.ant.Main" dir="${dbreader.home}" failonerror="true" fork="true">
<jvmarg value="-Dant.home=${ant.home}"/>
<arg value="build-jnlp"/>
<classpath path="${java.class.path}"/>
</java>
</target>
<target name="pre-dist" depends="build-dbreader-jnlp">
<!-- dbreader.home must point to DatabaseReader Application home directory -->
<mkdir dir="${build.dir}/lib"/>
<copy todir="${build.dir}/lib">
<fileset dir="${dbreader.home}/build/jnlp/app" includes="*.jar" />
<fileset dir="${dbreader.home}/build/jnlp/branding" includes="*.jar" />
<fileset dir="${dbreader.home}/build/jnlp/netbeans" includes="*.jar" />
</copy>
</target>
You are able to access build.xml file in Files view.
After editing you should see something like this.
<property name="dbreader.home" value="../"/>
<target name="build-dbreader-jnlp">
<java classname="org.apache.tools.ant.Main" dir="${dbreader.home}" failonerror="true" fork="true">
<jvmarg value="-Dant.home=${ant.home}"/>
<arg value="build-jnlp"/>
<classpath path="${java.class.path}"/>
</java>
</target>
<target name="pre-dist" depends="build-dbreader-jnlp">
<!-- dbreader.home must point to DatabaseReader Application home directory -->
<mkdir dir="${build.dir}/lib"/>
<copy todir="${build.dir}/lib">
<flattenmapper/>
<fileset dir="${dbreader.home}/build/jnlp/app" includes="**/*.jar" />
<fileset dir="${dbreader.home}/build/jnlp/branding" includes="**/*.jar" />
<fileset dir="${dbreader.home}/build/jnlp/netbeans" includes="**/*.jar" />
</copy>
</target>
If you're not using Mac then also don't forget to exclude "Apple Application Menu" module (module suite project properties -> libraries -> PlatformX). Also make sure you're including only modules from platformX cluster.
We have dbreader-ear project infrastructure prepared. Now we have to generate entity classes from sample database. Right click on dbreader-ear-ejb project in Project tab and select New -> Entity Classes From Database. In wizard chose as datasource jdbc/sample datasource and select CUSTOMER table.
On the next wizard panel type package for entity classes. Type db. Then Click on create persistence unit. Persistence unit dialog will appear. Click on Create. Now finish the wizard by clicking on the Finish button.
Now we have generated entity classes from jdbc/sample database. Under dbreader-ear-ejb project you can see generated classes.
We need to create stateless session bean with remote interface to communicate with persistence unit. Create one and call it DataBean.
When you have session bean created add business method called getData. You are able to do it by right clicking on the editor pane (in DataBean.java file opened) and select EJB Methods -> Add Business Method. Pass through the wizard and create getData method which returns java.util.List.
Now use entity manager. Once again do a right click on the editor pane and select Persistence -> Use Entity Manager. Entity manager code is generated. Now implement getData method.
public List getData() {
//TODO implement getData
return em.createQuery("SELECT c FROM Customer c").getResultList();
}
After that you should see in editor (in DataBean.java file) something like this.
We prepared EJB module and now we have to implement functionality into dbreader-ear-app-client Application Client module. Open Main.java file in dbreader-ear-app-client project.
Now call your session bean DataBean. Right click on editor pane and select Enterprise Resources -> Call Enterprise Bean. In the dialog select your DataBean and click OK.
Now we need to implement main method and create getCustomers method. Before that add <dbreader_project_home>/build/jnlp/netbeans/boot.jar (or <dbreader_project_home>/build/jnlp/netbeans/org-netbeans-bootstrap/boot.jar in case of NetBeans 6.1) file on classpath. Do it by right clicking on dbreader-ear-app-client project and select Properties. There select Libraries and then click on Add JAR/Folder and in open file dialog select boot.jar file. Don't forget to uncheck the checkbox. We do not want to package this file with dbreader-ear-app-client module. Actually you have to run build-jnlp target on dbreader suite. Before that please perform step Set Up Suite. Then you can right click on dbreader project and select Build JNLP Application.
Implement main method by this code.
public static void main(String[] args) {
try {
String userDir = System.getProperty("user.home") + File.separator + ".dbreader";
org.netbeans.Main.main(new String[] {"--branding", "dbreader", "--userdir", userDir});
} catch (Exception ex) {
ex.printStackTrace();
}
}
Now create getCustomers static method.
public static List getCustomers() {
return dataBean.getData();
}
After doing this you should see something like this in editor pane.
Great ! We have finished development of the dbreader-ear Enterprise Application. Let's go to develop NetBeans Modules.
Now we set up the dbreader NetBeans module suite. We have to set it as standalone application and also we are able to change splash screen. Right click on dbreader project and select Properties. There select Application and then click on the Create Standalone Application.
Also you are able to set up your own splash screen. Do it by same way and under the Application node in project Properties click on Splash Screen.
Now we set up the customers NetBeans Module. We have to add dbreader-ear-ejb.jar, dbreader-ear-app-client.jar and javaee.jar on compile classpath. First of all set sources level of the module to 1.5. Right click on customers project and on the first panel select 1.5 for sources level.
Open project.properties file from project tab.
Add this code into project.properties file. Of course use your own path to dbreader and glassfish.
cp.extra=\ /home/marigan/temp/dbreader/dbreader-ear/dbreader-ear-ejb/dist/dbreader-ear-ejb.jar:\ /home/marigan/temp/dbreader/dbreader-ear/dbreader-ear-app-client/dist/dbreader-ear-app-client.jar:\ /home/marigan/apps/glassfish/lib/javaee.jar
After that you should see something like this in editor pane.
Now we create a new window component which will serve as viewer for database data. Right click on customers project and select New -> Window Component. On the first wizard panel choose editor as Window Position and select Open on Application Start.
On the second panel specify component Class Name Prefix (use Customers) and finish the wizard.
After that you should see this in Project tab.
We have to write application logic for customers top component. Open CustomersTopComponent.java file in design mode and drag and drop a jTable component from palette into it.
Now switch into source view and modify constructor and add initData method.
private CustomersTopComponent() {
initComponents();
setName(NbBundle.getMessage(CustomersTopComponent.class, "CTL_CustomersTopComponent"));
setToolTipText(NbBundle.getMessage(CustomersTopComponent.class, "HINT_CustomersTopComponent"));
// setIcon(Utilities.loadImage(ICON_PATH, true));
initData();
}
private void initData() {
List<Customer> data = Main.getCustomers();
Object[][] rows = new Object[data.size()][3];
int i = 0;
for (Customer c : data) {
rows[i][0] = c.getName();
rows[i][1] = c.getEmail();
rows[i++][2] = c.getPhone();
}
Object[] colums = new Object[] {"Name", "E-mail", "Phone"};
jTable1.setModel(new DefaultTableModel(rows, colums));
}
After that you should see something like this.
Great job !! Everything is done. Now you can run your application. Right click on dbreader-ear project and select Run Project. Wait a minute do build and glassfish to start. Enjoy your application :o)
There of course comes a time when you need to debug your application. Debugging the server side is relatively easy: start Glassfish in Debug mode and simply "Attach" to it ('Attach Debugger...' from the 'Run' menu).
Debugging the client side is a little harder. On NetBeans 6.1, simply right-clicking on the EAR project and select "Debug" doesn't seem to work. It fails with error messages saying that your classes from your other modules are not found on the classpath. Manually referring to them isn't sufficient either, because once you've done that the Ant debug script will complain about not finding classes belonging to the Platform modules you depend on.
The simple solution is to add the following 2 Ant targets to your build.xml :
<target name="Debug platform (Attach-debug)" description="Debug the platform, need to attach the debugger once the JVM is started"
depends="-debug-init-jvm,run"/>
<target name="-debug-init-jvm">
<property name="j2ee.appclient.jvmoptions.param" value="-agentlib:jdwp=transport=dt_socket,server=y,address=9009"/>
</target>
To run the "Debug platform (Attach-debug) target, right-click on the 'build.xml' file in the "Files" (can't see it from the "Project") view and select it from the "Run target" menu item. Once the JVM is started (the console stops scrolling but the program is still running), attach to the JVM just like when debugging the server.
The idea is to call the already-existing "run" target, but specify arguments to be passed to the JVM when its launched. The above arguments will launch the JVM in debug mode, asking it to wait for a connection (default behavior) and the address will be 9009. You could even specify a different port number if you want to run Glassfish in debug mode at the same time (note that the debugger can only attach to one JVM at a time, so you have to detach from the client and then attach to the server).
For more details about the JPDA debugging arguments, see here.
One major difference between developing a platform application and a monolithic Java application is that there's no main method. This sometimes leaves developers wondering where they can insert their own code. This FAQ entry describes some places where this is possible.
Although a bit drastic for most cases, you can replace the main class used to start NetBeans (DevFaqPlatformAppAuthStrategies) with your own class and then delegate back to NetBeans' normal main class. This offers you a hook early in the startup sequence without having to modify the launchers or shell scripts.
Any module may provide a ModuleInstall class. The validate method will be called before your module is even loaded, so it's the first module-level hook typically available in the startup sequence. Note that many services and classes offered by the platform are unlikely to be initialized at this point.
A short time afterwards, the restored() method will be called on each ModuleInstall class. More services and classes will be initialized at this point than with the validate() method, but the GUI will probably not yet be realized. You can post some code to be executed when the UI is fully loaded like this:
@Override public void restored() {
WindowManager.getDefault().invokeWhenUIReady(
new Runnable() {
public void run() {
// any code here will be run with the UI is available
SomeTopComponent.findInstance().open();
}
});
}
The ModuleInstall class offers two methods which let you plug into the exit sequence. The closing method is called first and requires that you return a boolean value. If true, then your module agrees to be closed, but if false, then you will prevent the exit sequence from continuing. The close method is called after all ModuleInstall classes return true from the closing method and is the final hook in which modules can participate in the application's lifecycle.
Note that providing a ModuleInstall class will increase total startup time a little, even if you have taken care to execute any long-running tasks from its methods in a background thread. It is always preferable to register objects declaratively, and/or run procedural code when it is first needed rather than eagerly.
Another major class in platform development is the TopComponent class. It offers several methods which allow you to hook into its lifecycle.
Here are some events you can hook into for when a TopComponent is opened:
When you set focus on a TopComponent, the componentActivated method is called. Likewise, the componentDeactivated method is called when focus is moved away from that TopComponent.
Here are some events you can hook into for when a TopComponent is closed:
It seems that the exact sequence in which the opening/closing hooks are invoked could vary, perhaps based on platform. Tom Wheeler listed them above in they order in which he observed them on Windows XP but later found they were invoked in a slightly different order. Likewise, Fabrizio Giudici found that they occurred in yet a different sequence.
Note that you can return false from TopComponent.canClose to prevent the TopComponent from being closed at all.
Applies to: NetBeans 6.5
Since NetBeans 6.0 there is a public API to use Autoupdate Services. Autoupdate API provides several services to applications built on NetBeans Platform: it allows users to download and install available updates of installed plugins, search and install new plugins from subscribed Update Centers, browsing and manipulating plugins already installed. To use these services NetBeans Platform supplies a GUI (Plugin Manager in Tools->Plugins menu item) to easy call these services. AutoUpdate API also cares about registration of Update Centers.
It's easy to distribute new and/or updated modules via AutoUpdate, but you might also like to update branding items like the splash screen and version number in the application's title bar to reflect the changes.
To do this, create a new module in your suite. Edit its build.xml to add the following, but replace "app" with the branding token for your application (see your suite's project.properties if you don't know the value to use):
<target name="netbeans-extra" depends="init">
<branding cluster="${cluster}" overrides="${suite.dir}/branding" token="app"/>
</target>
Next, add the following to the modules' nbproject/project.properties file, again replacing "app" with your branding token. You may also need to update the list of files in extra.module.files to include only those JARs which your suite actually brands.
nbm.needs.restart=true
nbm.is.global=true
nbm.target.cluster=app
extra.module.files=\
core/locale/core_app.jar,\
modules/ext/locale/updater_app.jar,\
modules/locale/org-netbeans-core-windows_app.jar,\
modules/locale/org-netbeans-core_app.jar,\
modules/locale/org-netbeans-modules-autoupdate-ui_app.jar,\
modules/locale/org-netbeans-modules-favorites_app.jar,\
modules/locale/org-netbeans-modules-javahelp_app.jar,\
modules/locale/org-netbeans-modules-projectui_app.jar
Finally, run the "nbms" Ant target on your suite and deploy the updates to your AutoUpdate center.
Note that you may encounter problems doing this in NetBeans 6.0.
Thanks to Matteo Di Giovinazzo for sharing how to do this on the dev@openide list.
As with most user interface (UI) toolkits, Swing is single threaded. That means there is one and only one thread that should create or alter the state of UI components, and that is the AWT Event Dispatch Thread (also known as the EDT or the "event thread"). It processes things like key and mouse events and calls components to respond to them.
This also means that code that responds to a key or mouse event, or some call triggered by one, should run very quickly, because the user can by typing or clicking, but the entire application is blocked from responding to more events until your code exits. So sometimes you will want to move expensive or slow work onto a background thread.
A background thread is any thread that is not the event thread.
If you are running on some other thread, but need to modify some component, a simple way to do this is EventQueue.invokeLater(new Runnable() { public void run() { //this code can modify components } });
Note that the caveat about Swing includes creating components - it is provably not safe to even construct Swing components on a background thread, because of synchronization on Component.getTreeLock().
NetBeans has a learning curve. The goal of this article is to get you over the basic humps quickly. Being proficient does not necessarily mean knowing everything there is to know. It means being able to find what you need to know quickly when you need it. Here are some pointers.
All of the documents linked here are also available from del.icio.us.
It's a good thing to bookmark.
If you want a local copy of it, you can either download it from the update center, or build it from your source checkout
cd $NB_SRC_ROOT ant build-javadoc
Some people claim that they should never need to look at source code - documentation should suffice. That's just silly. The NetBeans codebase is a treasure-trove of examples of how to do things.
Since the end of January 2008 the NetBeans sources are stored in Mercurial repository at hg.netbeans.org.
You can see useful documentation about Mercurial and also about its specifics for NetBeans repository in HgMigrationDocs wiki topic. If you are already familiar with Mercurial you cat go directly to HgHowTos topic.
You will end up with a large number of directories representing top-level NetBeans projects. Most of them will be openable as projects in NetBeans.
Here's how to build it.
The build of NetBeans will be created in nbbuild/netbeans.
To build platform run
cd $NB_SRC_ROOT ant build-platform
This How-To is based on GlassFish EJB Faq
Important: Application Client must be created as it is described in Java EE Application Client on top of the NetBeans Platform Tutorial otherwise this will not work
protected mypkg.MySessionBeanRemote lookupMySessionBean() {
try {
javax.naming.Context c = new javax.naming.InitialContext();
return (mypkg.MySessionBeanRemote) c.lookup("java:comp/env/ejb/MySessionBean");
} catch(javax.naming.NamingException ne) {
java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE,"exception caught" ,ne);
throw new RuntimeException(ne);
}
}
<ejb-ref>
<ejb-ref-name>ejb/MySessionBean</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<remote>mypkg.MySessionBeanRemote</remote>
</ejb-ref>
run.args.extra=-J-da -J-Dorg.omg.CORBA.ORBInitialHost=localhost -J-Dorg.omg.CORBA.ORBInitialPort=3700 \
-cp:a $GLASSFISH_HOME/lib/appserv-rt.jar:$GLASSFISH_HOME/lib/appserv-ext.jar:\
$GLASSFISH_HOME/lib/appserv-deployment-client.jar:$GLASSFISH_HOME/lib/javaee.jar:\
$GLASSFISH_HOME/lib/jmxremote_optional.jar:someejb.jar
to module suite project.properties
// for EJB 3.0 bean
protected mypkg.MyBeanRemote lookupMyBeanRemote30 throws NamingException {
javax.naming.Context ic = new javax.naming.InitialContext();
return (mypkg.MyBeanRemote) ic.lookup("mypkg.MyBeanRemote");
}
// for EJB 2.1 and/or earlier
protected mypkg.MyBeanRemote lookupMyBeanRemote21 throws NamingException {
javax.naming.Context ic = new javax.naming.InitialContext();
Object remote = c.lookup("java:comp/env/ejb/MyBean");
mypkg.MyBeanRemoteHome rv = (mypkg.MyBeanRemoteHome) PortableRemoteObject.narrow(remote, mypkg.MyBeanRemoteHome.class);
return rv.create();
}
Platforms: all
If you want to test running with a different look and feel during development of your application, and you know it will be on the application's classpath, see the example in DevFaqPassCommandLineArgumentsAtDevTime for how to include --laf in the runtime arguments to your module suite.
The main menus and toolbars of a NetBeans Platform application are configured based on the contents of folders in the system filesystem. There are many benefits of this approach, such as improved performance since the platform can create all the menus and toolbars without having to actually instantiate the actions with which they are associated.
Because the platform builds the menus and toolbars for you, it might seem like you have little control over how those items appear. In practice, you have a great deal of control over the appearance for any action you create. Typically, actions in a NetBeans platform application which will be shown in the main menu or toolbar extend from CallableSystemAction, perhaps indirectly through its CookieAction subclass.
In the code you've written for one of these actions, you can override getMenuPresenter to change the appearance of the menu item associated with your action and/or override getToolbarPresenter to change the appearance of the toolbar component associated with your action.
For example, if you wanted to make the menu item for your action have a blue background and yellow text, you could do something like this:
@Override
public JMenuItem getMenuPresenter() {
JMenuItem item = super.getMenuPresenter();
item.setOpaque(true);
item.setBackground(Color.BLUE);
item.setForeground(Color.YELLOW);
return item;
}
Note that if you are changing the menu item to support a tooltip, the object returned by getMenuPresenter needs a property change listener on the action's SHORT_DESCRIPTION so that its tooltip value is updated correctly upon change.
Note about using alternate components in the main menu: If you want your action to work properly on Mac OS, you probably don't want to return anything other than a JMenu or JMenuItem from getMenuPresenter() if you implement Presenter.Menu. In general, Swing allows you to treat menu popups as generic Swing containers you can put what you like into. This is not true at all of the Mac OS screen menu bar - it expects normal menu items, and will not handle unusual contents for menus.
It's pretty simple to change the font color, style or weight for your node's label. Simply override getHtmlDisplayName and provide some HTML in your return value. (An example can be found in this tutorial.) Here is another example:
public class MovieNode extends AbstractNode {
private Movie movie;
public MovieNode(Movie key) {
super(Children.LEAF, Lookups.fixed(new Object[]{key}));
this.movie = key;
setName(key.getTitle());
setDisplayName(key.getTitle());
getHandle();
setIconBaseWithExtension("org/nb/marilyn/pics/marilyn.gif");
}
@Override
public String getHtmlDisplayName() {
return "<b>" + this.getDisplayName() + "</b>";
}
}
The javadoc for the HtmlRenderer class explains what subset of HTML is supported. You can also change the icon's node by overriding various methods such as getIcon(int type) or getOpenedIcon().
It's also possible, but far more difficult, to control other aspects of the node's appearance; for example, drawing a box around the node or changing its background color. To do this you must create or modify the explorer view in which the node is rendered. Fabrizio Giudici posted code that illustrates this on the dev@openide list.
The splash screen is here:
autoupdate.services/libsrc/org/netbeans/updater/resources/updatersplash.gif
You can brand this in your application by following the instructions in this FAQ entry.
This FAQ item should be a companion to the main classpath documentation. Please refer to the original document for additional details.
There are basically three main class loader types used in the platform. Most code is loaded by module class loaders. In special cases the "system" class loader can be used, when you need access to resources from unknown modules. Resources directly on the classpath from the launch script (mainly platform*/lib/*.jar) are loaded by the application loader. (There are also bootstrap and extension loaders in the JRE, and the platform has a special loader for a couple of JARs in platform*/core/*.jar.)
Most of the class loaders in the NetBeans platform are multi-parented class loaders. This means that the class loader can have zero or more parents. org.netbeans.ProxyClassLoader implements the search across multiple parents.
Every module loaded by the module system has its own class loader. This loader loads resources primarily from the module's JAR. The application loader is an implicit parent of each module loader.
The module loader is able to load from additional JARs (besides delegating to various parents):
The implementation class is org.netbeans.StandardModule$OneModuleClassLoader.
The "system" loader loads no resources on its own, but has as its parents all enabled module's class loaders. It is accessible via Lookup.getDefault().lookup(ClassLoader.class) or by using the fact that it is the context loader on all threads by default: Thread.currentThread().getContextClassLoader()
This class loader is set up by the launch script (or by javaws if running in JNLP mode). It can load classes from lib/*.jar in specified clusters. It is generally discouraged to use this loader for your own classes, but it is sometimes needed e.g. for Look & Feel classes (which must be loaded very early during the startup sequence).
Take a very simple module a:
Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.a
and module b depending on a:
Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.b OpenIDE-Module-Module-Dependencies: org.netbeans.modules.a Class-Path: ext/library-b-1.1.jar
Classes in org-netbeans-modules-a.jar will be loaded in a's module class loader. Classes in both org-netbeans-modules-b.jar and library-b-1.1.jar will be loaded in b's module loader, and can refer to classes in org-netbeans-modules-a.jar.
Yes. In fact, once you have some code written, request the developer role on contrib.netbeans.org (you need to have an account and be logged in to see the link), so you have write access to CVS there, and go ahead and check it in. You will need to sign a Contributor Agreement to get CVS write access.
For more info on how to start module projects or join existing ones, see the contributing page on netbeans.org .
If you have initially written the module as a standalone module project using the NB 5.0 IDE, beware that the format for module projects inside the netbeans.org CVS tree is slightly different. To convert a standalone module project to a netbeans.org module:
Now you ought to be able to open the new project in the IDE and work with it as usual.
If you had specified an explicit NBM license (=license.file=) before, you might just remove it - netbeans.org modules by default use nbbuild/standard-nbm-license.txt which is appropriate for any SPL module with no external (non-SPL) components.
If you had a module suite project with several module suite component projects, just discard the suite project (netbeans.org modules cannot reside in suites) and copy the component module projects into the netbeans.org CVS tree. You will be deleting <suite-component/> from project.xml and deleting suite.properties rather than platform.properties but the process is otherwise similar.
A future release of the module development support should permit you to simply select Move Project or Copy Project from the GUI and handle these format changes automatically.
Explorer views are generic Swing components, not subclasses of TopComponent , the Swing panel class that is used for top level components (tabs) in the main window. So an explorer view component is added to a TopComponent, using the TopComponent as a Swing container for the view.
A little bit of plumbing is needed to wire up an explorer view to the global Node selection so that code that is sensitive to selection such as context sensitive actions . Basically you want the TopComponent to expose the selection in your Explorer View so that when your view has focus, the global selection that affects everything will be whatever the user selects in your view.
In olden times, there was a convenient class called ExplorerPanel (now in org.openide.compat ) which would do this for you; due to a tragedy of being in the wrong package, it is now deprecated, but the required plumbing is not hard:
public class MyView extends TopComponent implements ExplorerManager.Provider {
private final ExplorerManager manager = new ExplorerManager();
private final JComponent view = new BeanTreeView();
public MyView() {
setLayout (new BorderLayout());
add(view, BorderLayout.CENTER);
manager.setRootContext(someNode);
// Probably boilerplate (depends on what you are doing):
ActionMap map = getActionMap();
map.put(DefaultEditorKit.copyAction, ExplorerUtils.actionCopy(manager));
map.put(DefaultEditorKit.cutAction, ExplorerUtils.actionCut(manager));
map.put(DefaultEditorKit.pasteAction, ExplorerUtils.actionPaste(manager));
// This one is sometimes changed to say "false":
map.put("delete", ExplorerUtils.actionDelete(manager, true));
// Boilerplate:
associateLookup(ExplorerUtils.createLookup(manager, map));
}
// This is optional:
public boolean requestFocusInWindow() {
super.requestFocusInWindow();
// You will need to pick a view to focus:
return view.requestFocusInWindow();
}
// The rest is boilerplate.
public ExplorerManager getExplorerManager() {
return manager;
}
protected void componentActivated() {
ExplorerUtils.activateActions(manager, true);
}
protected void componentDeactivated() {
ExplorerUtils.activateActions(manager, false);
}
}
The primary difference between the above code and ExplorerPanel is that ExplorerPanel automagically persisted paths from the selected nodes to the root, so that it could be deserialized on restart with the same selection it had before shutdown (assuming that selection still existed - this was never terribly robust).
-- Main.timboudreau w/ cut & paste from Jesse Glick - 17 Sep 2005
Integer fontSize = (Integer) UIManager.get("customFontSize");
if (fontSize != null) {
//--fontsize was passed - adjust your fonts accordingly
}
You can also just set your font with UIManager.getFont("controlFont") which will be set according to --fontsize, but sometimes you do need the actual value for using fixed width fonts or computing fixed cell height for an unusual font or similar.
The code in core.swing.plaf processes the --fontsize argument and sets the UIManager key/value pair if it was passed on startup.
You need to extend IOProvider and override/implement following methods:
// registration, you can change default instance returned by IOProvider.getDefault() by adjusting position
@org.openide.util.lookup.ServiceProvider(service=org.xxx.MyIOProvider.class, position=200)
public final class MyIOProvider extends IOProvider {
// unique name of your provider
private static final String NAME = "My IO provider"; // NOI18N
public OutputWriter getStdOut() {
// implement
}
public InputOutput getIO(String name, boolean newIO) {
// implement
}
@Override
public InputOutput getIO(String name, Action[] toolbarActions) {
// override
}
@Override
public InputOutput getIO(String name, Action[] additionalActions, IOContainer ioContainer) {
// override
}
@Override
public String getName() {
return NAME;
}
}
Add "OpenIDE-Module-Provides: org.openide.windows.IOProvider" to your module manifest (manifest.mf file) to inform that your module provides IOProvider service.
Then instance of your provider could be obtained by IOProvider.get("My IO provider")
An AutoUpdate server (also called an AutoUpdate Center or AUC) it not as complicated as it sounds. It's just a server which contains a set of modules and an XML file that describes them all (the autoupdate XML descriptor).
There are four main steps in setting up your AUC, all of which are quite simple:
Note: Whenever you need to deploy an update, be sure you have incremented the module's specification version number and then repeat steps 2 and 3 above. Users should be able to easily install the updates you've published. There is more explanation of module versioning and dependencies elsewhere in this FAQ.
The subject of properly handling cut, copy and paste is underdocumented in modern material on the NetBeans Platform and I am not aware of any clear and concise examples that show how to handle all aspects of these common actions. Anyone who can improve these shortcomings would be doing a great service for the NetBeans Platform developer community.
The Nodes API documentation provides some guidance, while chapter 14 of NetBeans: The Definitive Guide goes into greater detail. Although some parts of NetBeans: The Definitive Guide are now outdated, the portions related to the Nodes API are likely still relevant.
DataLoaders are factories for DataObjects. Typically there is a 1:1 mapping between file-type:DataLoader-subclass and a 1:1 mapping from files:DataObject instances for files that are visible in the UI. When a file is encountered, a DataLoader is found and used to produce a DataObject for that file.
Modules that provide the ability the system to open (or otherwise use) files of a particular type will register DataLoaders for those types. When the system needs to display a file in the UI, or when some code calls DataObject.find(someFileObject), the registered loaders are queried and one of them will claim it, and create a DataObject to represent it. So typically for each file type (as defined by file name extension, or XML subtype) there is a matching DataObject subclass.
DataLoaders are registered in the module manifest - for example:
Name: org/netbeans/modules/povray/PovDataLoader.class OpenIDE-Module-Class: Loader
Note that the empty line above the Name: line must be present; create such entries at the bottom of the module manifest.
A DataObject represents one or more (typically only one) FileObjects. A DataObject knows what kind of a file it represents; it may represent the parsed contents of a file such as a .java file; or, as in the case of InstanceDataObject the file name may provide all the information it needs to be useful.
DataObjects are produced by DataLoaders, which modules register for specific file types. So for each file type, there is (usually) one DataLoader; and for each file of that type, there is (typically) one DataObject.
DataObjects are seldom referred to by type - if you are casting a DataObject to its implementation class, you are probably doing something wrong. This is a general rule for which there can be exceptions, but is especially true if you're doing the cast from code in a different module than the one in which the DataObject was defined. The usage pattern is to ask a DataObject for instances of interfaces that are the things your code will actually interact with. The method for doing this is dataObject.getCookie(Interface.class) (or in newer versions of NB, dataObject.getLookup().lookup(Interface.class)).
As a simple example, the NetBeans APIs define an interface org.openide.cookies.OpenCookie. It has one method, open(). That method will open the file in the editor. What exactly will happen when open() is called is entirely up to the module that implements the DataObject. The rest of the system does not need to know any of the implementation details - it just needs to know if the DataObject has an OpenCookie. If it does, then the Open action on its context menu can be enabled, and that action will call ((OpenCookie) theDataObject.getCookie(OpenCookie.class)).open().
The name cookie is historical and somewhat unfortunate; the getCookie() pattern is an older variation on the Lookup pattern for service discovery. The two are equivalent, with the exception that getCookie() requires all returned objects to implement the empty Node.Cookie marker interface, and Lookup can return any object. Expect getCookie() to be replaced with a new getLookup() method at some point in the future.
As suggested above, a DataObject may actually represent more than one file - so when you expand a folder in the UI, there may actually be fewer DataObjects in that folder than there are files. This is why, in the NetBeans IDE, a Swing form is represented by a .java file and a corresponding .form file, but the .form file is not visible in the UI; in the past, .properties files have also used this mechanism to present resource bundles in various languages as a single node in the files tree.
However, this ability to represent multiple files with a single DataObject is strongly discouraged for new code and will probably eventually be deprecated - it has serious negative implications for scalability.
DataObject.find (theFileObject)
Most likely your DataObject does not put itself into its own CookieSet/Lookup. If you call setCookieSet(), or override getLookup() or getCookie(), this can happen. There are a few parts of NetBeans which will expect Nodes to be present in their own Lookups and DataObjects to be present in theirs. It is always best to make sure they are there. For a DataObject, the code for this is usually as simple as:
getCookieSet().add(this);
DataObject dob = (DataObject) theNode.getLookup().lookup (DataObject.class);
if (dob != null) {
//do something
}
There is a folder in the System Filesystem called Loaders. It is where various things are registered that apply to specific DataObject types. For example, there is a folder Loaders/text/x-java that contains things that pertain to Java files (notice that the path is a MIME type). It has an Actions subfolder where you can add actions to the popup menu for Java files.
You may not think of a folder as being a file type, but to NetBeans it is. There is a subfolder Loaders/folder/any/Actions which contains actions that should appear in the popup menu for folders. Just add your action in your layer file to that folder, i.e.
<filesystem>
<folder name="Loaders">
<folder name="folder">
<folder name="any">
<folder name="Actions">
<file name="com-foo-module-MyAction.instance"/>
</folder>
</folder>
</folder>
</folder>
</filesystem>
No. Not if you want your module to work in the future. Copy the code instead. If it is a thing that seems generally useful, file an enhancement request requesting an API for the thing you need to do (and make sure there isn't already a supported way to do it).
Anything under org.netbeans.core is non-public, not an API, and subject to change without notice. An API is a contract - an agreement about compatibility. There is no such contract for this namespace, under any circumstances. The class or method you are using may not even exist in the future. Depend on it at your peril.
A perfect example of why not to do this is JProfiler's plugin for NetBeans - it broke very badly across releases because it needlessly depended on the implementation of DialogDisplayer rather than on the API class - so when that class moved, it could no longer link, so the module didn't work.
If you really must use some non-API classes to do what you need to do, use an implementation dependency (DevFaqImplementationDependency) - your module probably won't load in any version except the one it was built against, but at least your users won't get nasty surprises. And ideally, notify the maintainer of the thing you're depending on - they can give you a heads-up if they think they're about to make a change that will break your module.
Applies to: NetBeans 6.5
The NetBeans Dialogs API makes it easy to create consistent dialogs that behave as users would expect. But since you don't directly create the OK button, it may not be obvious how you can enable it or disable it.
You can enable the OK button by calling setValid(true) and disable it by calling setValid(false). This thread from the dev@openide list might also be helpful.
The Dialogs API provides support for dialogs and wizards.
Whenever you'd use JDialog or JOptionPane in Swing, using the Dialogs API provides some alternatives. These are easier to use as they automatically take care of centering and other display details, but also allow you to later plug in a different implementation of how they're actually "displayed." Instead of showing them on screen, for example, you could override the default DialogDisplayer class to specify your own that logged them to a printer or read them aloud using speech synthesis.
I'll illustrate three of the most common use cases. The first is when you want to simply show a dialog box with some text:
String msg = "There is something you should know..." NotifyDescriptor nd = new NotifyDescriptor.Message(msg, NotifyDescriptor.INFORMATION_MESSAGE); DialogDisplayer.getDefault().notify(nd);
For exceptions, you'll use a similar mechanism. The msg argument is optional here:
String msg = "Something bad happened..." NotifyDescriptor nd = new NotifyDescriptor.Exception(throwable, msg); DialogDisplayer.getDefault().notify(nd);
And to request simple user input:
String txt = "Name: ";
String title = "State your name";
NotifyDescriptor.InputLine input = new NotifyDescriptor.InputLine(txt, title);
input.setInputText("John Doe"); // specify a default name
Object result = DialogDisplayer.getDefault().notify(input);
if (result != NotifyDescriptor.OK_OPTION) {
return;
}
String userInput = input.getInputText();
And finally, the DialogDescriptor subclass, handles complex cases (there are many variants here; see Dialog Descriptor's Javadoc for details):
JPanel form = new MyComplexForm();
String msg = "Something bad happened..."
DialogDescriptor dd = new DialogDescriptor(form, msg);
Object result = DialogDisplayer.getDefault().notify(dd);
if (result != NotifyDescriptor.OK_OPTION) {
return;
}
// you can now examine the form's state...
The IDE often checks for updates on startup. This behavior may not be desired in some cases; for example, when running tests on the GUI, because they can slow the application down or potentially change its behavior from what you expected. In order to prevent the Auto Update check, run the application with the netbeans.full.hack system property set to true. For example, use -J-Dnetbeans.full.hack=true on the command line.
Setting this property has some other side-effects. Among them:
Functional tests using NbModuleSuite get this property set automatically. So do unit tests using NbTestCase (though they would rarely need it anyway).
*.settings files are similar to *.instance files (DevFaqInstanceDataObject), with the difference that they can contain serialized data rather than just default instances. They may also use custom persistence formats according to the Convertors API.
After the introduction of NbPreferences to replace SystemOption, very little new code uses these files, and they should be considered semi-deprecated. They are difficult and error-prone to use, and have a fair amount of overhead. The Window System still requires them to be used for TopComponent persistence.
*.shadow files are mainly used in the system filesystem (DevFaqSystemFilesystem) for configuration data. They are the functional equivalent of Unix symbolic links - a *.shadow file is a pointer to another file whose behavior in every respect except its path and file name is the same as the original.
*.shadow files are commonly used where only a single instance of an object is needed, but it must be registered in multiple folders. For example, a general Action is declared in the Actions/ folder of the System Filesystem. But the action also needs to appear in menus and toolbars, possibly other places. So, rather than create multiple instances of an action, one *.instance file (DevFaqInstanceDataObject) is created in the module's layer file (DevFaqModulesLayerFile), in the Actions/ folder. Then *.shadow files are created in all of the other places the *.instance file would be needed, pointing to the original file.
Declaring a .shadow file in the system filesystem looks like this:
<folder name="A">
<file name="com-foo-mymodule-MyClass.instance"/>
</folder>
<folder name="B">
<file name="Shadow1.shadow">
<attr name="originalFile" stringvalue="A/com-foo-mymodule-MyClass.instance"/>
</file>
</folder>
<folder name="C">
<file name="anotherShadow.shadow">
<attr name="originalFile" stringvalue="A/com-foo-mymodule-MyClass.instance"/>
</file>
</folder>
Shadow files can also point to real files on disk. For example, the Favorites tab in the NetBeans IDE uses shadow files to link to real directories on disk.
To add a drop-down menu to a component in a toolbar, you can either extend CallableSystemAction and override public Component getToolbarPresenter(), or implement javax.swing.Action or any subclass thereof, and implement Presenter.Toolbar which defines that method.
You might want to create a JToggleButton, and when the button is pressed, show a JPopupMenu. (Also try org.openide.awt.DropDownButtonFactory.)
Example:
public class PickDrawingLineAction extends CallableSystemAction {
private static JToggleButton toggleButton;
private static ButtonGroup buttonGroup;
private static JPopupMenu popup;
private MyMenuItemListener menuItemListener;
List handledCharts;
public void performAction() {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
toggleButton.setSelected(true);
}
});
}
public String getName() {
return "Pick Drawing Line";
}
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
protected boolean asynchronous() {
return false;
}
public Component getToolbarPresenter() {
Image iconImage = Utilities.loadImage(
"org/blogtrader/platform/core/netbeans/resources/drawingLine.png");
ImageIcon icon = new ImageIcon(iconImage);
toggleButton = new JToggleButton();
toggleButton.setIcon(icon);
toggleButton.setToolTipText("Pick Drawing Line");
popup = new JPopupMenu();
menuItemListener = new MyMenuItemListener();
handledCharts = PersistenceManager.getDefalut()
.getAllAvailableHandledChart();
buttonGroup = new ButtonGroup();
for (AbstractHandledChart handledChart : handledCharts) {
JRadioButtonMenuItem item =
new JRadioButtonMenuItem(handledChart.toString());
item.addActionListener(menuItemListener);
buttonGroup.add(item);
popup.add(item);
}
toggleButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
int state = e.getStateChange();
if (state == ItemEvent.SELECTED) {
/** show popup menu on toggleButton at position: (0, height) */
popup.show(toggleButton, 0, toggleButton.getHeight());
}
}
});
popup.addPopupMenuListener(new PopupMenuListener() {
public void popupMenuCanceled(PopupMenuEvent e) {
toggleButton.setSelected(false);
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
toggleButton.setSelected(false);
}
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
});
return toggleButton;
}
private class MyMenuItemListener implements ActionListener {
public void actionPerformed(ActionEvent ev) {
JMenuItem item = (JMenuItem)ev.getSource();
String selectedStr = item.getText();
AnalysisChartTopComponent analysisTc =
AnalysisChartTopComponent.getSelected();
if (analysisTc == null) {
return;
}
AbstractChartViewContainer viewContainer =
analysisTc.getSelectedViewContainer();
AbstractChartView masterView = viewContainer.getMasterView();
if (!(masterView instanceof WithDrawingPart)) {
return;
}
DrawingPart drawingPart =
((WithDrawingPart)masterView).getCurrentDrawing();
if (drawingPart == null) {
JOptionPane.showMessageDialog(
WindowManager.getDefault().getMainWindow(),
"Please add a layer firstly to pick line type",
"Pick line type",
JOptionPane.OK_OPTION,
null);
return;
}
AbstractHandledChart selectedHandledChart = null;
for (AbstractHandledChart handledChart : handledCharts) {
if (handledChart.toString().equalsIgnoreCase(selectedStr)) {
selectedHandledChart = handledChart;
break;
}
}
if (selectedHandledChart == null) {
return;
}
AbstractHandledChart handledChart =
selectedHandledChart.createNewInstance();
handledChart.setPart(drawingPart);
drawingPart.setHandledChart(handledChart);
Series masterSeries = viewContainer.getMasterSeries();
DrawingDescriptor description =
viewContainer.getDescriptors().findDrawingDescriptor(
drawingPart.getLayerName(),
masterSeries.getUnit(),
masterSeries.getNUnits());
if (description != null) {
Node stockNode = analysisTc.getActivatedNodes()[0];
Node node =
stockNode.getChildren()
.findChild(DescriptorGroupNode.DRAWINGS)
.getChildren().findChild(description.getDisplayName());
if (node != null) {
ViewAction action =
(ViewAction)node.getLookup().lookup(ViewAction.class);
assert action != null :
"view action of this layer's node is null!";
action.view();
}
} else {
/** best effort, should not happen */
viewContainer.setCursorCrossVisible(false);
drawingPart.setActived(true);
SwitchHideShowDrawingLineAction.updateToolbar(viewContainer);
}
}
}
}
The end result of this code is that we create a Java context for our JEditorPane. This context initializes code completion with a default class path, and that grants us access to the standard Java APIs (i.e. the code completion box can include classes such as java.lang.String, java.util.List, etc.). However, this context has no visibility into any additional jars nor Java projects. In order to expand this default Java context, you will need to create your own class path provider (see the "Java Support APIs" module).
First, let's take a look at some of the classes we'll be using:
Now we're ready to take a look at the actual code:
JEditorPane editorPane = new JEditorPane();
// This will find the Java editor kit and associate it with
// our editor pane. But that does not give us code completion
// just yet because we have no Java context (i.e. no class path, etc.).
// However, this does give us syntax coloring.
EditorKit kit = CloneableEditorSupport.getEditorKit("text/x-java");
editorPane.setEditorKit(kit);
// You can specify any ".java" file.
// If the file does not exist, it will be created.
// The contents of the file does not matter.
// The extension must be ".java", however.
String newSourcePath = "tmp.java";
File tmpFile = new File(newSourcePath);
FileObject fob = FileUtil.createData(tmpFile);
DataObject dob = DataObject.find(fob);
editorPane.getDocument().putProperty(
Document.StreamDescriptionProperty,
dob);
// This sets up a default class path for us so that
// we can find all the JDK classes via code completion.
DialogBinding.bindComponentToFile(fob, 0, 0, editorPane);
// Last but not least, we need to fill the editor pane with
// some initial dummy code - as it seems somehow required to
// kick-start code completion.
// A simple dummy package declaration will do.
editorPane.setText("package dummy;");
JTextComponent ed = org.netbeans.api.editor.EditorRegistry.lastFocusedComponent(); Document doc = ed.getDocument();
In order to get MimeLookup you have to supply MimePath. With the default MimeLookup implementation provided by Netbeans the contents of MimeLookup is defined by a hierarchical structure of folders on the system FileSystem. The structure starts in the Editors folder and then follows all the components of the MimePath you have supplied.
For example if you ask for MimeLookup for the following MimePath of text/x-java you will get Lookup with contents from the following folders:
Editors/text/x-java
Editors
As you can see MimeLookup for text/x-java contains not only editor features registered for the text/x-java mime type itself, but it also inherits general features registered for an empty MimePath (i.e. in the root of the hierarchy).
The inheritence algorithm used for composing MimeLookup for a given MimePath supports more than just simple inheritance from the root. It also supports compound mime types such as text/x-ant+xml and embedded mime types such as text/x-jsp/text/x-java.
Let's have a look at the MimeLookup composition for a compound mime type text/x-ant+xml. The resulting Lookup will contain things registered in the following folders:
Editors/text/x-ant+xml
Editors/text/xml
Editors
That's the reason why editor features provided by XML modules for general XML files work also for specialized, but XML-based, files.
The inheritance hierarchy becomes even more complicated when dealing with embedded mime types. Let's use a java scriplet inside a JSP page as an example of language embedding. The MimePath for the scriplet is text/x-jsp/text/x-java and its MimeLookup will contain features registered in the following folders:
Editors/text/x-jsp/text/x-java
Editors/text/x-java
Editors
The algorithm for computing the inheritance tree for a particular MimePath combines all the above cases together and works always the same way no matter what feature you are going to look for in the resulting MimeLookup.
Applies to: NetBeans 6.0 and with some exceptions also to 5.0, 5.5
Platforms: All
See also:
What is MimeLookup?,
What is MimePath?,
MimeLookup API
WARNING: The API described here is not official! Check the javadoc for its stability level.
// Suppose you have javax.swing.text.Document
String mimeType = NbEditorUtilities.getMimeType(document);
// Suppose you have javax.swing.text.JTextComponent
String mimeType = NbEditorUtilities.getMimeType(component);
The method accepting JTextComponent is generally more practical, because JTextComponent or its subclasses is what you usually have to start with. Internally the method calls the Document version of itself on the document loaded in the component and returns its mime type (if it has any assigned). If the document does not have mime type information attached (and non-Netbeans documents generally don't) the method will use the component's EditorKit to get it.
Applies to: NetBeans 6.0, the algorithm in NbEditorUtilities.getMimeType(JTextComponent)
works fine in 5.0 and 5.5, but the method is not public.
Platforms: All
See also: Editor Module API
You need to find the right EditorKit first and then set it on your JEditorPane.
Here is an example showing how to do that for a java file.
EditorKit kit = CloneableEditorSupport.getEditorKit("text/x-java");
JEditorPane jep = new JEditorPane();
jep.setEditorKit(kit);
Applies to: NetBeans 6.0
Platforms: All
See also:
CloneableEditorSupport.getEditorKit()
The MimeLookup is a mechanism for extending editor functionality provided by NetBeans modules.
Most of the editor functionality in NetBeans is organized by mime types of documents that are edited. There are special modules that provide support for editing java, xml, jsp, c++ and many other file types. All those modules need to provide more or less the same features such as syntax coloring, code completion, code folding, popup menu, etc. However, the implementation of those features is different for particular file types.
The editor insfrastructure provides many SPI interfaces that can be implemented by modules providing specific implementation of editor features and the MimeLookup is the way how these implementations can be plugged in to the system.
As its name suggests MimeLookup is a mime-type specific Lookup. This means that modules can register instances in the Lookup dedicated to the mime type of files that they support. For example there are different Lookups for text/x-java and text/xml mime types and both contain FoldManager implementations specific for java and XML languages respectively.
The MimeLookup implementation is split in two parts. The first part is pretty much independent on most of the other NetBeans libraries and provides a simple API and SPI for accessing contents of MimeLookup. The second part is a NetBeans specific implementation of the MimeLookup's registry based on the modules XML layers. This registry is in fact a hierarchical structure of folders under the Editors/ folder on the system filesystem.
# How to get Lookup for java files?
MimePath mimePath = MimePath.parse("text/x-java");
Lookup lookup = MimeLookup.getLookup(mimePath);
# How to register instances (e.g. EditorKit) in the Lookup for java files?
<folder name="Editors>
<folder name="text">
<folder name="x-java">
<file name="org-netbeans-modules-java-JavaEditorKitImpl.instance"/>
</folder>
</folder>
</folder>
Applies to: NetBeans 5.0, 5.5, 6.0
Platforms: All
See also:
What is Lookup?,
MimeLookup API
Basically, MimePath is an ordered list of mime types.
The reason why we have come up with the concept of MimePath is that we need to support embedded languages. In the simple world where code is written only in one language and stored in files dedicated for that language it is enough to know the mime type of a file in order to know its language and to load appropriate editor features. The world, however, is not simple anymore and the reality requires us to deal with situations when one file contains a mixture of several different languages. An example is a web applications development when people write JSP files that contain snippets of code in JSP, HTML, an expression language, Java, JavaScript and possibly some other languages. The whole JSP file can be broken up into sections containing code in different languages and user expects to get features like coloring and code completion that are appropriate for the mime type of each section. This is called language embedding.
In order to be able to describe the exact 'type' of each embedded block of text Netbeans use an ordered list of mime types that describe languages along the way from the top level language (i.e. the mime type of the file itself) to the embedded block. So, for example a java scriplet in a JSP file can be identified by the list of two mime types text/x-jsp, text/x-java. The list can be encoded in one String using the forward slash character as a separator and that's what MimePath is. In our example the MimePath of java scriplet in a JSP file is text/x-jsp/text/x-java.
The embedding can go indefinitely deep and you can have MimePath like text/x-jsp/text/html/text/el for an expression language used in an HTML attribute inside a JSP file - <a href="${myLink}">...</a>.
Since MimePath is required when you want MimeLookup and since MimeLookup is the way for pluging-in language specific editor features it is possible to provide features tailored specifically for any type of language embedding. If you want to know more about the contents of MimeLookup generally and for compound and embedded mime types, read more in How is MimeLookup composed?.
Applies to: NetBeans 5.0, 5.5, 6.0
Platforms: All
See also:
What is MimeLookup?,
MimePath
Javadoc,
MimeLookup API
In short, the current NetBeans IDE (6.7) only provides limited support for changing application icons. Alternate solutions are described below, but NetBeans itself does not include any way to change the icon of the Windows launcher executable called <your branding name>.exe, nor does it provide a way to specify an .icns file for Mac OS X. There is already an enhancement request for Windows icon support: issue #64612.
Similar to toolbar icons, these files always use the .gif extension, regardless of their actual format. The frame.gif file is used for the smallest icon size of 16x16, which shows up in three places: the taskbar (Windows/Linux), in the upper-left corner of the application's title bar (Windows/Linux), and in the upper-left corner of most dialog windows (Windows/Linux). Another file called frame32.gif (which is not generated by the NetBeans Project Properties dialog) provides a 32x32 icon that shows up in the Alt-Tab menu on Windows. Lastly, the frame48.gif file provides a 48x48 icon that shows up in the Alt-Tab menu on Linux.
If you want a simple commandline program to call as part of your Windows build process, the free ReplaceVistaIcon.exe from RealWorld Graphics works well, and can be invoked as simply as:
ReplaceVistaIcon.exe build\launcher\bin\<your branding name>.exe YourIconFile.ico
To do this automatically when building, simply place a copy of ReplaceVistaIcon.exe and <your branding name>.ico into your project's root directory (where build.xml is), and add the following to your suite's Build Script (build.xml) after the import line:
<condition property="isWindows">
<os family="windows" />
</condition>
<target name="build-launchers" depends="suite.build-launchers">
<!-- Replace the icon for the Windows launcher exe. -->
<antcall target="replaceWindowsLauncherIcon"/>
</target>
<!-- Windows-only target that replaces the icon for the launcher exe with our own icon. -->
<target name="replaceWindowsLauncherIcon" if="isWindows" description="Replace the icon for the Windows launcher exe">
<echo message="Replacing icon of Windows launcher executable."/>
<exec executable="ReplaceVistaIcon.exe" resolveexecutable="true">
<arg line="build/launcher/bin/${app.name}.exe ${app.name}.ico"/>
</exec>
</target>
If you would prefer to simply do it manually and need a GUI resource editor, try the free programs:
If you need an editor for creating/converting both Windows .ico files and Mac .icns files, try the excellent, free program IcoFX.
In order to replace it automatically when building, name your .icns file as <your branding name>.icns and place a copy into your project's root directory (where build.xml is), and add the following to your suite's Build Script (build.xml) after the import line:
<!-- Override to change Mac application icon. -->
<target name="build-mac" depends="suite.build-mac" description="Build Mac OS X Application">
<property name="nbdist-contents.dir" value="${dist.dir}/${app.name}.app/Contents"/>
<property name="nbdist-resources.dir" value="${nbdist-contents.dir}/Resources"/>
<!-- Replace the icns file. -->
<delete file="${nbdist-resources.dir}/${app.name}.icns"/>
<copy tofile="${nbdist-resources.dir}/${app.name}.icns" file="${app.name}.icns" />
</target>
This is a simplified version of Tonny Kohar's (of http://www.kiyut.com) build script posted on: http://forums.netbeans.org/ptopic10504.html
In general you cannot. See issue #7551.
If you created the Explorer view (e.g. you created a BeanTreeView or similar and put it in a Swing panel of some sort) then you can use ExplorerManager.setSelectedNodes)] and more rarely TreeView.expandNode to display a given node in your tree (The node must be a descendant of the current root node. You cannot construct a new "similar" Node and hope to select it).
If you did not create the Explorer view then there is no reliable way to find it. However you might try scanningTopComponent.Registry.getOpened() for instances of ExplorerManager.Provider and looking for appropriate nodes that way. Such tricks must be done with care - the fact that you can find the component to do this does not imply that the author of the component intends that it be there forever, remain of the same type, continue implementing ExplorerManager.Provider or anything else. Check nulls, check casts, be prepared for it not to work on future versions.
In the particular case of making a new file wizard, you can and should ask for the file(s) you create to be selected when the wizard finishes, simply by returning them from WizardDescriptor.InstantiatingIterator.instantiate()
Applies to: NetBeans 5.0, 5.5, 6.x
There is thing that is explorer; the name is historical - very old versions of NetBeans had a window named "Explorer" that contained a tree of files and other components. Colloquially, the term is still used to refer to the area in the left side of the main window where the Files and Projects tabs live in the IDE - but NetBeans has long since stopped having names for or frames around tabbed containers. There is an API in NetBeans which contains Swing components that can render Nodes , which is called the Explorer API.
Once you have a component to show Nodes , you will need to set the root node whose children it will display (some views show the root node, some don't, in some cases you can set whether it does or not).
Presumably you have an ExplorerManager set up for your view - just get that and call setRootContext (someNode) and the view will display it.
You do not directly set the Node that is displayed by an Explorer view component (Swing components that display Nodes ) by calling a method on that component. Rather, you set that kind of information by finding the manager for that component - it's what is in charge of what node is displayed, selected, etc.
The manager may be explicitly set on an Explorer view, but usually this is not necessary. When you add a view component (such as a BeanTreeView ) to a Swing container, it will search backward through its parent, it's parent's parent, and so forth, looking for a component that implements ExplorerManager.Provider (an interface with one method - getExplorerManager()). That ExplorerManager is what will determine what is displayed.
While this may seem like an unnecessary layer of indirection, it is actually quite powerful: It makes it possible to very simply create master-detail views ala Windows Explorer: Just add two views to a JPanel subclass that implements ExplorerManager.Provider . It is very easy to set it up so changing the selection in one causes the other one to show the children of the selected object - just the way selecting a folder in Windows Explorer does.
See also the ExplorerManager javadoc . The FAQ about showing explorer views in the main window includes sample usage of ExplorerManager.
An explorer view is a GUI component which can display a Node and (optionally) its child nodes. While Nodes are, by definition, a tree structure, explorer views are much more than just JTrees. Here is a list of the components available:
With the exception of PropertySheetView, all of these classes live in the package org.openide.explorer.view (sources in openide/explorer in NetBeans' CVS).
An explorer view's content is controlled by its ExplorerManager - you don't set the root node directly on the view component, you use its manager. This is so that more than one view can share a single manager, to do master-detail views (for example, the first page of the New Project wizard is one such view - the right hand panel displays children of the left hand panel's selection).
There are a number of advantages to using Nodes and Explorer Views
A common usage is to get a Node for some folder on disk or in the configuration filesystem, optionally create a FilterNode to filter out some child nodes of it or its children, and display that.
In the spirit of building on the shoulders of giants, NetBeans takes advantage of external libraries which are not developed on netbeans.org. Those libraries are either open-source software, or binary-only software but with liberal licenses. A few examples: Apache Tomcat; JUnit; JavaHelp (runtime); javac compiler; JSR-88 interface classes.
For convenience, these libraries are stored in the same Hg repository as the source code under CDDL/GPL. They are placed in well-known places in the source tree. The license text is associated with the binary file to make it clear which terms and conditions the users/developers must agree to besides being compliant with the CDDL/GPL itself. Only source code covered by the CDDL/GPL (or BSD, in the case of samples) can be hosted in the http://hg.netbeans.org/main/ repository. As the NetBeans Hg tree is growing, we need to initiate stricter rules and check that all external binary files have a correct associated license. There are also several recommendations on avoiding unnecessary additions of binary files into Hg.
The build system will automatically check if all binary files under <nbmodule>/external are stored correctly with appropriate license and all required information. <nbmodule> means NetBeans project module, e.g. external is on same level as nbproject. Failing to do so will result in a broken build!
Questions:
Here are the rules NetBeans committers must follow when placing external libraries into NetBeans Hg:
License files should be in the following format:
Name: SomeLib Version: 1.2.3 Description: Library for management of some blah blah blah. License: Apache_V20 [see note regarding normalized names] OSR: 1234 [OSR number, refer to LFI previously; Sun-internal legal] Origin: http://www.xyz.org [where file(s) were downloaded from] Files: xyz.jar, xyz-doc.zip, xyz-src.zip [optional; see below for explanation] Source: URL to source [mandatory for LGPL, otherwise optional] Comment: needed until NB runs on JDK 6+ [optional: why is this library here?] Use of SomeLib version 1.2.3 is governed by the terms of the license below: [TEXT OF THE LICENSE]
As hinted at above, the OSR field refers to a Sun-internal system. Those contributing patches from outside of Sun can leave this field blank. Also note that a single license file may cover multiple JAR files from the same project. For example, if your patch depends on a third-party library distributed under the same license as two JARs, you will only need one license file and can account for both of these JARs in its Files header.
If the Files header is not present, then a license name-x.y.z-license.txt must correspond to a binary name-x.y.z.jar or name-x.y.z.zip. If present, it should list the names of all binaries to which it corresponds.
The header fields are read during the build process and removed. Therefore this information will not appear in the final build or NBMs.
If there is template-based license (like BSD one http://www.opensource.org/licenses/bsd-license.php), e.g. the license file has several ad hoc places to be updated accordingly. The template itself should have the license file stored under nbbuild/licenses with well-defined tags __TAGNAME__; these tags will be replaced during the build. Template-based licenses stored along with the binary in Hg must have be in original form as they came with binary:
Example BSD License, as it is stored in nbbuild/licenses:
Copyright (c) __YEAR__, __OWNER__
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of __ORGANIZATION__ nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Example BSD License, as it is stored in Hg along with binary:
Copyright (c) 2007, NetBeans
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of NetBeans nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Required licenses should be listed in project.properties. (There still must be a license along with the binary in Hg.) The new entry will be called extra.license.files, where the license files will be relative to project basedir, e.g.
extra.license.files=external/x-1.0-license.txt,external/y-2.0-license.txtThis will create an NBM with two extra licenses besides the usual CDDL. This also maintains compatibility with the current build system.
As a convenient shortcut for the common case that you simply want to copy some files to the target cluster (but cannot use the release directory since third-party binaries are involved), you may use the newly introduced release.* Ant properties which should be specified in project.properties. Each key names a file in the source project; the value is a path in the target cluster. Any such pair will automatically:
release.external/beansbinding-0.6.1.jar=modules/ext/beansbinding-0.6.1.jar release.external/beansbinding-0.6.1-doc.zip=docs/beansbinding-0.6.1-doc.zip(Note: if you wish for the binary to be in the classpath of the module as a library, you will still need a <class-path-extension> in your project.xml.) You can also use a ZIP entry on the left side and it will be extracted from the ZIP to your cluster:
release.external/stuff-1.0.zip!/stuff.jar=modules/ext/stuff-1.0.jar
There will be a license repository under nbbuild/licenses where all licenses in use should be available. Each license type will be given a unique name: Apache_V11, Apache_V20, etc. This name must be referred to in the License field. This allows us to count licenses and file names and build a 3rd-party README as well as NBMs. Make sure that the license for a new binary is correctly included under nbbuild/licenses. If there is no existing license of the same type, it must be reviewed prior to committing.
If a sample is created for NetBeans itself, it can be packaged into ZIP file and should not be in the external/ folder. To ensure tests correctly skip over it, the owner must add an entry for the binary into nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-binaries and include a brief explanatory comment.
Alternately, it may be preferable to keep the sample files unpacked directly in Hg, and create the ZIP during the module's build process (either directly into the cluster, or into build/classes for inclusion inside the module). This not only prevents tests from warning about it, but can make it easier to update minor parts of a sample and may make version control operations more pleasant.
The sample itself must be covered by the BSD license; the license must be included in every file (excepting binaries such as icons).
Copyright (c) <YEAR>, Sun Microsystems, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sun Microsystems, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
If sample is not created solely for NetBeans, e.g. bundled in a third-party product and covered by a separate license, it must follow the same rules as for any other binary library.
A binary file has no associated license. (E.g. xyz.jar is missing xyz-license.txt.)
A binary file has an associated license, but does not maintain the naming convention, or has typos. (E.g. xyz.jar with xy-license.txt.)
Licenses are not pure text. (E.g. they contain HTML.)
A binary file is duplicated in several places. Before adding a new library, please make sure that library is not already available in the Hg tree. If it is, check if the version there is suitable for you; if so, communicate with the owner regarding possible upgrades and/or available packages if they are not available. You might need to move the library to a parent cluster as well. If you do depend on such a third cluster, make sure your module is marked as eager, otherwise it will get disabled.
The names of the binary and its license file will change when the binary is upgraded to a newer version. Update project.properties (or, less commonly, build.xml) to reflect this change.
Before moving from my own repository to NetBeans Hg, I used release/modules/ext/ for storing my binary libraries. They need to be moved into external/ unless the library itself is covered by CDDL, build script, licenses etc., must be updated accordingly!
How do I know if some other modules is relying on the source location of my external binaries? Answer: it's not hard to find out. For example, if you want to know who uses httpserver/external, try this (Unix / Bash syntax):
cd nb-main
for f in */{build.xml,nbproject/*.{properties,xml}}; \
do fgrep -H httpserver/external $f; done
Interesting files from build:
Part of regular build. Only pays attention to Hg-controlled files in the checkout, so can run on a built source tree without becoming confused. Writes results in JUnit format for easy browsing from Hudson.
Generate a third-party JAR & license summary. Find every binary in the IDE build which is either present directly in some */external dir or present inside a ZIP in some */external dir. For every such binary, retrieve the license from nbbuild/licenses. Make a single document listing all of the binaries and licenses.
Verify that no such binary is present in more than one place.
Saved as THIRDPARTYLICENSE-generated.txt in development builds.
nbbuild/templates/projectized.xml (netbeans.org modules only) will look up extra.license.files and use them in Info.xml.
release.* properties honored (see above).
nbbuild/build/generated/external-libraries.txt is generated directly from external dirs.
Does not yet take account extra.license.files correctly. Also may not be a complete list of libraries.
Applies to: NetBeans 6.5
Attributes are routinely used in the System Filesystem for providing additional configuration data. In pre-4.0 NetBeans, they were relatively commonly used for user files on disk. They still may be used for user files, but this is now discouraged - the infrastructure has been changed to store all attributes in the userdir rather than sprinking .nbattrs throughout the users disk; using attributes for user files at this point does have negative performance implications.
If you think you have found a bug in the NetBeans Platform or IDE which affects your module development, please file it so it can be fixed. Generally exceptions coming from platform code are bugs in NetBeans (unless it is e.g. an IllegalArgumentException thrown after your code called a method with invalid arguments). Other things can of course be bugs if NetBeans is not behaving according to its documentation, or if something just looks wrong.
As of NetBeans 6.7, org.openide.filesystems.FileChooserBuilder makes this easy. Pass a Class or unique String key to the constructor of a FileChooserBuilder. The next time the same key is passed, the new file chooser will automatically be rooted on the directory selected the last time.
Yes. In NetBeans 5.0 and later there is a wizard available for NetBeans modules which does this for you. Just run New File | Module Development | Action, as an action type choose Conditionally Enabled. Choose "Data Object" in Cookie Class(es) combobox. On the next step select "File Type Context Menu Item" and choose text/x-java in the File Type Context Menu Item combobox, finish the wizard and you are done. For more information you may press the Help button available at the bottom of each step of the wizard.
In NetBeans 4.1 you just need to insert an action ( *.instance ) into the folder Loaders/text/x-java/Actions/ in your module's XML layer. (Available in NetBeans 4.1 and later only.)
In any version of NetBeans, you can also add a global "tools" action to your module's manifest, which will appear in the Tools submenu of the context menu of any node to which the action applies (i.e. on which it is enabled).
Applies to: NetBeans 4.1+
Related: DevFaqFileEditorContextMenuAddition
Yes. In NetBeans 5.0 and later there is a wizard available for NetBeans modules which does this for you. Just run New File | NetBeans Module Development | Action, as an action type choose Conditionally Enabled. On the next step choose text/x-java in the Editor Context Menu Item combobox, finish the wizard and you are done. For more information you may press the Help button available at the bottom of each step of the wizard.
In NetBeans 4.1 you just need to insert an action (e.g. *.instance ) into the folder Editors/text/x-java/Popup/ in your module's XML layer.
Applies to: all versions of NetBeans
Related: DevFaqFileContextMenuAddition
FileUtil.toFile(FileObject fo)
FileObjects differ from java.io.File in certain ways:
They are part of the Filesystems API ; the javadoc for FileObject can be found here .
theDataObject.getPrimaryFile()
FileUtil.toFileObject (File f)
Sometimes it's desirable to work with a file but you don't want to commit it permanently to disk. It's easy to do this in NetBeans by using the in-memory filesystem. The first two lines in the following code will create a new text file and the next three will open it in an editor. You can easily change this to handle other types of files provided that their DataObject's also have an OpenCookie.
FileSystem fs = FileUtil.createMemoryFileSystem();
FileObject fob = fs.getRoot().createData(name, "txt");
DataObject data = DataObject.find(fob);
OpenCookie cookie = (OpenCookie)data.getCookie(OpenCookie.class);
cookie.open();
The Open File menu item is a part of the User Utilities module, in the ide cluster. This module can be added to your project using the Libraries page of the module property sheet.
The User Utilities module also adds the Find in Files feature and support for PDF files (they are recognized and can be opened by double-clicking on them).
A lot of applications show some UI that displays folders of files; also a lot of NetBeans UI is created by showing virtual files in the configuration filesystem.
The basic mechanism is this: Some folder is shown in the UI; files of known types have their own icons, menu items and behavior.
The way NetBeans detects files is this: The "files" being shown are FileObjects - wrappers around java.io.File, or in the case of configuration files, typically wrappers around data stored in some other way, such as inside XML files in modules. What you're actually seeing is Nodes, which are the things that provide actions, localized names, etc. to files or other things.
In between Nodes and FileObjects are DataObject s. A DataObject is like a FileObject, except that it knows what kind of file is being shown, and there are usually different types of DataObject (provided by different modules which implement support for file types - for example, the Image module makes it possible to recognize and open .gif and .png files) for files with different extensions, XML files with different DTDs, etc.
Modules that recognize different file types install DataLoaders - factories for file-type-specific DataObjects.
So what really happens is that, when a folder is expanded, the system asks each known DataLoader, "Do you know what this is?" The first one that says "Yes" gets to create the DataObject for the file (in reality the recognition process is a little more optimized than it sounds here, but it adds up to this description).
In order to actually display something for the each file, the system calls DataObject.getNodeDelegate() for each DataObject, and the Nodes are what you actually see in the UI.
If you use a Unix-based operating system, this concept will be familiar; if not, it may require some explanation.
NetBeans uses virtual files to refer to the users files on disk, and to refer to its own configuration files. If you used the NetBeans IDE 3.6 or earlier, you may remember that the way you constructed your classpath used to be by "mounting" filesystems - folders on your disk.
Filesystems are gone from the UI, but are alive and well under the hood in NetBeans.
A FileSystem is a hierarchical tree of folders and files. A filesystem has a "root folder", which may contain files and other folders. "Files" (FileObjects ) in a Filesystem may be actual files on disk, or entries in JAR file, or entries in an XML file, or anything else that walks and talks like a file that someone has implemented the Filesystem interface for.
In the NetBeans Platform there are implementations of FileSystem for
Filesystems are used both to represent user files on disk, and also to represent configuration data internal to NetBeans - the System Filesystem . This is one of the reason that it takes only minimal code to create a GUI view of the system filesystem - the same file recognition code that recognizes user files, gives them actions, icons and display names is what recognizes internal configuration data.
Especially in the case of the System Filesystem, it can be useful to think of a Filesystem as a "namespace" in which objects (which may contain data or represent Java objects) live - for the System Filesystem, the fact that the entries in it are referred to as files is incidental.
As of NetBeans 4.0 you will rarely work directly with the FileSystem class.FileUtil.toFileObject is the normal way of getting a file object from a disk file.FileUtil.getArchiveRoot is the normal way of getting file objects from a JAR or ZIP file. In NetBeans 4.x FileSystem implementations are also used for version control integration but the 5.0 CVS support no longer uses this system.
Applies to: NetBeans 4.0, 4.1, 5.0, 5.5, 6.0, 6.1
What exactly is the difference between a filename on disk and a FileObject? How do I convert them?
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.
Under the Filesystems API, raw files are not usually manipulated directly. Rather, you should usually be using FileObject. Besides the fact that most other APIs that work with files expect FileObject, these have a number of advantages:
However a FileObject must always exist, unlike File.
In case translation from one to the other is necessary:
Q: I have a node from somewhere (e.g. a NodeAction ).
I think it corresponds to a file on disk; 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
}
In the other direction you can use DataObject.find and then DataObject.getNodeDelegate to get a node representing a file object.
Also see DevFaqFileVsFileObject if you need java.io.File for some reason.
The layer file browser in NetBeans project support shows the default file system.
Open a NetBeans module project in the Projects window. Navigate to Important Files > XML Layer > <this layer in context>. You can examine the IDE's default file system by browsing the <this layer in context> node.
While browsing the filesystem remember that each node has a "Name" property (use the Properties Window to see the properties of each node). You must use the "Name" to refer to the node in the filesystem.
For instance, in default (English) locale the menu bar appears as the node Menu Bar in the filesystem viewer, but its nonlocalizable code name is Menu. So to refer to the menu bar in your layer.xml file you have to use the name of the node, like this:
<folder name="Menu">
instead of
<folder name="Menu Bar">
Applies to: NetBeans 5.5, 6.0, 6.1, 6.5, 6.7
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 selection
}
}
}
Applies to: NetBeans 4.0 and newer
Using InstanceCookie (note that if you have an entire folder of .instance files, there's a more efficient way to get all of them):
DataObject dob = DataObject.find (theDotInstanceFileObject); InstanceCookie ck = (InstanceCookie) dob.getCookie (InstanceCookie.class); MyObject obj = (MyObject) ck.instanceCreate();
Looking at text in IDE such as a menu item, window title, node display name, etc. you may want to change it. But first you need to find where in the code this string is produced. It is very easy to find if you add the following switch into your .../etc/netbeans.conf:
-J-Dorg.openide.util.NbBundle.DEBUG=true
If you use this switch all strings loaded from Bundle.properties files using org.openide.util.NbBundle will have two numbers appended to them. The first number identifies the bundle file. Look for this number in the IDE log to find the location of the properties file that provides this string.
Another handy trick: in a built source tree, run
ant index-layer-paths
to see which module (by code name) contributes each layer file (or folder), including menu items and so on. You can also just look at the trunk version of this file here.
FileUtil.getConfigRoot() FileUtil.getConfigFile(path) // usually you don't need to use this Repository.getDefault()
In NetBeans 6, if the folder is in the System Filesystem (as it almost always will be), it is very simple:
Lookup myObjects = Lookups.forPath ("path/to/folder/in/sysfs");
(note the separator is always / with NetBeans filesystems).
In NetBeans 5.x and earlier, to get a folder from the system filesystem and get all of the .instance files of some particular class in it, instantiated as java objects:
FileObject myFolder = Repository.getDefault().getDefaultFileSystem().findResource (
"MyFolder/MySubFolder");
DataFolder df = DataFolder.findFolder (myFolder);
FolderLookup lkp = new FolderLookup (df);
Collection <? extends MyType> c = lkp.lookupAll (MyType.class);
If you are using NetBeans 5.x, the lookup code above should read:
Lookup.Template template = new Lookup.Template (MyType.class); Collection c = lkp.getLookup().lookup (template).allInstances();
There is a naming convention for APIs in NetBeans. Generally when a new API is introduced, it will be under development and not stable for a while. During that period, the naming convention for its package is org.netbeans.modules.something.api. So, if you rely on an API with a name like that, your code could break. Generally it is the responsibility of the author of that API to refactor all modules in NetBeans source repository when the API graduates to "official" status.
An official API uses the naming convention org.netbeans.api.something. APIs named thusly should remain backward compatible.
netbeans.exe is the Windows launcher for NetBeans. Basically it assembles the class path string for starting up NetBeans, passes the command line arguments, etc., and launches Java with those arguments.
The main reasons for the exe are:
It's nothing terribly exciting, it's just a small C++ app; the sources are in ide/launcher.
Up to NetBeans 6.5 there were actually two executables - nb.exe and netbeans.exe. netbeans.exe will suppress the console window (so you won't see any logging on the command line); nb.exe will show the command line. Under the hood, netbeans.exe invokes nb.exe (so don't rename it).
Starting with NetBeans 6.7 the following changes in the Windows launcher were introduced - WinNB67Launcher.
There is no separate set of Javadoc for the NetBeans Platform. However, as the Platform is just a subset of the IDE, the Javadoc for the IDE will apply.
You can browse the Javadoc online (this link always points to the latest development version).
You can also download the Javadoc for a particular NetBeans release:
Finally, you can go to the update center (Tools > Plugin Manager) in the NetBeans IDE and request the NetBeans API Documentation module, which bundles Javadoc matching your IDE release. Use View > Documentation Indices (Help > Javadoc References in NetBeans 6.x) to see the overview pages for API sets currently used by your open projects. The IDE should also automatically display this Javadoc in code completion popups.
Applies to: NetBeans 5.x, 6.x
Platforms: all
In versions of NetBeans prior to 6.0, two major products were available for download: the IDE and the platform. The platform is the foundation on which the IDE is built, or looking at it another way, the platform is what's left over when you remove all the IDE features from the IDE. At any rate, the platform provides user interface components, build scripts, declarative configuration and many other features that can save you a lot of time and effort in creating your own application.
Because platform-based applications are themselves platforms that can be extended, the IDE can also be extended just as the platform can. Since you can remove features from a platform as well as add new ones, the availability of the platform and IDE let you choose between starting small and adding on (platform) or starting large and removing things (the IDE). Some feel the latter approach is better and even facing such a choice can be confusing to new users. If you're a new user, you'd do well to heed this advice and just use the IDE as a platform. It works just as well and is a lot less trouble.
But if you're still here, you may be asking where is the platform? Binary distributions of the platform are not being made available from version 6.0 onward (and issue #124372 filed to bring them back was closed without any reasonable explanation). So if you want a platform binary, you'll have to create one yourself.
Building the platform is not difficult, but it's not intuitive either. To start, you will need to download the platform source ZIP file and unpack it to some directory. Open a command prompt to that directory and change to the nbbuild subdirectory. From there, issue the following command:
ant -Dcluster.config=platform build-platform
If you're using Java 6, you'll need to add an extra property:
ant -Dcluster.config=platform build-platform -Dpermit.jdk6.builds=true
But be aware that it is not guaranteed to build under Java 6 due to language changes or compiler bugs. It is unlikely you will encounter such a problem in the platform build, though it has certainly been known to happen in the IDE build. If you find something that won't compile under Java 6 but does compile under Java 5, file a bug report (preferably with a patch) about it so it can be corrected. Meanwhile, you can use Java 5 to compile -- even when Java 6 is first in your path -- by using the nbjdk.home system property to point to your Java 5 installation:
ant -Dcluster.config=platform build-platform -Dnbjdk.home=c:/devtools/jdk/jdk-1.5.0_u15
This will build the platform into the netbeans subdirectory (i.e. nbbuild/netbeans). You can zip or tar up the netbeans directory to create a ZIP distribution.
It's also possible to create platforms based on a different subset of the NetBeans project. Hints for doing this can be found here:
Using the IDE is certainly easier, but there are inherent dangers associated with developing against your own IDE as the platform. In particular, another developer on your team may have a different version of the IDE, have different modules/clusters installed or even have simply named the platform something different in the Platform Manager. This can result in a broken build or the introduction of unwanted features. It also makes doing an automated build, such as through Hudson or CruiseControl, far more difficult.
If you want to avoid these problems, you can check the platform you want to build against into source control and then set the netbeans.dest.dir and harness.dir properties in your suite's nbproject/platform.properties file to point to the platform and harness, respectively. Building from a known version checked out from source control avoids these problems and makes it possible to historically reproduce any build. I show example values for these below:
# NOTE: You must remove the nbplatform.default line which might already exist in this file.
# Also note that editing the properties of your suite via the suite customizer (dialog)
# can add that line back in, so you'll need to watch for this and delete it again in this case.
# where the suite is located; you don't need to change this. It exists
# to allow us to use relative paths for the other values
suite.dir=${basedir}
# the path to the NetBeans IDE or platform binary we want to build against
# (e.g. if building against the IDE, this points to the directory created when
# you unpack the IDE zip file). this example assumes your platform directory
# is parallel to the suite directory, but you can change it to suit your needs
netbeans.dest.dir=${suite.dir}/../platform
# path to the build harness you want to use. This is typically in the
# harness subdirectory of your platform, but you could point to a directory
# containing customized build scripts if you want to.
harness.dir=${netbeans.dest.dir}/harness
If you have generated your projects in IDE version 6.7 and later, you have to modify the above described method slightly (6.5.1 and earlier projects compile against newer platform/harness without changes). You can distinguish "newer" project by the presence of cluster.path property in nbproject/platform.properties file or simply by the fact that an attempt to build a suite with above described platform.properties results in error:
.../harness/suite.xml:60: When using cluster.path property, remove netbeans.dest.dir, enabled.clusters and disabled.clusters properties from platform config, they would be ignored.
In such case you have to replace netbeans.dest.dir, enabled.clusters and disabled.clusters properties with new property cluster.path, e.g.:
# NOTE: You must remove the nbplatform.default line which might already exist in this file.
# Also note that editing the properties of your suite via the suite customizer (dialog)
# can add that line back in, so you'll need to watch for this and delete it again in this case.
# where the suite is located; you don't need to change this. It exists
# to allow us to use relative paths for the other values
suite.dir=${basedir}
# just a helper property pointing to the same location as netbeans.dest.dir did before;
# Referenced only in this properties file, has no meaning for NB harness.
platform.base=${suite.dir}/../platform
# classpath-like list of absolute or relative paths to individual clusters
# against which you want your suite to build; Note that you can use
# "bare", i.e. not numbered cluster names, which simplifies later transitions
# to newer version of the platform. E.g:
cluster.path=${platform.base}/platform:\
${platform.base}/ide:\
../otherSuite/build/cluster
# path to the build harness you want to use. This is typically in the
# harness subdirectory of your platform, but you could point to a directory
# containing customized build scripts if you want to.
harness.dir=${platform.base}/harness
Note that the content of cluster.path is not limited to clusters from NB platform, you can add clusters from other suites, standalone modules, etc. This allows to reuse non-platform modules in several RCP apps. More on module reuse here, other details about setting up cluster.path can be found in harness/README.
To obtain a reference to the currently selected editor:
Node[] arr = TopComponent.getRegistry().getCurrentNodes();
for (int i = 0; i < arr.length; i++) {
EditorCookie ec = (EditorCookie) arr[i].getCookie(EditorCookie.class);
if (ec != null) {
JEditorPane[] panes = ec.getOpenedPanes();
if (panes != null) {
// USE panes
}
}
}
To obtain references to all opened editors:
TopComponent[] comps = TopComponent.getRegistry().getOpened();
for (int i = 0; i < comps.length; i++) {
Node[] arr = comps[i].getActivatedNodes();
for (int j = 0; j < arr.length; j++) {
EditorCookie ec = (EditorCookie) arr[j].getCookie(EditorCookie.class);
if (ec != null) {
JEditorPane[] panes = ec.getOpenedPanes();
if (panes != null) {
// USE panes
}
}
}
}
To obtain references to all opened editors-
TopComponent[] comps = TopComponent.getRegistry().getOpened().toArray(new TopComponent[0]);
for (int i = 0; i < comps.length; i++) {
Node[] arr = comps[i].getActivatedNodes();
for (int j = 0; j < arr.length; j++) {
EditorCookie ec = (EditorCookie) arr[j].getCookie(EditorCookie.class);
if (ec != null) {
JEditorPane[] panes = ec.getOpenedPanes();
if (panes != null) {
// USE panes
}
}
}
}
Set<TopComponent> comps = TopComponent.getRegistry().getOpened();
for (TopComponent tc: comps) {
Node[] arr = tc.getActivatedNodes();
for (int j = 0; j < arr.length; j++) {
EditorCookie ec = (EditorCookie) arr[j].getCookie(EditorCookie.class);
if (ec != null) {
JEditorPane[] panes = ec.getOpenedPanes();
if (panes != null) {
// USE panes
}
}
}
}
The editor has its own mechanism for registering keybindings, which is separate from global keybindings (in essence, they belong to the Swing EditorKit for the editor, but there are some registration mechanisms in the editor for this). So if you want to register a keyboard shortcut only against the editor when editing a certain type of file (as opposed to a keyboard shortcut that is a shortcut for an action on the main menu), you'll be using editor-based keybindings.
If there is a global shortcut bound to a key combination, and also an editor-specific one defined for the type of file being edited, the editor wins if the editor has focus.
Simply choose "Combo Box" from the "Swing Controls" palette and drop it onto your interface. Then select the combo and select the "Code" tab in the properties window. In the "custom creation code" field type: "new ChoiceView()". Then return to the "Properties" tab an clear the "model" field. This step is absolutely mandatory otherwise it won't work: by default the Form Editor creates a dummy model for you. It is forbidden to set a model on a ChoiceView. If you do anyway you will get errors like:
java.lang.ClassCastException: java.lang.String cannot be cast to org.openide.explorer.view.VisualizerNode
at org.openide.explorer.view.NodeRenderer.findVisualizerNode(NodeRenderer.java:232)
at org.openide.explorer.view.NodeRenderer.getListCellRendererComponent(NodeRenderer.java:152)
at javax.swing.plaf.basic.BasicComboBoxUI.paintCurrentValue(BasicComboBoxUI.java:1202)
at com.sun.java.swing.plaf.windows.WindowsComboBoxUI.paintCurrentValue(WindowsComboBoxUI.java:293)
at javax.swing.plaf.basic.BasicComboBoxUI.paint(BasicComboBoxUI.java:888)
at com.sun.java.swing.plaf.windows.WindowsComboBoxUI.paint(WindowsComboBoxUI.java:199)
at javax.swing.plaf.ComponentUI.update(ComponentUI.java:143)
at javax.swing.JComponent.paintComponent(JComponent.java:763)
at javax.swing.JComponent.paint(JComponent.java:1029)
at javax.swing.JComponent.paintChildren(JComponent.java:864)
at javax.swing.JComponent.paint(JComponent.java:1038)
at javax.swing.JComponent.paintChildren(JComponent.java:864)
at javax.swing.JComponent.paint(JComponent.java:1038)
...
Finally switch to the "Source" view and fix the import errors.
Generally if it's a third party library (you didn't write it, you can't or don't want to change it), you will want to use a wrapper module (see DevFaqWrapperModules). An NBM file (a module packaged for delivery over the net) can contain more than one JAR, so all your libraries can be included in a single file that packages your module.
Note you can multi-select JARs in the New Library Wrapper Module wizard.
You can add libraries manually to a standard module; or add additional libraries to an existing library wrapper module. The relevant data is in the project.xml for the module. What you would do is add entries similar to this one for each JAR.
<class-path-extension>
<runtime-relative-path>ext/hexedit.jar</runtime-relative-path>
<binary-origin>release/modules/ext/hexedit.jar</binary-origin>
</class-path-extension>
Note if you want these libraries to be usable outside of the module they're declared in, then you must add the relevant packages to the list of public packages for that module.
As your code evolves, you may find that it no longer needs dependencies on some modules that it used to require. In this case, you can run the fix-dependencies Ant target on your module to remove any unnecessary dependencies from your project.xml.
As with any automated modification, it's a good idea to ensure that this file is up-to-date in source control before running this task, although in an emergency you can use the IDE's local history feature to revert changes.
Since 6.7 you can use use non-netbeans.org modules (yours or 3-rd party) directly in your suite and configure it conveniently via GUI. Go to Properties of your suite project, Libraries tab:
If you have sources of modules you want to reuse, click Add Project... button and browse for suite or standalone module project you want to add.
If you want to use 3-rd party binary modules, just unpack them into cluster folder somewhere on your disk. Preferably put the cluster under your suite's root so that you can use relative paths, which makes setup in team environment easier. Then click Add Cluster... button and browse for the cluster folder:
You can also add sources and/or Javadoc for binary modules, just like for whole NetBeans Platform.
Once projects and clusters are added to Libraries and checked, they behave just like part of the platform. They will appear in running platform application, will be included in binary distribution, modules from your suite can depend on them, etc.
If you cannot even use new harness and/or IDE, you have to use suite chaining, build your own platform and depend on it. See harness/README file for details. See also HowToReuseModules.
NetBeans implements the FileEncodingQuery object (FEQ) to determine the language encoding for projects and files. The FEQ is an interface for obtaining information about which encoding should be used for reading from/writing to a particular file. It can be best defined as a layer model that adheres to the following precedence rules (level of importance from top to bottom):
For example:
For JSP pages, the JSP parser is responsible for determining the encoding value. For example: if the file itself doesn't contain the encoding declaration, the parser looks in web.xml. If there is no declaration there either, it returns ISO-8859-1.
The fallback FEQ is applied (i.e. the encoding of the system locale). This applies to imported projects and projects created in NetBeans versions 5.x and prior.
Note: This does not have any impact on the global project encoding value, which is still used for the creation of new NetBeans 6.0 projects, and is by default UTF-8. Nor does this affect the encoding value of previously created NetBeans 6.0 projects created during the same session, or opened projects created from previous sessions.
Project Types
File Types
'ide.welcome' is the map ID of the first help topic shown in the JavaHelp window. It is defined in the 'userguide' module. If you have excluded this module, you need to define this map ID yourself, otherwise you will encounter unexpected/unpleasant behavior:
Project mainProject = org.netbeans.api.project.ui.OpenProjects.getDefault().getMainProject();
For this, you need to declare dependencies on Project UI API, Project API and File System API.
As an example usage of the above line of code, here is an action that display a JOptionPane containing the path to the main project, if a project is selected:
public final class ShowMainProjectAction extends CookieAction {
protected void performAction(Node[] activatedNodes) {
String projectPath = OpenProjects.getDefault().getMainProject().getProjectDirectory().getPath();
JOptionPane.showMessageDialog(null, projectPath);
}
protected int mode() {
return CookieAction.MODE_EXACTLY_ONE;
}
public String getName() {
return NbBundle.getMessage(ShowMainProjectAction.class, "CTL_ShowMainProjectAction");
}
protected Class[] cookieClasses() {
return new Class[]{Project.class};
}
@Override
protected void initialize() {
super.initialize();
// see org.openide.util.actions.SystemAction.iconResource() Javadoc for more details
putValue("noIconInMenu", Boolean.TRUE);
}
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
@Override
protected boolean asynchronous() {
return false;
}
}
Be sure that what you really want to be doing is implement FileSystem . Unless you really need to access objects in a database, remote server, or some other such storage as if they were files, you are probably heading in the wrong direction.
If you do need to implement a FileSystem, you should probably start with AbstractFileSystem - it handles a lot of knotty locking semantics correctly and will save you a lot of time, effort and bugs.
Normally modules interact with one another using public packages: a module can (indeed, must) declare which, if any, of its Java packages are intended to be visible to other modules. When you declare a specification dependency on another module, you only get access to the public packages. This kind of dependency looks like this in the manifest:
OpenIDE-Module-Module-Dependencies: some.other.module > 1.5
(requesting version 1.5 or greater of some.other.module) or like this:
OpenIDE-Module-Module-Dependencies: some.other.module
(requesting any version; not recommended).
Occasionally you may find that the author of a module neglected to expose certain classes in public packages which you know (from reading the source code) that you need to use and know how to use properly. The classes are public but not in declared public packages. It is possible to access these classes if you really have to. But you need to declare a dependency on that exact version of the other module, since such classes might change incompatibly without notice in a newer copy of that module. Since such a change could break your module, the NB module system requires that you declare the implementation dependency so that it can verify before loading your module that it matches the other module. The general idea is that if module B has an implementation dependency on module A, the system should not be able to load B unless it has the exact same version of A that B was compiled against. To make an implementation dependency in the manifest, use
OpenIDE-Module-Module-Dependencies: some.other.module = 3
where the "3" is what that other module declared as its current implementation version:
OpenIDE-Module-Implementation-Version: 3
In order to add an implementation dependency, first add the dependency to the project (e.g. click on "Add Module Dependency" from the "Libraries" node or by click the "Add Dependency..." button in Project->Properties->Libraries panel). Make sure you've checked the "Show Non-API Modules" checkbox when you're looking for the non-API module, otherwise you're not going to find it. Then, after you've added the module as a dependency, edit the dependency (either Project->Properties->Libraries->Select Dependency->Edit or Project->Right click on dependency Libraries node->Edit) and just select the "Implementation Version" radio box in the Edit dependency dialog. If you don't want to "see" all packages within the module, but only a subset, uncheck the "Include Packages in Classpath" checkbox and select the packages you want to see. This works best if the other module uses a nonnegative integer for the implementation version, and if you also check Append Implementation Versions Automatically in the properties dialog.
Implementation dependencies are to be avoided unless you really need access to all the classes in another module, for the following reason: If your module has an implementation dependency on module A, and module A is upgraded, your module probably must be upgraded as well, or the system will not load it (assuming module A's implementation version has changed with the upgrade - it should have). It is a particularly bad idea to use implementation dependencies if you do not know what the other module's author's intentions are for keeping the classes you use available and compatible. It is always possible to make an enhancement request asking for the other module to make the classes you want to use available publicly. Do not use implementation dependencies just to have access to one or two some convenience or utility classes in another module - copy them instead, and file a bug report asking for an API for doing what you're trying to do.
Friend dependencies are a little different. A module may have an API which its author is not yet comfortable exposing to just anyone - it might not be fully stabilized yet. In this case, the module with the API can declare some public packages, but also stipulate that only a predefined list of "friend modules" are permitted to use them. The friend modules just declare a regular specification version dependency, but unknown modules are not permitted to use any packages from the API module without an implementation dependency.
(Look at the Versioning panel in the API module's project Properties dialog.)
Always prefer friend APIs to implementation dependencies where there is a choice.
Implementation dependencies cause special problems for Auto Update. (Some background information is available in NetBeans API & Module Versioning Policy / Numbering Scheme for Updates.)
The problem is that when an implementation version of a module published to an update server changes, any modules declaring implementation dependencies on it must also be published, with dependencies on the new version of the base module. Furthermore, the Auto Update client has just one method for deciding whether an NBM on a server is an "update" relative to what you already have installed: if its specification version is larger. So consider the following snapshot of an update center. (The syntax is not what the actual XML file looks like, just an abbreviated version that shows parts relevant to this example.)
[Monday] OpenIDE-Module: infrastructure OpenIDE-Module-Specification-Version: 1.0 OpenIDE-Module-Implementation-Version: 070120 OpenIDE-Module: guifeature OpenIDE-Module-Specification-Version: 1.0 OpenIDE-Module-Implementation-Version: 070120 OpenIDE-Module-Module-Dependencies: infrastructure = 070120
These two modules were built at the same time and could be installed together into a NetBeans instance. So far so good.
Now consider what happens when the developer of guifeature adds a major new feature and decides to publish a new version, 1.1. The next day's build produces
[Tuesday] OpenIDE-Module: infrastructure OpenIDE-Module-Specification-Version: 1.0 OpenIDE-Module-Implementation-Version: 070121 OpenIDE-Module: guifeature OpenIDE-Module-Specification-Version: 1.1 OpenIDE-Module-Implementation-Version: 070121 OpenIDE-Module-Module-Dependencies: infrastructure = 070121
Again, these two modules could be installed together.
But what if a user connected to the update center on Monday and downloaded both modules, and then connects again on Tuesday looking for updates? infrastructure is still listed as 1.0 so Auto Update ignores it (1.0 is "already installed", after all). guifeature 1.1 is however a possible update. What if you install this update? The module system will refuse to enable guifeature because it requests infrastructure = 070121, whereas you have infrastructure = 070120. Oops!
The solution (short of not using implementation dependencies at all) is to use the NetBeans build harness to compute a specification version. The developer removes OpenIDE-Module-Specification-Version from manifest.mf in the source projects for both modules. manifest.mf for infrastructure instead will get
OpenIDE-Module-Implementation-Version: 1
(only positive integers 1, 2, ... are supported!). And nbproject/project.properties for both modules will get the specification version in a new form:
spec.version.base=1.0.0
The IDE's GUI for module projects lets you do all this without editing metadata files manually; just click the option Append Implementation Versions Automatically in the Versioning panel of the Properties dialog.
(The extra .0 is required for modules in the NetBeans distribution. When sources are branched for a release, spec.version.base is incremented to 1.0.1, 1.0.2, ... for each release on the branch. "Trunk" (development) changes increment the first or second digits, e.g. 1.1.0, 1.2.0, ...)
The effect of using spec.version.base is that our AU snapshots now look like this instead:
[Monday] OpenIDE-Module: infrastructure OpenIDE-Module-Specification-Version: 1.0.0.1 OpenIDE-Module-Build-Version: 070120 OpenIDE-Module-Implementation-Version: 1 OpenIDE-Module: guifeature OpenIDE-Module-Specification-Version: 1.0.0.1 OpenIDE-Module-Implementation-Version: 070120 OpenIDE-Module-Module-Dependencies: infrastructure = 1 [Tuesday] OpenIDE-Module: infrastructure OpenIDE-Module-Specification-Version: 1.0.0.1 OpenIDE-Module-Build-Version: 070121 OpenIDE-Module-Implementation-Version: 1 OpenIDE-Module: guifeature OpenIDE-Module-Specification-Version: 1.1.0.1 OpenIDE-Module-Implementation-Version: 070121 OpenIDE-Module-Module-Dependencies: infrastructure = 1
The update to guifeature is now safe; it can still use infrastructure from Monday. Note the new "build version" tag which is used only for diagnostics, not for dependencies.
If there is actually a change in the signature of anything in infrastructure that might affect guifeature, then the developer merely needs to increment the implementation version in infrastructure/manifest.mf:
[Wednesday] OpenIDE-Module: infrastructure OpenIDE-Module-Specification-Version: 1.0.0.2 OpenIDE-Module-Build-Version: 070122 OpenIDE-Module-Implementation-Version: 2 OpenIDE-Module: guifeature OpenIDE-Module-Specification-Version: 1.1.0.2 OpenIDE-Module-Implementation-Version: 070122 OpenIDE-Module-Module-Dependencies: infrastructure = 2
If the user connects to the update center on Wednesday, the wizard will display both modules as needing to be updated - which is exactly what you want.
How is this system enforced? For one thing, attempts to use inherently unsafe implementation dependencies, or incorrect uses of spec.version.base, should produce warnings during the module build process. So look at the output of Ant once in a while and see if the build harness is telling you something.
There is also a continuous builder at http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/ which (among other things) tries to build NBMs for all modules in the NetBeans standard distribution plus those experimental "alpha" modules normally published on the update center for development builds. If you commit changes to experimental modules this build will be triggered; failures are mailed to broken_builds@netbeans.org, which all developers of modules in netbeans.org ought to subscribe to.
This builder uses an Ant task <verifyupdatecenter> to detect dependency problems among NBMs. There are two checks:
The second check is what will catch a lot of mistakes in usage of implementation dependencies as described above. Unfortunately it is not feasible to run the second check as part of an offline build process in your own source checkout, as it depends on a build of older sources; so you will need to commit changes and wait for the next build to verify them.
Generally there are two possible solutions to a test failure from this stage:
In either case, to fix a test failure you will generally also need to increment the specification versions of modules on both sides of the dependency.
Applies to: NetBeans 5.x, 6.x
Platforms: all
*.instance files are NetBeans parlance for a file which is actually standing in for an instance of an object.
An instance file typically says what class it is an instance of via its class name - for example, com-foo-mymodule-MyObject.instance. A *.instance file may create its instance from any Java class with a default constructor, or by calling a static method on a class.
In NetBeans infrastructure, *.instance files result in InstanceDataObjects. InstanceDataObjects can supply InstanceCookies, which in turn instantiate the object. So, code to actually get an instance of an object declared in the system filesystem (DevFaqSystemFilesystem) would look like this (plus error checking):
public static Object getTheObject(String pathInSystemFilesystem) throws Exception {
return DataObject.find(
Repository.getDefault().getDefaultFileSystem().findResource(pathInSystemFilesystem)).
getLookup().lookup(InstanceCookie.class).instanceCreate();
}
A much easier way to get all instances of objects in a folder exists:
for (WhatISaidToPutHere instance :
Lookups.forPath("MyFolder").lookupAll(WhatISaidToPutHere.class)) {
// ...
}
Note that a default constructor is not required in an XML layer; you can also use a static method, using the following syntax:
<file name="ObjectTypes.instance"> <attr name="instanceCreate" methodvalue="org.netbeans.core.ui.UINodes.createObjectTypes"/> <attr name="instanceOf" stringvalue="org.openide.nodes.Node"/> </file>(The instanceOf attribute is optional; it lets the system avoid instantiating your object just to see if it is assignable to Node. This is only useful in folders that contain objects of many different types mixed together, which is normally true only in the semi-deprecated Services folder: code looking for instances of one type only would rather not load everything.)
See also: DevFaqDotSettingsFiles
It's easy to add a separator to the menus by editing the module's layer file; in fact, the Action wizard will do this for you. Items in the main toolbar are also configured through the layer file, but you may find that adding a separator as you would for the menu does not work in the toolbar. So how do you add a separator to the toolbar?
You can do this by creating a class like:
package com.example.util.widgets;
public class VerticalSeparator extends JSeparator {
public VerticalSeparator() {
super(JSeparator.VERTICAL);
}
@Override
public Dimension getMaximumSize() {
return new Dimension(getPreferredSize().width, super.getMaximumSize().height);
}
@Override
public Dimension getSize() {
return new Dimension(getPreferredSize().width, super.getSize().height);
}
}
Then simply reference an instance of this separator in the layer file:
<file name="SeparatorAfterModelToolbarActions.instance">
<attr name="instanceClass" stringvalue="com.example.util.widgets.VerticalSeparator"/>
<attr name="position" intvalue="25"/>
</file>
Yes. See the JavaHelp Integration 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.
There is an IDE wizard for creating a help set for your module.
Applies to: NetBeans 5.x, 6.x
Platforms: all
The first thing to check is that you're JavaHelp configuration files are correct. Since these files contain IDs and file paths, it's easy to make a mistake. Unfortunately, the JavaHelp system doesn't provide you with much warning when such an error occurs.
It's also worth noting that the JavaHelp implementation in NetBeans IDE 6.5.x and earlier seems to require the .html file extension.
Keybindings are specified in yet another folder in the system filesystem . The folder Shortcuts/ contains .instance files or .shadow files (shadow files are like symlinks to another file in the system fs) - these map to Actions.
The file name for the action (.instance or .shadow) file in Shortcuts/ is used to specify what keys are bound. This is done using an emacs-like syntax for specifying keybindings - e.g., CA-P equals Ctrl-Alt-P.
For a full listing of the hard-coded and cross-platform prefixes for key definitions, see the javadoc for Utilities.stringToKey() - that and its analogue, Utilities.keyToString() are used to encode and decode these.
There are special modifier characters which map to Command on Mac and Ctrl on PC, Ctrl on Mac and Alt on PC. You should use those unless you're really sure your app will never be used on macintosh or never be used by someone with a non-English macintosh.
Here is an example of what a layer file might look like if you bound the Ctrl+Shift+Equals sequence to the com.tomwheeler.example.fooviewer.FooAction action:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.0//EN"
"http://www.netbeans.org/dtds/filesystem-1_0.dtd">
<filesystem>
<folder name="Actions">
<folder name="View">
<folder name="FooViewer">
<file name="com-tomwheeler-example-fooviewer-FooAction.instance" />
</folder>
</folder>
</folder>
<folder name="Shortcuts">
<!--
set up a shortcut key for executing the Foo Action:
Ctrl + Shift + Equals on Linux and MS Windows, but Command + Shift + Equals on a Mac
-->
<file name="DS-EQUALS.shadow">
<attr name="originalFile" stringvalue="Actions/View/FooViewer/com-tomwheeler-example-fooviewer-FooAction.instance"/>
</file>
</folder>
</filesystem>
For an existing release you can look at e.g. KeymapProfileFor60 to see the specification.
If you are developing a module for NetBeans development builds, you can just look at this file in the section Shortcuts/. This will show all global keybindings being used by modules in the standard IDE as well as experimental update center in http://hg.netbeans.org/main/ and http://hg.netbeans.org/main/contrib/ as of a few hours ago.
Editor-specific keybindings are listed in Editors/*/*/Keybindings/ folders, which is unfortunately harder to browse through.
Be conservative about adding new keybindings; they are a precious resource. Be careful with bindings using Alt, as these often clash with mnemonics, Linux window manager shortcuts, etc. If at all possible, use a multistroke binding: for example, Shortcuts/D-J R C.shadow binds the 3-stroke sequence Ctrl-J R C.
Answer:
In NB 6.0 and 6.1, the Platform includes only the following 3rd party libraries and licenses:
#2 and #3 are only required for compatibility with JDK 1.5. If you can afford to require JDK 6 or later, those two libraries are not necessary.
However, the above list applies only if your only dependencies are on the content of the 'platform' cluster (folder). If your application depends on other parts of the NetBeans IDE (e.g. the 'ide', or 'java' cluster) there may be derived dependencies on other 3rd party libraries. Such as scenario would require a more thorough investigation.
The editor keeps a registry of open editors; you can track changes in which editor is active using this method: org.netbeans.api.editor.EditorRegistry.addPropertyChangeListener(myListener);. If you attach any listeners to the Document or anything else in the active editor text component, remember to remove them when the active editor changes.
DataObject.Registry registries = DataObject.getRegistry();
registries.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
System.out.println("ChangedListener: o = " + e.getSource().getClass());
System.out.println("ChangedListener: o.source = " + e.getSource());
}
});
DataObject[] objects = registries.getModified();
for (int i = 0; i < objects.length; i++) {
DataObject dataObj = objects[i];
System.out.println("data object name = " + dataObj.getName());
System.out.println("data object pimary file name = " + dataObj.getPrimaryFile().getName());
Set fss = dataObj.files();
Iterator iter = fss.iterator();
while (iter.hasNext()) {
FileObject fo = (FileObject) iter.next();
System.out.println("\tset file object: " + fo.getName());
}
}
The DataObject.Registry in LoadersAPI gives you a set of modified DataObjects. You can also add a listener and be notified when the set of modified objects changes.
As this operates on DataObjects you will first need to get a DataObject for your FileObject using DataObject.find() . Or you can take the modified DataObjects and do dataObject.getPrimaryFile().
(Wondering what this is about? See the general FAQ item on keybindings).
There should be no Alt-bound keyboard shortcuts on the mac ever - it is used on international keyboards as the compose key (for a long time, we didn't know it, but Norwegian and French users could not type } or { in NetBeans - kind of limits the usefuless of a Java IDE).
All standard shortcuts should be bound with wildcard keys - e.g., in the layer, not AS-P for Alt-Shift-P, but OS-P, which will map to Alt-Shift-P on PC and Ctrl-Shift-P on mac. For Ctrl/Command, in the layer put DS-P instead of CS-P to bind conditionally to ctrl on PC and command on mac.
All Alt-bound keybindings in the IDE are specified with O so that they are rebound to Ctrl on mac, because they will interfere with typing in a lot of locales (the real way to think about it is "the mac does not have an Alt key" - it does but it is a composition key - you can't use it).
Any alt-bound keybinding on mac is a bug. If you use the logical syntax for keybindings, your app will always work intuitively on any platform.
Note that the mapping handling does skip key combinations that simply can't work on Mac - for example, Command-H and Command-Q are always consumed by the OS, so D-H and D-Q map to Ctrl-H and Ctrl-Q, respectively, everywhere.
Lookup is a mechanism for finding instances of objects. It is pervasively used in NetBeans APIs. The general pattern is that you pass a Class object and get back an instance of that class or null. See the Javadoc for links to articles describing its inspiration and purpose.
The simplest way to think of Lookup is that it is a Map where the keys are Class objects and the value for each key is an instance of the key class.
There is the global lookup which is used to find objects (often, but not always, singletons) that are registered throughout the system. Also, many types of objects have a method getLookup() that enables other code to get things specific to that object. In particular, Nodes and Project objects have a Lookup.
The primary purpose of Lookup is decoupling - it makes it possible to use generic objects to get very specific information, without having to cast objects to a specific type. Confused yet? It's simple. Take the example of OpenCookie - it has one method, open() that will open a file in the editor.
Say that I want to write some code that will open the selected file when the user does something. It could be an Action, a button, or maybe my code has just created a file and I want to open it. This is what I will do:
Node[] n = TopComponent.getRegistry().getActivatedNodes();
if (n.length == 1) {
OpenCookie oc = n[0].getLookup().lookup(OpenCookie.class);
if (oc != null) {
oc.open();
}
}
One can even write this code more simply, without the Nodes API at all, using Utilities.actionsGlobalContext():
OpenCookie oc = Utilities.actionsGlobalContext().lookup(OpenCookie.class);
if (oc != null) {
oc.open();
}
The power of all this is in the level of decoupling it provides: My code that wants to open the file does not have to know anything at all about what happens when the file is opened, or what kind of file it is, or what module supports opening it. And the module that supports opening it does not need to know anything about who is going to open it. They both simply share a dependency on the abstract interface OpenCookie. So either one can be replaced without affecting the other at all.
A good example of this is in the POV-Ray tutorial. It launches an external process that generates a .png file. When the process ends, it wants to open it, so it does the following:
FileObject fob = FileUtil.toFileObject(new File(pathWePassedToProcess));
if (fob != null) { //the process succeeded
DataObject dob = DataObject.find(fob);
OpenCookie oc = dob.getCookie(OpenCookie.class);
if (oc != null) { //the Image module is installed
oc.open();
}
}
The fact is that it is the Image module that makes it possible to open .png files in NetBeans. But the POV-Ray tutorial does not need to know or care that the Image module exists, or what it does - it simply says "open this".
The common pattern you'll see for Lookup usage is one where there are three components:
For global services, the model is more simple - typically there will be some singleton object, implemented as an abstract class:
public abstract class GlobalService {
public abstract void doSomething(Something arg);
public static GlobalService getDefault() {
GlobalService result = Lookup.getDefault().lookup(GlobalService.class);
if (result == null) {
result = new NoOpGlobalService();
}
return result;
}
private static class NoOpGlobalService extends GlobalService {
public void doSomething(Something arg) {}
}
}
Some other module entirely actually registers an implementation of this interface in the default Lookup. StatusDisplayer is a good example of this pattern.
Collection<? extends SomeIface> c = Lookup.getDefault().lookupAll(SomeIface.class);
Note: In NetBeans versions prior to 6.0 you need to use Lookup.Template as the lookupAll method is not present yet.
The Lookup.Result can be listened on for changes in the result of the query. It is often useful to think of a Lookup as a space in which objects appear and disappear, and your code can respond as that happens (the following code uses the NB 6.0 lookupResult method - just use the pattern above with the Lookup.Template for NetBeans 5):
class ObjectInterestedInFooObjects implements LookupListener {
final Lookup.Result<Foo> result; //result object is weakly referenced inside Lookup
ObjectInterestedInFooObjects() {
result = someLookup.lookupResult(Foo.class);
result.addLookupListener(this);
resultChanged(null);
}
public void resultChanged(LookupEvent evt) {
Collection<? extends Foo> c = result.allInstances();
// do something with the result
}
}
Another question is, on the side that's providing the lookup, if you have a collection already, how can you expose that in a Lookup. For that, you can create your own AbstractLookup and use InstanceContent to provide the collection of objects that belong in your Lookup.
Objects in a Lookup often are not instantiated until the first time they are requested; depending on the implementation, they may be weakly referenced, so that if an object is not used for a while, it can be garbage collected to save memory. So Lookup additionally enables lazy instantiation of objects, which is useful for performance reasons.
Do not do any of these things in production code!
The simplest way is to call Lookup.toString(). If you want the output in a more readable form, do the following and print/format the resulting collection as you wish:
| NetBeans 5.x Code | NetBeans 6 and up |
|---|---|
| Lookup.Template tpl = new Lookup.Template ( Object.class); Lookup.Result result = theLookup.lookup (tpl); Collection c = result.allInstances(); for (Iterator i=c.iterator(); i.hasNext();) { Object o = i.next(); //do what you want } | Collection<? extends Object> c = theLookup.lookupAll(Object.class); for (Object o : c) { //do what you want } |
All of these are really historical variations on the same theme. In all cases, you pass a Class object and get back null or an instance of that class. You can see the progression in genericness:
SharedClassObject is the oldest version of the Lookup pattern in NetBeans APIs, dating to circa 1997 (because of various performance issues, eventually all usages of SharedClassObject should be deprecated and removed from the APIs). You'll see that form used in SystemOption for storing settings, and most of the singleton Action objects in the actions API. All objects returned by it will be instances of SharedClassObject.
getCookie() (circa 1999) is specific to Nodes and DataObjects. It uses the same pattern, but all objects returned by it will implement the empty Node.Cookie marker interface.
The down-side to both of the above is that they specify the return type. In the case of Node.Cookie, in practice, this meant that anything that might possibly need to be provided by a DataObject or Node needed to implement this silly marker interface, forcing it to have a dependency on the Nodes API, or a wrapper Cookie class had to be created to provide the underlying object, which just added useless classes and noise.
Lookup is the most modern and generic version of this pattern, and probably the final one. It offers two advantages:
The default lookup is Lookup.getDefault(). It is the registry for global singletons and instances of objects which have been registered in the system by modules. Note that in JDK 6, ServiceLoader operates on the same principle. The default lookup searches in two places:
Objects contained in lookup are instantiated lazily when first requested.
Here is the usual usage pattern:
1. A central "controller" module defines some interface, e.g.
package controller.pkg;
public interface MyService {
void doSomething();
}
2. Each module which wants to implement that service depends on the controller module which defines the interface, and creates and registers an implementation:
@ServiceProvider(service=MyService.class)
public class MyImpl implements MyService {
public void doSomething() {....}
}
It is also possible to declaratively mask other people's implementations and declaratively order implementations so some will take precedence.
3. The controller finds all implementations and uses them somehow:
for (MyService s : Lookup.getDefault().lookupAll(MyService.class)) {
s.doSomething();
}
Applies to: NetBeans 6.7
Dne Monday 26 November 2007 17:37:48 Rob Ratcliff napsal(a): > For the bus we developed, we could subscribe by a specific type, for all > subclasses of a type or for certain message header attributes of an > event. We also had a bus per "session" (the GUI could display multiple > sessions/workspaces using tabs -- equivalent to a JMS topic) Â so that > only events related to that session would be delivered. And, like I > mentioned earlier, there was support to register as a "GUI" listener or > a "business" listener so that events would automatically be delivered in > the correct thread to avoid EDT lockup and rendering issues. > > I'd be interested in hearing what you and others think about these types > of capabilities and how they compare to the NetBeans paradigms.
I've been thinking about this for a while and I believe that there is event bus like system in NetBeans. It is Utilities.actionsGlobalContext()
We have our event bus and it is accessible via Utilities.actionsGlobalContext(). Indeed it may not be perfect, but it plays exactly the role described in the presentation. Menu, Toolbar, etc. listen on it, while somebody else updates it.
Indeed, there could be some improvements. We do not support merging of events or network access, but if one really cares, there is a way to plug into the system. All one needs to do is to implement ContextGlobalProvider One sample implelemention is in openide/windows and second in imagine.dev.java.net.
I've heard a complain that...
> This is a central listener, not an event bus
... however this boils down to a question: How do you envision an event bus? It is a place to contain events or objects that somehow appear in the system. It allows anyone to selectively listen on what is happening in the bus
So in fact event bus is a central listener. Just like Utilities.actionsGlobalContext().
Indeed it could be improved. Is there anyone who would like to contribute in improving our actionsGlobalContext? If so, what should be done?
Hi Jaroslav, I think it'd be useful to define exactly what an event bus is (like you mentioned),  what use cases it supports and how NetBeans supports these use cases currently and how it might support these in the future. I used an EventBus approach in my last project  for receiving asynchronous data events from the Network (such as position updates, network status events) and internal events such as service status (network disconnected) and other state change events such as "sensor network reconfigured"...essentially when it made more sense to use a hub and spoke communication model rather than a point-to-point.  There could be multiple instances of the EventBus, which used a  EDT type of model (dispatcher thread/queue) and supported subscriptions by type (any event derived from a base class)  or property of the header.
Since the "Lookup Library" allows you to uncouple senders from receivers, and allows receivers to be notified of changes, I consider it as a small event bus.
I consider "local lookups" as a small event bus, where you can listen to different "event topics". In the previous case, it would probably be enough to dedicate on Lookup for the network events and create various types holding enough information about the events. The you could add/remove/change the content of the lookup and deliver events about such changes.
The instances could be looked up globally or injected into a given component. It supported "business" and "GUI"  subscriptions to automatically deliver the event in the correct thread. If I did it again,  I'm  thinking I'd use a JMS style API that supported a Hibernate style OQL subscription. (I have some more details here: http://developers.sun.com/learning/javaoneonline/j1sessn.jsp?sessn=TS-3723&yr=2007&track=2) The EventBus talk given at JavaOne 2006 had some great use case examples: EventBus https://eventbus.dev.java.net/HopOnTheEventBus-Web.ppt These frameworks provide some other use cases and API examples: D-Bus http://www.freedesktop.org/wiki/Software/dbus http://www.freedesktop.org/wiki/IntroductionToDBus JUIPiter  http://juipiter.sourceforge.net Bradlee Johnson's ReflectionBus  http://sourceforge.net/projects/werx/ Jasper-Potts - Why Spaghetti Is Not Tasty: Architecting Full-Scale Swing Apps, 2007 JavaOne Conference, TS-3316 http://developers.sun.com/learning/javaoneonline/j1sessn.jsp?sessn=TS-3316&yr=2007&track=2 (Also see the JMS API and the OMG COS Notification Service API.) I don't have much time to spend a lot of time coding on the side right now, but I'd be happy to help define requirements and use cases if that would be useful to you. Thanks! Rob
DataObject does not yet support a getLookup() method, but is expected to do so in the future.
You can simulate this behavior in the meantime by using InstanceContent and AbstractLookup. Simplified typical usage:
public SomeObject {
private InstanceContent content = new InstanceContent();
private final AbstractLookup lkp = new AbstractLookup(content);
public someMethod() {
ic.set (someCollection...);
}
public Lookup getLookup() {
return lkp;
}
}
This is how you create a lookup with dynamic content of your choosing. See also Tom Wheeler's TodoListManager for an example of some code that illustrates how to do this.
As of NetBeans 6, a number of convenience methods have been added to lookup, and support for Java generics has been added to Lookup. The following are differences:
| NB 5.x Code | NB 6 Code |
|---|---|
| Lookup.Result r = lkp.lookup( new Lookup.Template(X.class)) | Lookup.Result<? extends X> r = lkp.lookupResult(X.class) |
| Collection c = r.allInstances() | Collection<? extends X> c = r.allInstances() |
| Lookup.Template t = new Lookup.Template(X.class); Lookup.Result r = lkp.lookup(t); Collection c = r.allInstances(); | Collection<? extends X> c = lkp.lookupAll(X.class); |
The new style also works well with for-loops and avoids casts:
for (SomeService s : Lookup.getDefault().lookupAll(SomeService.class)) {
// ...
}
// ...
SomeSingleton s = Lookup.getDefault().lookup(SomeSingleton.class);
if (s != null) {
// ...
}
As a result of NetBeans design for extensibility, you'll find a lot of code like this:
DialogDisplayer displayer = DialogDisplayer.getDefault();
in which an API is defined (DialogDisplayer) as an abstract class or interface and an implementation is indirectly made available through a static method like {getDefault(). This approach gives you a default implementation of DialogDisplayer, but also lets you "plug in" a different one of your own design.
How do you do that? First, here's the implementation of the {getDefault() method:
public static DialogDisplayer getDefault() {
DialogDisplayer dd = (DialogDisplayer) Lookup.getDefault().lookup(DialogDisplayer.class);
if (dd == null) {
dd = new Trivial();
}
return dd;
}
As you see, it will attempt to find some instance of DialogDisplayer from the default Lookup (in other words, one that has been registered via META-INF/services/). If it cannot find one, it will return the default implementation (an instance of Trivial, which is an inner class of DialogDisplayer).
Therefore, it seems that you could override the default simply by registering your own implementation of DialogDisplayer). If you tried it, you'd find it doesn't work (or at least may not work consistently) because there are already other instances registered and they'll likely take precedence over yours.
So, how do you mask out any other implementations so that yours will be used? In the file where you register the new implementation (META-INF/services/org.openide.DialogDisplayer in this case), you will prefix the other implementation with a pound sign and a minus sign before listing your own on a different line. For example, here's what the file should look like:
#-org.netbeans.core.windows.services.DialogDisplayerImpl com.tomwheeler.example.SpecialDialogDisplayerImpl
More information about this and other Lookup-related topics, including how to set the order of registered services, can be found in the Utilities API documentation.
It is not uncommon to be subclassing an class, such as TopComponent or Node which has a method getLookup(), and to need to add to or filter the original Lookup's contents. There are a number of convenience factories and classes which make it easy to do this:
If you need to customize a Node's lookup, read the FAQ item on how to do that.
A node is typically used to represent some business object and it's a common idiom to place that business object in the node's lookup so that, for example, a context-sensitive action can operate on it. Sometimes fully initializing that business object can involve an expensive operation that would be wasted effort if the user never invoked the action that used it anyway.
So how can you defer loading or initializing the business object until it is truly needed?
There are probably several ways, but two common ones are:
Consider the following example in which you have a Token class which represents a database record ID and a business object class AnExpensiveClass which will be populated from the database based on the supplied token's ID.
public final class Token {
private final long id;
public Token(long id) {
this.id = id;
}
public long getId() {
return id;
}
}
import org.openide.util.lookup.InstanceContent;
public class LazyLoadingDelegate implements InstanceContent.Convertor<Token, AnExpensiveClass> {
@Override
public AnExpensiveClass convert(Token token) {
// this will be called when something actually requests an instance
// of AnExpensiveClass from the lookup. We just need to create and
// return an instance based on the supplied token (i.e. assume that
// the AnExpensiveClass constructor will load data from the database
// and populate the instance we're returning).
return new AnExpensiveClass(token);
}
@Override
public Class<? extends AnExpensiveClass> type(Token token) {
return AnExpensiveClass.class;
}
@Override
public String id(Token token) {
return String.valueOf(token.getId());
}
@Override
public String displayName(Token token) {
return "my lazy loading delegate";
}
}
ic = new InstanceContent();
al = new AbstractLookup(ic);
Token token = new Token(12345);
ic.add(token, new LazyLoadingDelegate());
And your context-sensitive action will look like normal; it does not need to know about the lazy loading (code not relevant to lazy loading has been removed for the sake of brevity):
public final class ExpensiveClassAction extends CookieAction {
@Override
protected void performAction(Node[] activatedNodes) {
AnExpensiveClass expensiveClass = activatedNodes[0].getLookup().lookup(AnExpensiveClass.class);
// now you have the actual do AnExpensiveClass instance, so do something with it...
}
@Override
protected Class[] cookieClasses() {
return new Class[]{AnExpensiveClass.class};
}
@Override
protected boolean asynchronous() {
return true;
}
}
As noted in the overview of Lookup, a Lookup can contain more than one instance of a given class; Lookup is often used for singletons, but not exclusively for singletons. For example, in the Projects API , there is a class called ProjectFactory that recognizes different types of user projects on disk; each module that provides a project type registers another factory in the system.
So the inevitable question is, if there are two instances of X in a Lookup, and I call lookup(X.class), which one do I get?
The answer is, it's undefined - don't do that. The next inevitable question is, but how can that be?
A Lookup makes no assumptions about what's in it, or what you might want to put in it, or how many of anything there should be. That contract is an agreement between whoever tells you that you should get an instance of X from some Lookup and you. If they document that there will only be one, use Lookup.lookup(Class); if they document that there can be more than one, use Lookup.lookup(Lookup.Template) and iterate the results.
In practice this is a non-problem - anything you are going to try to find in a Lookup is going to document whether it is supposed to be a singleton or not.
A: You might have tried to place the interface and the implementation class in different modules but used the same package name. NetBeans prohibits two or more modules to define classes in the same package. Choose a distinctive package name (or package name prefix) for each module.
Other platforms do use string-keyed maps for this sort of thing, but there are some weaknesses with that approach:
There are some other capabilities of Lookup (such as getting classes of results without instantiating them, and naming result items) but these are rarely used in practice.
Lookup is very powerful, yet simple and generic; people quickly learn to love it, once they realize what it can do.
There are a number of places Lookup is commonly found/used in NetBeans. Generally, if you have found some class and you are wondering where on earth you get an actual instance of one of those, the answer is probably "from something-or-other's Lookup".
Common cases:
There is a wizard available for NetBeans modules which does this for you. Just run New File | Module Development | File Type. Fill in the MIME Type text field and choose whether to recognize file by extension or XML root element. If you want more sophisticated recognition, choose "by Filename Extension" and edit created resolver xml after you finish the wizard. In the next step type in "Class Name Prefix" your preferred prefix and finish the wizard.
Description of declarative MIME resolve can be found in this document. In most cases it should be enough to resolve files only by their extensions as the wizard does (see ext element). Other types of resolution can be more time expensive, so use them only exceptionally. Useful can be file name matching, file content matching or magic matching for binary files. Also consider existence of exit element intended for negative matching which can skips next conditions.
Applies to: NetBeans 6.1+, pattern and name elements to 6.7+
Related: DevFaqFileRecognition
When you click the close button in the top right corner the application closes. If you want to do something before the application closes (e.g. show a dialog with OK and Cancel options) or prevent it from closing, this is possible.
Make a class in your module that extends org.openide.modules.ModuleInstall. There is a Module Installer template that will create this class for you.
Override the closing method and insert your special logic. If you return false the application will not exit.
For example, say you want to make a template which will appear in File | New File which will prompt the user for a name and location but then actually create several related files.
Just use an arbitrary empty file as the template, and declare it to have an instantiatingWizardURL attribute with an instance of WizardDescriptor.InstantiatingIterator. The wizard iterator can specify any sequence of Swing panels you like to ask the user whatever questions you like, and at the end it can do whatever you like to create the new files. Return the created files in instantiate().
Here is an example of a wizard that creates a number of files. This is the layer file that declares it (look at emptyLibraryDescriptor).
You may wish to reuse a standard GUI panel for picking a folder and name, as in Templates.createSimpleTargetChooser.
The NetBeans 5.0 module development support has a (meta-)wizard New Wizard. Choose New File for Registration Type and follow the wizard steps.
Applies to: NetBeans 5.0, 5.5, 6.X
The first problem is to identify what is the root problem causing memory to not be used effectively. The usual approach for this is to analyze the complete contents of memory when the problem appears, using one of a number of appropriate tools, and ideally then find a solution.
Below are some hints on how to analyze the content of memory:
If the problem causes OutOfMemoryError, it is possible to customize the JVM to provide a memory dump automatically whenever an OutOfMemoryError is thrown. FaqNetBeansAndOOME describes what options can be used for this. If you are developing modules, it is a very good idea to set the option -J-XX:+HeapDumpOnOutOfMemoryError.
If the memory leak is not so aggresive to fill all the available memory and cause an OutOfMemoryError, it is still possible to use jmap to generate the same dump. Running full GC before you create this dump can be a good idea as it can strip the size of dump file and remove some unimportant objects from the snapshot. You can do this by turning memory toolbar on (do a right click in toolbar area and check Memory). Repeating this several times can even collect large amounts of data held in various caches throug soft or weak references and make it easier to browse the dump.
Alternately, you can use the JDK's tool jhat. It will start simple web server and you can use a web browser to see the data. There are many functions starting with lists of classes with numbers of objects and their size, navigation between references, finding of reference chains from GC root to certain objects. JavaScript can be used to express more complex queries.
INSANE is a home-grown tool that is useful for analysis of memory content and also can be used in automated tests - so once you have fixed a memory leak, you can write a test that will fail if the memory leak is ever recreated. NbTestCase.assertGC is all you need to know.
DTrace can be used to monitor object allocation and garbage collection. Nice article about using DTrace with the HotSpot provider: Java and DTrace
There are some typical classes where it should be easily possible to tell what the appropriate number of their instances in memory should be, and if these are leaking there is a serious problem:
There are two different ways how memory can be wasted: leaks and improper retention of memory.
Leaks are cases when repeated invocation of certain activity creates new set of objects that cannot be reclaimed after activity is finished. The biggest problem is accumulation of these objects that leads to increased memory usage and after a long enough time leads to OutOfMemoryError. The nature of this error is that it leaves data structures of an application in undefined state so anything executed after this moment may lead to unexpected results.
Retained memory is memory occupied by objects that were created to serve some purpose but these objects are held longer than necessary. This may mean that some action has to be performed that flushes these objects or they will remain in memory until the end of the session. An example of the former is LRU caches (often holding last component in UI, files or projects). A common example of the latter is resources like parsed bundles or images statically referenced in classes that use them.
-J-Dnetbeans.debug.heap can make profiling easier as it more quickly releases references to collapsed nodes.
If you have the Timers module enabled (normally it is in dev builds), click its button in the Memory toolbar to get a summary of interesting live objects and statistics.
Platforms: All
It is not safe to modify a FileObject (via getOutputStream which is open and modified in the editor. In fact, it is not possible: calling FileObject.lock() will fail. If you modify the java.io.File (bypassing the Filesystems API) you may cause a conflict.
If the file is not modified in the editor (easily checkable via DataObject.isModified() then after your output stream is closed the file will be reloaded with the new contents.
If the file is modified you can make changes to the open editor buffer: use EditorCookie to acquire the Swing Document and make changes through that. It may be considered impolite to then save the file if it was modified before.
Applies to: NetBeans 4.0 and newer
Q: I am getting an exception when I run my module, such as
java.lang.ClassCastException: Implementation cannot be cast to Interface
at Factory.newInstance (Factory.java:123)
But Implementation implements Interface, so why is this a CCE?
A: Usually this is because the Interface that Implementation actually implements was loaded from a different class loader than what Factory sees.
That is very likely the immediate cause of the CCE. The root cause is not always obvious, but it is likely to be that some code uses Thread.currentThread().getContextClassLoader() to load a class by name. In NB by default the context class loader just looks around in loaded modules (lacking any better information), so it is possible for the following situation to arise:
module A: Interface, Factory module B > A: Implementation implements Interface module C: Interface, Factory module D > A: Implementation implements Interface
where Factory does something like:
Interface i = (Interface) Class.forName("Implementation", true,
Thread.currentThread().getContextClassLoader()).newInstance();
(This is a common design pattern for XML parsers, etc.)
Now if A's Factory happens to get D's Implementation (or C's gets B's) then you get a CCE at runtime.
An especially silly variant of this problem, known to occur at least in Xerces, is that Implementation actually resides in the same JAR as Interface and Factory, and is the standard impl almost everyone uses unless overridden somehow - yet Factory loads it by name from the CCL rather than simply loading it directly using e.g. new Implementation().
The usual workaround is to wrap the problematic call(s) in a dynamic block:
ClassLoader orig = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(SomeReferenceClass.class.getClassLoader());
try {
Factory.load(...);
} finally {
Thread.currentThread().setContextClassLoader(orig);
}
Note: NetBeans forbids ambiguous delegations. (Issue 118020) If a class could be loaded from two (or more) places, it will not be loaded at all. This does not solve your problem but it at least ensures it gets reported more reliably and with a descriptive message rather than an odd ClassCastException.
The nuts and bolts of module dependencies are as follows:
What this means is that if
An exception to the second item is that if Module B declares an implementation dependency on module A, then it will have access to the full set of classes. Normally you should not need to do this, and anyway it will then be hard to upgrade B independently of A.
Modules can also load classes from libraries - JAR files that are packaged with the module (see DevFaqHowPackageLibraries). Some points to remember about libraries:
If you are using the IDE's module development support, you will manage module dependencies in the properties dialog for your module (or the Libraries node in the Projects tab). This just modifies yourmodule/nbproject/project.xml. The data saved there is then used to generate the appropriate manifest entries for you.
If you are writing a module that will use some third party libraries, you probably want to read DevFaqWrapperModules and also DevFaqWhenUseWrapperModule.
For more details, see the reference documentation about classloading in NetBeans.
Applies to: NetBeans 6.5
Yes, NetBeans (as of 6.1) does permit multiple modules to load from a single package. However, this should be considered poor style and avoided whenever possible. In particular, the Java runtime forbids package-private accesses between classes defined by different class loaders (e.g. classes contained in different modules).
Applies to: NetBeans 6.1+
Let's begin by stating that you probably do not need to know when other modules are loaded or unloaded. The module system takes care of dependency management for you, so your module should never be loaded unless all of its stated dependencies are loaded too.
The normal means of communicating between modules about available services (which you could consider indirect dependencies) is using the Lookup API. If what you really wanted was to know when a service became available, you do not need to listen to module load or unload events as such; which module hosts the service is not of direct interest. Instead:
If you simply want to run some code when your module loads or unloads, registering an instance of ModuleInstall will do the trick. There is even a wizard in the IDE for setting this up.
But in the very rare case that you want to be notified when other modules are loaded or unloaded, it is possible because:
By using these two facts together, it is possible to listen to changes in the installed modules by running code like this at some point in the application's lifecycle:
final Lookup.Result<ModuleInfo> result = Lookup.getDefault().lookupResult(ModuleInfo.class);
result.addLookupListener(new LookupListener() {
public void resultChanged(LookupEvent event) {
// it seems a module was installed or removed
}
});
Once you detect that a module has been created you may also want to register a PropertyChangeListener and listen to ModuleInfo.PROP_ENABLED. (A module present in the installation will provide a ModuleInfo but isEnabled might be false if it is not currently loaded.)
If you want to protect a NetBeans module from disassembly, you can obfuscate it. For example you can use ProGuard, an open-source obfuscator.
<!-- Replace the non-obfuscated jar with the obfuscated one when compiling -->
<target name="netbeans-extra" depends="obfuscate">
<copy file="${suite.dir}/build/obfuscated/${module.jar}" tofile="${cluster}/${module.jar}"/>
</target>
<!-- Overridden debug target that depends on netbeans-debug -->
<target name="debug" depends="netbeans-debug,-jdk-presetdef-nbjpdastart">
<ant antfile="${harness.dir}/run.xml" target="debug"/>
</target>
<!-- netbeans-debug DOES NOT depends on netbeans-extra (then DOES NOT depends on obfuscate) -->
<target name="netbeans-debug"
depends="init,jar,module-xml-regular,module-xml-autoload,module-xml-eager,javahelp,module-auto-deps,release,verify-class-linkage">
<genlist outputfiledir="${cluster}" module="${module.jar}">
<fileset dir="${cluster}">
<patternset refid="module.files"/>
</fileset>
</genlist>
</target>
<!-- Overridden to delete also the obfuscated jar -->
<target name="clean" depends="files-init,testuserdir-delete">
<delete failonerror="false" includeemptydirs="true">
<fileset dir="build">
<exclude name="testuserdir/"/>
</fileset>
</delete>
<delete dir="${netbeans.javadoc.dir}/${code.name.base.dashes}"/>
<delete file="${netbeans.javadoc.dir}/${code.name.base.dashes}.zip"/>
<delete failonerror="false"> <!-- #59457: OK if cluster does not exist currently -->
<fileset dir="${cluster}">
<patternset refid="module.files"/>
</fileset>
</delete>
<delete file="${cluster}/update_tracking/${code.name.base.dashes}.xml"/>
<delete file="${suite.dir}/build/obfuscated/${module.jar}"/>
</target>
<!-- Just a cut and paste of how the proguard obfuscator works.
This is not supposed to work below. In fact, this seems to work
on jars, not .class files, so it will have to be placed in a
post jar target, which I haven't identified yet -->
<target name="obfuscate" depends="init">
<taskdef resource="proguard/ant/task.properties"
classpath="${proguard.jar.path}" />
<echo message="Obfuscating ${cluster}/${module.jar}..."/>
<mkdir dir="${suite.dir}/build/obfuscated"/>
<proguard printmapping="${suite.dir}/build/obfuscated/${code.name.base.dashes}.map"
renamesourcefileattribute="SourceFile" ignorewarnings="true">
<!-- Specify the input jars, output jars, and library jars. -->
<injar file="${cluster}/${module.jar}" />
<outjar file="${suite.dir}/build/obfuscated/${module.jar}" />
<libraryjar path="${module.run.classpath}" />
<libraryjar file="${nbjdk.home}/jre/lib/rt.jar" />
<!-- Keep some useful attributes. -->
<keepattribute name="InnerClasses" />
<keepattribute name="SourceFile" />
<keepattribute name="LineNumberTable" />
<keepattribute name="Deprecated" />
<keepattribute name="*Annotation*" />
<keepattribute name="Signature" />
<!-- Preserve all public classes, and their public and protected fields and methods. -->
<keep access="public">
<field access="public protected" />
<method access="public protected" />
</keep>
<!-- Preserve all .class method names. -->
<keepclassmembernames access="public">
<method type ="java.lang.Class"
name ="class$"
parameters="java.lang.String" />
<method type ="java.lang.Class"
name ="class$"
parameters="java.lang.String,boolean" />
</keepclassmembernames>
<!-- Preserve all native method names and the names of their classes. -->
<keepclasseswithmembernames>
<method access="native" />
</keepclasseswithmembernames>
<!-- Preserve the methods that are required in all enumeration classes. -->
<keepclassmembers extends="java.lang.Enum">
<method access="public static"
type="**[]"
name="values"
parameters="" />
<method access="public static"
type="**"
name="valueOf"
parameters="java.lang.String" />
</keepclassmembers>
<!-- Explicitly preserve all serialization members. The Serializable
interface is only a marker interface, so it wouldn't save them.
You can comment this out if your library doesn't use serialization.
With this code serializable classes will be backward compatible -->
<keepnames implements="java.io.Serializable"/>
<keepclassmembers implements="java.io.Serializable">
<field access ="final"
type ="long"
name ="serialVersionUID" />
<field access ="!static !transient"
name ="**"/>
<field access ="!private"
name ="**"/>
<method access ="!private"
name ="**"/>
<method access ="private"
type ="void"
name ="writeObject"
parameters="java.io.ObjectOutputStream" />
<method access ="private"
type ="void"
name ="readObject"
parameters="java.io.ObjectOutputStream" />
<method type ="java.lang.Object"
name ="writeReplace"
parameters="" />
<method type ="java.lang.Object"
name ="readResolve"
parameters="" />
</keepclassmembers>
<!-- Your application may contain more items that need to be preserved;
typically classes that are dynamically created using Class.forName -->
</proguard>
</target>
In this way when running and when creating the NBM (as well from a suite) the module will be obfuscated. When debugging your module you use the non-obfuscated JAR, so you can step through source as well.
Applies to: ??? (unverified)
If you need to patch an existing module, you can place a JAR file relative to the original. For example, to patch ide5/modules/org-openide-example.jar you make a JAR like ide5/modules/patches/org-openide-example/mypatch.jar. The mypatch part of your JAR file patch can be named anything you like. The JAR file should only contain those classes you want to patch. It does not need a manifest, though an empty manifest is harmless.
The patch must be in the same cluster as the original. (Issue 69794) If you want to create an NBM containing a patch, you must ensure it will be installed in the same cluster (use the nbm.target.cluster property), but note that you cannot test such a dummy module as part of a module suite (since this property is interpreted only by Plugin Manager). If you are distributing a complete application including a patch to the NB Platform, you will need to either manually preinstall the patch JAR in your copy of the Platform; or override your build-zip target to include the JAR in the final ZIP (in which case testing using Run Project will not have the patch active).
Long ago, in a galaxy far far away, most objects in NetBeans were created during startup. That works fine for a small application. It is disaster for a large application - with each new component in the system, startup time gets longer, garbage collections get more frequent and memory requirements rise.
Because of this, today, most of the APIs you will use to install things into the system involve a text entry (DevFaqWhenToUseWhatRegistrationMethod), such as putting something in an XML file (DevFaqModulesLayerFile), not running Java code. Ideally a module should do nothing on startup. Let's say that again, forcefully: Ideally a module should do nothing on startup.
The main ways to do this are using @ServiceProvider or using annotations which create XML layer entries that declare information about the things your module is installing. Then, when they are needed to do actual work, your objects will be instantiated.
If you really need to run some code on startup, see DevFaqWhenToUseWhatRegistrationMethod about ModuleInstall.
Applies to: NetBeans 6.7
There are four basic ways a module can install configuration data or objects. Three of the ways are declarative (DevFaqModulesDeclarativeVsProgrammatic); these mechanisms are preferred.
If you are writing a module that has an API you want other modules to plug in to, you probably want to read DevFaqWhenToUseWhatRegistrationMethod.
For global services, singletons and such, this is the preferred way. In NetBeans 6.7+, you can simply use the @ServiceProvider annotation.
What exactly you register is a contract between you and whatever module is providing the interface and will, presumably, do something with what you put there. What's really happening is that you are adding your implementation of this interface to the default lookup (see DevFaqLookupDefault).
Any module can specify interfaces that it would like other modules to register instances of. For example, the basic Project support module asks that each module that implements a project type (the things you see in the New Project wizard in NetBeans) register their ProjectFactorys there.
To get an instance of something registered this way, call
TheInterface i = Lookup.getDefault().lookup(TheInterface.class);
If there can be more than one registered object of this type, you can get them all as follows:
for (TheInterface i : Lookup.getDefault().lookupAll(TheInterface.class)) {...}
The system filesystem (see DevFaqSystemFilesystem) allows for more detailed information in registering objects. It is a virtual filesystem composed of XML fragments (see DevFaqModulesLayerFile) from all (well, most) of the modules in the system. The top layer of the system filesystem is $USERDIR/config which is where changes that are written at runtime are put.
The system filesystem is composed of folders. Some folders have special meanings to the system; what folders exist and are meaningful depends on what modules you have installed. For example, the core window system defines the folder Menu/, which contains subfolders - one for each menu in the main window's menu bar. If you add a file to the folder Menu/File called com-foo-mymodule-MyAction.instance, an instance of com.foo.mymodule.MyAction will be created, and a menu item will be put on the menu for it.
For more details on registering objects, defining an order in which they should appear, etc., see the DevFaqModulesLayerFile. In the short form, a module registers a layer by including a line in its manifest:
OpenIDE-Module-Layer: com/foo/mymodule/resources/layer.xmlwhich points to an actual XML file by that name inside the module JAR file. A layer file is an XML file defining a mini-filesystem:
<filesystem>
<folder name="SomeFolder">
<file name="SomeFile"/>
</folder>
</filesystem>
Starting with NetBeans 6.7, more or more layer registrations can be made by using various source code annotations. If you use these exclusively, you will not need to declare a layer in your module's sources at all.
Some types of objects used to be installed by adding a section to the module manifest. This is now deprecated.
The module system allows you to provide a ModuleInstall class, which runs some code during startup or when the module is loaded, and can run cleanup code when it is uninstalled or disabled. This is the least desirable way to do things, because running code on startup means slowing down startup. Before you use such a class, be sure there is no declarative way to do what you're trying to do; see: DevFaqModulesDeclarativeVsProgrammatic
To have some code run on startup/installation/uninstallation/etc., add a line like the following to your module's manifest file:
OpenIDE-Module-Install: org/netbeans/modules/paintcatcher/PaintCatcherModule.classThis line should be part of the group of lines at the top of the manifest, with no blank lines before it. It is a pointer to a class file inside the module. The class file must extend the class org.openide.modules.ModuleInstall. There is a wizard in the development support to create and register such a class for you.
Layer files are small XML files provided by modules, which define a virtual filesystem (DevFaqFileSystem). The layer file defines folders and files that will be merged into the system filesystem (DevFaqSystemFilesystem) that makes up the runtime configuration information NetBeans and its modules use.
Layer files help to make it possible for modules to be dynamically installed. If you've read about FileObjects (DevFaqFileObjects) and FileSystems (DevFaqFileSystems), you know that you can listen for changes in folders and files in a filesystem. That's exactly what the components of NetBeans whose content is composed from folders in the system filesystem do. So if a module is added at runtime, the system filesystem fires changes; the UI notices that the contents of the folder has changed and updates the UI to reflect the changes.
If you created your module using the IDE, you may already have an XML layer in your module, and you can expand the node for it under Important Files in your module project to see and modify its contents. The way it is declared is simple:
OpenIDE-Module-Layer: com/foo/mymodule/resources/layer.xml
Starting with NB 6.7, some Java source code annotations generate layer entries for you (you do not need to have a layer.xml in your module's source tree).
To run some code when your module is loaded, and basically every time the IDE starts and your module is enabled, simply create a subclass of org.openide.modules.ModuleInstall and override the restored() method. Bear in mind that this is being executing during the time the IDE/platform is starting up. You should limit the work you do here to that which is absolutely necessary.
Once the class is created, you must declare it in your module's manifest.mf file, like so:
OpenIDE-Module-Install: org/netbeans/modules/editor/EditorModule.class
Likewise, to execute code when the IDE is shutting down, you can override the close() method. This method of ModuleInstall is called when the IDE is shutting down, contrary to the closing() method, which is called to alert the module that the IDE is about to shut down. However, another module may veto the shutdown by returning false from the closing() method, so the close() method is best for performing any cleanup work for your module.
You can simply use File > New File > Module Development | Module Installer to create the ModuleInstall class and its registration in the manifest.
The NetBeans Javadoc has some additional documentation about using certain APIs. Unfortunately, the index page does not link to these and so they can be difficult to find. Here are direct links to these documents from the most recent builds:
Create a Runnable that will do all of the code generation/munging you want to do. Pass it to org.openide.text.NbDocument.runAtomic(doc, runnable). Or BaseDocument.runAtomic() can be used (the Document in the editor should be an instance of BaseDocument - it will be for Java files and most other things).
There are a few cases where NetBeans has convenience classes or facilities that you should use, instead of doing them the way you may be used to. They are:
Applies to: NetBeans 6.5
Yes, you can use a pristine platform download (or platform built from sources) and use an external harness from another platform version without sacrificing repeatable builds. Just as the in-IDE module development support defaults to using the harness included with the IDE, ignoring the harness bundled with the platform, so you can configure your module or suite to use a specific harness location. As described in harness/README set up a relative path for the platform, but make the harness separate, e.g.
suite.dir=${basedir}
netbeans.dest.dir=${suite.dir}/../nb_sources/nbbuild/netbeans
# Rather than:
#harness.dir=${netbeans.dest.dir}/harness
# use:
harness.dir=${suite.dir}/../special-harness
Applies to: NetBeans 6.5
NBM allows to declare its own custom code in NBM archive. This code is called-back by Autoupdate/Updater at the end of installation of NBM into IDE.
your_module.nbm
|
+- Info
| |
| +--- info.xml
|
+- netbeans
|
+--- modules...
|
+-main
|
+--- main.properties
+--- <custom code>
If Autoupdate/Updater detects main directory in the NBM archive then main.properties descriptor contains information about the own code. Updater runs specified Java code according to these properties.
The properties expected in main.properties are:
| Property | Value |
|---|---|
| mainClass | name of the main class, run after module installation from the NBM |
| relativeClassPath | classpath elements, may contain more elements |
| jvm.parameters | properties for JVM, arguments inserted before the main class name |
| mainClass.arguments | more arguments for the main class, added after the main class name |
The run command is built on top of properties above.
The properties can contain several special variables which Autoupdate replaces by real values:
| Variable | Value |
|---|---|
| %IDE_HOME% | platform directory |
| %IDE_USER% | user directory [1] |
| %FS% | file separator char |
| %JAVA_HOME% | the current Java home |
To see that samplepostinstall project in action
I'm not author of this feature, it's only my investigation.
Do not hesitate to contact me on mailto:jrechtacek@netbeans.org if you have any question.
Setting $CLASSPATH or %CLASSPATH% on the command line will not affect anything - NetBeans uses its own class loader system to find classes from modules.
What you need is for your libraries to be a module; see DevFaqWrapperModules.
Applies to: NetBeans 6.5
If you set the system property netbeans.full.hack to true, the following IDE behaviors will be disabled to make it quicker or more reliable to test other functionality:
This property is set by default when you:
If you need to test one of the suppressed behaviors (e.g. you are working on the license dialog), just do not set this property. For the ant tryme and ant run cases, add
tryme.args=to nbbuild/user.build.properties or ~/.nbbuild.properties.
By default, a NetBeans Platform application will use the developer's copy of the IDE as the platform. This is certainly easy, but there are also drawbacks to using the current IDE as a platform. With that in mind, lets check out, and reference our own copy of the Netbeans source code. This way we can also use breakpoints to step through the Netbeans source code, make changes, and create patches!
At a high level the steps are as follows. First get the Netbeans source code checked out. This part is interesting because what you end up with is a complete copy of the Netbeans source repository on your local file system. The second thing you need to do is build the Netbeans platform using the source repository that you just checked out. This is important because without building the platform you will not have the dependencies required by the platform modules. The next step is to create a new platform reference. Of course the platform to reference will be the one that you just checked out and built. Then finally, in your module suite's project properties, select the platform reference that you just created.
So, in more detail then...
First get the source code from the Mercurial repository. In the following example the source code is checked out to a local ~/netbeans-repository/ directory. In this example the tilde is used to represent the home directory of your file system.
So far, so good, but you still need to build the source code so that you have a complete Netbeans platform, along with all the jar dependencies.
Building the Netbeans source is very easy, and very satisfying to watch! Just open up your favorite terminal client and navigate to your local repository.
cd ~/netbeans-repository/main/
Then simply run ant.
ant -Dpermit.jdk6.builds=true
Note, I am choosing to build Netbeans using JDK1.6 so I have to explicitly tell Netbeans that I understand that only JDK1.5 is supported.
It took about 25 minutes for the build to finish on my machine.
In order to work with the Netbeans platform that you just built it needs to be added as a platform in the IDE.
Now just select the platform in your module suite's Project Properties. There you will see a Netbeans Platform dropdown box where you can select the platform that you set up.
Note: I did have to go through and resolve some of the cluster dependencies. That just means that I had to check the dependencies that Netbeans said that other modules needed. Once you get this far it will be very obvious what to do.
In order to use JDK1.6 with the Netbeans source code we need to tell the Netbeans platform that we understand that only JDK1.5 is supported. What you need to do is create a "user.build.properties" file and put it in the nbbuild directory.
touch ~/netbeans-repository/main/nbbuild/user.build.properties
Inside the user.build.properties file put the following line.
permit.jdk6.builds=true
This tutorial applies to: versions 6.7 and earlier of the NetBeans Java IDE.
In 5.0, it can be done without a patch. It does, however, require an impl dependency on xml-core. I'd really like this to be simpler, and a supported API.
See below for issues with pre-release 6.0
Anyway, here's how I do it for an xml flavor called SCXML:
1. in the manifest:
Name: com/nuance/tools/xhmi/ScxmlDataLoader.class Install-Before: org.openide.loaders.XMLDataObject, org.netbeans.modules.xml.core.XMLDataObject OpenIDE-Module-Class: Loader
import java.io.IOException;
import org.netbeans.modules.xml.core.XMLDataLoader;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObjectExistsException;
import org.openide.loaders.MultiDataObject;
import org.openide.loaders.UniFileLoader;
import org.openide.util.NbBundle;
public class ScxmlDataLoader extends UniFileLoader
{
public static final String REQUIRED_MIME = "application/scxml+xml";
private static final long serialVersionUID = 1L;
public ScxmlDataLoader()
{
super("com.nuance.tools.xhmi.ScxmlDataObject");
}
protected String defaultDisplayName()
{
return NbBundle.getMessage(ScxmlDataLoader.class, "LBL_Scxml_loader_name");
}
protected void initialize()
{
super.initialize();
getExtensions().addMimeType(REQUIRED_MIME);
}
protected MultiDataObject createMultiObject(FileObject primaryFile) throws DataObjectExistsException, IOException
{
return new ScxmlDataObject(primaryFile, this);
}
protected MultiDataObject.Entry createPrimaryEntry (MultiDataObject obj, FileObject primaryFile) {
return new XMLDataLoader.XMLFileEntry (obj, primaryFile); //adds smart templating
}
protected String actionsContext()
{
return "Loaders/" + REQUIRED_MIME + "/Actions";
}
}
3. The data object:
import java.io.IOException;
import org.netbeans.modules.xml.core.XMLDataObjectLook;
import org.netbeans.modules.xml.core.cookies.DataObjectCookieManager;
import org.netbeans.modules.xml.core.sync.DataObjectSyncSupport;
import org.netbeans.modules.xml.core.sync.Synchronizator;
import org.netbeans.modules.xml.core.text.TextEditorSupport;
import org.netbeans.spi.xml.cookies.CheckXMLSupport;
import org.netbeans.spi.xml.cookies.DataObjectAdapters;
import org.netbeans.spi.xml.cookies.ValidateXMLSupport;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObjectExistsException;
import org.openide.loaders.MultiDataObject;
import org.openide.nodes.CookieSet;
import org.openide.nodes.Node;
import org.openide.text.DataEditorSupport;
import org.xml.sax.InputSource;
public class ScxmlDataObject extends MultiDataObject implements
XMLDataObjectLook
{
private transient final DataObjectCookieManager cookieManager;
private transient Synchronizator synchronizator;
public ScxmlDataObject(FileObject pf, ScxmlDataLoader loader) throws DataObjectExistsException, IOException {
super(pf, loader);
CookieSet cookies = getCookieSet();
cookieManager = new DataObjectCookieManager (this, cookies);
cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies));
InputSource is = DataObjectAdapters.inputSource(this);
cookies.add(new CheckXMLSupport(is));
cookies.add(new ValidateXMLSupport(is));
// editor support defines MIME type understood by EditorKits registry
TextEditorSupport.TextEditorSupportFactory editorFactory =
new TextEditorSupport.TextEditorSupportFactory (this, org.netbeans.modules.xml.core.XMLDataObject.MIME_TYPE);
editorFactory.registerCookies (cookies);
}
protected Node createNodeDelegate() {
return new ScxmlDataNode(this);
}
////////// XMLDataObjectLook interface /////////////////
public DataObjectCookieManager getCookieManager() {
return cookieManager;
}
public synchronized Synchronizator getSyncInterface() {
if (synchronizator == null) {
synchronizator = new DataObjectSyncSupport (ScxmlDataObject.this);
}
return synchronizator;
}
}
<filesystem>
<folder name="Loaders">
<folder name="application">
<folder name="scxml+xml">
<folder name="Actions">
<file name="org-openide-actions-OpenAction.instance"/>
<attr name="org-openide-actions-OpenAction.instance/org-openide-actions-FileSystemAction.instance" boolvalue="true"/>
<file name="org-openide-actions-FileSystemAction.instance"/>
<attr name="org-openide-actions-FileSystemAction.instance/sep-1.instance" boolvalue="true"/>
<file name="sep-1.instance">
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
</file>
<attr name="sep-1.instance/org-openide-actions-CutAction.instance" boolvalue="true"/>
<file name="org-openide-actions-CutAction.instance"/>
<attr name="org-openide-actions-CutAction.instance/org-openide-actions-CopyAction.instance" boolvalue="true"/>
<file name="org-openide-actions-CopyAction.instance"/>
<attr name="org-openide-actions-CopyAction.instance/sep-2.instance" boolvalue="true"/>
<file name="sep-2.instance">
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
</file>
<attr name="sep-2.instance/org-openide-actions-DeleteAction.instance" boolvalue="true"/>
<file name="org-openide-actions-DeleteAction.instance"/>
<attr name="org-openide-actions-DeleteAction.instance/org-openide-actions-RenameAction.instance" boolvalue="true"/>
<file name="org-openide-actions-RenameAction.instance"/>
<attr name="org-openide-actions-RenameAction.instance/sep-3.instance" boolvalue="true"/>
<file name="sep-3.instance">
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
</file>
<attr name="sep-3.instance/org-openide-actions-SaveAsTemplateAction.instance" boolvalue="true"/>
<file name="org-openide-actions-SaveAsTemplateAction.instance"/>
<attr name="org-openide-actions-SaveAsTemplateAction.instance/sep-4.instance" boolvalue="true"/>
<file name="sep-4.instance">
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
</file>
<attr name="sep-4.instance/org-openide-actions-ToolsAction.instance" boolvalue="true"/>
<file name="org-openide-actions-ToolsAction.instance"/>
<attr name="org-openide-actions-ToolsAction.instance/org-openide-actions-PropertiesAction.instance" boolvalue="true"/>
<file name="org-openide-actions-PropertiesAction.instance"/>
</folder>
</folder>
</folder>
</folder>
<folder name="Services">
<folder name="MIMEResolver">
<file name="ScxmlResolver.xml" url="resources/ScxmlResolver.xml">
<attr name="SystemFileSystem.localizingBundle" stringvalue="com.nuance.tools.xhmi.Bundle"/>
</file>
</folder>
</folder>
<folder name="Templates">
<folder name="Other">
<file name="ScxmlTemplate.scxml" url="resources/ScxmlTemplate.scxml">
<attr name="SystemFileSystem.localizingBundle" stringvalue="com.nuance.tools.xhmi.Bundle"/>
<attr name="template" boolvalue="true"/>
</file>
</folder>
</folder>
<MIME-resolver>
<file>
<ext name="scxml"/>
<resolver mime="application/scxml+xml"/>
</file>
</MIME-resolver>
public <T extends org.openide.nodes.Node.Cookie> T getCookie (Class<T> c);
then rebuilding the xml/core module with 1.5.
One should also note that 6.0 has the "Schliemann" module for language support. If your needs are simple ( i.e. you don't want to use templates etc.), it may be easier to start from scratch with that.
Normally this should not happen because the module build harness tries to protect you from such cases. Still, if it does happen, it could mean
If the problem is #1, you need to declare a dependency on the module where the class is (remember that all of NetBeans APIs are modules, and in separate jars - so if it's the IO API, that's a module org.openide.io, if it's the Window System, that's a module org.openide.windows... and so forth).
Setting dependencies is easy - open the Properties for your project, and choose the Libraries page. (Or just get the context menu for the Libraries node under the project in the Projects window.) Click Add and a small dialog opens - just type the name of a class you need to use, and it will filter the list to find the module that provides that class - so you don't have to memorize a huge list of mappings from classes to modules.
If it's problem #2, then you are already declaring a dependency, but get full access to all classes in a module, you need to declare an implementation dependency (DevFaqImplementationDependency). Be sure you really need to use the class you're trying to use, in this case - it will make your module hard to upgrade because generally it will need to be paired with the exact version of the other module's JAR that it was built with - if that module is upgraded, your module may end up being disabled.
Problem #3 may happen if you change your modules name. If some module declared yours as a friend it will no longer recognize it.
For a nice way to resolve all module dependencies at once, to force all of the errors to be exposed simultaneously, just add the following to the command line when starting NetBeans:
-J-Dnetbeans.preresolve.classes=true
The message displayed states that when using this flag, you should not use the -J-Xverify:none flag (often specified in the IDE configuration file), so you may need to edit the .conf file to remove the -Xverify option before using the pre-resolve option.
For help on working with class paths, please see
Applies to: NetBeans 6.x
Platforms: all
Nodes are not asked for their child nodes until the user tries to expand them - to do otherwise would be very bad for performance. If your Node is not supposed to have child nodes, use Children.LEAF as the children object passed to the constructor. That will eliminate the expand handle.
Very simply:
theDataObject.getNodeDelegate()
By default, you will be prompted to confirm your intention whenever you try to delete a node from within an explorer manager view (for example, the projects tab). You can prevent this dialog from being shown, which is handy if the node is not important enough to warrant confirmation or if you want to instead show your own confirmation.
To do this, call setValue("customDelete", Boolean.TRUE) on the node on which you want to suppress confirmation. This can be done at any time before the destroy() method is invoked.
The above will suffice if you just want to suppress the aforementioned dialog which is sufficient for most customization cases. But if you need total control over node deletion, you can implement the ExtendedDelete interface.
Applications which manage sets of data items often offer to users the capability of selecting and deselecting all the items currently on the screen with a single menu (or key shortcut). In some cases even a "Invert selection" option could be useful which selects all unselected nodes an vice versa.
Implementing such a feature with the OpenIDE API is quite a simple task. We first define a subclass of SystemAction which listens for changes in the selection of the current TopComponent and tracks the currently active ExplorerManager:
public abstract class ExplorerManagerAction extends SystemAction
{
private ExplorerManager activeExplorerManager;
public ExplorerManagerAction()
{
TopComponent.getRegistry().addPropertyChangeListener(new PropertyChangeListener()
{
public void propertyChange (PropertyChangeEvent event)
{
if (TopComponent.Registry.PROP_ACTIVATED.equals(event.getPropertyName()))
{
Object value = event.getNewValue();
if (value instanceof ExplorerManager.Provider)
{
activeExplorerManager = ((ExplorerManager.Provider)value).getExplorerManager();
setEnabled(true);
}
else
{
activeExplorerManager = null;
setEnabled(false);
}
}
}
});
}
final public void actionPerformed (ActionEvent actionEvent)
{
if (activeExplorerManager != null)
{
try
{
performAction(activeExplorerManager);
}
catch (PropertyVetoException e)
{
// ...
}
}
}
abstract protected void performAction (ExplorerManager explorerManager)
throws PropertyVetoException;
public HelpCtx getHelpCtx()
{
return HelpCtx.DEFAULT_HELP;
}
protected void initialize()
{
super.initialize();
putValue("noIconInMenu", Boolean.TRUE);
}
protected boolean asynchronous()
{
return false;
}
}
Now in order to implement the specific node selection actions we just have to subclass and provide a concrete implementation of the performAction() method which takes an ExplorerManager as parameter.
For the "Select All" action we have:
public final class SelectAllAction extends ExplorerManagerAction
{
protected void performAction (ExplorerManager explorerManager)
throws PropertyVetoException
{
explorerManager.setSelectedNodes(explorerManager.getRootContext().getChildren().getNodes());
}
public String getName()
{
return NbBundle.getMessage(SelectAllAction.class, "CTL_SelectAllAction");
}
}
For the "Deselect all" action we have:
public final class DeselectAllAction extends ExplorerManagerAction
{
protected void performAction (ExplorerManager explorerManager)
throws PropertyVetoException
{
explorerManager.setSelectedNodes(new Node[0]);
}
public String getName()
{
return NbBundle.getMessage(DeselectAllAction.class, "CTL_DeselectAllAction");
}
At last for the "Invert selection" action we have:
public final class InvertSelectionAction extends ExplorerManagerAction
{
protected void performAction (ExplorerManager explorerManager)
throws PropertyVetoException
{
List nodes = new ArrayList(Arrays.asList(explorerManager.getRootContext().getChildren().getNodes()));
nodes.removeAll(Arrays.asList(explorerManager.getSelectedNodes()));
explorerManager.setSelectedNodes((Node[])nodes.toArray(new Node[0]));
}
public String getName()
{
return NbBundle.getMessage(InvertSelectionAction.class, "CTL_InvertSelectionAction");
}
}
The above code for "Select All" and "Invert selection" only works for "flat" node structures with a root and a single level of children. For more complex structures we just need to replace explorerManager.getRootContext().getChildren().getNodes() with a piece of code that recursively explores the node tree contents.
To complete our work, this is the XML code to put in the layer.xml in order to add actions in the menu, the toolbar and to define the proper key bindings:
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
<!-- Declares the relevant actions. -->
<folder name="Actions">
<folder name="Select">
<file name="my-package-action-SelectAllAction.instance"/>
<file name="my-package-action-DeselectAllAction.instance"/>
<file name="my-package-action-InvertSelectionAction.instance"/>
</folder>
</folder>
<!-- Adds the actions to the Select main menu. -->
<folder name="Menu">
<folder name="Select">
<file name="my-package-action-SelectAllAction.shadow">
<attr name="originalFile" stringvalue="Actions/Select/my-package-action-SelectAllAction.instance"/>
</file>
<attr name="my-package-action-SelectAllAction.shadow/my-package-action-DeselectAllAction.shadow" boolvalue="true"/>
<file name="my-package-action-DeselectAllAction.shadow">
<attr name="originalFile" stringvalue="Actions/Select/my-package-action-DeselectAllAction.instance"/>
</file>
<attr name="my-package-action-DeselectAllAction.shadow/my-package-action-InvertSelectionAction.shadow" boolvalue="true"/>
<file name="my-package-action-InvertSelectionAction.shadow">
<attr name="originalFile" stringvalue="Actions/Select/my-package-action-InvertSelectionAction.instance"/>
</file>
<attr name="my-package-action-InvertSelectionAction.instance/it-tidalwave-bluemarine-catalog-tagstamper-action-separatorBefore.instance" boolvalue="true"/>
</folder>
</folder>
<!-- Declares the shortcuts. D- maps to "command" on Mac OS X and to "ctrl" on Linux and Windows. -->
<folder name="Shortcuts">
<file name="D-A.shadow">
<attr name="originalFile" stringvalue="Actions/Select/my-package-action-SelectAllAction.instance"/>
</file>
<file name="D-D.shadow">
<attr name="originalFile" stringvalue="Actions/Select/my-package-action-DeselectAllAction.instance"/>
</file>
<file name="D-I.shadow">
<attr name="originalFile" stringvalue="Actions/Select/my-package-action-InvertSelectionAction.instance"/>
</file>
</folder>
<!-- Adds the actions to the Select toolbar -->
<folder name="Toolbars">
<folder name="Select">
<file name="my-package-action-InvertSelectionAction.shadow">
<attr name="originalFile" stringvalue="Actions/Select/my-package-action-InvertSelectionAction.instance"/>
</file>
<attr name="my-package-action-InvertSelectionAction.shadow/my-package-action-DeselectAllAction.shadow" boolvalue="true"/>
<file name="my-package-action-DeselectAllAction.shadow">
<attr name="originalFile" stringvalue="Actions/Select/my-package-action-DeselectAllAction.instance"/>
</file>
<attr name="my-package-action-DeselectAllAction.shadow/my-package-action-SelectAllAction.shadow" boolvalue="true"/>
<file name="my-package-action-SelectAllAction.shadow">
<attr name="originalFile" stringvalue="Actions/Select/my-package-action-SelectAllAction.instance"/>
</file>
</folder>
</folder>
</filesystem>
-- Main.fabriziogiudici - 06 Jul 2006
When you serialize your nodes, you save them to disk so that when the application restarts, they can be used again in the application in the state that they were when the application shut down.
From Serialization and traversal in the NetBeans Javadoc:
"If you need to store (serialize) a node for any reason, this is generally impossible due to the welter of Java-level references connecting it to the rest of the system. Rather, you must use a special serializable handle which represents the node by its position in the hierarchy, and permits finding the original node again after deserialization (if it still exists). To create a handle, just call Node.getHandle(), and to restore the node call Node.Handle.getNode().
Creation of a usable handle is implemented in AbstractNode, and you should not need to override it. However, note that a handle consists of a handle for the root node of the target node's hierarchy together with a path (by system name) down to the target node; so if you are creating a root node, and want it or its children to be serializable, then you should create a specific implementation of Node.Handle capable of reconstructing your root from scratch, and return it from Node.getHandle().
The methods in NodeOp such as NodeOp.findPath(...) may also be used for general-purpose navigation along the hierarchy, should this be necessary."
Some concrete examples:
Nodes are useful for many things beyond just representing files. If you just need a placeholder Node, you do not need a subclass - just instantiate an AbstractNode - despite its name, AbstractNode is not an abstract class. For example:
AbstractNode nue = new AbstractNode (Children.LEAF);
nue.setDisplayName ("Please wait...");
nue.setIcon (Utilities.loadImage ("path/in/jar/to/image.gif"));
return nue;
If you are creating Node s, you will typically deal with one of four things
Note that if you just want to write context sensitive code, not provide your own Nodes, you may be able to do it without a dependency on the Nodes API, using Utilities.actionsGlobalContext().
Let's say that you've added support for a new file type in your application. You want to be able to provide an action by which users can "view" the file, which might open it up in the source editor (for text-based files) or a custom editor you've created in Swing. How can you add this view action?
It turns out that there are a few ways:
The first approach (ViewCookie) is the simplest of the three, though it can really only operate on a single node. If you just need something quick and easy, then it is probably your best bet.
The second approach (NodeAction) will work but is discouraged since someone creating a FilterNode on your node might inadvertently disable your action.
The third approach (Node.Cookie/CookieAction) is the most difficult of the three but also the most versatile. Your CookieAction can be enabled for multiple classes and can also operate on several nodes at once.
If you have a Node that needs to provide child Nodes, and computing the objects the child nodes represent is slow or expensive (i.e. you need to parse a file, connect to a database, or do some sort of I/O), you do not want to compute the child nodes in the event thread (which is what happens by default).
NetBeans 6.0 introduces org.openide.nodes.ChildFactory and Children.create(ChildFactory factory, boolean asynchronous). Simply subclass ChildFactory and implement protected boolean createKeys(List <T> toPopulate) to build the list of objects that will be represented by the child nodes. Implement protected Node createNodeForKey(T key) to create a Node - it will be passed each object in the list of child objects. createKeys will be called on a background thread.
Typically you'll want to make the model object from createKeys available on the Node you create. So a simple implementation of createNodeForKey would look something like:
protected Node createNodeForKey(T key) {
AbstractNode result = new AbstractNode (Children.LEAF, Lookups.singleton (key));
result.setDisplayName (key.getName()); //or whatever
result.setIcon (Utilities.loadImage ("path/in/jar/to/image.gif"));
return result;
}
ChildFactory can also simplify creating Nodes synchronously, and has the convenience that by using generics, your code can be type safe with respect to key objects. Generally it can be used anywhere Children.Keys would be used (it uses Children.Keys under the hood).
If it's just adding something, use
return new ProxyLookup(
new Lookup[] {
super.getLookup(),
Lookups.fixed(
something, somethingElse)
});
If there's only one object, substitute Lookups.singleton ( someObject ).
If you need to change the content of the lookup on the fly, it's a little more complicated, but not too much. Use the above ProxyLookup technique if there's a Lookup returned by the superclass and you still want to use its content. What you'll use to change content on the fly is the combination of AbstractLookup (which, as fate would have it, is not actually abstract), and InstanceContent, which is a grab bag of stuff you can add to and remove from.
The result will look something like this:
class MyNode extends AbstractNode {
private final InstanceContent lookupContents;
public MyNode() {
this(new InstanceContent());
}
private MyNode(InstanceContent ic) {
super(Children.LEAF, new AbstractLookup(ic));
this.lookupContents = ic;
}
}
When you need to change the contents of your lookup, you can call InstanceContent.add() or and InstanceContent.remove(), e.g.:
lookupContents.add(someObject);
lookupContents.remove(someObject);
Your lookup will be updated to include all items in the InstanceContent.
Say you have a reference to the root of a tree of Node instances, and you want to add icons or actions to those nodes. First, what you do not do is call setDisplayName or any other setter on that Node (unless you created it - the point here is that it is rude and can have bad side effects to call setters on random Nodes somebody else created - setters in APIs are bugs - the fact that Node has them is a historical artifact, not proper design).
If you own the component that will display the Nodes, this sort of thing is very easily done by subclassing FilterNode and overriding the appropriate methods (e.g. getActions(), getIcon(), etc.), wrapping the original node inside your FilterNode. Now let's say that the Node you want to decorate builds out its children in a lazy fashion, that is, only when the user expands the tree in some tree view. How would you decorate that node and all of its children, without traversing the entire tree and effectively undoing the benefits of the lazy population of the tree?
Fortunately, while this sounds rather challenging, it turns out to be surprisingly easy and simple to achieve. The trick is to subclass the FilterNode.Children class and override the copyNode() method. Below is a short example:
class NodeProxy extends FilterNode {
public NodeProxy(Node original) {
super(original, new ProxyChildren(original));
}
// add your specialized behavior here...
}
class ProxyChildren extends FilterNode.Children {
public ProxyChildren(Node owner) {
super(owner);
}
protected Node copyNode(Node original) {
return new NodeProxy(original);
}
}
As you can see, NodeProxy is intended to wrap around another Node and provide some additional appearance or behavioral changes (e.g. different icons or actions). The fun part is the ProxyChildren class. While very short and simple, it provides that critical ability for our NodeProxy to act as a decorator for not only the root node, but all of its children, and their children, and so on, without having to traverse the entire tree at once.
While FilterNode should NOT be used to insert additional nodes at the beginning or end of the list (see its JavaDoc), it can be easily used to filter out some of the children nodes. For instance, this refinement of ProxyChildren overrides the createNodes() method and conditionally selects the children nodes by submitting them to a custom accept() method:
class ProxyChildren extends FilterNode.Children {
public ProxyChildren (Node owner) {
super(owner);
}
@Override
protected Node copyNode (Node original){
return new NodeProxy(original);
}
@Override
protected Node[] createNodes (Object object) {
List<Node> result = new ArrayList<Node>();
for (Node node : super.createNodes(object)) {
if (accept(node)) {
result.add(node);
}
}
return result.toArray(new Node[0]);
}
private boolean accept (Node node) {
// ...
}
}
Below a complete example of a FileFilteredNode that can be used to show a file hierarchy where only a subset of files is shown, selected by means of the standard java.io.FileFilter class:
class FileFilteredNode extends FilterNode {
private final FileFilter fileFilter;
static class FileFilteredChildren extends FilterNode.Children {
private final FileFilter fileFilter;
public FileFilteredChildren (Node owner, FileFilter fileFilter) {
super(owner);
this.fileFilter = fileFilter;
}
@Override
protected Node copyNode (Node original) {
return new FileFilteredNode(original, fileFilter);
}
@Override
protected Node[] createNodes (Object object) {
List<Node> result = new ArrayList<Node>();
for (Node node : super.createNodes(object)) {
DataObject dataObject = (DataObject)node.getLookup().lookup(DataObject.class);
if (dataObject != null) {
FileObject fileObject = dataObject.getPrimaryFile();
File file = FileUtil.toFile(fileObject);
if (fileFilter.accept(file)) {
result.add(node);
}
}
}
return result.toArray(new Node[0]);
}
}
public FileFilteredNode (Node original, FileFilter fileFilter) {
super(original, new FileFilteredChildren(original, fileFilter));
this.fileFilter = fileFilter;
}
}
Note that if you're showing the filtered nodes in a tree view according to the code above, you might find expansion handles on leaf nodes. This thread from the dev@openide list discusses some solutions to this problem.
While most documentation explains the NetBeans Platform in terms of Java desktop applications, it is possible to build a non-GUI application on the NetBeans Platform. This might be useful, for example, when creating a platform-based application which will distribute computationally expensive work among a group of machines.
This is done by simply starting with the NetBeans Platform and removing all but the most essential components. NetBeans architect Jaroslav Tulach calls this subset of the NetBeans platform the "runtime container" and wrote an application which uses it to control his television.
Here are the steps for creating a runtime container application:
You will also need to supress the splash screen by passing --nosplash argument when starting the app.
The "New Window Component" wizard in the NetBeans IDE generates a singleton TopComponent. That's fine for some uses, but often you will want to create multiple instances of your TopComponent.
The good news is that you won't have to write any code -- you'll just have to delete some of the code that was generated for you.
In your TopComponent's .java source file:
Next, open the settings file that the wizard generated for your TopComponent. This is typically named something like FooTopComponentSettings.xml. Locate the instance XML element (NOT one of the instanceof elements and remove method="getDefault" from the end of that line.
Finally, you will need to change any code, such as from the action which opens the TopComponent, which called the getDefault() method. It should now simply create a new instance of your TopComponent instead.
NOTE: These instructions should apply to NetBeans 5.0 through 6.5. You may need to adapt them slightly for newer versions.
You have to create IOContainer which provides access (for IOProvider) to your component where you want to embed OW tab (IO tab). Then you need to pass IOContainer instance to IOProvider.getIO(String name, Action actions, IOContainer ioContainer). IOContainer is created by IOContainer.create(IOContainer.Provider). The following code demonstrates how to add OW to custom TopComponent.:
IOContainer ioc = IOContainer.create(new IOC());
InputOutput io = IOProvider.getDefault().getIO("test", new Action[0], ioc);
io.getOut().println("Hi there");
io.select();
// implement IOContainer.Provider in TopComponent where OW tab will be added
class IOC extends TopComponent implements IOContainer.Provider {
JComponent ioComp;
CallBacks ioCb;
public IOC() {
setLayout(new BorderLayout());
setDisplayName("Test");
}
@Override
public int getPersistenceType() {
return PERSISTENCE_NEVER;
}
public void add(JComponent comp, CallBacks cb) {
if (ioComp != null) {
remove(ioComp);
if (ioCb != null) {
ioCb.closed();
}
}
ioComp = comp;
ioCb = cb;
add(comp);
validate();
}
public JComponent getSelected() {
return ioComp;
}
boolean activated;
public boolean isActivated() {
return activated;
}
@Override
protected void componentActivated() {
super.componentActivated();
activated = true;
if (ioCb != null) {
ioCb.activated();
}
}
@Override
protected void componentDeactivated() {
super.componentDeactivated();
activated = false;
if (ioCb != null) {
ioCb.deactivated();
}
}
public boolean isCloseable(JComponent comp) {
return false;
}
public void remove(JComponent comp) {
if (comp == ioComp) {
ioComp = null;
ioCb = null;
}
}
public void select(JComponent comp) {
}
public void setIcon(JComponent comp, Icon icon) {
}
public void setTitle(JComponent comp, String name) {
}
public void setToolTipText(JComponent comp, String text) {
}
public void setToolbarActions(JComponent comp, Action[] toolbarActions) {
}
}
Sometimes it is necessary to open source code for a Java file from your NetBeans plug-in. The source code may be located in the currently open projects, in a library or in the platform JRE. It is assumed that libraries and platform JRE have associated sources in your environment. The associations are configured from the Tools > Libraries and Tools > Java Platform menu items.
Here is an example of how to find a FileObject corresponding to the class javax.swing.JComponent:
String classResource = "javax/swing/JComponent.java";
for (FileObject curRoot : GlobalPathRegistry.getDefault().getSourceRoots()) {
FileObject fileObject = curRoot.getFileObject(classResource);
if (fileObject != null) {
// source file object found
// do something, e.g. openEditor(fileObject, lineNumber);
return;
}
}
In the if block you can do something with the source file you found. For example, you can open it in the Java editor. DevFaqOpenFileAtLine describes how.
Here is the basic idea (there are variations depending on your needs, so read the Javadoc):
File f = ...;
int lineNumber = ...;
FileObject fobj = FileUtil.toFileObject(f);
DataObject dobj = null;
try {
dobj = DataObject.find(fobj);
} catch (DataObjectNotFoundException ex) {
ex.printStackTrace();
}
if (dobj != null)
LineCookie lc = (LineCookie) dobj .getCookie(LineCookie.class);
if (lc == null) {/* cannot do it */ return;}
Line l = lc.getLineSet().getOriginal(lineNumber);
l.show(Line.SHOW_GOTO);
}
Applies to: NetBeans 5.x, 6.0, 6.1
For NetBeans 6.5 you should use something like:
File f = ...;
int lineNumber = ...;
FileObject fobj = FileUtil.toFileObject(f);
DataObject dobj = null;
try {
dobj = DataObject.find(fobj);
} catch (DataObjectNotFoundException ex) {
ex.printStackTrace();
}
if (dobj != null)
LineCookie lc = (LineCookie) dobj .getCookie(LineCookie.class);
if (lc == null) {/* cannot do it */ return;}
Line l = lc.getLineSet().getOriginal(lineNumber);
l.show(Line.ShowOpenType.OPEN, Line.ShowVisibilityType.FOCUS
}
See the JavaDoc for Line.ShowOpenType and Line.ShowVisibilityType to see different options of showing the line (with focus, without focus, opening the editor if not open, etc.).
You will need to make a special file editor which refuses to take a write lock:
public final class ROEditor extends DataEditorSupport {
private ROEditor(DataObject d) {
super(d, new E(d));
}
public ROEditor(FileObject fo) throws DataObjectNotFoundException {
this(DataObject.find(fo));
}
private static final class E extends DataEditorSupport.Env {
public E(DataObject d) {
super(d);
}
protected FileObject getFile() {
return getDataObject().getPrimaryFile();
}
protected FileLock takeLock() throws IOException {
throw new IOException("No way!");
}
}
}
Demo of usage:
JFileChooser jfc = new JFileChooser();
if (jfc.showOpenDialog(null) != JFileChooser.APPROVE_OPTION) {
return;
}
File f = jfc.getSelectedFile();
FileObject fo = FileUtil.toFileObject(f);
try {
new ROEditor(fo).open();
} catch (DataObjectNotFoundException e) {
e.printStackTrace();
}
Yes. Use ordering attributes in the layer file for your module.
If you are using the IDE's module project, the new Action template will let you specify a location for the action in the wizard and generate the right attribute for you.
If you are using module development support the IDE, you can manage the order of menu or toolbar items by the help of special node XML Layer which can be found underneath your Important Files. Just find your menu/toolbar item and drag and drop it wherever you need. Appropriate content in the project metadata (layer file) will be generated for you. If this does not work, or you want to know more, read on.
FileObjects (DevFaqFileObject) in a folder have no defined order by default. You can use FileUtil.getOrder to sort them. (DataObjects (DevFaqDataObject) in a folder are always sorted this way.) The order is determined by numeric position attributes. For all details, see: FolderOrdering103187
Normally to work on modules versioned in the NetBeans main Mercurial repository you need to clone the entire repository. (For modules in contrib, you need contrib cloned as a subdirectory of main.) For people interested in just playing with patches to one or two modules this can be onerous, however. In NetBeans 6.1 there was no alternative.
As of NetBeans 6.5, you can work on "orphan" modules from the netbeans.org source base. There are two issues to consider:
Issue 143236 describes the enhancement in NetBeans 6.5 that permits this development mode.
Source projects should open without error and without displaying error badges, assuming all dependencies are available in either source or binary form.
You can build the projects normally. The modules will be built into the target platform (overwriting any existing copy of the module).
You can use Run and Debug to start the target platform with a test userdir after building the modules, set breakpoints etc.
You can Test the source projects normally.
Code completion should work against APIs present in other modules. If those modules are available in source form, you will get popup Javadoc automatically, and can navigate to sources. If not, you can still add popup Javadoc capability for all published APIs:
InputOutput io = IOProvider.getDefault().getIO ("Hello", true);
io.getOut().println ("Hello from standard out");
io.getErr().println ("Hello from standard err"); //this text should appear in red
io.getOut().close();
io.getErr().close();
It is important to close the output streams when you are done with them - output is written to a memory mapped file, which cannot be deleted if the stream is still open - and the tab title will remain boldfaced until the streams are closed, which helps indicate to the user that the process has finished.
-- Main.timboudreau - 10 Jun 2006
Note: For platform based applications to correctly use InputOutput and IOProvider an Output Window implementation must be available and enabled. Follow the below steps to be sure you include everything to allow the output window and tabs to be used and shown.
To add 'I/O APIs'
To force 'Output Window' (the implementation of the tabbed output window) to be enabled,
Relevent to 6.0: If the dependencies do not show up in the selection list check the 'Module Suite' to make sure they have not been excluded from the platform.
Hint: It is sometimes helpful to call InputOutput.select() to make sure the tab is made visible in the output window.
Writing to the output window is tricky - typically you need to launch an external process via Runtime.exec(). The code that does this should not be running on the event thread. Then you need a couple of threads to monitor the standard out and error out of the process, and pipe any output to the output window. The following code is a rough example:
public void launchProcess (String commandline) {
if (EventQueue.isDispatchThread()) {
throw new IllegalStateException ("Cannot be called from EDT");
}
final Process process = Runtime.getRuntime().exec (cmdline);
//Get the standard out
InputStream out = new BufferedInputStream (process.getInputStream(), 8192);
//Get the standard in
InputStream err = new BufferedInputStream (process.getErrorStream(), 8192);
//Create readers for each
final Reader outReader = new BufferedReader (new InputStreamReader (out));
final Reader errReader = new BufferedReader (new InputStreamReader (err));
InputOutput io = IOProvider.getDefault().getIO(scene.getName(), false);
io.select();
io.getOut().println(cmdline); //XXX
io.getOut().println(); //XXXd
OutHandler processSystemOut = new OutHandler (outReader, io.getOut());
OutHandler processSystemErr = new OutHandler (errReader, io.getErr());
//Get two different threads listening on the output & err
RequestProcessor.getDefault().post(processSystemOut);
RequestProcessor.getDefault().post(processSystemErr);
try {
//Hang this thread until the process exits
process.waitFor();
} catch (InterruptedException ex) {
ErrorManager.getDefault().notify(ex);
}
processSystemOut.close();
processSystemErr.close();
if (process.exitValue() == 0) {
//Get a localized string
String successMessage = (NbBundle.getMessage(getClass(), "MSG_Success",
outFile.getPath()));
StatusDisplayer.getDefault().setStatusText (successMessage);
} else {
//Get a localized string
String successMessage = (NbBundle.getMessage(getClass(), "MSG_Failure",
outFile.getPath()));
StatusDisplayer.getDefault().setStatusText (successMessage);
}
}
//The runnable that will handle an output stream, piping it to the output window:
static class OutHandler implements Runnable {
private Reader out;
private OutputWriter writer;
public OutHandler (Reader out, OutputWriter writer) {
this.out = out;
this.writer = writer;
}
public void run() {
while (true) {
try {
while (!out.ready()) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
close();
return;
}
}
if (!readOneBuffer() || Thread.currentThread().isInterrupted()) {
close();
return;
}
} catch (IOException ioe) {
//Stream already closed, this is fine
return;
}
}
}
private boolean readOneBuffer() throws IOException {
char[] cbuf = new char[255];
int read;
while ((read = out.read(cbuf)) != -1) {
writer.write(cbuf, 0, read);
}
return read != -1;
}
private void close() {
try {
out.close();
} catch (IOException ioe) {
ErrorManager.getDefault().notify(ioe);
} finally {
writer.close();
}
}
}
See also issue #57000.
There are cases in which you want to exercise great control over who is allowed to use your application. You might, for example, be required to check the user's network credentials, validate client-side certificate or check a license server before the platform application is even launched.
A platform application is typically started from an executable launcher (Windows) or shell script (Unix). This invokes the org.netbeans.core.startup.Main.main method. However, as described in the Module System documentation, you can use the netbeans.mainclass system property to specify a different class to run at startup.
This main class must be present in the startup classpath (you can put it alongside core.jar in platform8/core or similar) and must have a main method. The main method can then invoke whatever authorization logic you like. If it fails, you'll probably want to show a dialog to the user and call System.exit. If it passes, you can then simply invoke org.netbeans.core.startup.Main.main yourself to continue the normal NetBeans startup procedure.
If you simply want to enable a single module based on some criteria (for example, the existence of a license file), you can use ModuleInstall.validate().
To disable assertions or set some other VM property for your application, there are two places to pay attention to. First, $APP_HOME/etc/*.conf in your distribution should set things for users of your application - do this for things that should be set for any user.
You also will probably want to test these settings - and *.conf is not going to be used when you launch your application by running your project from Ant (nor the NetBeans IDE). So to handle this, you can set any of the properties documented in $NB_HOME/harness/README. For example, to disable assertions when testing your application from the IDE, edit your module suite's nbproject/project.properties to include run.args.extra=-J-da or similar.
See $NB_HOME/harness/README in your copy of NetBeans for the full list of properties that affect how NetBeans-based-applications are run when developing them in the IDE.
Generally speaking, you will follow the same threading principles for a platform application as you would for any Swing application. The most important things to keep in mind are:
In addition to standard Java utilities for threading such as SwingWorker and EventQueue, the platform APIs provide some classes to assist with threading. These include RequestProcessor and Mutex.
Additionally, the NetBeans Web site contains a document about threading in the platform and IDE.
There are many possibilities how to profile Java applications and that can be applied to NetBeans profiling. For different task it can be good to select different ways because each of them has its strengths and weaknesses.
See also: DevFaqMemoryLeaks
To be able to profile an application it is usually needed to start it with a modified command that typically adds some (JVMPI or JVMTI) libraries, some classes to (boot)classpath, specifies options for profiling and often initializes profiling support before the application starts to run its code.
The NB module development support is integrated with the NB Profiler. Just select a module and click Profile to start.
Want to cover some typical activities like:
It is a sampling profiler working on Solaris and Linux (with limited functionality) that collects data during runtime. These data are later available for offline processing.
It provides some capabilities that are not available in other Java profilers namely timeline view. This view shows timeline for each thread visualizing if the thread actually executes some code or not.
Performance Analyzer that is part of Sun Studio tools and can be downloaded from the developers' site.
export _NB_PROFILE_CMD='collect -p 1 -j on -S off -g NetBeans.erg -y 38 -d /export/home/radim/analyzer
Startup: start with profiling enabled, send a signal when startup is completed. When sampling every 1ms it takes ~70 seconds instead of 40.
Quite simple way how to measure time spent in some code is to wrap the code with
long t0 = System.nanoTime();
try {
... measured code
} finally {
long t1 = System.nanoTime();
System.out.println("action took "+(t1-t0)/1000000+"ms");
}
JVMTI is powerful interface that allows to write custom libraries that will track behavior of application.
DTrace is a comprehensive dynamic tracing framework for the Solaris™ Operating Environment. It is one of the few tools that allows to track activities running deeply in the system and analyze the system. Because there are also probes provided by Java VM and function like jstack it is also possible to map observed actions to parts of Java code in running application.
Node pop-ups: interesting starting point is o.o.awt.MouseUtils$PopupMouseAdapter.mousePressed()
See What is UI responsiveness for overview.
Older Performance web page contains few links to documentation of one possible approach how to measure and profile responsiveness. This is based on use of modified event queue and patches classes from JDK.
Recently we changed the support a bit to avoid modifications of core JDK's classes and and use small utility library available in Hg. This is used in current automated testing and can be used for manual checks too. To run such test:
Issue: How do I specify the kind of renderer which will represent the look of property value cell (in the same manner as it usually does for JTable)? For example, I want a string property not to be shown in grey if it's not editable.
Solution: If you specify a custom inplace editor it will also be used for rendering.
Issue: How can I provide a new kind of inplace editor? For example, I want to have a property which should be shown at Property Sheet like a combo-box or a spinner control.
Solution: See the docs for the InplaceEditor interface.
Issue: How do I specify that a value is not editable in-place?
Solution: Provide a custom inplace editor that provides a disabled component for the inplace editor. Or mark it non-editable, but supply a custom editor that does edit it.
Issue: How do I specify that a property has a custom editor?
Solution: See the docs for the PropertyEditor interface.
Issue: How can I copy text from a non-editable property?
Solution: Not implemented yet.
Issue: How do I access the Node the property belongs to?
Solution:
class MyEditor implements ExPropertyEditor {
PropertyEnv env;
public void attachEnv(e) {
env = e;
}
public void anyMethod() {
Object[] arr = env.getBeans();
// now arr contains either the real bean objects if invoked on Object
// or it contains Node that the properties belong to
}
}
A: It is simply a matter of listening for the selected nodes property change and then setting the activated nodes on the parentTopComponentwhich contains your tree view.
public class MyComponent extends TopComponent implements PropertyChangeListener {
private ExplorerManager explorerManager;
public MyComponent() {
explorerManager = new ExplorerManager();
explorerManager.addPropertyChangeListener(this);
}
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getSource() == explorerManager &&
ExplorerManager.PROP_SELECTED_NODES.equals(evt.getPropertyName())) {
setActivatedNodes(explorerManager.getSelectedNodes());
}
}
}
Note that the example above is not a complete TopComponent implementation with a tree view and nodes. It is simply demonstrating how to have the selected node's properties shown in the Properties view.
This is only really useful if you have a lot of properties - and having lots of properties is usually not a great idea, as the user has to search for the things they want to change.
Nonetheless, if you want to group different sets of properties on different buttons, it is quite simple. Node.PropertySet extends java.beans.FeatureDescriptor. It inherits the methods putValue (key, value) and getValue(key, value) which allow you to add ad-hoc key/value pairs to any PropertySet. What you do is pass a String with the text that should be on the button, assigned to the key "tabName":
somePropertySet.putValue ("tabName", NbBundle.getMessage (getClass(), "KEY_Something"));
(the above code gets you a localized string - you can also pass a hard-coded one if you are sure your UI will never need to be translated).
All PropertySets that have the same value will be included under the same button/tab in the property sheet.
The simple answer is no: The global keymap (get Keymap from Lookup) is a master keymap for the whole application, 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 Ctrl-N for New File, 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.
If you are creating a custom application (e.g. Standalone Application in suite project properties) you specify a branding for the application. You can then override localized text strings from platform modules without modifying those modules directly; the overrides will be active whenever your branding is selected (this part is taken care of for you by the suite build harness). You will need to locate the module which defines the menu item and find the localized Bundle.properties which gives a label for it. Then you can create a file in your suite project like
branding/modules/jarname.jar/path/Bundle.properties
containing definitions of overridden labels.
When you enable branding on a suite the IDE automatically brands a few bundle strings for the main window title and so on, so you can look at these files for examples.
See also Technical details
Applies to: NetBeans 5.0, 5.5, 6.x
Perhaps your users are a bit confused by the ability to close, slide or dock windows or maybe you're trying to retain the behavior of some existing application's window system. There are times, however rare, in which you want to replace the typical NetBeans Window Manager org.netbeans.core.windows.WindowManagerImpl with a different one.
Before doing this you should know that starting with NetBeans 6.5, it will be easy to change certain behaviors of the window system. So needing to replace the window manager is rare already and will be needed even less often in the future.
But if you still want to do it, you can:
Of course, there's a lot of work involved in creating your own WindowManager implementation, but you can have a look at the org.openide.windows.DummyWindowManager class for starters. It's a simple implementation that opens all TopComponents in their own frame but which can also make windows invisible which is handy for testing.
The DummyWindowManager is used as a last resort when no other window manager is present; you will not need to register it in the default Lookup as described earlier. If you want to use it, keep in mind these two tips:
RequestProcessor.getDefault() is tempting to use, but it is also dangerous. This FAQ item will tell you when not to use it.
RequestProcessor has a constructor argument for its throughput. That says how many threads this RequestProcessor is allowed to use at the same time. When you call new RequestProcessor("Useful name for thread dump", 3) you are creating a thread pool that can have 3 threads available to run things on simultaneously.
The throughput of RequestProcessor.getDefault() is Integer.MAX_VALUE. Think about what that means: it can potentially create thousands of threads; but your OS cannot necessarily handle thousands of threads, and you probably don't have thousands of CPUs. Which means the OS does extra work time-slicing between the threads and things get slower, not faster.
RequestProcessor.getDefault() is useful for one-off operations - you have some situation that happens once in a great while, and, say, while constructing some object, you need to do some work in the background; that work will probably never need to be done again for the life of the Java VM. That's a perfect case for RequestProcessor.getDefault().
Now here is the anti-example: You are creating a Node that represents a file. It needs to mark itself with an error badge and color its text in red if the file contains errors. You can't read the file when you create the Node - that takes to long. So when the node is created, it runs a background task to check its status, and updates its icon and display name after it has read the file. Now imagine you did this with RequestProcessor.getDefault(). What happens when the user expands a folder that contains 1000 of your files? 1000 threads get created, and the whole application gets very, very slow. For that, you are much better off creating one RequestProcessor and using it for all your nodes. The FAQ entry about RequestProcessor.Task shows how to do this correctly.
If you create your own RequestProcessor, please always use a name. If you get a deadlock it makes debugging much easier.
There are a lot of reasons you might want to reschedule a background operation. For example, you want to re-parse a file 3 seconds after the user stops typing, so you can show errors. But at 2 seconds she starts typing again. You don't want that task to run a second from now anymore. You can either cancel the task, or even simpler, call task.schedule(3000) every time a key is pressed. If it was already scheduled, it will be rescheduled for 3 seconds from now again.
Or imagine you have the situation described in the FAQ about RequestProcessor.getDefault() - a node for a file needs to read the file after it is created to mark itself if the file has errors. RequestProcessor.Task makes this sort of thing easy.
public class FooDataNode extends DataNode implements PropertyChangeListener, Runnable {
private boolean error;
private static final RequestProcessor THREAD_POOL = new RequestProcessor("FooDataNode processor", 1);
private final RequestProcessor.Task task = THREAD_POOL.create(this);
FooDataNode(FooDataObject obj) {
super(obj, Children.LEAF);
obj.addPropertyChangeListener(WeakListeners.propertyChange(this, obj));
task.schedule(100);
}
public void propertyChange(PropertyChangeEvent evt) {
DataObject obj = (DataObject) evt.getSource();
if (DataObject.PROP_MODIFIED.equals(evt.getPropertyName()) && !obj.isModified()) { //file was saved
task.schedule(100);
}
}
@Override
public String getHtmlDisplayName() {
return error ? "<font color=\"!nb.errorForeground\">" + getDisplayName() : null;
}
public void run() {
boolean old = error;
error = doesTheFileHaveErrors();
if (old != error) {
fireDisplayNameChange(null, null);
}
}
private boolean doesTheFileHaveErrors() {
assert !EventQueue.isDispatchThread();
//parse the file here
return true; //whatever the value should be
}
}
As described in the Module System API documentation, the module classloader refuses to load classes from the default package. This is actually a fairly common restriction in multi-classloader systems like the NetBeans Platform (such as Eclipse RCP and various application servers). The module system will load resources (such as properties files and images) from the default package, but will log warning messages about doing so.
If you're unable to move the resources (e.g. a third-party library requires them to be there), then you can suppress this warning as described in the Module System API documentation by setting the org.netbeans.ProxyClassLoader.level system property to a value of 1000 or higher.
There are a large number of samples. Many of these correspond to the tutorials. You can find the samples in module platform in main/misc repository at hg.netbeans.org. They are in the samples/ subdirectory.
The platform/samples/ folder can be browsed online here. But for really trying things out it is usually more useful to have a local copy - then you can open them as projects in the IDE.
The process is simpler than you might think. You will use the Javac API to analyze files.
So first, you'll want to get the classpath - for example
ClassPath path = project.getLookup().lookup(
ClassPathProvider.class).findClassPath(someFile, ClassPath.SOURCE);
(or if you can directly access the project, get the classpath some other way).
Then you'll recursively iterate all of the children of the FileObjects that are the classpath roots. For each one with the extension .java, you will call
JavaSource src = JavaSource.forFileObject(fo);to get the JavaSource for that FileObject. The JavaSource is something you can pass a task to, and it will run the compiler against it and you can write a visitor or scanner that visits every element of the compilation tree.
So you will implement CancellableTask<CompilationController> to create a task that can analyze a source file. The first thing that task will do is ask the compiler to completely resolve all of the types (this can be slow):
compiler.toPhase(Phase.RESOLVED);Then we get the type declarations from the source we are currently analyzing:
List <? extends Tree> types =
compiler.getCompilationUnit().getTypeDecls();
Now we iterate the types found and get a /TypeMirror/ for each. TypeMirror.toString() gives a fully qualified class name, so we just need to compare it against the string we're looking for:
for (Tree tree: types) {
if (cancelled) return;
TypeMirror mirror = compiler.getTrees().getTypeMirror(
TreePath.getPath(compiler.getCompilationUnit(), tree));
...
See the full source code below for how to do this. Note that it is very important for performance that you implement CancellableTask and not just Task, run this code on a background thread, regularly check if the cancelled flag has been set and abort if so (your UI or whatever is running this code should know if it is no longer needed - for example, if you are writing a Swing panel, cancel the task in removeNotify()).
public class TypeFinder implements CancellableTask<CompilationController> {
private volatile boolean cancelled;
private final ClassPath classpath;
private final String fqn;
private Callback callback;
public TypeFinder(String fqn, ClassPath path) {
this.classpath = path;
this.fqn = fqn;
}
public interface Callback {
public void foundFileObject(FileObject fo, String className);
}
public void findTypes(Callback callback) {
//Don't ever do this kind of IO-heavy work on the event thread
assert !EventQueue.isDispatchThread();
this.callback = callback;
//recursively iterate all children
for (FileObject fo : classpath.getRoots()) {
analyze(fo);
}
}
private void analyze(FileObject fo) {
if (fo.isFolder()) {
for (FileObject child : fo.getChildren()) {
analyze(child);
}
} else if ("java".equals(fo.getExt())) { //NOI18N
JavaSource src = JavaSource.forFileObject(fo);
try {
src.runUserActionTask(this, true);
} catch (IOException ex) {
Logger.getLogger(TypeFinder.class.getName()).log(Level.INFO,
"Problem scanning " + fo.getPath(), ex); //NOI18N
}
}
}
public void reset() {
cancelled = false;
}
@Override
public void cancel() {
cancelled = true;
}
@Override
public void run(CompilationController compiler) throws Exception {
if (cancelled) {
return;
}
compiler.toPhase(Phase.RESOLVED);
if (cancelled) {
return;
}
List <? extends Tree> types =
compiler.getCompilationUnit().getTypeDecls();
for (Tree tree: types) {
if (cancelled) {
return;
}
TypeMirror mirror = compiler.getTrees().getTypeMirror(
TreePath.getPath(compiler.getCompilationUnit(),
tree));
if (tree instanceof ClassTree && match(mirror, compiler.getTypes())) {
callback.foundFileObject(compiler.getFileObject(),
mirror.toString());
}
}
}
boolean match(TypeMirror mirror, Types types) {
boolean result = false;
if (mirror != null &&
!"java.lang.Object".equals(mirror.toString())) { //NOI18N
result |= fqn.equals(mirror.toString());
if (!result) {
List<? extends TypeMirror> l =
types.directSupertypes(mirror);
for (TypeMirror tm : l) {
result |= match(tm, types);
if (cancelled) return result;
if (result) break;
}
}
}
return result;
}
}
Yes, though there is not yet any GUI support for this.
1. Make a module project.
2. Generate a keystore, e.g.
cd .../path/to/module/ keytool -genkey -storepass specialsauce -alias myself -keystore nbproject/private/keystore
and answer the questions posed.
To make NetBeans build script sign the NBM module. The keystore and key password needs to be the same. At keytool, when the question below is asked, just press ENTER key, to make keystore and key alias the same password.
Enter key password for <myself> (RETURN if same as keystore password):
3. Edit nbproject/project.properties to contain e.g.
keystore=nbproject/private/keystore nbm_alias=myself
4. Edit nbproject/private/private.properties to contain e.g.
storepass=specialsauce
You could also pass -Dstorepass=specialsauce on the command line. If you specify a keystore but ${storepass} is undefined, you will be prompted for the password during the build.
5. Build the NBM for the module. (Context menu of the project.) It should be signed.
6. Try installing the NBM. (Expand build folder in Files view and double-click it.) It will not be trusted initially (and so the checkbox to really install it will initially be unchecked), since NetBeans does not know about your signature. But you can click View Certificate to examine the certificate. If you allow installation of this module, NetBeans will remember you approved this certificate and it will not ask you for confirmation next time.
Some notes:
1. You can probably get a root-authorized certificate from VeriSign or the like, and the Auto Update wizard should treat this as more trusted. Not yet investigated (please update this FAQ entry if you experiment with this).
2. Keeping the keystore and its password in the private dir ensures that you will not accidentally commit either to source repository or include it in a source ZIP made with the Project Packager module. It may be safe to put the keystore in a shared directory (e.g. nbproject) if you are sure that the storepass is too hard to guess.
Applies to: NetBeans 6.5
http://blogs.sun.com/geertjan/entry/enriching_your_treetableview
If you've unpacked or checked out the NetBeans sources, you'll see more then 600 directories. Almost every one of these directories is a module. Although the directory names indicate the purpose of each, sometimes it's still not clear what each does.
The easiest way to find out about a module in the source tree is to open its manifest file, then look for the entry named OpenIDE-Module-Localizing-Bundle. The file referenced there (located deeper inside the module directory) typically contains the module's display name, descriptions and other information. You could automate the extraction of these values through a simple shell or perl script, but for your convenience, I've included the short description of each one below:
ant.browsetask=Adds an Ant task <nbbrowse> to run inside NetBeans to open a web browser. ant.debugger=Enables debugging on Ant scripts. ant.freeform=Special project type for projects with pre\u00EBxisting Ant scripts. ant.grammar=Code completion for textual editing of Ant scripts. ant.kit=Support for Ant build scripts. antlr=Antlr Developement Libraries api.debugger=Enables debugging with the AAA debugger implementation. api.debugger.jpda=JPDA Debugger API api.debugger=NetBeans Debugger APIs. api.java.classpath=Classpath APIs api.java=APIs for Java development support modules api.mobility=Mobility Core API module. api.progress=Task progress visualization APIs. apisupport.apidocs=Local documentation for the NetBeans APIs. apisupport.feedreader=Feed Reader Application apisupport.feedreader=Wrapper for JDOM library apisupport.feedreader=Wrapper for ROME Fetcher Library apisupport.feedreader=Wrapper for ROME Library apisupport.feedreader=Bundles a demonstration application using the NetBeans Platform. apisupport.harness=Lets you build external plug-in modules from sources. apisupport.paintapp=Sample NetBeans platform application. apisupport.project=Defines an Ant-based project type for NetBeans modules and module suites. apisupport.project=Some short description apisupport.refactoring=Additional refactoring support for NetBeans module projects. api.visual=Visual Library API api.web.webmodule=APIs for web module development support modules. api.xml=This module contains XML tools API and SPI. applemenu=Enables proper support for the Apple \ asm=Assembler support autoupdate.services=Support for searching for module updates on Update Center and for downloading and installing modules autoupdate.ui=Supplies UI of Auto Update Services beans=Support for creating JavaBeans(TM) components. bpel.core=BPEL Core. bpel.debugger.api=Enables debugging on BPEL files. bpel.debugger.bdi=BPEL Debugger RMI. bpel.debugger=BPEL Debugger. bpel.debugger.ui=BPEL Debugger UI. bpel.editors.api=BPEL Editors API. bpel.editors=BPEL Editors. bpel.help=BPEL Help. bpel.kit=BPEL development support. bpel.mapper=BPEL Mapper. bpel.model=Object model for BPEL 2.0. bpel.project=Composite Application Base Project. bpel.project=BPEL Project. bpel.refactoring=BPEL Refactoring. bpel.samples=BPEL Samples. bpel.validation=BPEL Validation. classfile=Provides read-only access to Java class files. clearcase=Clearcase Versioning System cnd.antlr=Supports the C/C++ Code Model - contains ANTLR parser generator library cnd.api.model=API that represents C/C++ code cnd.api.project=A bridge between C/C++ project system and C/C++ code assistance cnd.apt=APT presentation for files with preprocessor cnd.callgraph=C/C++ Call Graph cnd.classview=C/C++ Class View cnd.completion=Code completion for C, C++, and Fortran languages cnd.debugger.gdb=Supports debugging of native programs with gdb cnd.discovery=C/C++ Discovery API/SPI cnd.dwarfdiscovery=C/C++ Dwarf-based Discovery Provider cnd.dwarfdump=Reading dwarf debugging information cnd.editor=C/C++ Editor cnd.folding=C/C++ APT-based Folding cnd.gotodeclaration=C/C++ Go To Declaration cnd.highlight=Provides error highlighting for the C/C++ languages. cnd.kit=C/C++ development support. cnd.lexer=Lexical analysis for C/C++ Pack languages cnd.makeproject=Supports C/C++ projects cnd.modeldiscovery=C/C++ Model-based Discovery Provider cnd.modelimpl=Implementation of C/C++ Code Model API cnd.model.services=Code Model Services cnd.modelui=UI for Implementation of C/C++ Code Model API cnd.modelutil=Miscellaneous utilities used by C/C++ Code Model cnd.navigation=C/C++ Code Navigation cnd.qnavigator=Provides navigator content for C/C++ files cnd.refactoring=C/C++ Experimental Refactoring cnd.remote=Support remote developement cnd.repository.api=Api for the CND repository cnd.repository=Persistence mechanism for Code Assistance features cnd=Enables development of C and C++ programs in the IDE cnd=Enables editing of C, C++, and Fortran files in the IDE. cnd.utils=C/C++ Utilites collab.channel.chat.java=Support for developer-friendly instant messaging chat (Java). compapp.casaeditor=Composite Application Service Assembly editor. compapp.configextension=JBI descriptor configuration extensions. compapp.help=Composite Application Help Topics. compapp.kit=Composite application development support. compapp.manager.jbi=Composite Application JBI Manager. compapp.projects.base=Composite Application Project. compapp.projects.jbi=Composite Application JBI Project. compapp.projects.wizard=Supplies the generic wizard interface for CAPS projects in the IDE. core.execution=Implementation of the Execution engine. core.ide=Makes the IDE from the platform. core.kit=NetBeans Platform core.multiview=MultiView Windows framework and APIs core.nativeaccess=Uses native bindings via JNA library to provide advanced visual effects for window system. core.output2=A simple text area based output window implementation core.startup=Loads and enables modules. core.ui=User interface of the platform. core.windows=Implementation for windowing support. css.editor=Editor support for editing CSS files css.visual=CSS authoring support module for visual CSS editing dbapi=Database support APIs db.core=Core database support. db.dataview=SQL query editable resultset view db.drivers=JDBC database drivers db.kit=Database browser, visual and text SQL editor. db.mysql.sakila=Provides Sakila sample database for NetBeans MySQL support db.mysql=Provides MySQL-specific db support for NetBeans dbschema=Enables you to capture and view the structure of a database in the IDE. db.sql.editor=Supports editing SQL files in the IDE db.sql.visualeditor=Visual Query Editor db=Views and modifies the structure of the connected database. debugger.jpda.ant=Lets you use the NetBeans JPDA debugger from Ant. debugger.jpda.heapwalk=Provides heap walking functionality in Java Debugger. debugger.jpda.projects=JPDA Debugger integration with Java projects. debugger.jpda=Enables debugging with the JPDA debugger implementation. debugger.jpda.ui=JPDA Debugger. defaults=Contains font, color and shortcut defaults for IDE. deployment.wm=Windows Mobile Deployment derby=Integration with the Java DB database. diff=Provides the diff action to view file differences. editor.bookmarks=Contains support for bookmarks handling in the edited files editor.bracesmatching=Support for highlighting matching braces editor.codetemplates=Contains support for creation and using of code templates editor.completion=Contains support for Code Completion in Editor editor.errorstripe.api=The API for the right hand side bar showing errors, hints, etc. editor.errorstripe=The right hand side bar showing errors, hints, etc. editor.fold=Contains support for Code Folding in Editor editor.guards=Provides support for manipulating garded sections in a document. editor.indent=Contains indentation APIs and SPIs. editor.kit=Editting support for various types of files. editor.lib2=Contains core editor APIs and SPIs. editor.lib=Contains Editor functionality independent on the IDE editor.macros=Support for editor macros editor.mimelookup.impl=The default implementation of MimeDataProvider. editor.mimelookup=The MIME lookup API. editor.plain.lib=Contains plain editor library implementation editor.plain=Contains plain text editor implementation editor.settings=Contains support for editor settings editor.settings.storage=Implements Netbeans editor settings storage editor=Enables editing of files in the IDE. editor.structure=Contains Editor support functionality for tag based editors editor.util=Contains various support classes for editor related modules el.lexer=Lexical Analysis for Expression Language etl.editor=Data Editor for editing and creating extract-transform-load collaboration documents. etl.project=Data Integrator Application Projects. extbrowser=Enables integration of external web browsers with the IDE. extbrowser=Webclient module enables embedding of external web browsers into the IDE. extexecution=Supports execution of external processes favorites=Support for organizing favorite files. form.kit=Enables you to visually design Java desktop (AWT and Swing) applications. glassfish.common=Shared support module for GlassFish V3 server integration glassfish.eecommon=shared code for glassfish servers glassfish.javaee=GlassFish V3 server support for JavaEE projects. glassfish.jruby=GlassFish V3 s