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.
Future<Object> result = new Intent(Intent.ACTION_VIEW, new URI("scheme://path/")).execute(); Object value = result.get();
new Intent(Intent.ACTION_VIEW, new URI("scheme://path/")).execute(new Callback() { void success(Object result) { // use the result somehow } void failure(Exception exception) { // report the failure somehow } });
@IntentHandlerRegistration( displayName = "Show my item in MyEditor", position = 800, uriPattern = "myscheme://.*", actions = {Intent.ACTION_VIEW, Intent.ACTION_EDIT} ) public static Object handleIntent(Intent intent) { SomeType result = parseAndPerformIntentSomehow(intent); return result; }
@IntentHandlerRegistration( displayName = "Show my item in MyEditor", position = 800, uriPattern = "myscheme://.*", actions = "*" ) public static void handleIntent(final Intent intent, final Result result) { // Move the execution to another thread. Do not wait for the result // here, just pass the result object. EventQueue.invokeLater(new Runnable() { public void run() { try { Object result = doSomethingInEDT(intent); result.setResult(e); } catch (Exception e) { result.setException(e); } } }); }
The basic use-case is printing a simple text, e.g. text output of an application, into a dedicated pane in the UI, e.g. a tab in Output Window in the IDE.
InputOutput io = InputOutput.get("UseCase1", true); io.getOut().println("This is a simple output"); io.getOut().close();
Hyperlinks can be also used to invoke some code when clicked.
InputOutput io = InputOutput.get("UseCase3", true); io.getOut().print("A line containing a "); io.getOut().print("hyperlink", Hyperlink.from(new Runnable() { public void run() { System.gc(); } })); io.getOut().println(" for invocation of custom code."); io.getOut().close();
Print a color text. Users can select a predefined color for common cases (debug, warning, failure, success), or custom color specified as RGB value.
InputOutput io = InputOutput.get("UseCase4", true); io.getOut().println("Let's print some info", OutputColor.debug()); io.getOut().println("or warning with appropriate color", OutputColor.warning()); io.getOut().println("Maybe also text with custom reddish color", OutputColor.rgb(255, 16, 16)); io.getOut().close();
It is possible to reuse already created output pane and clear all the previously printed text if it is not needed any more.
InputOutput io = InputOutput.get("UseCase5", true); io.getOut().println("Let's print some text"); io.getErr().println("and reset the pane immediately."); io.reset(); io.getOut().println("The pane is now empty and we can reuse it simply"); io.getOut().close();
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..
XXX no answer for arch-usecases
SearchHistory
is synchronising history content through netbeans modules and it saves history to preferences.
There are two separate histories. One for search and another for replace.
When you add your propertyListener to
SearchHistory
- you can listen for changes in histories.
SearchHistory
has methods for adding new entries and getting whole history list.
This use-case was formerly covered by module org.openidex.search.
The SearchInfo API+SPI allows other modules to specify whether and how should nodes they define be searched.
The definition is represented by objects extending class
SearchInfoDefinition
. To customize searching on a custom node, a
SearchInfoDefinition
object must be added to the node's lookup.
In most cases, there is no need to define own class extending the
class - one can use factory methods of class
SearchInfoDefinitionFactory
.
In some cases implementators may need to apply the same set
of SearchFilterDefinitions in the whole subtree of a node.
If so, it is not needed to put SearchInfoDefinition
to all
nodes' lookups, but only one instance of
SubTreeSearchOptions
have to be put into the lookup of the root node.
SearchInfoDefinition
,
SearchFilterDefinition
,
SubTreeSearchOptions
and a factory class
SearchInfoDefinitionFactory
People that want to enhance IDE searching features (with custom search criteria or specialized algorithms) can add a new tab to the "Search in Projects" dialog.
They need to implement several classes:
SearchProvider
to register the new search feature to the IDE or platform application.
SearchProvider.Presenter
that creates visual component for adding to the search dialog and that can interact with dialog buttons.
SearchResultsDisplayer
to show search results to the user.
SearchComposition
that encapsulates setting and state of searches, provide access to result displayer, and is able to start
and terminate the search.
SearchProvider
,
SearchProvider.Presenter
,
SearchResultsDisplayer
,
SearchComposition
and relative classes.
SearchInfo
,
that defines which files should be searched (it can be retrieved from methods in
SearchInfoUtils
,
or UI component controller ScopeController
),
SearchListener
that you should inform about events that happen during searching, and helper classes
SearchInfoUtils
(getting SearchInfo objects for nodes) and
FileNameMatcher
(filtering files by file name).
This API enables other modules to open Find in Projects dialog with some pre-defined criteria and to start searching programatically.
An existing file can be used as a boilerplate for creation of a new file. The boiler plate can contain necessary skeleton, comments, content. As the boilerplate resides on config filesystem, it is also customizable by the user and the user can eventually develop custom templates.
In previous NetBeans versions, templating system was built into
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 (hacking into DataObject implementation), however since
version 6.1 there are interface in
Runtime or project-related values may be supplied by
The CreateFromTemplateAttribute implementation knows which template is being used, where the outcome should be placed, so it can derive appropriate values for both the template and the target location.
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
java.util.Date
representing current data and time like
String
the file encoding of the template instance
Other properties can indeed be provided by
CreateFromTemplateAttributess.
After processing, the output is also sent to appropriate
org.openide.text.IndentEngine
associated
with the mime type of the template, for formating.
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. 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.
A CreateFromTemplateHandler should be able to create multiple files, one of them important so it will open after user initiates the creation action. The template of set of related files may be represented by a folder with a handler attached, and the operation deploys multiple files in the target directory.
There is a way to create a portable wizard (e.g. one that can
be executed inside of NetBeans as well as in a browser). The
most portable UI these days is written in HTML. To
register such HTML based wizard with your file template,
use @TemplateRegistration
annotation and include page()
attribute referencing
your own HTML page:
public class X { {@code @TemplateRegistration}( page = "x.html", scriptEngine = "freemarker", displayName = "JS Wizard", folder = "Other", content = "x.fmk" ) public static String jsWizard() { return "yourInitializationCode();"; } }
the return value of the annotated method (named jsWizard
)
should be of type String and its content should be snippet of
JavaScript code to execute inside of your specified HTML page
(e.g. x.html
) to create an instance of
KnockoutJS model to
drive the wizard. Here is a sample code for the model:
function yourInitializationCode() { var ok = ko.observable(false); var msg = ko.observable(''); var current = ko.observable('Init'); var data = { 'errorCode': ko.computed(function() { if ('Init' == current()) return 0; if (!ok()) return 1; if (msg()) return 0; return 2; }), 'current': current, 'ok': ok, 'msg' : msg } ko.applyBindings(data); return data; }
The model defines wizard composed of few panels (defined in following
HTML file) and a verification function (registered as errorCode
)
to check if everything is OK. In addition to that it defines
proprietary text value msg
which is
going to be filled by the wizard and cannot be empty. Each
page of the wizard is registered using a custom
Knockout.js binding called
step
. Here is an HTML page defining three steps:
<section data-bind="step: { 'id' : 'init', text : 'Initial Page'}" > <p> Write your UI in portable HTML and display it in NetBeans or on web! Read more at <a href="http://wiki.netbeans.org/HtmlUIForTemplates">our wiki</a>... </p> </section> <section data-bind="step: 'info'" > <p> Use <a href="http://knockoutjs.com">knockout.js</a> bindings to isolate your view model from the actual look of your HTML page. Bind your view to model written in Java or JavaScript. </p> <h3>Is everything OK?</h3> <input type="checkbox" data-bind="checked: ok"/> <h3>How do you feel?</h3> <input type='text' data-bind="textInput: msg"/> </section> <section data-bind="step: { 'id' : 'summary' }" > <p> You are feeling <span data-bind="text: msg"></span>! Let's proceed to create a file which will express your feeling by using <a href="http://freemarker.org/">Freemarker</a> templating engine and values filled in this wizard. </p> </section>
The Next/Finish buttons are controlled by the errorCode
property.
If it is non-zero, there is an error and these buttons are disabled.
Also once can use that inside of the HTML page to display user related errors:
<div data-bind="visible: errorCode() == 1"> <span style="color: red">Please check you are OK!</span> </div> <div data-bind="visible: errorCode() == 2"> <span style="color: red">Tell us how do you feel!</span> </div>
The L10N of the wizard is done on the level of HTML pages.
The whole page gets translated into different language with appropriate
suffix like x_cs.html
and it is then
selected instead of the default one, when user runs in such locale.
When the wizard is successfully finished, all the values
specified in the model (except system ones like current
,
errorCode
, etc.) are transfered to the templating engine,
so they can influence the content of created files.
Here is a sample x.fmt
content which reuses the msg
value provided by the wizard:
Hi, I am Freemarker. I feel ${wizard.msg}.
When such file is instantiated, the ${wizard.msg}
is
replaced by the actual value taken from the wizard.
Some people would rather use Java instead of Java script while getting the portability of the HTML. There is a simple way to rewrite the HTML and JavaScript sample to Java (and possibly run it in a plugin-less browser via bck2brwsr VM). Keep the same HTML, Freemarker, etc. files - just instead of encoding the logic in JavaScript use Java:
{@link net.java.html.json.Model @Model}(className = "JavaWizard", properties = { {@link net.java.html.json.Property @Property}(name = "current", type = String.class), {@link net.java.html.json.Property @Property}(name = "ok", type = boolean.class), {@link net.java.html.json.Property @Property}(name = "msg", type = String.class) }) public class JavaWizardCntrl { {@link net.java.html.json.ComputedProperty @ComputedProperty} static int errorCode( String current, boolean ok, String msg ) { if ("Init".equals(current)) return 0; if (!ok) return 1; if (msg == null || msg.isEmpty()) return 2; return 0; } {@code @TemplateRegistration}( page = "x.html", scriptEngine = "freemarker", displayName = "HTML/Java Wizard", folder = "Java", content = "x.fmk" ) public static JavaWizard javaWizardFactory() { return new JavaWizard("Init", false, ""); } }
The return value of the annotated method is now an HTML/Java model class which can naturally represent the essential Knockout.js objects in Java.
It is very common that the HTML file creation wizards (either controled by JavaScript or by Java) need to allow user to specify target location of their file. To simplify such common task and to ensure its UI is consistent with the rest of the environment, one can just include following code snippet in the HTML file and leave its actual rendering on the system:
<section data-bind="step: 'targetChooser'" > </section>
Such section will then be replaced by a panel which provides appropriate UI for choosing target directory as well as name for the newly created file.
In case one prefers more Java-like chooser, it is possible to use
'targetChooser:java'
as name of the step. Then all
Java source groups in target project will be listed and presented
in a typical Java package view selection mode. Once can use different
suffix than java
to list other types of source groups.
This feature requires presence of org.netbeans.modules.java.project.ui
module, otherwise the target chooser falls back to classical one.
See documentation for complete set of use-cases.
First of all, you don't need to worry about serialization if all your MultiViewDescription
instances
contained in the multiview state to be non serializable.
Meaning they all return TopComponent.PERSISTENCE_NEVER
in MultiViewDescription.getPersistenceType()
.
If at least one of the views requires serialization, you have no choice but to make all
MultiViewDescription
implementors serializable.
You also can persist the MultiViewElement instances that the multiview contains. The algorithm here is a bit complicated for
performance reasons. Only those Elements are stored that were created during the session and which are Serializable.
So if the user never switches to the 4rd tab, and it's corresponding element and visual component never get created, then
it won't be stored. (We would have to create it just for the sake of persistance).
So if your visual component needs some inital context for creation, you should store it in the description instance, and if the visual component
wants to store it's state (location of cursor, selected field, something else that makes sense for the opened component) you should store it in the MultiViewElement.
So basically if you are always able create the Element from Description without any persisted data, you don't need to persist anything.
If you define your own CloseOperationHandler
implementation for the multiview component, then you also ought to define it
Serializable. Otherwise it will be silently replaced by the default handler on restoration of the multiview component.
Each MultiViewDescription
defines display name and icon. While the icon
is meant for the whole document/view tab, the display name is just for the inner switching button.
So how does one set the name for the whole MultiView component? It can be done when creating the component.
TopComponent mvtc = MultiViewFactory.createMultiView(myDescriptions); mvtc.setDisplayName("My static mvtc displayName");
Later in the lifecycle of the component, it can be also influenced from within the individual
multiview visual elements using the MultiViewElementCallback.updateTitle()
method.
XXX no answer for arch-usecases
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
XXX no answer for arch-usecases
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)
.
public final class MyOption implements Runnable { @Arg(longName="hello") public String name; public void run() { System.out.println("Hello " + name + "!"); } public static void main(String... args) { CommandLine line = CommandLine.create(MyOption.class); line.process(args); } }
If the above main class is called with parameters --hello World
it
will print out Hello World!
.
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.
When using the declarative annotation style
one can always specify
@Arg(longName="text", shortName='t')
.
The longName
attribute is required, but if there is supposed to be
no long version of the argument, it can be set to empty string.
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.
boolean
to create
an option without an argument.
--. 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.
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.
When using the
declarative annotation style
one may annotate a field of type String[]
which then means
this field should be filled with all additional arguments.
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. With the
declarative annotation style
one can annotate a field of type String[]
and specify that
it is supposed to be implicit.
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. This kind of grouping is not currently supported with the declarative annotation style registration.
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.
Primary purpose of this module is collect information about the user actions that take place in the running NetBeans based application.
Important part of the behaviour of this module is the ability to cooperate with information analyzing tools and present their results. This is done thru special HTTP contracts, where the module reads and understands various server responses and is able to open browser after submitting data to analysis.
org.netbeans.modules.uihandler.Bundle
that specifies the location of the page on a server one shall
query and display to the user when the module is about the
submit usage data for analysis.
org.netbeans.modules.uihandler.Bundle
that specifies the location of the page on a server one shall
query and display to the user when the module is about the
submit metrics data for analysis.
org.netbeans.modules.uihandler.Bundle
that specifies the location of the page on a server one shall
query and display to the user when the module is about the
submit an error report.
WELCOME_URL
can contain
any XHTML text, but it also should contain a <form/> tag
that defines <input type="hidden" name="submit" value="localizedName"/>.
The localizedName
is then going to be used for a button
for the dialog displaying the summary. When this button is invoked,
the "action" URL is then feed with data from the UI logs.
The server is then supposed to process the data, create
some analytics pages and return them back to the client.
If the returned page contains tag like
<meta http-equiv='Refresh' content='3; URL=somepage'>
an (external) browser is opened with the specified URL and
the user can then interact directly with the server, thru
pages it serves.
org.netbeans.modules.uihandler.Submit
property when invoking NetBeans.
The base module is in fact just an infrastructure which collects data about UI gestures, but the actual gestures need to be delivered to it somehow. Here is the description of the ways how one can extend own modules to cooperate with this UI gestures infrastructure.
To feed own data about special UI gestures one can just create own
Logger.getLogger(UI_LOGGER_NAME_VALUE+".ownname")
and send own log records to it.
UI_LOGGER_NAME_VALUE is a value of UI_LOGGER_NAME
resource bundle key, defining the name of the logger.
The format of the log messages shall follow the one described by the
structured logging
document, e.g. the
LogRecord
shall have associated
ResourceBundle
and the record's getMessage
shall point to a key
in that bundle.
MSG_KEY_ICON_BASE
in the bundle associated
with the
LogRecord
(where the MSG_KEY is the string returned by record.getMessage()
)
and the value is then going to be used for the
Node
representing the UI gesture.
Sometimes direct logging may not be possible. For example for
performance data it might be meaningful to collect the information
over a longer time period and only at the end output some statistics.
This is supported as well. Just implement and register one
of the
These usecases are realized as described in here in provided UI specification.
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
Border
instance into
UIManager
under key nb.quicksearch.border
.
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.
For general overview of the concepts of NetBeans action system and related UI elements,
together with code samples, see chapter 5,
of NetBeans Platform for Beginners
by Jason Wexbridge and Walter Nyland.
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.
For general overview of the concepts related to nodes and explorers,
together with code samples, see chapter 7,
of NetBeans Platform for Beginners
by Jason Wexbridge and Walter Nyland.
For general overview of the filesystem concepts, related topics,
together with code samples, see chapter 3,
of NetBeans Platform for Beginners
by Jason Wexbridge and Walter Nyland.
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.
See documentation about dynamically changing the system filesystem.
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
The support was moved to a new module; please see api.templates module for more information.
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.
To maintain binary compatibility, method implementations may be injected at runtime, in a form of a superclass in the class' inheritance hierarchy. Modules compiled against older version of APIs which contains MethodReferences to methods removed from the oficial APIs will be then linked according to JVM Resolution algorithm to a matching method present in the superclass of the referenced type.
Annotations are used to instruct the ClassLoader to make transformations to the API classes. PatchFor causes the annotated class to be injected as a superclass of the API class identified by the annotation's value. ConstructorDelegate marks a method, which is called as constructor implementation in the case that it is necessary to preserve a constructor for binary compatibility.
For general overview of the concepts related to nodes and explorers,
together with code samples, see chapter 7,
of NetBeans Platform for Beginners
by Jason Wexbridge and Walter Nyland.
XXX no answer for arch-usecases
Use-cases can be found in org.openide.util.ui module arch summary.
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.
For general overview of the concepts,
together with code samples, see chapter 6,
of NetBeans Platform for Beginners
by Jason Wexbridge and Walter Nyland.
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(); } } }