blob: 9f71d1011c2b0d3edb3125011b4b065379b05b23 [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= Writing POV-Ray Support for NetBeans VCreating an API
:jbake-type: platform_tutorial
:jbake-tags: tutorials
:jbake-status: published
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:icons: font
:experimental:
:description: Writing POV-Ray Support for NetBeans VCreating an API - Apache NetBeans
:keywords: Apache NetBeans Platform, Platform Tutorials, Writing POV-Ray Support for NetBeans VCreating an API
This is a continuation of the tutorial for building a POV-Ray rendering application on the NetBeans Platform. If you have not read the link:nbm-povray-1.html[first], link:nbm-povray-2.html[second], link:nbm-povray-3.html[third], and link:nbm-povray-4.html[fourth] parts of this tutorial, you may want to start there.
== Creating the API
As discussed when we link:nbm-povray-2.html[designed POV-Ray support], we will need an APIthere 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 projectthe 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 supporteither module will be loadable by itself as long as the module providing the API is there. Also, this helps in delivering updatesthe 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.
[start=1]
1. Right-click the application's Modules node and choose Add New, as shown below:
image::images/povray_71_ch5_pic1.png[]
This is a faster mechanism for creating new modules, compared to going to File | New Project and then clicking through to the New Module wizard.
[start=2]
1. Name the project "api" and click Next or press Enter.
[start=3]
1. 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". Click Finish or press Enter to create the project
[start=4]
1.
Right-click the Libraries node of the Povray API project and choose Add Module Dependency.
image::images/povray_71_ch5_pic2.png[]
This is a faster mechanism for setting new module dependencies, compared to going to using the Project Properties window to do so.
Add a new dependency on the File System API module (look for `FileObject` for a fast way to find it).
[start=5]
1. Create a new abstract Java class in `org.netbeans.modules.examples.api.povray` called `MainFileProvider`, and implement it as follows:
[source,java]
----
package org.netbeans.examples.api.povray;
import org.openide.filesystems.FileObject;
public abstract class MainFileProvider {
public abstract FileObject getMainFile();
public abstract void setMainFile (FileObject file);
public boolean isMainFile (FileObject file) {
return file.equals(getMainFile());
}
}
----
[start=6]
1. Create a new abstract Java class in `org.netbeans.modules.examples.api.povray` called `RendererService`, and implement it as follows:
[source,java]
----
package org.netbeans.examples.api.povray;
import java.util.Properties;
import org.openide.filesystems.FileObject;
public abstract class RendererService {
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);
}
----
[start=7]
1. Create a new Java interface in `org.netbeans.modules.examples.api.povray` called `ViewService`, and implement it as follows:
[source,java]
----
package org.netbeans.examples.api.povray;
import org.openide.filesystems.FileObject;
public interface ViewService {
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 `RendererService`, it is highly probable 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.
[start=8]
1. Right-click the Povray API project in the Projects window and choose Properties. Go to the API Versioning page in the dialog.
* Fill in "1" for the Major Version Number
*
Click the Autoload radio buttonthis means this module is a libraryit will only be loaded if something else starts to use a class from it, which is more effecient.
* In the Public Packages list, put a checkmark into the checkbox for the API package. This means the package will be available to any module that has set a dependency on this one.
The dialog should now look as follows:
image::images/povray_71_ch5_pic3.png[]
[start=9]
1. Finally, right-click the Povray Projects project and add a dependency on our new modulejust 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.
[start=1]
1. In the Povray File Support module, add a new module dependency on the Project API. You need this API because we need to use the ``FileOwnerQuery`` class. This class is part of the Project 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.
[start=2]
1.
In the ``org.netbeans.examples.modules.povfile`` package, create a new class named `PovrayDataNode`. Let it extend ``DataNode`` and create a constructor that receives our ``PovrayDataObject`` . The class should now look as follows:
[source,java]
----
package org.netbeans.examples.modules.povfile;
import org.openide.loaders.DataNode;
import org.openide.nodes.Children;
public class PovrayDataNode extends DataNode {
public PovrayDataNode(PovrayDataObject obj) {
super(obj, Children.LEAF);
}
}
----
[start=3]
1. Add the following methods to `PovrayDataNode`:
[source,xml]
----
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;
}
@Override
public String getHtmlDisplayName() {
return isMainFile() ? "<b>" + getDisplayName() + "</b>" : null;
}
----
What the above code does is fairly straightforward. `getFile()` returns a `FileObject`, which is a virtual filesystem file, that this `Node` represents. `getFromProject` tries to find the project that owns the file, and if it finds one, queries its ` link:https://netbeans.apache.org/wiki/devfaqlookup[Lookup]`, asking it for an instance of the `Class` that was passed into this method (i.e., 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 projectPOV-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 roadthis method will return a boldface HTML string if this `Node` represents the main file.
[start=4]
1. Check that the ``PovrayDataNode`` has this content:
[source,xml]
----
package org.netbeans.examples.modules.povfile;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.examples.api.povray.MainFileProvider;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataNode;
import org.openide.nodes.Children;
public class PovrayDataNode extends DataNode {
public PovrayDataNode(PovrayDataObject obj) {
super(obj, Children.LEAF);
}
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;
}
@Override
public String getHtmlDisplayName() {
return isMainFile() ? "<b>" + getDisplayName() + "</b>" : null;
}
}
----
[start=5]
1. Finally, we want to use the above ``Node`` class instead of the default ``Node`` class that the ``PovrayDataObject`` has been using thus far. Open the ``PovrayDataObject`` class and add the following method to register our new ``Node`` :
[source,java]
----
@Override
protected Node createNodeDelegate() {
return new PovrayDataNode(this);
}
----
== Next Steps
In the link:nbm-povray-6.html[next section] we will implement the API we have created. But, from the above code, you can see how the API will be used by our ``Node`` class, to determine whether a ``Node`` should be boldfaced. Here we don't need to know nor care how the API is implemented. We simply ask for the availability of the ``MainFileProvider`` and, depending on its availability, we change the display name of the ``Node`` .