| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
| <html xmlns="http://www.w3.org/1999/xhtml"> | |
| <head> | |
| <!-- -*- xhtml -*- --> | |
| <title>NetBeans APIs in a Nutshell</title> | |
| <link rel="stylesheet" type="text/css" href="https://netbeans.org/netbeans.css"> | |
| <meta name="AUDIENCE" content="NBUSER"> | |
| <meta name="TYPE" content="ARTICLE"> | |
| <meta name="EXPIRES" content="N"> | |
| <meta name="author" content="gwielenga@netbeans.org"> | |
| <meta name="indexed" content="y"> | |
| <meta name="description" | |
| content="A short guide to understanding everything."> | |
| <!-- Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. --> | |
| <!-- Use is subject to license terms.--> | |
| </head> | |
| <body> | |
| <h1>NetBeans APIs in a Nutshell</h1> | |
| <p> | |
| This overview will quickly familiarize you with how NetBeans modules | |
| interact with the NetBeans Platform and with each other. It is not intended | |
| as a comprehensive document—the <a href="http://bits.netbeans.org/dev/javadoc/index.html">NetBeans API List</a>, | |
| the <a href="https://netbeans.org/kb/trails/platform.html">NetBeans Platform Learning Trail</a>, and | |
| the video series "<a href="https://platform.netbeans.org/tutorials/nbm-10-top-apis.html">Top 10 NetBeans APIs</a>" go into greater | |
| detail—but should serve as a guide to understanding the basic concepts of NetBeans module | |
| development. | |
| <p> | |
| A key thing to understanding the NetBeans Platform is to realize that very often | |
| the same API or infrastructure does double-duty—playing one role | |
| in dealing with the user's files on disk, and another role when it comes to | |
| configuration information and runtime data. For example: | |
| <ul><li>A <a href="http://bits.netbeans.org/dev/javadoc/org-openide-filesystems/org/openide/filesystems/FileSystem.html">FileSystem</a> represents the | |
| user's files, but the <a href="https://platform.netbeans.org/tutorials/nbm-glossary.html">System Filesystem</a> represents the IDE's configuration | |
| data. | |
| <li>A <a href="http://bits.netbeans.org/dev/javadoc/org-openide-loaders/org/openide/loaders/DataObject.html">DataObject</a> represents the parsed content of a Java or other file, but | |
| DataObjects also are used to instantiate a Java object installed | |
| by a module. | |
| <li><code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/Lookup.html#getDefault()">Lookup.getDefault()</a></code> is the way you access global | |
| services and singletons, but you also call <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.html#getLookup()">Node.getLookup()</a></code> to | |
| find services specific to an individual file or object. | |
| </ul> | |
| <p> | |
| It is this reuse that has led some to say the NetBeans APIs are confusing, and it | |
| is the purpose of this overview to rapidly familiarize you with what these things | |
| are and how they are used in both roles. | |
| <h2><a href="http://bits.netbeans.org/dev/javadoc/" name="fs">FileSystems</a></h2> | |
| In NetBeans 3.x, adding items to the classpath was accomplished by "mounting" | |
| FileSystems - a FileSystem had a root directory and everything under it | |
| amounted to a virtual namespace in which files lived. | |
| <p> | |
| Since NetBeans 4.0, the "mounting" is gone, and FileSystems are not | |
| a concept that users are exposed to in the UI - but the infrastructure | |
| behind FileSystems - <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-filesystems/org/openide/filesystems/FileSystem.html">org.openide.filesystems.FileSystem</a></code> is alive | |
| and well under the hood. In coding NetBeans modules, you will typically | |
| interact with instances of <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-filesystems/org/openide/filesystems/FileObject.html">org.openide.filesystems.FileObject</a></code>, | |
| not <code><a href="http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html">java.io.File</a></code>. | |
| <h3>Differences Between <code>java.io.File</code> and <code>FileObject</code>s</h3> | |
| The main differences between them are as follows: | |
| <ul> | |
| <li>You get FileObjects from a FileSystem, rather than create them with a constructor.</li> | |
| <li>Typically you don't have FileObjects which represent something that doesn't | |
| exist (as you can with <code>new File ("some/place/that/doesnt/exist")</code>).</li> | |
| <li>You can listen for changes on FileObjects, including listening on folders | |
| for changes that happen anywhere underneath them</li> | |
| <li>FileObjects don't necessarily represent actual files on disk</li> | |
| <li>FileObjects can have <i>attributes</i> which are essentially key-value | |
| pairs that can be associated with a file. An attribute might be a | |
| string, or a serialized object (note that use of attributes on | |
| user files on disk is discouraged as of NetBeans 4.0, but they are | |
| still commonly used in configuration files). | |
| <li>The path separator for FileObjects is always <code>/</code>, no | |
| conversions with File.separator are needed</li> | |
| </ul> | |
| <h3><a name="sfs">What FileSystems Are Used For</a></h3> | |
| FileSystems are used in two basic but very distinct ways in NetBeans. The | |
| first is representing the user's files on disk. To get a FileObject for some | |
| path in NetBeans, just call, e.g. | |
| <pre class="examplecode">Repository.getDefault().getDefaultFilesystem().getRoot().getFileObject("path/to/some/File.txt");</pre> | |
| The <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-filesystems/org/openide/filesystems/Repository.html">Repository</a></code> is the master registry of all filesystems | |
| NetBeans knows about. In 4.0, it is the contents of the user's hard drive(s). | |
| <p> | |
| The second usage is to represent configuration data - this is the "System Filesystem", | |
| which is where modules can install their files. Folders in the | |
| System Filesystem act as "extension points" - there are some which | |
| have predefined meanings (for example, NetBeans' main menu is a tree of | |
| folders you will place special "files" into to add menu items); | |
| modules are free to create their own folders and do as they wish with the | |
| contents. | |
| <p> | |
| How does all this work? Well, once you have the concept of a virtualized | |
| <code>FileSystem</code> full of <code>FileObject</code>s, | |
| it's relatively easy to imagine a <code>FileSystem</code> | |
| which took several other <code>FileSystems</code> as arguments, and presented a merged | |
| view of the sub-filesystems as if all the data lived in one tree. | |
| <p> | |
| Add into this the notion that the "files" in a <code>FileSystem</code> | |
| don't actually have to be physical files on disk at all - anything that | |
| can be made to walk and talk like a file will do. So you could have an | |
| XML "filesystem" where the contents of files lived in an | |
| XML document, not a bunch of files on disk. | |
| <h2>XML Layers</h2> | |
| That is what the NetBeans Platform does: Each module can define an XML "layer" | |
| file, which contains some virtual "files" and folders that are merged into | |
| the System Filesystem. In this way modules add their configuration data | |
| to the system. And because the System Filesystem is composed from discrete | |
| XML fragments from modules, when a module is disabled or unloaded, its | |
| XML layer is simply removed. <code>FileObject</code>s for the various folders that | |
| had files removed from them fire changes indicating some files were | |
| deleted, so the UI can get rid of any objects that represented the now-unloaded | |
| module's files. This is why you can uninstall and reload modules at runtime. | |
| <p> | |
| In its jar manifest, a module will contain a line such as:<pre class="examplecode"> | |
| OpenIDE-Module-Layer: org/netbeans/modules/mymodule/layer.xml | |
| </pre> | |
| This is a pointer to an XML file inside the module jar (meaning that you | |
| simply create this file somewhere in your sources so it will be compiled | |
| into the jar when your module is built). In its simplest form, that could contain | |
| something like: | |
| <pre class="examplecode"> | |
| <filesystem> | |
| <folder name="myFolder"> | |
| <file name="myFile.txt" url="resources/aTextFile.txt"/> | |
| </folder> | |
| </filesystem> | |
| </pre> | |
| The <code>url</code> attribute is important: It says where the contents | |
| of <code>myFile.txt</code> lives in the module's jar file. This path is | |
| relative to the location of the layer file. So, if the layer file is | |
| <code>org/netbeans/modules/mymodule/layer.xml</code>, then in the module | |
| jar there should also be a text file | |
| <code>org/netbeans/modules/mymodule/resources/aTextFile.txt</code>. When | |
| some code requests an <code>InputStream</code> for <code>myFolder/myFile.txt</code>, | |
| that text file in the module jar is what will actually be read. | |
| <p> | |
| Of course, this particular fragment doesn't do much of anything, but it is | |
| useful to illustrate what can be done here. Since <code>myFolder</code> | |
| has no predefined purpose to NetBeans, it is up to the module defining | |
| that folder to do something with its contents. But one could imagine | |
| a module that provided <code>myFolder</code>, let other modules add more | |
| files to that folder, and provided one menu item for each file, letting | |
| the user view them. | |
| <p> | |
| Accessing this file programmatically is quite simple: | |
| <pre class="examplecode"> | |
| FileObject myFile = Repository.getDefault().getDefaultFileSystem().findResource ("myFolder/myFile.txt"); | |
| InputStream in = myFile.getInputStream(); | |
| <font color="gray">//...do something with it</font> | |
| </pre> | |
| <p> | |
| <h3>Providing Java Objects through Module Layers</h3> | |
| Just being able to install text files isn't terribly interesting. Where the | |
| system of layers gets its power is in the ability to make files act as | |
| factories for Java objects. This is made possible using the same | |
| infrastructure that recognizes user data on disk, which will be discussed | |
| in more detail in <a href="#loaders">the section on Loaders</a>. Effectively, | |
| there is a specific file-extension registered in the system, <code>.instance</code> | |
| which identifies a file that actually represents a Java object and can create | |
| the actual object. | |
| <p> | |
| <pre class="examplecode"> | |
| <filesystem> | |
| <folder name="Menu"> | |
| <folder name="File"> | |
| <file name="org-netbeans-modules-mymodule-MyAction.instance"/> | |
| </folder> | |
| </folder> | |
| </filesystem> | |
| </pre> | |
| The above module layer actually adds a Swing Action (implemented by the class | |
| <code>org.netbeans.modules.mymodule.MyAction</code>) into the File menu on the | |
| main menu bar in NetBeans. The NetBeans core defines the folder <code>Menu</code>; | |
| the <code>core/ui</code> defines common menus that are in NetBeans, and provides | |
| the infrastructure that listens on these folders and keeps the GUI up-to-date if | |
| things are added or removed. Toolbars work in a similar fashion, as do many other | |
| things in NetBeans. | |
| <p> | |
| <h3>Hiding Files in the System Filesystem</h3> | |
| The System Filesystem also allows one module to remove what another module | |
| adds. The semantics are extremely simple - for example, if you wanted to delete | |
| the File menu in NetBeans when your module is enabled, simply put the following | |
| into your module layer: | |
| <pre class="examplecode"> | |
| <filesystem> | |
| <folder name="Menu"> | |
| <folder name="File_hidden"/> | |
| </folder> | |
| </filesystem> | |
| </pre> | |
| To make this work, modules can, in their manifest, request to be installed only | |
| after another module is installed - thus there is a defined stacking order to | |
| module layers. | |
| <p> | |
| <h3>The System Filesystem is Read-Write</h3> | |
| If it were all just static XML fragments, it wouldn't be possible to actually | |
| store configuration changes the user has made - but of course, this is possible. | |
| Recall that we have the notion of a filesystem composed of merging multiple | |
| other filesystems - and that we know that we have an implementation of | |
| <code>FileSystem</code> over actual files on disk, which is how a user's data | |
| files are accessed. | |
| <p> | |
| The top layer to the system filesystem is the <code>config/</code> subdirectory | |
| of the user's settings directory - typically this lives in the user's home | |
| directory under the directory <code>.netbeans</code>. So when a user makes | |
| changes (like rearranging menu items), the diff of the changes is written | |
| to disk in the settings directory; since this layer lives at the top of the | |
| stack, whatever changes are there (such as hiding files, as discussed above), | |
| override anything a module has in its layer file. | |
| <h2><a name="loaders">DataLoaders and DataObjects</a></h2> | |
| <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-loaders/org/openide/loaders/DataObject.html">DataObject</a></code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-loaders/org/openide/loaders/DataObject.html">s</a> are wrappers for <code>FileObjects</code>. A FileObject simply represents a file-like | |
| entity; <code>DataObject</code>s are the level at which the system understands what the contents | |
| of a file are. So a module that implements handling for a particular file | |
| type provides its own subclass of | |
| <code>DataObject</code> and a factory which can create an instance of that DataObject type | |
| when it is passed a <code>FileObject</code>. <code>DataObjects</code> are what provide programmatic | |
| access to the contents of a file - such as parsing a file and providing a | |
| model for its content. | |
| <p> | |
| The factory for these objects, which a module installs, is called a | |
| <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-loaders/org/openide/loaders/DataLoader.html">DataLoader</a></code>. It is declared directly in the module's manifest: | |
| <pre class="examplecode"> | |
| Name: org/netbeans/modules/povray/PovDataLoader.class | |
| OpenIDE-Module-Class: Loader | |
| </pre> | |
| An example of how to write a DataLoader can be found in the <a href="nbm-filetype.html">NetBeans DataLoader Module Tutorial</a>. DataLoaders typically register themselves to support specific | |
| file extensions or mime types. | |
| <p> | |
| Unless you are writing support for a language or file-type, typically you | |
| will be using, not creating, <code>DataObject</code>s. Getting the DataObject | |
| for a file is simple: Just call <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-loaders/org/openide/loaders/DataObject.html#find(org.openide.filesystems.FileObject)">DataObject.find(someFileObject)</a></code>. | |
| <p> | |
| <h3><a name="dataobjects">Using <code>DataObject</code>s</a></h3> | |
| <code>DataObject</code>s don't do a lot in and of themselves - that is, it is | |
| almost always a mistake to be casting a DataObject as a particular subclass. | |
| The way to do most interesting interaction with DataObjects is via the method | |
| <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-loaders/org/openide/loaders/DataObject.html#getCookie(java.lang.Class)">getCookie()</a></code>. The pattern, which we will see in more detail in | |
| the section on <a href="#lookup"><code>Lookup</code></a> is: | |
| <pre class="examplecode"> | |
| OpenCookie open = someDataObject.getCookie (OpenCookie.class); | |
| open.open(); | |
| </pre> | |
| The above code will actually open a file in the editor. The key here is that, | |
| rather than providing programmatic access to a file's content as a bunch of | |
| instance methods on itself (which would quickly lead to a tangled mess of inheritance issues), | |
| you <i>ask</i> a <code>DataObject</code> for an instance of some known interface | |
| that does what you need. This is accomplished by passing a <code>Class</code> | |
| object to <code>getCookie()</code>, which will return that object if possible, | |
| or <code>null</code> if not. | |
| <p> | |
| As another example, determining if an opened file has unsaved changes is as | |
| simple as: | |
| <pre class="examplecode"> | |
| boolean needsSaving = someDataObject.getCookie (SaveCookie.class) != null; | |
| </pre> | |
| Modules can provide their own public interfaces, and make instances of those | |
| objects available via <code>getCookie()</code>. So, for example, a <code>DataObject</code> | |
| for an XML file might make a DOM tree or some other structural representation | |
| of the file available via <code>getCookie()</code> for other modules to | |
| use to manipulate the file's contents. Some common interfaces modules will | |
| typically use via <code>getCookie()</code> can be found in the package | |
| <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/cookies/package-summary.html">org.openide.cookies</a></code>. | |
| <p> | |
| Note that the term "cookie" in this context has nothing to do | |
| with the web browser concept of cookies. | |
| <h3>Putting it Together: Why <code>.instance</code> Files Work</h3> | |
| To illustrate the power of loaders and <code>DataObjects</code>, recall that | |
| loaders are registered against a file type. And recall that modules can | |
| install actual Java objects via <code>.instance</code> files. What's | |
| going on here? | |
| <p> | |
| What is actually happening is that the very same infrastructure (<code>DataLoader</code>s) that lets | |
| NetBeans recognize a user's <code>.java</code> file on disk and create an appropriate | |
| <code>DataObject</code> is what recognizes <code>.instance</code> files - after | |
| all, the System Filesystem is a filesystem too. There is simply a <code>DataLoader</code> | |
| registered in the system that claims all files with the <code>.instance</code> | |
| extension. | |
| <p> | |
| Under the hood, what's really happening is that the <code>DataObject</code> | |
| for a <code>.instance</code> file provides an <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/cookies/InstanceCookie.html">InstanceCookie</a></code>. | |
| So to get the actual object in question manually, you would do something | |
| like this: | |
| <pre class="examplecode"> | |
| FileObject file = Repository.getDefault().getDefaultFileSystem().findResource ( | |
| "someFolder/com-foo-mymodule-MyClass.instance"); | |
| DataObject dob = DataObject.find (file); | |
| InstanceCookie cookie = (InstanceCookie) dob.getCookie (InstanceCookie.class); | |
| MyClass theInstance = (MyClass) cookie.instanceCreate(); | |
| </pre> | |
| <h2>Nodes: The Presentation Layer</h2> | |
| You've probably noticed that there are quite a few tree components in | |
| NetBeans - the Files and Projects tabs, and others. The <a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/doc-files/api.html">Nodes API</a> is what | |
| provides the contents to those trees. Think of <code>DataObject</code>s as being | |
| the data model; a Node is where interacting with the user comes in. | |
| <p> | |
| A <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.html">Node</a></code> provides human-visible things like an icon and a | |
| (possibly localized) display name to DataObjects. And a Node provides a | |
| list of <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/Actions.html">Actions</a></code> that can appear in a popup menu for that node. | |
| <p> | |
| <code>Node</code>s define <i>context</i> for NetBeans - at any given moment, | |
| there is usually one or more <i>activated nodes</i> which determine what | |
| menu and toolbar actions are enabled - they are the clue to the rest of the | |
| system as to what the user is doing. Each UI component (such as the Files tab | |
| or the Editor) provides an array of <code>Node</code>s which are activated - | |
| selected. In a tree component, it is rather obvious how this works; but even | |
| when editing in the editor, the activated node triggers what actions are | |
| enabled, depending on where the caret is - if the caret is inside the body | |
| of a method, the activated node is actually the same node you would find | |
| if you expanded the structure tree of that java class in the Projects tab. | |
| <p> | |
| So, to get the <code>Node</code> corresponding to a <code>DataObject</code>, simply call | |
| <code>someDataObject.<a href="http://bits.netbeans.org/dev/javadoc/org-openide-loaders/org/openide/loaders/DataObject.html#getNodeDelegate()">getNodeDelegate()</a></code>. | |
| <h3>Nodes, DataObjects and lookup Patterns</h3> | |
| <code>Node</code>s use the same pattern as <code>DataObject</code> - they have | |
| a <code>getCookie()</code> method that can be used as described above. | |
| <code>Node</code>s that represent <code>DataObject</code>s will typically | |
| delegate to their <code>DataObject</code>'s <code>getCookie()</code> method. | |
| <p> | |
| With <code>Node</code>, it is common to see a second form of this call: | |
| <code>Node.<a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/Node.html#getLookup()">getLookup()</a>.<a href="http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/Lookup.html#lookup(java.lang.Class)">lookup (SomeClass.class)</a></code>. The latter is a | |
| newer idiom, which will eventually replace <code>getCookie()</code> in both | |
| <code>Node</code>s and <code>DataObject</code>s. The specific reason is | |
| that <code>getCookie()</code> requires that the returned object implement | |
| an empty marker interface, <code>Node.Cookie</code>, which unnecessarily | |
| limits what can be returned by <code>getCookie()</code>. The only thing you | |
| need to remember is that the two are functionally equivalent, and in new | |
| code, use <code>getLookup().lookup()</code> where possible. There is further | |
| discussion of what <code>Lookup</code> is <a href="#lookup">below</a>. | |
| <p> | |
| Note that all <code>Node</code>s do not represent <code>DataObject</code>s - | |
| the Nodes API is useful in and of itself for creating tree like hierarchies. | |
| <p> | |
| There are a number of UI components that can represent a tree of nodes as | |
| trees, combo boxes, lists, etc. - so typically when one needs to display a | |
| UI with a list or tree in it, the natural choice is to use the Nodes API, | |
| and simply create the appropriate component and set the root node appropriately. | |
| <p> | |
| A key thing to remember is that Nodes are intended as a presentation layer for | |
| an underlying data model (which might be files on disk, or whatever you want). | |
| If you find you're putting a lot of logic into your <code>Node</code> | |
| subclass, consider that your model is what needs enhancing - <code>Node</code>s | |
| should be lightweight and simple, and the model should do the heavy lifting. | |
| <h2>Lookup</h2> | |
| <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/Lookup.html">org.openide.util.Lookup</a></code> is NetBeans' form of | |
| <a href="http://www.martinfowler.com/articles/injection.html">dependency | |
| injection</a>. As with <code>DataObject</code>s and <code>FileObject</code>s, | |
| it has two common usages: | |
| <ul> | |
| <li><i>Local lookup</i> - asking an object for an instance of some interface, | |
| as we saw above with <code>Node.getLookup().lookup (SomeClass.class)</code> | |
| </li> | |
| <li><i>Global lookup</i> - services - often singleton instances of some | |
| class - can be registered into the <i>default lookup</i>. </li> | |
| </ul> | |
| <h3>The Default Lookup</h3> | |
| The default lookup is an instance of <code>Lookup</code> returned by | |
| calling <code>Lookup.getDefault()</code>. The OpenAPIs define a number of | |
| abstract service classes which allow you to get an instance of some object | |
| that is of general use - for example, <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/ErrorManager.html">org.openide.ErrorManager</a></code>, | |
| used to log errors and exceptions, | |
| or <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-dialogs/org/openide/DialogDisplayer.html">org.openide.DialogDisplayer</a></code>, which displays dialogs to | |
| the user. These are typically things that there only needs to be one of | |
| in the system, so they are effectively singleton objects. To get an | |
| instance of <code>ErrorManager</code>, you could do as follows: | |
| <pre class="examplecode"> | |
| ErrorManager err = (ErrorManager) Lookup.getDefault().lookup (ErrorManager.class); | |
| err.log ("log message"); | |
| </pre> | |
| In practice this code is a little clunky to ask people to write all the time, | |
| so most such abstract classes will have their own method <code>getDefault()</code> | |
| implemented as: | |
| <pre class="examplecode"> | |
| public abstract class MyService { | |
| public static MyService getDefault() { | |
| MyService result = (MyService) Lookup.getDefault().lookup (MyService.class); | |
| if (result == null) { | |
| result = new TrivialImplementationOfMyService(); | |
| } | |
| return result; | |
| } | |
| public abstract void doSomething (...); | |
| } | |
| </pre> | |
| Modules can register their own objects into the default lookup in one of two | |
| ways - via the Java <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Provider%20Configuration%20File"> | |
| provider extension mechanism</a> - putting a file into the <code>META-INF/services</code> | |
| directory of their module jar, or by putting a <code>.instance</code> file in | |
| the <code>Services</code> folder of the System Filesystem. | |
| <p> | |
| The preferred mechanism is the provider extension mechanism, and doing this | |
| is extremely simple: To provide your own implementation of ErrorManager, | |
| for example, simply create two folders under the <code>src/</code> folder | |
| of your module: <code>META-INF/services</code>. In the <code>services/</code> | |
| folder, put a file called <code>org.openide.ErrorManager</code>. That | |
| file will contain one line of text - the name of the class in your module | |
| that should be used - e.g. <code>com.mymodule.MyLog4JErrorManager</code>. | |
| <p> | |
| While we won't go into this in detail here, it is also possible to register | |
| multiple instances of an interface into the default lookup, retrieve | |
| all of them and even listen for changes on the result of that query. | |
| <p> | |
| A very thorough discussion of <code>Lookup</code> can be found | |
| <a href="http://openide.netbeans.org/lookup/">here</a>. | |
| <h2>Summary</h2> | |
| The salient points to remember are: | |
| <ul> | |
| <li>FileObjects wrap files (and sometimes other things)</li> | |
| <li>DataObjects wrap FileObjects and understand what's in a file</li> | |
| <li>You typically don't call methods on a DataObject, you ask it for objects via <code>getCookie()</code></li> | |
| <li>Configuration information is just another filesystem you can get DataObjects out of</li> | |
| <li>Nodes wrap DataObjects and provide human-displayable information - actions, icons, names</li> | |
| <li>Nodes are a presentation layer, not the place to put lots of logic</li> | |
| <li>Lookup is how you get globally registered services</li> | |
| <li>Lookup is also how you ask individual objects (Nodes, DataObjects, Projects) for the | |
| objects that do real work</li> | |
| </ul> | |
| <h2>Interconverting between Files, DataObjects, FileObjects and Nodes</h2> | |
| Very often you may be integrating an external tool that wants to be passed | |
| instances of <code>java.io.File</code>; also there are many cases where you need to interconvert between | |
| the various types NetBeans offers which in some way or other represent files. | |
| Here are the typical ways to interconvert between all of the above: | |
| <pre class="examplecode"> | |
| <font color="gray">Find a file on disk</font> | |
| FileObject f = Repository.getDefault().getDefaultFilesystem().getRoot().getFileObject("some/folder/someFile.txt"); | |
| <font color="gray"> or if something passes you a File...</font> | |
| FileObject f = FileUtil.toFileObject (new File("some/folder/someFile.txt")) | |
| <font color="gray">Turn a FileObject into a File (may fail for virtual filesystems)</font> | |
| File f = FileUtil.toFile (someFileObject) | |
| <font color="gray">Get the DataObject for a FileObject</font> | |
| DataObject obj = DataObject.find (someFileObject) | |
| <font color="gray">Get the FileObject a DataObject represents</font> | |
| FileObject file = someDataObject.getPrimaryFile() | |
| <font color="gray">Get the Node that represents a FileObject</font> | |
| Node n = someDataObject.getNodeDelegate() | |
| <font color="gray">Get the DataObject a Node represents (if any)</font> | |
| DataObject obj = (DataObject) someNode.getLookup().lookup(DataObject.class) | |
| </pre> | |
| <h2>Other Things Worth Mentioning...</h2> | |
| Below we go through two other critical pieces of NetBeans APIs which complete | |
| the basic picture of things modules typically interact with; they don't have | |
| the type of dual-use issues that the previous topics do, but are included for | |
| completeness. | |
| <p> | |
| <h3>Explorer Views</h3> | |
| <code>Node</code>s provide a hierarchy of objects; the Explorer API provides | |
| Swing UI components that display a <code>Node</code> and its children. There | |
| are a large variety of Explorer view classes which can variously represent a | |
| hierarchy of <code>Node</code>s as a <code>JList</code>, a <code>JMenu</code>, | |
| a <code>JComboBox</code>, a <code>JTree</code>, | |
| a JTable and more. Typically when you want to display some hierarchical data | |
| structure in NetBeans, you locate or implement the appropriate Node, create | |
| an appropriate Explorer component for it, and set the Explorer view's root | |
| node to be the node you want to display. | |
| <p> | |
| In older versions of NetBeans, the place where the Files and Projects tabs | |
| live was a separate window with the title "Explorer" - you will see | |
| the phrase "open in the Explorer" in older documentation. | |
| <p> | |
| <h3>The Window System</h3> | |
| The API of the Window System is found in <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/package-summary.html">org.openide.windows</a></code>. A | |
| basic overview is that in NetBeans, you don't deal with <code>JFrame</code>s or | |
| <code>JDialog</code>s - rather, you supply components which are displayed, | |
| and NetBeans window management system decides where and how they appear in | |
| terms of top-level frames. The main thing to know is that all components in | |
| NetBeans are subclasses or usages of <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.html">org.openide.windows.TopComponent</a></code>. | |
| <code>TopComponent</code> has relatively self-explanatory methods such as | |
| <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.html#open()">open()</a></code> and <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.html#requestActive()">requestActive()</a></code>. <code>TopComponent</code>s | |
| live in <i>docking modes</i> (the somewhat confusingly named | |
| <code><a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/Mode.html">org.openide.windows.Mode</a></code>). A <code>Mode</code> is a container for | |
| multiple <code>TopComponents</code> - a thing that has Tabs. <code>Mode</code> | |
| itself is not a GUI component, it is an abstract class that acts as a controller. | |
| <p> | |
| <code>TopComponents</code> can be instantiated and opened on the fly, but typically | |
| a module installs its UI components via several XML files inside its jar file and | |
| pointers to those files in the module's XML layer file. Fairly comprehensive | |
| examples of usage can be found in the NetBeans source base in | |
| <code>platform/samples/window-system-*/</code>. | |
| <h2>When You're Wondering Where Something is Implemented</h2> | |
| Sometimes you just want to go read the code - but it's a jungle of jars out there. | |
| Here are some of the things people often want to track down - the locations | |
| are the actual directories in a checkout of NetBeans sources: | |
| <ul> | |
| <li><b>Where are the standard menus defined?</b> - core/ui</li> | |
| <li><b>Where is dialog and windowing handled?</b> - core/windows</li> | |
| <li><b>Where is the tab control NetBeans uses for tabs?</b> - core/swing/tabcontrol</li> | |
| <li><b>What sets the fonts for NetBeans?</b> - core/swing/plaf</li> | |
| </ul> | |
| <br /> | |
| <br> | |
| <div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&subject=Feedback:%20NetBeans%20APIs%20in%20a%20Nutshell">Send Us Your Feedback</a></div> | |
| <br style="clear:both;" /> | |
| </body> | |
| </html> |