When the method return value is important value to check (or the only effect the method has) the method can be annotated with CheckReturnValue. Annotation servers as documentation as well as a hint for static code analysis.
Method annotated with
CheckForNull
may return null
value. Annotation servers as documentation
as well as a hint for static code analysis.
When the field, parameter, local variable or return value of the method
must not be null
the
NonNull
annotation can be used to clearly express this. It servers as documentation
as well as a hint for static code analysis.
Field, parameter or local variable annotated with
NullAllowed
can contain null
value so null
check should
occur before any dereference. Annotation servers as documentation
as well as a hint for static code analysis.
Annotation
NullUnknown
complementing other nullness annotations servers for cases where
the element may or may not be null
under certain
defined circumstances (usage).
When the analysis tool report false warning it is possible to use SuppressWarning annotation to suppress the warning.
There are 3 types of progress indication:
The default location of the progress indication is the status bar which aggregates all tasks running in the IDE that show progress. However it's possible to exclude the task from the default location and show the progress in one's custom dialog component. In such a case the same task should not appear in the status line component as well.
It's possible to request cancelling the task from status line progress aggregator if the task allows cancelling.
Progress tasks that get started as a result of explicit user action takes precedence in the status line docked component over tasks that are triggered by the system. (say filesystem refresh for example)
The most common usecase of the API looks like this:
ProgressHandle handle = ProgressHandleFactory.creatHandle("My custom task"); ... // we have 100 workunits // at this point the task appears in status bar. handle.start(100); ... handle.progress(10); ... handle.progress("half way through", 50); ... handle.progress(99); // at this point the task is finished and removed from status bar // it's not realy necessary to count all the way to the limit, finish can be called earlier. // however it has to be called at the end of the processing. handle.finish();
In case your usage of the API
then you should consider using the aggregating version of APIs which is similar to the simple APIs but has distinctive differences and additions that allow for more complex scenarios.
It allows to compose the progress bar from 1+ independent sources, all sharing proportional piece of the progress bar. Additionally you can monitor the task's overall progress from one central place and possibly add more contributing sources of the progress during processing.
// let's have a factory for client code that performs some part of the job to be done.. Lookup.Result res = Lookup.getDefault().lookup(new LookupTemplate(MyWorkerFactory.class)); Iterator it = res.allInstances().iterator(); ProgressContributor[] contribs = new ProgressContributor[res.allInstances().size()]; int i = 0; while (it.hasNext()) { MyWorkerFactory prov = (MyWorkerFactory)it.next(); contribs[i] = AggregateProgressFactory.createProgressContributor("Module X contribution"); MyWorker worker = prov.createWorker(contribs[i]); //... snip ... do something with the worker.. i = i + 1; } AggregateProgressHandle handle = AggregateProgressFactory.createHandle("My Task", contribs, null, null); // non-cancellable and with out output link. // calling start() at the time when the actual long running task starts processing handle.start("here we go"); // ...snip... // now the individual MyWorker instances log their progress. // possibly in other threads too.. // ... snip... // if (myConditionThatSpawnsAnotherContributor()) { ProgressContributor cont = AggregateProgressFactory.createProgressContributor("Additional exceptional contribution"); handle.addContributor(cont); // ... snip ... } // the task is finished when all the ProgressContributors finish..
See documentation for complete set of use-cases.
UpdateUnit
which describes all instances of unit, e.g. installation in IDE,
all its available updates, optionlly its backup instance.
UpdateUnit
can represent either a feature (e.g. group
of modules), a single module or a localization.
Proposed usage of API: Call List<UpdateUnit> UpdateManager.getDefault().getUpdateUnits()
List<UpdateUnit> UpdateManager.getDefault().getUpdateUnits(UpdateStyle style)
List<UpdateUnit> UpdateManager.getDefault().getUpdateUnits(UpdateStyle style)
and filter units which haven't been installed yet.
UpdateUnit
s
which are applicable to active IDE. UpdateManager
will
search all available UpdateUnit
given attribute.
UpdateUnit
by module's code name and finds UpdateElement
what fits the required version.UpdateElement
which wants to install.OperationContainer
for install, e.g. OperationContainer.createForInstall
OperationContainer.add(UpdateElement)
and gets OperationInfo
for that operation.OperationInfo.getRequiredElements()
OperationInfo.getBrokenDependency()
Note: if there are some broken dependencies then operation cannot continue.OperationContainer.doOperation()
UpdateElement
which wants to uninstall.OperationContainer
for uninstall, e.g. OperationContainer.createForUninstall
OperationContainer.add(UpdateElement)
and gets OperationInfo
for that operation.OperationInfo.getRequiredElements()
OperationContainer.doOperation()
UpdateElement
which wants to uninstall.OperationContainer
for disable, e.g. OperationContainer.createForDisable
OperationContainer.add(UpdateElement)
and gets OperationInfo
for that operation.OperationInfo.getRequiredElements()
OperationContainer.doOperation()
UpdateElement
which wants to uninstall.OperationContainer
for enable, e.g. OperationContainer.createForEnable
OperationContainer.add(UpdateElement)
and gets OperationInfo
for that operation.OperationInfo.getRequiredElements()
OperationContainer.doOperation()
OperationContainer
and OperationInfo
identifies some problems,
i.e. broken dependencies, needs to install more units, the operation causes disable some
other modules and so on. The client can use this information to consult these with end-user.
UpdateUnitProvider
.
Proposed usage of API: Call UpdateUnitProviderFactory.getUpdateUnitProviders()
UpdateUnitProviderFactory.create()
which creates and registered
new one subscription in the system and will be used from that time in the future.
UpdateUnitProviderFactory.setEnable(UpdateUnitProvider, boolean)
.
UpdateUnitProviderFactory.remove(Id)
.
UpdateUnitProvider.refresh()
.
org.openide.util.Lookup
allowing to provide
the registered instances as a Lookup.Result
allowing to listen for changes (e.g. caused by the module enabling/disabling).
class MimeLookup extends Lookup
containing
static MimeLookup getMimeLookup(String mimeType)
.
static Lookup getLookup(MimePath mimePath)
method in MimeLookup
.
org.netbeans.spi.editor.fold.FoldManagerFactory
classes).
org.netbeans.spi.editor.completion.CompletionProvider
classes).
javax.swing.Action
classes or names of actions
(i.e. value of Action.NAME attribute) present in editor kit e.g. "goto-source").
org.netbeans.editor.SideBarFactory
classes).
org.netbeans.lib.editor.hyperlink.spi.HyperlinkProvider
classes).
org.netbeans.lib.editor.codetemplates.spi.CodeTemplateProcessorFactory
classes).
org.netbeans.modules.editor.hints.spi.HintsProvider
classes).
MimeLookup lookup = MimeLookup.getMimeLookup("text/x-java");
can be used for getting the mime specific lookup. Having this we can lookup class
or template:
Object obj = lookup.lookup(LookedUpClass.class);
or
Lookup.Result result = lookup.lookup(new Lookup.Template(LookedUpClass.class));
MimePath scriptletPath = MimePath.parse("text/x-jsp/text/x-java"); Lookup lookup = MimeLookup.getLookup(scriptletPath);
MimeLookup
. Implementation of MimeLookupInitializer
should be created and
registered to default lookup via META-INF/services
registration.
For details, please look at the simplified
TestMimeLookupInitializer
in mimelookup/test/unit
or LayerMimeLookupInitializer
.
Usage of MimeLookupInitializer is deprecated, please use MimeDataProvider instead in similar way
Client can install new panel to Options Dialog - see JavaDoc for OptionsCategory class.
Client can install new panel to Advanced Options Panel - see JavaDoc for AdvancedOption class.
The typical client of Print module can be any tool to print custom data.
The simple way to enable printing for a custom data is:
If the data is a Swing component which extends javax.swing.JComponent
and shown in a org.openide.windows.TopComponent
, the key "print.printable"
(PrintManager.PRINT_PRINTABLE) with value "Boolean.TRUE"
in the component must be set as a client property. See example:
public class MyComponent extends javax.swing.JComponent { public MyComponent() { ... putClientProperty("print.printable", Boolean.TRUE); // NOI18N } ... }
Particular use cases are enumerated in the Javadoc for each query API. Usage consists of simple static method calls. Potentially a wide variety of modules could use these queries; implementations are typically registered by project type providers, though also by Java library and platform implementations.
CommandLine.getDefault().process(args)
.
getopts
supports short form of options - e.g. a dash
followed with one letter - or long form using two dashes followed with a word.
Moreover the long form is optimized for abbrevations. If there are no
conflicts between multiple options, then one can only use double dash followed
with a prefix of a long option.
One can create an Option
by calling any of its factory methods
(like
withoutArgument)
and provider char
for the one letter option and/or string for
the long getopts option.
--. If these characters do appear on the command line, the rest of it is treated as extra arguments and not processed. The sendopts infrastructure supports this as well.
META-INF/services/org.netbeans.spi.sendopts.OptionProcessor
in its
JAR file.
Q: How shall one write an
OptionProcessor
that recognizes set of basic options, however contains one open slot
?
The processor wants other modules to provide recognizers for that slot
and wants to communicate with them. For example, by default the processor
recognizes option --channel <name_of_the_channel>
which describes a source of data, and stores such data into a sink
.
There can be multiple sinks - discard the output, save it to file, show
it on stdout, stream it to network. The processor itself can handle the
copying of data, but does not itself know all the possible sink
types.
To implement
OptionProcessor
like this one shall define an additional interface to communicate with
the sink
providers:
package my.module; public interface SinkProvider { /** gets the option (even composite) that this sink needs on command line */ public Option getOption(); /** processes the options and creates a "sink" */ public OutputStream createSink(Env env, Map<Option,String[]> values) throws CommandException; }
Other modules would then registered implementations of this
interface in the
META-INF/services/my.module.SinkProvider
files.
The
OptionProcessor
itself would just look all the implementations up, queried for
the sinks
, and then did the copying:
class CopyingProvider extends OptionProvider {
public Option getOption() {
List<Option> l = ...;
for (SinkProvider sp : Lookup.getDefault().lookupAll(SinkProvider.class)) {
l.add(sp.getOption());
}
// we need only one provider to be present
Option oneOfSinks = OptionGroups.oneOf(l.toArray(new Option[0]));
// our channel option
Option channel = ...;
// the channel option needs to be present as well as a sink
return OptionGroups.allOf(channel, oneOfSinks);
}
public void process(Env env, Map<Option,String[]> values) throws CommandException {
OutputStream os = null;
for (SinkProvider sp : Lookup.getDefault().lookupAll(SinkProvider.class)) {
if (values.containsKey(sp.getOption())) {
os = sp.createSink(env, values);
break;
}
}
if (os == null) {
throw CommandException.exitCode(2);
}
// process the channel option and
// handle the copying to the sink os
}
}
Another possible approach how to allow sharing of one option between multiple modules is to expose the option definition and its handling code as an interface to other modules, and then let the modules to write their own OptionProcessors. Necessary condition is that each of the processor is uniquely identified by some additional option, so when the shared option appears the infrastructure knows which processor to delegate to. This is demonstrated in the SharedOptionTest which basically does the following:
/** the shared option, part of an interface of some module */ public static final Option SHARED = ...; /** finds value(s) associated with the SHARED option and * creates a JPanel based on them */ public static JPanel getSharedPanel(Map<Option,String[]> args) { ... }
Then each module who wishes to reuse the SHARED option and the factory method that knows how to process their values for their own processing can just:
public static final class ShowDialog extends OptionProcessor { private static final Option DIALOG = Option.withoutArgument('d', "dialog"); protected Set<Option> getOptions() { // the following says that this processor should be invoked // everytime --dialog appears on command line, if the SHARED // option is there, then this processor wants to consume it // as well... return Collections.singleton(Option.allOf(DIALOG, Option.anyOf(SHARED))); } protected void process(Env env, Map<Option, String[]> optionValues) throws CommandException { JPanel p = getSharedPanel(optionvalues); if (p == null) { // show empty dialog } else { // show some dialog containing the panel p } } }
The other modules are then free to write other processors refering to
SHARED
, for example one can write ShowFrame
that does the same, just shows the panel in a frame, etc. The infrastructure
guarantees that the exactly one provider which matches the command
line options is called.
--open X.java Y.java Z.txt X.java Y.java --open Z.txtif the option
openhandles
extra arguments. The sendopts infrastructure must distinquish between them and pass the non-option ones to the only one handler (active because it processed an option) that knowns how to parse them. It is an error if more than one or no handler expresses an interest in extra arguments and those are given. One can register such option by using the
Option.additionalArgument
factory method.
CommandLine.getDefault().parse
methods taking additional arguments like input and output streams.
This gets transfered to providers as an
Env
argument of their methods.
Option.defaultArguments
factory method.
class PP extends OptionProcessor { private static Option tune = Option.requiredArgument(Option.NO_SHORT_NAME, "tune"); private static Option stream = Option.requiredArgument(Option.NO_SHORT_NAME, "stream"); public Set<Option> getOptions() { return Collections.singleton( OptionGroups.allOf(tune, stream) ); } public void process(Env env, Map>Option,String[]> values) throws CommandException { String freq = values.get(tune)[0]; String output = values.get(stream)[0]; // XXX handle what is needed here } }When the two options are registered and command line like
--tune 91.9 --stream radio1.mp3is being processed, the
PP
's process
method is going to get
called with values 91.9and
radio1.mp3.
Option freq = Option.requiredArgument(Option.NO_SHORT_NAME, "tune"); Option station = Option.requiredArgument(Option.NO_SHORT_NAME, "station"); Option tune = OptionGroups.oneOf(freq, station);The option
tune
then signals that just one of the station or
freq options can appear and that they both are replaceable.
SearchProvider.evaluate
method
like suggested in its javadoc.
<folder name="QuickSearch"> <folder name="Category1_ID"> <attr name="position" intvalue="300"/> <file name="org-netbeans-module1-package1-Provider1Impl.instance"/> </folder> <folder name="Category2_ID"> <!--Attribute for localization - provide localized display name of category!--> <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.yourmodule.YourBundle"/> <!--Attribute for command prefix - used to narrow search to this category only!--> <attr name="command" stringvalue="p"/> <!--Attribute for category ordering!--> <attr name="position" intvalue="200"/> <!--Note that multiple providers can contribute to one category!--> <file name="org-netbeans-module2-package2-Provider2Impl.instance"/> <file name="org-netbeans-module2-package3-Provider3Impl.instance"/> </folder> </folder>Syntax explanation:
Quick Search UI shows search results divided into visually separeted sections,
called categories. Several SearchProvider
implementations may decide to display
their results in one shared category of results in Quick Search UI.
In order to share category, module writers have to agree on shared category and its properties, especially its name. It means that all providers (possibly in different NetBeans modules) need to be registered under the same folder, as shown below:
SharedCategory
such as
display name, position and command prefix.
<folder name="QuickSearch"> <folder name="SharedCategory"> <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.yourmodule.YourBundle"/> <attr name="command" stringvalue="p"/> <attr name="position" intvalue="200"/> <file name="org-netbeans-module1-package1-Provider1Impl.instance"> <attr name="position" intvalue="300"/> </file> </folder> </folder>
SharedCategory
,
as they were already defined by Provider 1.
Note that module dependency on the module of Provider 1 is needed
to ensure that SharedCategory
is fully defined.
<folder name="QuickSearch"> <folder name="SharedCategory"> <file name="org-netbeans-module2-package2-Provider2Impl.instance"/> <attr name="position" intvalue="200"/> </file> </folder> </folder>
<folder name="QuickSearch"> <folder name="SharedCategory"> <file name="org-netbeans-module2-package3-Provider3Impl.instance"/> <attr name="position" intvalue="100"/> </file> </folder> </folder>
FirstCategory
to be first, and SecongCategory
to be second :),
which means that FirstCategory
and its results will be
displayed above Secondcategory
in QuickSearch results window.
<folder name="QuickSearch"> <folder name="SecondCategory"> <attr name="position" intvalue="300"/> ... </folder> <folder name="FirstCategory"> <attr name="position" intvalue="200"/> ... </folder> </folder>
spi.quicksearch
module, so its functionality is automatically always available by default.
However, if your module wants to disable "Recent Searches" or any other
category, follow the steps below:
spi,quicksearch
,
on which you probably already depend.
<folder name="QuickSearch"> <folder name="Recent_hidden"> </folder> </folder>"Recent" is a name of category for "Recent Searches" provider and by appending "_hidden" suffix you are telling system to "hide" it. This technique can be used also to disable invidual search providers.
<folder name="Toolbars"> <folder name="QuickSearch"> <attr name="SystemFileSystem.localizingBundle" stringvalue="com.myapp.mymodule.MyBundle"/> <file name="org-netbeans-modules-quicksearch-QuickSearchAction.shadow"> <attr name="originalFile" stringvalue="Actions/Edit/org-netbeans-modules-quicksearch-QuickSearchAction.instance"/> </file> </folder> </folder>
com.myapp.mymodule.MyBundle
in the xml registration
above with path to your properties file, in which you'll define
localized name of Quick Search toolbar:
Toolbars/QuickSearch=Quick Search
By default, providers for searching in actions and recent searches will be enabled. Web search provider is disabled by default, see use case below for info how to turn it on.
<folder name="QuickSearch"> <folder name="WebSearch"> <!--Attribute for localization - provide localized display name of category!--> <attr name="SystemFileSystem.localizingBundle" stringvalue="com.myapp.mymodule.MyBundle"/> <!--Attribute for command prefix - used to narrow search to this category only!--> <attr name="command" stringvalue="g"/> <!--Attribute for category ordering!--> <attr name="position" intvalue="200"/> <!--Note that multiple providers can contribute to one category!--> <file name="org-netbeans-modules-quicksearch-web-WebQuickSearchProviderImpl.instance"/> </folder> </folder>You can also add branding for
org.netbeans.modules.quicksearch.web
Bundle to restrict
the search to a particular site only:
quicksearch.web.site=mywebsite.comAnd you can also restrict the search to some parts your website only:
quicksearch.web.url_patterns=mywebsite.com/docs,mywebsite.com/files/tutorials
In order for the action to show up in Keyboards Shortcut dialog you need the action defined in the layer file under "Actions" folder and have the shortcut defined there under "Keymaps/<Profile Name>" linking to your action.
<folder name="Actions" > <folder name="Window"> <file name="org-netbeans-core-actions-PreviousViewCallbackAction.instance"/> </folder> </folder> <folder name="Keymaps"> <folder name="NetBeans"> <file name="S-A-Left.shadow"> <attr name="originalFile" stringvalue="Actions/Window/org-netbeans-core-actions-PreviousViewCallbackAction.instance"/> </file> </folder> </folder>
The mentioned Action has to be a subclass of org.openide.util.actions.CallbackSystemAction
. It does not necessarily has to
perform the action, it's just a placeholder for linking the shortcut. You might want to override it's getActionMapKey()
and give it a
reasonable key.
The actual action that does the work in your component (preferably a simple Swing javax.swing.Action
)
is to be put into your TopComponent
's ActionMap
. The key for the ActionMap
has to match the key defined in the global action's getActionMapKey()
method.
getActionMap().put("PreviousViewAction", new MyPreviousTabAction());
This way even actions from multiple TopComponent
s with the same gesture (eg. "switch to next tab") can share the same configurable shortcut.
Note: Don't define your action's shortcut and don't put it into any of the TopComponent
's
javax.swing.InputMap
. Otherwise the component would not pick up the changed shortcut from the
global context.
XXX no answer for arch-usecases
A: You can change the format of your wizard's title by WizardDescriptor.setTitleFormat(MessageFormat format) and rid of 'wizard' word in the default wizard's title.
XXX no answer for arch-usecases
Many of the usecases are described at the overall documentation, in a way how to register a mime type. Some of the additional usecases are covered here.
Since version 7.1 there is a way to change the content of system file system in a dynamic way. As system file systems contains various definitions (in NetBeans Platform menus, toolbars, layout of windows, etc.) it de-facto allows global change to these settings for example when user logs into some system.
First thing to do is to create an implementation of filesystem. It can be created either from scratch, or by subclassing AbstractFileSystem, or MultiFileSystem. In this example we will subclass the MultiFileSystem:
public class LoginFileSystem extends MultiFileSystem { private static LoginFileSystem INSTANCE; public LoginFileSystem() { // let's create the filesystem empty, because the user // is not yet logged in INSTANCE = this; } public static void assignURL(URL u) throws SAXException { INSTANCE.setDelegates(new XMLFileSystem(u)); } }
It is necessary to register this instance in lookup by creating the file:
META-INF/services/org.openide.filesystems.FileSystem
with a single line containing the full
name of your filesystem - e.g. your.module.LoginFileSystem
.
When done, the system will find out your registration of the filesystem
on startup and will merge the content of the filesystem into the
default system file system. You can show a dialog letting the user
to log in to some system anytime later, and when the user is successfully
in, just call LoginFileSystem.assignURL(url)
where the
URL is an XML file in the same format
as used for regular layer files inside of many NetBeans modules.
The system will notice the change in the content and notify all the
config file listeners accordingly.
Of course, instead of XMLFileSystem you can use for example memory file system, or any other you write yourself.
Since version 7.23 you can use Boolean.TRUE
from call
to fs.getRoot().getAttribute("fallback")
you can place your filesystem behind all layers provided
by standard modules and form a kind of fallback
There is an SPI but additional implementations are not expected. The API is most important.
Simple usage example:
InputOutput io = IOProvider.getDefault().getIO("My Window", true); io.select(); OutputWriter w = io.getOut(); w.println("Line of plain text."); OutputListener listener = new OutputListener() { public void outputLineAction(OutputEvent ev) { StatusDisplayer.getDefault().setStatusText("Hyperlink clicked!"); } public void outputLineSelected(OutputEvent ev) { // Let's not do anything special. } public void outputLineCleared(OutputEvent ev) { // Leave it blank, no state to remove. } }; w.println("Line of hyperlinked text.", listener, true);
Often many people require ability to create a "clever" template - e.g. write piece of simple text and at the time of its processing do some advanced changes to it using either scripting or templating languages.
This traditionally used to be a bit complicated task, however since
version 6.1 there are new interfaces
Smart Templating Quick How-To
First of all create a file in your module layer located somewhere
under the Templates/
folder. Make it a template by
adding <attr name="template" boolvalue="true"/>. Associate
this template with a scripting language, for example by
<attr name="javax.script.ScriptEngine" stringvalue="freemarker"/>.
Now make sure that the scripting language integration is also available
by requesting a token in standard format, for freemarker just put
OpenIDE-Module-Needs: javax.script.ScriptEngine.freemarker
in your manifest. Also create a runtime dependency
on
org.netbeans.modules.templates
-
the module providing the templates/scripting languages integration.
the module providing the templates/scripting languages integration.
This tells the NetBeans module system that a
module providing integration with such scripting engine has to be
enabled. Now you can use regular script language tags inside of
your template file. When you write your instantiate
method in your wizard, you can create a Map<String,Object> and
fill it with parameters collected from your wizard and then pass it
to
createFromTemplate(targetFolder, targetName, mapWithParameters)
. This will invoke the scripting language and make the
mapWithParameters
values available to it. Beyond this
there is few standard parameters predefined including name
, user
, date
, time
, etc.
and also additional parameters are collected from all registered
CreateFromTemplateAttributesProviders.
Moreover there is a built in support for scripting languages in
the standard NetBeans IDE. If a template is annotated with
ScriptEngine
interface or
a String
name of the engine that is then used to
search for it in the javax.script.ScriptEngineManager
.
Usually the freemarker engine is the one that is
supported by the NetBeans IDE - if your module wants to use it
then include a token dependency OpenIDE-Module-Needs: javax.script.ScriptEngine.freemarker
in your manifest file (also accessible through project customizer GUI)
to indicate to the system that you need it.
String
representing the current day like 23. 3. 2007
String
the current time like 17:18:30
String
the file encoding of the template instance
Other properties can indeed be provided by
CreateFromTemplateAttributesProviders.
After processing, the output is also sent to appropriate
org.openide.text.IndentEngine
associated
with the mime type of the template, for formating.
Loaders/folder/any/Actions
so if any module wishes
to extend, hide or reorder some of them it can just register its actions there.<folder name="Loaders" > <folder name="folder" > <folder name="any" > <folder name="Actions" > <file name="org-mymodule-MyAction.instance" > <attr name="instanceCreate" stringvalue="org.mymodule.MyAction" /> </file> </folder> </folder> </folder> </folder>As described in general actions registration tutorial. This functionality is available since version 5.0 of the loaders module. Please use
OpenIDE-Module-Module-Dependencies: org.openide.loaders > 5.0
in your
module dependencies.
In version 5.8 all the standard loaders were changed to read actions from layer:
Loaders/text/xml/Actions
Loaders/content/unknown/Actions
Loaders/application/x-nbsettings/Actions
DataObject
s produced by your DataLoader
and you
are either using DataNode
or its subclass, you can just override
protected String actionsContext()
method to return non-null
location of context in layers from where to read the actions.
The usual value should match Loaders/mime/type/Actions
scheme,
for example java is using Loaders/text/x-java/Actions
, but
the name can be arbitrary.
This functionality is available since version 5.0 of the loaders module. Please use
OpenIDE-Module-Module-Dependencies: org.openide.loaders > 5.0
in your
module dependencies.
XXX no answer for arch-usecases
How can I specify (in the xml, or programmatically) that this service should only be added to the Lookup if the platform is Windows? >
In general there are three ways to achieve this.It is possible to write a specific module and enable it only on windows. See os specific modules documentation. Then you can put a registration of your instance into your module's META-INF/services directory and it will be available only on Windows.
Another possibility that does not require new module, but which executes
a code on startup (which may have performance implications) is to use methodvalue
attribute. Register your instance in layer using your-Object.instance
file
as described at
services
documentation and in your factory method either return the instance
your want or null
depending on result of
Utilities.isWindows() call.
In some cases, the interface for which you will register an implementation permits a
no-operation semantics. For example, InstalledFileLocator.locate(...)
can
return a valid File
, or null. You could always register an
InstalledFileLocator
instance yet disable it on non-Windows platforms
(always returning null).
Q: I have more modules one of them providing the core functionality and few more that wish to extend it. What is the right way to do it? How does the Netbeans platform declare such extension point?
Start with declaring an extension interface in your
core module and put it into the module's public packages. Imagine
for example that the core module is in JAR file org-my-netbeans-coremodule.jar
and already contains in manifests line like
OpenIDE-Module: org.my.netbeans.coremodule/1
and wants
to display various tips of the day provided by other modules and thus defines:
package org.my.netbeans.coremodule; public interface TipsOfTheDayProvider { public String provideTipOfTheDay (); }
And in its manifest adds line
OpenIDE-Module-Public-Packages: org.my.netbeans.coremodule.*
to specify that this package contains exported API and shall be
accessible to other modules.
When the core module is about to display the tip of the day it can ask
the system for all registered instances of the TipsOfTheDayProvider
,
randomly select one of them:
import java.util.Collection; import java.util.Collections; import org.openide.util.Lookup; Lookup.Result result = Lookup.getDefault ().lookup (new Lookup.Template (TipsOfTheDayProvider.class)); Collection c = result.allInstances (); Collections.shuffle (c); TipsOfTheDayProvider selected = (TipsOfTheDayProvider)c.iterator ().next ();
and then display the tip. Simple, trivial, just by the usage of
Lookup interface once
creates a registry that other modules can enhance. But such enhancing
of course requires work on the other side. Each module that would like
to register its TipsOfTheDayProvider
needs to depend on the
core module - add
OpenIDE-Module-Module-Dependencies: org.my.netbeans.coremodule/1
into its manifest and write a class with its own implementation of the
provider:
package org.my.netbeans.extramodule; class ExtraTip implements TipsOfTheDayProvider { public String provideTipOfTheDay () { return "Do you know that in order to write extension point you should use Lookup?"; } }
Then, the only necessary thing is to register such class by using the
J2SE standard META-INF/services/org.my.netbeans.coremodule.TipsOfTheDayProvider
in the module JAR containing just one line:
org.my.netbeans.extramodule.ExtraTip
and your modules are now ready to communicate using your own extension point.
If you are interested in logging from inside your module, or in writing your own log handler or in configuring the whole system, then best place to start is the NetBeans logging guide.
protected void componentDeactivated () { // close window group containing propsheet, but only if we're // selecting a different kind of TC in the same mode boolean closeGroup = true; Mode curMode = WindowManager.getDefault().findMode(this); TopComponent selected = curMode.getSelectedTopComponent(); if (selected != null && selected instanceof FooTopComponent) closeGroup = false; if (closeGroup) { TopComponentGroup group = WindowManager.getDefault().findTopComponentGroup(TC_GROUP); if (group != null) { group.close(); } } }