The principal use cases for the API are covered in the overall API architecture.
The API is widely used by all sorts of IDE modules which need to work with Java sources. They can find Javadoc, unit tests, source level, etc. The SPI is intended mainly for Java platform and library providers, and project type providers, to declare all of this information.
The API is widely used by all sorts of IDE modules which need to work with sources. The SPI is intended mainly for (java) platforms and library providers, and project type providers, to declare all of this information.
There are 3 types of progress indication:
The default location of the progress indication is the status bar which aggregates all tasks running in the IDE that show progress. However it's possible to exclude the task from the default location and show the progress in one's custom dialog component. In such a case the same task should not appear in the status line component as well.
It's possible to request cancelling the task from status line progress aggregator if the task allows cancelling.
Progress tasks that get started as a result of explicit user action takes precedence in the status line docked component over tasks that are triggered by the system. (say filesystem refresh for example)
The most common usecase of the API looks like this:
ProgressHandle handle = ProgressHandleFactory.creatHandle("My custom task");
...
// we have 100 workunits
// at this point the task appears in status bar.
handle.start(100);
...
handle.progress(10);
...
handle.progress("half way through", 50);
...
handle.progress(99);
// at this point the task is finished and removed from status bar
// it's not realy necessary to count all the way to the limit, finish can be called earlier.
// however it has to be called at the end of the processing.
handle.finish();
In case your usage of the API
then you should consider using the aggregating version of APIs which is similar to the simple APIs but has distinctive differences and additions that allow for more complex scenarios.
It allows to compose the progress bar from 1+ independent sources, all sharing proportional piece of the progress bar. Additionally you can monitor the task's overall progress from one central place and possibly add more contributing sources of the progress during processing.
// let's have a factory for client code that performs some part of the job to be done..
Lookup.Result res = Lookup.getDefault().lookup(new LookupTemplate(MyWorkerFactory.class));
Iterator it = res.allInstances().iterator();
ProgressContributor[] contribs = new ProgressContributor[res.allInstances().size()];
int i = 0;
while (it.hasNext()) {
MyWorkerFactory prov = (MyWorkerFactory)it.next();
contribs[i] = AggregateProgressFactory.createProgressContributor("Module X contribution");
MyWorker worker = prov.createWorker(contribs[i]);
//... snip ... do something with the worker..
i = i + 1;
}
AggregateProgressHandle handle = AggregateProgressFactory.createHandle("My Task", contribs, null, null);
// non-cancellable and with out output link.
// calling start() at the time when the actual long running task starts processing
handle.start("here we go");
// ...snip...
// now the individual MyWorker instances log their progress.
// possibly in other threads too..
// ... snip...
//
if (myConditionThatSpawnsAnotherContributor()) {
ProgressContributor cont = AggregateProgressFactory.createProgressContributor("Additional exceptional contribution");
handle.addContributor(cont);
// ... snip ...
}
// the task is finished when all the ProgressContributors finish..
See documentation for complete set of use-cases.
The WebModule class, which encapsulates a web module, has methods
for retrieving the module's properties:
FileObject myServlet = ...;
WebModule wm1 = WebModule.getWebModule (myServlet);
String version = wm1.getJ2eePlatformVersion ();
System.out.println ("Servlet is in version:" + version + " web module");
Most often the web module is implemented inside a project:
public class MyProjectType implements Project {
Lookup getLookup () {
return Lookups.fixed(new Object[] {
new MyProvider (),
...
}
}
private class MyProvider implements WebModuleProvider {
WebModule findWebModule(FileObject file) {
if (isMyFile (file)) {
WebModule wm;
synchronized (this) {
wm = cachedWebModule (file);
if (wm == null) {
wm = WebModuleFactory.createWebModule (new WebModuleImpl ());
cache (file, wm);
}
}
return wm;
}
}
boolean isMyFile (FileObject file) {...}
WebModule cachedWebModule (FileObject file) {...}
cache (FileObject file, WebModule wm) {...}
}
private class WebModuleImpl implements WebModuleImplementation {
...
}
}
It is also possible to implement web modules backed by other means than
a project by implementing a WebModuleProvider and registering
it in the default lookup.
Support for web frameworks, such as Struts and JSF, can
extend a WebModule with framework-specific features, such as configuration files. An implementor
wanting to provide such support implements WebFrameworkProvider and registers it in the
j2ee/webtier/framework in the default file system.
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.
UpdateUnits
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().
The normal usecase is for a client wanting to inspect a classfile. A ClassFile
instance is created with either a String path, File or
InputStream instance. All attributes of the classfile are available as properties,
such as getSourceFileName or getMethods.
An external module can register JDBC drivers. A typical example is a module which provides integration with a database server. In this case the module contains the JDBC driver for that database server and uses the Database Explorer API to add it do the Database Explorer.
Another client of this API could be a module providing integration with a J2EE application server. Sometimes a J2EE application server bundles a database server for improving the out-of-the-box experience. When the server is registered in the IDE the JDBC drivers for the bundled database server are added to the Database Explorer.
The drivers are registered by making calls on JDBCDriverManager or by registering an XML file which describes the driver in the module layer. The XML file is described by the JDBC Driver DTD. An example of a registration file describing the JDBC driver for PostgreSQL follows:
<?xml version='1.0'?>
<!DOCTYPE driver PUBLIC '-//NetBeans//DTD JDBC Driver 1.0//EN' 'http://www.netbeans.org/dtds/jdbc-driver-1_0.dtd'>
<driver>
<name value='postgresql-7'/>
<display-name value='PostgreSQL (v7.0 and later)'/>
<class value='org.postgresql.Driver'/>
<urls>
<url value='file:/folder1/folder2/drivers/pg74.1jdbc3.jar'/>
</urls>
</driver>
This file should be registered in the Databases/JDBCDrivers folder of the module layer.
To addres a bundled JAR inside the IDE the nbinst protocol can be used in the URLs:
nbinst:/modules/ext/bundled-driver.jar.
You can use the JDBCDriver.getDriver() method to obtain a reference to the underlying JDBC Driver instance. This is useful if you want to use the registered drivers but create your own JDBC connections independent of the Database Explorer.
When creating a new connection the JDBC driver which it should use can be specified. A list of all the registered JDBC drivers can be retrieved using JDBCDriverManager.getDrivers().
An external module can register new database runtimes. A database runtime
is an abstraction of a database server instance
(usually bundled with the IDE, an integration module or with a J2EE server). It allows a database
server instance to be started and stopped when a connection to this
instance is made in the IDE. Database runtimes are represented by the
DatabaseRuntime
SPI interface and are registered in the Databases/Runtimes of the module layer.
A module can create new database connections (for example to a bundled database). New connections can be added by calling DatabaseConnection.create() to create a new DatabaseConnection instance and then ConnectionManager.addConnection() to add the connection to the Database Explorer.
New connections can also be added by registering them in the module layer. The format of the registration file is described by the Database Connection DTD. An example of a registration file describing a connection to a PostgreSQL database follows:
<?xml version='1.0'?>
<!DOCTYPE connection PUBLIC '-//NetBeans//DTD Database Connection 1.1//EN' 'http://www.netbeans.org/dtds/connection-1_1.dtd'>
<connection>
<driver-class value='org.postgresql.Driver'/>
<driver-name value='postgres-7'/>
<database-url value='jdbc:postgresql:test'/>
<schema value='public'/>
<user value='test'/>
<password value='cGFzc3dvcmQ='/>
</connection>
This file should be registered in the Databases/Connections folder
of the module layer.
The password element is optional, but if it is included, its value must be the Base64 encoding of the UTF-8 representation of the password. Note that the UTF-8 representation of passwords composed entirely of ASCII characters is the same as their ASCII representation, so for such passwords all that needs to be done is to convert them to Base64.
Base64 encoding serves as a simple scrambling to prevent accidental revelation of the password. It is not indended to offer any real security. You can protect the password by assigning appropriate file protections to the connection XML file.
Sometimes the list of connections needs to be displayed somewhere else in the IDE than the Runtime tab. A typical example is the SQL Editor, which allows the user to select the database connection which the SQL statement will be executed against in a combo box in the editor toolbar. The list of connections can be obtained by calling ConnectionManager.getConnections(), which returns an array of DatabaseConnection instances.
The client usually needs to show the display name of the connection. The display name can be retrieved using the DatabaseConnection.getDisplayName() method.
Sometimes a client needs to retrieve the connection properties, such as the driver class.
An example could be a module for a J2EE server creating a connection pool. The properties can
be retrieved using the getDriverClass(), getDatabaseURL(),
getSchema(), getUser() and getPassword()
methods of the
DatabaseConnection
class.
Usually when displaying a list of connections (usually in a combo box), the last item is "New Connection", which displays the standard New Database Connection dialog of the Database Explorer. This can be achieved by calling one of the ConnectionManager.showAddConnectionDialog() methods.
A user of this API may want to remove a connection from the list of connections registered by the Database Explorer. This is done using ConnectionManager.removeConnection()
A component which provides database functionality (such as the SQL Editor)
will need to connect to a database. This can be achieved using the
DatabaseConnection.showConnectionDialog()
method and the java.sql.Connection instance can be retrieved using the
getJDBCConnection()
method.
If you want to connect to the database without showing a dialog or any kind of UI, you can use the DatabaseConnection.connect() method.
You may want to test to make sure the underlying physical JDBC connection obtained from a DatabaseConnection is either valid or null. This is done using the DatabaseConnection.getJDBCConnection(boolean test) method, which validates the underlying connection before returning it. If the connection is invalid, it marks the DatabaseConnection as disconnected and returns null.
A component which provides database functionality (such as the SQL Editor
or a module providing support for data sources) will need to let the user
select the a database connection, usually through a combo box.
This can be achieved using the
DatabaseExplorerUIs.connect()
method. The JComboBox passed to the method will be filled with the list of connections as returned by
ConnectionManager.getConnections(), followed by a separator
and a New Database Connection item which will display the dialog for adding a new database connection when selected.
A component might need to allow database tables from the Database Explorer to
be dragged to a visual editor. An API is provided in DatabaseMetaDataTransfer
containing DataFlavors for database objects and nested classes
encapsulating those database objects during a drag and drop transfer.
A component might need support for working with SQL identifiers. In particular, it's important to know when to quote a SQL identifier. The SQLIdentifiers.Quoter class is provided for this.
From an action or wherever you like you can call this:
public void diff(final StreamSource local, final StreamSource remote){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
DiffView view = Diff.getDefault().createDiff(local, remote);
showDiff(view);
} catch (IOException ex) {
Logger.getLogger(ThisClass.class.getName()).throwing(ex);
}
}
});
}
public void showDiff(final DiffView view){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//create our panel with our view
//right now I am just going to use the diff component
// instead of a panel
//create a topcomponent with our panel
DiffTopComponent tc = new DiffTopComponent(view);
tc.setName("MY_DIFF");
tc.setDisplayName("Some display name");
tc.open();
tc.requestActive();
}
});
}
Here is a top component to display it:
public class DiffTopComponent extends TopComponent {
/** Creates a new instance of DiffTopComponent */
public DiffTopComponent(Component diffPanel) {
setLayout(new BorderLayout());
add(diffPanel, BorderLayout.CENTER);
getAccessibleContext().setAccessibleName(
NbBundle.getMessage(DiffTopComponent.class,
"ACSN_Diff_Top_Component")); // NOI18N
getAccessibleContext().setAccessibleDescription(
NbBundle.getMessage(DiffTopComponent.class,
"ACSD_Diff_Top_Component")); // NOI18N
}
public DiffTopComponent(DiffView view) {
this(view.getComponent());
}
public int getPersistenceType(){
return TopComponent.PERSISTENCE_NEVER;
}
protected String preferredID(){
return "DiffTopComponent"; //NOI18N
}
public HelpCtx getHelpCtx() {
return new HelpCtx(getClass());
}
}
Although the SPI can generally by used for highlighting areas in a document that have something in common, the usecases below are demonstrated on a simple braces matching example. This example is used for its simplicity and clarity, but it could be substituted by more complex examples.
The usecases listed here were heavily inspired by comments in issues 95126 and 66037.
Probably the main reason why this SPI exists is to allow Netbeans editor to
highlight matching braces in a document. The highlighting itself is done by the
infrastructure and is not of a concern for BracesMatcher implementors.
It should be possible to highlight independently
(ie. in a different color) both the original brace and the matching brace. It should also
be possible to highlight the original brace in a special color when its matching
brace can't be found. The colors obviously have to be customizable by users.
If the original brace is detected and its matching brace is found Netbeans editor needs to allow an easy navigation between those two positions (ie. jumping from the original brace to the matching one and back).
In general if there is more than one matching area users should be allowed to cycle
through all of them. Since there is only one editor action (shortcut) for navigating
between matching areas the cycling is only done in one direction (backward). In
order for cycling to work properly the BracesMatcher implementation has to report
matching areas in a consistent way. That is the matching areas should always be
sorted by their position in a document, starting with the one at the lowest offset.
The users are likely to have different preferences for the way how braces matching works therefore its behavior should be customizable. We have listed below several possible scenarios that can be used simply by setting different values for the search parameters described before. They all differ in the way how the original area is detected.
The shortcuts for the parameters have the following meaning - MBL
... max backward lookahead, MFL ... max forward lookahead,
SD ... search direction, CB ... caret bias. The values
are B ... backward or backward preferred and
F ... forward or forward preferred. The question mark ?
means that the value of this parameter has no effect for the search.
MBL = 0, MFL = 0, SD = ?, CB = F : Check only the right hand side
character of the caret. This is the default for Netbeans overwrite mode (ie. the
block shaped caret).
MBL = 1, MFL = 1, SD = ?, CB = B : Check characters right next to
the caret on both its sides. This is the default for Netbeans normal mode (ie.
the I-beam shaped caret).
MBL = 0, MFL = 256, SD = F, CB = B : Check the character on the
left hand side of the caret, otherwise search forward. This is the default
for jVi insert mode.
MBL = 0, MFL = 256, SD = F, CB = F : Search forward from the caret.
This is the default for jVi command mode.
MBL = 256, MFL = 256, SD = F, CB = B : Check the left hand side
character, otherwise search forward first and then backward. Suitable for
I-beam carets that detect the original area anywhere on the line with the
caret giving preferrence to the area positioned forward from the caret. The
preferrence for the backward positioned area can simply be achieved by changing
the search direction (ie. SD = B).
MBL = 256, MFL = 256, SD = F, CB = F : The same as
option E, but for the block carets.
The editor in Netbeans can operate in two modes - normal and overwrite. They both use different shape of a caret and thus need a different caret bias. The braces matching infrastructure should detect what mode the editor is in and change the parameters used for searching for the original area accordingly. The caret bias is the most important parameter, but other parameters may need to be changed too.
This feature should of course be only active for text components where the parameters have not been overwritten by some other module.
By default the two search scenarios used for the normal and overwrite modes in Netbeans editor are the scenario B for the normal mode and scenario A for the overwrite mode. In general scenarios from both editor modes should be customizable by users.
${...}.
index
parameter which means that the infrastructure should fill in
a fresh index variable e.g. i.
java.util.Collection.
${i index}
or ${c instanceof=java.util.Collection}.
${x default="Hello world"}.
${x default="\"quoted string\""}.
JTextComponent pane = ...
String tempCodeTemplateText = ...
CodeTemplate ct = CodeTemplateManager.get(pane.getDocument()).createTemporary(tempCodeTemplateText);
ct.insert(pane);
editable having
true/false.
The API is small and it only allows to explicitly show or hide the completion window.
It's being used by code templates that need to explicitly show the code completion
window when tabbing to a particular parameter.
There may be certain actions that want to ensure that the code completion is hidden
at the time when they are invoked. For example the actions pasting the content
of the completion item into the document.
Completion infrastructure needs to obtain the results that are then displayed
in the completion window.
There are three types of displayed results related to the current caret offset:
For the purpose of obtaining these completion results
CompletionProvider
exists.
There may be an arbitrary number of independent completion providers for
a single completion popup window.
The completion providers are registered through the xml layer into
Editors/<mime-type>/CompletionProviders. Once the document
with the particular mime-type gets loaded the corresponding completion providers
will get instantiated and used.
Threading:
The code completion's infrastructure invokes the requests
for the completion results in the AWT thread.
Therefore all the methods of the completion providers are invoked
in AWT thread but they may reschedule their processing into other threads.
The completion provider creates a task that computes the resulting
data that will then be displayed by the code completion infrastructure.
The task creation and computation are called synchronously
from the AWT event dispatch thread.
However there can be potentially long-running tasks (e.g. working with MDR)
that are not desirable to be run in AWT thread.
Therefore the completion infrastructure provides a listener
to which the completion task notifies the results.
The support class
AsyncCompletionTask allows to post the task computation
into RequestProcessor.
The completion task computes a collection of completion items
which are then collected by the completion infrastructure and displayed.
Displaying. Each completion item must be able to display itself in a JList.
Sorting. The completion items may come from different completion providers
and they must be sorted before displaying. The sort order
should not only be alphabetical but it should also allow a prioritization
of the items according to their importance in the given context.
Actions. The interaction of the user with the completion item
is done by interacting with item's input map and action map.
Documentation. The item may want to display additional
detailed information in a documentation popup window.
A module in the IDE has information whether data shown in the Error Stripe is up-to-date or not. The Error Stripe may change the appearance according to this knowledge.
Implement the UpToDateStatusProvider that provides up-to-date status. Be sure that it fires PropertyChangeEvent when this status is changed.
Implement the UpToDateStatusProviderFactory that creates an instance of your UpToDateStatusProvider for a given JTextComponent and install it as described here.
The code folding structure (fold hierarchy) relates
to javax.swing.JTextComponent instance in one-to-one relationship.
To find the code folding hierarchy instance for the given non-null text component
the following code snippet can be used:
JTextComponent editorComponent = ...
FoldHierarchy hierarchy = FoldHierarchy.get(editorComponent);
The tree-based hierarchy has one non-removable and non-collapsable root fold that covers the whole document. It can be obtained by
FoldHierarchy foldHierarchy = ...
Fold rootFold = hierarchy.getRootFold();
The children folds of the root fold (or children folds) can be obtained by
// the hierarchy must be locked prior exploration or manipulation
hierarchy.lock();
try {
Fold rootFold = ...
int foldCount = rootFold.getFoldCount();
for (int i = 0; i < foldCount; i++) {
Fold childFold = rootFold.getFold(i);
}
} finally {
hierarchy.unlock();
}
Index of the child in its parent can be found by
hierarchy.lock();
try {
Fold rootFold = ...
int foldIndex = rootFold.getFoldIndex(childFold);
} finally {
hierarchy.unlock();
}
In the given fold hierarchy find the nearest fold right at or after the given offset and collapse it.
hierarchy.lock();
try {
Fold fold = FoldUtilities.findNearestFold(hierarchy, offset);
hierarchy.collapse(fold);
} finally {
hierarchy.unlock();
}
In the given fold hierarchy expand all folds that are currently collapsed.
FoldUtilities.expand(hierarchy, null);
In the given fold hierarchy collapse all e.g. javadoc folds that are currently collapsed.
The example can be generalized to any fold type.
FoldUtilities.collapse(hierarchy, JAVADOC_FOLD_TYPE);
In the given fold hierarchy expand the fold into which the caret
is going to be moved by Caret.setDot(offset).
The hierarchy must be locked and this example assumes that the underlying
document is already read-locked e.g. by Document.render().
FoldHierarchy hierarchy = FoldHierarchy.get(caretComponent);
hierarchy.lock();
try {
Fold collapsed = FoldUtilities.findCollapsedFold(hierarchy, offset, offset);
if (collapsed != null && collapsed.getStartOffset() < offset &&
collapsed.getEndOffset() > offset) {
hierarchy.expand(collapsed);
}
} finally {
hierarchy.unlock();
}
In the given fold hierarchy start to listen on all changes
done in the hierarchy.
This is actually used e.g. in the Editor's View Hierarchy that needs
to refresh views based on the fold changes.
hierarchy.addFoldHierarchyListener(new FoldHierarchyListener() {
public void foldHierarchyChanged(FoldHierarchyEvent evt) {
// Hierarchy does not need to be locked here
//
// evt.getAffectedStartOffset() and getAffectedEndOffset()
// give text area affected by the fold changes in the event
}
});
Listen on the hierarchy changes
and refresh the views in the text area affected by the fold change.
Inspect the collapsed folds in the affected area
because special views need to be created for the collapsed folds.
The actual code in the View Hierarchy is somewhat different
but the one given here is more descriptive.
hierarchy.addFoldHierarchyListener(new FoldHierarchyListener() {
public void foldHierarchyChanged(FoldHierarchyEvent evt) {
for (Iterator collapsedFoldIterator
= FoldUtilities.collapsedFoldIterator(hierarchy,
evt.getAffectedStartOffset(),
evt.getAffectedEndOffset()
);
it.hasNext();
) {
Fold collapsedFold = (Fold)it.next();
// Create special view for the collapsedFold
}
}
});
Manipulation of the folds is designed to be done by fold managers.
Those classes implement FoldManager interface in the SPI.
At initialization time they are given instance of FoldOperation
through which they can create, add or remove the fold instances.
To create and use a new FoldManager instance
it's necessary to
public class MyFoldManager implements FoldManager { // or extends AbstractFoldManager
...
}
public class MyFoldManager ...
...
public static final class Factory implements FoldManagerFactory {
public FoldManager createFoldManager() {
return new MyFoldManager();
}
}
}
NbJavaSettingsInitializer)
public class MySettingsInitializer ...
public void updateSettingsMap(Class kitClass, Map settingsMap) {
...
settingsMap.put(SettingsNames.CODE_FOLDING_ENABLE, Boolean.TRUE);
}
}
Create a new fold and add it to the hierarchy. The operation
is performed by the fold manager either at initialization phase
(in the initFolds() which gets called automatically
by the infrastructure) or at any other time when the fold manager's
operation gets invoked (usually by a listener that the fold manager
attaches to be notified about changes that can cause the folds structure
to be changed - e.g. a parsing listener for java folds).
Operations that manipulate the hierarchy are done
in terms of a valid transaction over the fold hierarchy.
Transactions allow to fire the collected changes as a single
FoldHierarchyEvent at the time when they are committed.
// In the FoldManager's context
FoldOperation operation = getOperation();
FoldHierarchyTransaction transaction = operation.openTransaction();
try {
Fold fold = operation.createFold(...);
operation.addFoldToHierarchy(fold, transaction);
} finally {
transaction.commit();
}
Remove the existing fold from the hierarchy
// In the FoldManager's context
FoldOperation operation = getOperation();
FoldHierarchyTransaction transaction = operation.openTransaction();
try {
Fold fold = ...
operation.removeFoldFromHierarchy(fold, transaction);
} finally {
transaction.commit();
}
String sectionName = ...;
StyledDocument doc = ...;
GuardedSectionManager guards = GuardedSectionManager.getInstance(doc);
GuardedSection g = guards.findSimpleSection(sectionName);
guards.createSimpleSection("new_name", doc.createPosition(g.getEndPosition().getOffset() + 1));
StyledDocument doc = ...;
GuardedSectionManager guards = GuardedSectionManager.getInstance(doc);
GuardedSection g = guards.findSimpleSection("sectionName");
g.deleteSection();
CloneableEditorSupport to provide
guarded sections you should implement the GuardedEditorSupport
interface.
private final class MyGuardedEditor implements GuardedEditorSupport {
...
}
Further implement reading and writing of existing sections.
protected void loadFromStreamToKit(StyledDocument doc, InputStream stream, EditorKit kit) throws IOException, BadLocationException {
if (guardedEditor == null) {
guardedEditor = new MyGuardedEditor();
// remember the provider
String mimeType = ((CloneableEditorSupport.Env) this.env).getMimeType();
guardedProvider = GuardedSectionsFactory.find(mimeType).create(guardedEditor);
}
// load content to kit
if (guardedProvider != null) {
guardedEditor.setDocument(doc);
Charset cs = FileEncodingQuery.getEncoding(this.getDataObject().getPrimaryFile());
Reader reader = guardedProvider.createGuardedReader(stream, cs);
try {
kit.read(reader, doc, 0);
} finally {
reader.close();
}
} else {
kit.read(stream, doc, 0);
}
}
protected void saveFromKitToStream(StyledDocument doc, EditorKit kit, OutputStream stream) throws IOException, BadLocationException {
if (guardedProvider != null) {
Charset cs = FileEncodingQuery.getEncoding(this.getDataObject().getPrimaryFile());
Writer writer = guardedProvider.createGuardedWriter(stream, cs);
try {
kit.write(writer, doc, 0, doc.getLength());
} finally {
writer.close();
}
} else {
kit.write(stream, doc, 0, doc.getLength());
}
}
Your module should also require a proper implementation. In case of java
content add to your module manifest file:
OpenIDE-Module-Requires: org.netbeans.api.editor.guards.Java
Altghough there are formatting actions already there may be clients wishing to explicitly fix indentation of e.g. a newly inserted code into a Swing document.
The same code is used after inserting a newline into a document.
The Indent is an entry point for performing reindentation. The following code should be used by clients:
Indent indent = Indent.get(doc);
indent.lock();
try {
doc.atomicLock();
try {
indent.reindent(startOffset, endOffset);
} finally {
doc.atomicUnlock();
}
} finally {
indent.unlock();
}
Code beautification should not only fix line indentation but it may also perform extra changes to code according to formatting rules. For example add newlines or additional whitespace or add/remove extra braces etc.
The Reformat class should be used:
Reformat reformat = Reformat.get(doc);
reformat.lock();
try {
doc.atomicLock();
try {
reformat.reformat(startOffset, endOffset);
} finally {
doc.atomicUnlock();
}
} finally {
reformat.unlock();
}
org.netbeans.spi.editor.highlighting package
overview.
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
All editor settings are mime type specific and therefore should be retrieved
using MimeLookup. The following example shows how to retrieve
the FontColorSettings for java files and how to get AttributeSet
with coloring attributes for a particular coloring (i.e. in this case the
colors used for highlighting selected text)
MimePath mimePath = MimePath.parse("text/x-java");
FontColorSettings fcs = (FontColorSettings) MimeLookup.getLookup(mimePath).lookup(FontColorSettings.class);
AttributeSet coloring = fcs.getFontColors(FontColorNames.SELECTION_COLORING);
If clients need to react on changes in editor settings they can attach LookupListener
to the LookupResult they got for their particular settings class
from MimeLookup. The following example shows how to do it.
MimePath mimePath = MimePath.parse("text/x-java");
Lookup lookup = MimeLookup.getLookup(mimePath);
LookupResult result = lookup.lookup(new Lookup.Template(FontColorSettings.class));
result.addLookupListener(new LookupListener() {
public void resultChanged(LookupEvent ev) {
//... the client's response to the settings change
}
});
The FontColorSettings class implementor is responsible and will create
a new instance of FontColorSettings whenever some coloring will change.
This new instance will be placed in MimeLookup replacing the old one.
The friend API provided by this module is used only by the new options dialog. It is not expected to have any other clients or users. The API gives the options dialog a read/write access to the editor settings storage allowing it to implement UI for maintaining the settings.
Various modules need to provide predefined font a colors for text tokens from
languages they support. An example of such a module is java/editor
which defines colorings for tokens in java files. Defining colorings is as simple
as writing an XML file with the appropriate information. The example below shows
how to do that.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fontscolors PUBLIC "-//NetBeans//DTD Editor Fonts and Colors settings 1.1//EN" "http://www.netbeans.org/dtds/EditorFontsColors-1_1.dtd">
<fontscolors>
<fontcolor name="mylang-keyword" foreColor="0000CC" default="keyword">
<font style="bold" />
</fontcolor>
</fontscolors>
Please see the http://www.netbeans.org/dtds/EditorFontsColors-1_1.dtd for more details.
As well as providing predefined colorings modules need to provide predefined key bindings. This can be accomplished by writing another simple XML file.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bindings PUBLIC "-//NetBeans//DTD Editor KeyBindings settings 1.1//EN" "http://www.netbeans.org/dtds/EditorKeyBindings-1_1.dtd">
<bindings>
<bind actionName="goto-source" key="O-O"/>
</bindings>
Please see the http://www.netbeans.org/dtds/EditorKeyBindings-1_1.dtd for more details.
The GapList class is a java.util.List implementation
similar to java.util.ArrayList but containing a gap in its underlying
array. After a first modification at a particular index
the subsequent modifications around that index are cheap.
The class is suitable for storage of any elements related to editing
such as positions, elements, views etc.
The PriorityMutex is a simple mutex implementation
allowing to find out that a priority thread (by default Event Dispatch Thread)
is waiting to enter the mutex.
It's used e.g. in editor's view hierarchy or in editor fold hierarchy.
GapList-based element implementation suitable for line elements and any other branch element types.
Client needs to execute an external process and handle process streams and display the output in the output tab.
In order to achieve this client creates the ExecutionDescriptor. Via this object client configures all the UI behaviour of the subsequent execution. As a next step client creates the ExecutionService itself and calls run to execute the job. Run can be called multiple times. The output and input streams are presented in output tab. Additional processing and printing conversion can be configured in descriptor through interfaces described in following usecases.
The creation of the external process is supported by ExternalProcessBuilder to make things easier.
Client needs to process character data coming from stream, file or other source.
To abstract the source of the data client must implement InputReader. To abstract the data processing client must implement InputProcessor or LineProcessor. For all three interfaces there are prepared common implementations (and bridge from character based to line based processing) at these three factory classes:
To configure additional functionality specific to org.openide.windows.OutputWriter
see the next usecase.
Once the data source and processing objects are prepared client creates InputReaderTask. Factory methods of the InputReaderTask can create either common task exiting on interruption or cancellation or draining task which is trying to drain out all available data before exiting.
Client intends to process input lines and print them to org.openide.windows.OutputWriter.
In addition printed lines should be transformed (converted) somehow
and enriched by line listeners.
The both default printing processors provide factory method accepting LineConvertor. Namely InputProcessors.printing(org.openide.windows.OutputWriter out, LineConvertor convertor, boolean resetEnabled) and LineProcessors.printing(org.openide.windows.OutputWriter out, LineConvertor convertor, boolean resetEnabled). Convertor is then used to convert received lines to printed ones. Common convertors (file, http) are provided in factory class LineConvertors.
<listener>
<listener-class>com.sun.xml.rpc.server.http.JAXRPCContextListener</listener-class>
</listener>
<servlet>
<servlet-name>Hello</servlet-name>
<display-name>Hello</display-name>
<description>JAX-RPC endpoint - Hello</description>
<servlet-class>com.sun.xml.rpc.server.http.JAXRPCServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
DD Editor significantly improves DD editing in the way that user doesn't need to know the syntax of deployment descriptor file. It is desirable for DD Editor module to work with DD API interfaces instead of working with s2b beans directly (generated specifically for the particular version of DD). This method was used in previous versions of IDE.
A module wishing to access Java EE metadata obtains a metadata model, which is encapsulated by the MetadataModel class. The client then implements a model action, represented by the MetadataModelAction class, and executes this action in the model context by calling the model's runReadAction() method:
MetadataModel<SomeMetadata> model = // ...
String result = model.runReadAction(new MetadataModelAction<SomeMetadata, String>() {
public String run(SomeMetadata metadata) {
// ... do something with metadata, e.g.
// compute a String value
return value;
}
}
The way to obtain the model itself, as well as the kinds of metadata encapsulated by MetadataModel is
metadata and metadata provider-specific and is not addressed by this API.
A metadata provider first defines a root class describing the metadata, e.g., SomeMetadata. Then the provider
implements the MetadataModelImplementation interface and
creates a MetadataModel using MetadataModelFactory.
Then the provider defines a way to return the model to its clients:
private SomeMetadataModelImplementation modelImpl = new SomeMetadataModelImplementation();
private MetadataModel<SomeMetadata> model = MetadataModelFactory.createMetadataModel(modelImpl);
/**
* Call this to retrieve the model of some metadata.
*/
public MetadataModel<SomeMetadata> getSomeMetadataModel() {
return model;
}
// ...
private class SomeMetadataModelImplementation implements MetadataModelImplementation<SomeMetadata> {
// ...
}
A metadata provider might need to provide several kinds of metadata models at once. Furthermore, since there can be many models available or for backward compatibility reasons it might be impractical to provide a method for each of the models. In this case the provider may define a method like:
public MetadataModel<T> getMetadataModel(Class<T> clazz) {
// ...
}
The types of Class which may be passed to the method is a part of the contract between
the provider and its clients.
In order to do this the plugin needs to add the server classes that implement Deployment API to classpath and to provide a layer file that will register the server in IDE (specifying URL, user name and password) and register a factory class from Deployment API. Beside that server plugin must also implement some other mandatory APIs.
In this case the plugin needs to implement the Deployment API interfaces ( DeploymentFactory, DeploymentManager) and delegate to whetever interface the specific J2EE server provides. An example of this is the Tomcat5 plugin implemented in tomcatint/tomcat5 module, although it only supports deployment of web modules and it also supports some optional interfaces defined by J2EE Server Module in addition to the Deployment API.
Plugins must implement J2eePlatformImpl interface and register it via J2eePlatformFactory whose instance must be registered in the module layer file. Devmodules then may access the J2eePlatform interface via the Deployment utility class.
In order to do this plugin needs to implement the additional interfaces and register their instances in module layer file and in netbeans-deployment.xml file (an additional config file with specified DTD).
Registration of various optional functionality is done via OptionalDeploymentManagerFactory.The classes and/or interfaces related to each optional functionality are:
This is achieved by devmodules implementing the devmodules API. Typically this will be done by subclassing a support class in devmodules SPI but direct implementation of devmodules API should also be possible. The mechanism of discovery of devmodule implementation is highly dependent on how devmodules are represented in IDE (e.g. as projects or DataObjects) so this part of API is the least stable.
The API package provides abstractions of J2eeModule and a J2EEModuleContainer (a module that contains other modules, such as J2EE application). Two utility classes provide direct access to Deployment and to a JSPServletFinder. The SPI package contains utility classes for implementation of J2eeModule and J2eeModuleContainer.
Not supported in current version.
Data sources created in the module and deployed on the server are accessed using J2eeModuleProvider methods. Some of the module's data source may be already also deployed on the server, in that case the client is responsible for the duplicity handling.
Retrieval from a server is done by a server plugin. Core part of the functionality is done by DatasourceManager implementation which is exposed from a server plugin through OptionalDeploymentManagerFactory.
Retrieval from a module is done with help of ConfigSupport and ConfigurationSupport implementations.
Data sources are created in a module if they does not exist yet on a server or in a module itself.
Data sources are stored in the server-specific format, only some common subset of attributes
(e.g. JNDI name, username, url, ...) is provided by a creator.
Data source creation is done with help of ConfigSupport and ConfigurationSupport implementations. In case of conflict with existing data source, the first conflicting data source is passed to the thrown DatasourceAlreadyExistsException.
Data sources stored in a module are deployed (registered) on a module's target server at the beginning
of a module deployment.
Data sources stored in a module are retrived and passed to a
DatasourceManager instance
for deploying (registering) on the server.
All module data sources having a conflict with some existing server data source are passed to the thrown
DatasourceAlreadyExistsException.
Covered by UI specification and design document.
The API can be used by any code wishing to know the list of installed platforms and information about each one; typically this would be used by project type providers to implement a customizer dialog. The SPI is intended to be implemented by a few modules supply support for locating and introspecting installed platforms, for example a JDK setup wizard.
Project type providers wishing to show Java packages in their logical views can use this SPI. Templates which are Java-centric can use it. Projects which wish to implement queries from the Java Support APIs can place implementations in their lookup and these will be delegated to automatically.
XXX no answer for arch-usecases
XXX no answer for arch-usecases
The SPI allows to plug a project-type-specific implementation of JUnit support into NetBeans. The current NetBeans implementation only supports JUnit on J2SE project types. The SPI describes services provided by the custom JUnit support plugin.
The functionality to be plugged in comprises:
For navigation, the plugin defines mapping betwee