Writing POV-Ray Support for NetBeans V - Creating an API
Tim Boudreau
7 June 2006
Feedback
This is a DRAFT!
This is a continuation of the tutorial for building POV-Ray support for
NetBeans. If you have not read the
first
or
second,
third
and
fourth
parts of this tutorial, you may want to start there.
Creating the API
As discussed when we
designed POV-Ray support,
we will need an API - there will be some intercommunication between
POV-Ray files and the project. In particular, we will need some interfaces:
MainFileProvider - find the main file of a project -
the one to render when the whole file is built, and allow a POV-Ray
scene file node to find out if it is the main file (so it can
bold-face its display name).
RendererService - an API a POV-Ray file node can call
to ask that it be rendered as an image
ViewService - an API a POV-Ray file can call to ask
that its associated image be shown in the IDE, rendering it if necessary.
For this, we will actually create a separate module. That way we avoid a
dependency between file support and project support - either module will be
loadable by itself as long as the module providing the API is there. Also,
this helps in delivering updates - the API presumably will remain stable,
and psychologically having it in a separate module helps the developer to be
aware when they are making API changes. It also means that a completely
different module supporting POV-Ray files could still get them rendered
via this API, and completely different project support could be provided
with no changes to the file support module.
So it's generally healthy for the codebase to do it this way.
- Create a new abstract Java class in
org.netbeans.modules.examples.api.povray
called MainFileProvider, and implement it as follows:
public abstract class MainFileProvider {
public abstract FileObject getMainFile();
public abstract void setMainFile (FileObject file);
public boolean isMainFile (FileObject file) {
return obj.equals(getMainFile());
}
}
- Create a new abstract Java class in
org.netbeans.modules.examples.api.povray
called RendererService, and implement it as follows:
public static final String PROJECT_RENDERER_KEY_PREFIX = "renderer.";
public static final String PRODUCTION_RENDERER_SETTINGS_NAME = "production";
public abstract FileObject render(FileObject scene, String propertiesName);
public abstract FileObject render (FileObject scene, Properties renderSettings);
public abstract FileObject render (FileObject scene);
public abstract FileObject render();
public abstract String[] getAvailableRendererSettingsNames();
public abstract Properties getRendererSettings (String name);
public abstract String getPreferredRendererSettingsNames();
public abstract String getDisplayName (String settingsName);
- Create a new Java interface in
org.netbeans.modules.examples.api.povray
called ViewService, and implement it as follows:
boolean isRendered (FileObject file);
boolean isUpToDate (FileObject file);
void view (FileObject file);
(if you are wondering why the first two are abstract classes instead of interfaces,
the answer is simple: In the case of MainFileProvider, it allows
us to implement isMainFile(); in the case of the other
RendererService, it is highly probably that there will be new
requirements for it in the future, and you can add methods [with some sort of
default implementation] to an abstract class semi-backward-compatibly [name
collisions with subclasses are still possible], but not to an interface.
ViewService is simple and well-defined enough that it will probably never
change).
- On the main menu, select File > New Project
- In the New Project wizard, select NetBeans Module Projects >
Module Project and click Next or press Enter
- Name the project "api" and click Next or press
Enter
- Provide the code name "org.netbeans.examples.api.povray" -
this follows the NetBeans package naming conventions that public
packages shall have the name
org.netbeans.api... to
indicate visually that they are intended to be API (and thus kept
backward compatible).
- Provide the display name "Povray API". Make sure the
Add to Module Suite radio button is checked and pointing at the
POV-Ray module suite
- Click Finish or press Enter to create the project
- Right click the newly created project in the Projects tab, and
choose Properties. Go to the API Versioning page in the dialog.
- Fill in "1" for the Major Version Number
- Click the Autoload radio button - this means this module is a
library - it will only be loaded if something else starts to
use a class from it, which is more effecient
- On the libraries page, add a new dependency on the Filesystems API
module (look for
AbstractFileObject for a fast way
to find it).
- In the editor, run Fix Imports on
MainFileProvider
and ViewService to import FileObject.
- Right click the Povray Projects project and add a dependency on
our new module - just search for one of the classes we've added.
Then do the same for the Povray File Support module, so both of
these modules can see API classes (but not each others' classes).
Using the API from PovrayDataNode
We haven't implemented the API yet, but we can set up some code that
will use it - we know we want the node for the file which is the
"main file" of our project to be shown in bold text. And
having some code that uses the API will help to test it once it is
written, which will be a bit of work.
- Right-click the Povray File Support project, choose Properties and
open the Libraries page of the Project Properties dialog
- Click the Add button, and in the dialog type "FileOwnerQuery" -
we are adding a dependency on the Projects API. FileOwnerQuery is part
of that API - a class with static methods that will return the project
(if any) which owns a given file. Our
Node will need to
look up the project it belongs to, and then query the project's Lookup
to try to find an implementation of our API classes.
- Open
org.netbeans.examples.modules.povfile.PovrayDataNode in
the code editor.
- Add the following methods to
PovrayDataNode:
private FileObject getFile() {
return getDataObject().getPrimaryFile();
}
private Object getFromProject (Class clazz) {
Object result;
Project p = FileOwnerQuery.getOwner(getFile());
if (p != null) {
result = p.getLookup().lookup (clazz);
} else {
result = null;
}
return result;
}
private boolean isMainFile() {
MainFileProvider prov = (MainFileProvider)
getFromProject (MainFileProvider.class);
boolean result;
if (prov == null) {
result = false;
} else {
FileObject myFile = getFile();
result = prov.isMainFile(myFile);
}
return result;
}
public String getHtmlDisplayName() {
return isMainFile() ? "" + getDisplayName() + "" : null;
}
What the above code does is fairly straightforward. getFile()
returns a FileObject (NetBeans
virtual
filesystem file) that this Node represents.
getFromProject tries to find the project that owns the file,
and if it finds one, queries its
Lookup,
asking it for an instance of the Class that was passed into
this method (e.g. one of the classes in the API we just defined).
isMainFile() uses the above two methods to decide if this
Node represents the "main file" of the project (the
one that should be rendered by POV-Ray if the user chooses to "build"
the project - POV-Ray supports file includes, so there may be many files in
a project, but only one master image. getHtmlDisplayName() is
where the rubber meets the road - this method will return a boldface
HTML string if this Node represents the main file.
Next Steps
In the
next section we will implement the
API we have created.