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 likeString
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
.
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases
XXX no answer for arch-usecases