当前页面:
在线文档首页 >
NetBeans API Javadoc (Current Development Version)
Binding based versus Bindingless Context - NetBeans API Javadoc (Current Development Version)
Overview
Binding based versus Bindingless Context
Current Registry API proposal is binding based API, ie. the bindings
and contexts are distinguished, can be enumerated separately, etc.. This is how the JNDI API and Preferences
API are designed. Other possibility is to do not distinguish then at all and have
bindingless API where context directly has getValue/setValue methods,
eg.:
public interface Context {
String getName();
Context[] getChildren();
Object getValue();
Object setValue();
// ... other methods
}
This paper analysis differences between these two approaches
on a few usecases and also shows how the registry will store data and
defaults will be defined. Each usecase chapter ends with summary of
differences (there are not many).
Usecase 1: store a primitive data
Store one integer value.
Binding based API
Context ctx = Context.getDefault().createSubcontext("/settings/mymodule");
The folder "/settings/mymodule/" will be create if it does not exist.
ctx.bindObject("proxyPort", new Integer(789));
The registry knows how to store basic object types like String, Integer,
Long, etc.. It creates file with fixed name "settings.properties" in
the given context's folder (ie. there will be created file
"/settings/mymodule/settings.properties") and persist the object as
property "proxyPort=789" in this file.
There can be helper method:
ContextUtil.putInt(ctx, "proxyPort", 789);
which can do the int to Integer conversion.
How the defaults would be declared:
<folder name="settings">
<folder name="mymodule">
<file name="settings.properties" url="url to properties file with
defaults"/>
</>
</>
One context was defined:
- /settings/mymodule
with one binding:
- /settings/mymodule/proxyPort
Bindingless API
Context ctx = Context.getDefault().createSubcontext("/settings/mymodule");
The folder "/settings/" will be create if it does not exist. The
"mymodule" file or subfolder or both will be created later lazily.
Properties prop = new Properties();
prop.putProperty("proxyPort",
new Integer(789).toString()
);
ctx.putValue(prop);
The bindingless concept requires that
Properties object is binded which contains simple object types. There
exists general convertor which converts Properties object into
.properties file and vice versa. The name of the file is same as name
of the context (ie. "/settings/mymodule.properties" file was created).
There can be helper method:
ContextUtil.putInt(ctx, "proxyPort", 789);
which would simplify this task and would do this:
Properties prop = (Properties)ctx.getValue();
prop.putProperty("proxyPort", new Integer(789).toString());
ctx.putValue(prop);
How the defaults would be declared:
<folder name="settings">
<file name="mymodule.properties" url="url to properties file with
defaults"/>
</>
One context was defined with Properties object value:
- /settings/mymodule
Difference:
The binding based case allows finer granularity. Context methods
(eg. revert(), hasDefault(), listening support, ...) operate directly
on the individual bindings while in the bindingless case it is limited
to the whole Properties object.
In bindingless case the context contains value and children as well, so
the implementation most probably would be that folder and file with the
same name would be created. The file contains the value of the context
and folder contains children of context. The folder should be created
lazily after some actual children was created.
Usecase 2: store an object
Store service instance.
Binding based API
CompilerType compiler = new MyCompilerType(...);
Context ctx = Context.getDefault().createSubcontext("/services/compilers");
The folder "/services/compilers/" will be create if it does not exist.
ctx.bindObject("someCompiler", compiler);
Registry will lookup convertor registered for the MyCompilerType and
creates file "/services/compilers/someCompiler.settings" (or .xml or
.instance or whatever) with converted compiler instance.
How the defaults would be declared:
<folder name="services">
<folder name="compilers">
<file name="someCompiler.instance">
<attr instanceClass="some class">
</>
</>
</>
One context was defined:
- /services/compilers
with one binding:
- /services/compilers/someCompiler
Bindingless API
CompilerType compiler = new MyCompilerType(...);
Context ctx =
Context.getDefault().createSubcontext("/services/compilers/someCompiler");
The folder "/services/compilers/" will be create if it does not exist.
ctx.putValue(compiler);
Registry will lookup convertor registered for the MyCompilerType and
creates file "/services/compilers/someCompiler.settings" (or .xml) with
converted compiler instance.
How the defaults would be declared:
<folder name="services">
<folder name="compilers">
<file name="someCompiler.instance">
<attr instanceClass="some class">
</>
</>
</>
One context was defined with a value:
- /services/compilers/someCompiler
Difference:
Bindingless Context is in fact wrapper for registry object.
Usecase 3: store both primitive data and objects in one context
Store mixed data in one context. This is currently used in projects,
but it is questionable whether this is requirement or not. Bindingless
concept does not support it but can workaround it by subcontexts.
Binding based API
Mixture of usecase 1 and usecase 2.
How the defaults would be declared:
<folder name="foo">
<folder name="bar">
<file name="settings.properties" url="url to file with
primitive data defaults"/>
<file name="object1.instance">
<attr instanceClass="some class">
</>
<file name="object2.instance">
<attr instanceClass="some class">
</>
</>
</>
One context was defined:
- /foo/bar
with several bindings:
- /foo/bar/object1
- /foo/bar/object2
- and all property names from the settings.properties file
Bindingless API
Not possible. Can be workarounded by subcontexts - one subcontext for
primitive data types, some others for objects.
How the defaults would be declared in case of workaround:
<folder name="foo">
<folder name="bar">
<file name="primitiveCtx.properties" url="url to file with
primitive data defauls"/>
<file name="object1Ctx.instance">
<attr instanceClass="some class">
</>
<file name="object2Ctx.instance">
<attr instanceClass="some class">
</>
</>
</>
Three contexts were defined and each has some value:
- /foo/bar/primitiveCtx
- /foo/bar/object1Ctx
- /foo/bar/object2Ctx
Difference:
The bindingless case is more restrictive.
Usecase 4: ordered context based popup menu
Let say there are following defaults from which should be created menu
which items can be reordered:
<folder name=someMenu">
<file name="actionA.instance"/>
<file name="actionB.instance"/>
<folder name="submenu1">
<file name="subActionK.instance"/>
<file name="subActionL.instance"/>
</>
<file name="actionC.instance"/>
<folder name="submenu2">
<file name="subActionZ.instance"/>
</>
</>
Binding based API
There exists following contexts:
- /someMenu
- /someMenu/submenu1
- /someMenu/submenu2
and following bindings:
- /someMenu/actionA
- /someMenu/actionB
- /someMenu/submenu1/subActionK
- /someMenu/submenu1/subActionL
- /someMenu/actionC
- /someMenu/submenu2/subActionZ
The popup menu based on the someMenu context is composed from both the
subcontexts and bindings. These two types of items must be kept
distinguished. The possible format for full order could be one string
with names of the subcontexts appended with ".subcontext" suffix and
names of the bindings appended with the ".binding" suffix eg. (slash is
separator):
"actionA.binding/actionB.binding/submenu1.subcontext/actionC.binding/submenu2.subcontext"
If user customized the order in a UI then the new full order must be
stored. There could exist helper method
ContextUtils.storeItemsOrder(Context ctx, String[]
itemNamesWithSuffixes) which would store the full order in a context's
attribute. There could also exist opposite method for reading it:
Collection<String> ContextUtils.getSortedItems(Context ctx).
The algorithm for menu creation could then look like (it is illustrative code of menu creation only):
public Menu createMenu(Context ctx) {
Menu m = new Menu();
Iterator it =
ContextUtils.getSortedItems(ctx).iterator();
while (it.hasNext()) {
String itemName =
(String)it.next();
if
(itemName.endsWith(".binding")) {
m.addMenuItem(ctx.lookupObject(itemName.substring(
itemName.length()-".binding".length())));
} else {
m.addSubMenu(createMenu(ctx.getSubcontext(
itemName.length()-".subcontext".length())));
}
}
return m;
}
Context menuCtx = Context.getDefault().createSubcontext("/someMenu");
Menu menu = createMenu(menuCtx);
The default value of full order attribute would have to be generated by
the Registry implementation from the module layer partial order
attributes.
Bindingless API
There exists following contexts:
- /someMenu/actionA
- /someMenu/actionB
- /someMenu/submenu1
- /someMenu/submenu1/subActionK
- /someMenu/submenu1/subActionL
- /someMenu/actionC
- /someMenu/submenu2
- /someMenu/submenu2/subActionZ
Contexts "/someMenu/submenu1" and "/someMenu/submenu2" has no value
set. All other contexts has value which is instance of Action subclass.
Order of items in popup menu based on the someMenu context can be
expressed as ordered list of context names. The possible format for full order could be one string with child
context names separated by slash, eg.:
"actionA/actionB/submenu1/actionC/submenu2"
If user customized the order in a UI then the new full order must be
stored. There could exist helper method
ContextUtils.storeChildrenOrder(Context ctx, Context[] fullOrder) which
would accept ordered list of child contexts of the given ctx and would
store the full order (ie. string similar to the above mentioned one) in
an attribute. There could also exist opposite method which could return
ordered list of children: Collection<Context>
ContextUtils.getSortedChildren(Context ctx).
The algorithm for menu creation could then look like (it is illustrative code of menu creation only):
public Menu createMenu(Context ctx) {
Menu m = new Menu();
Iterator it =
ContextUtils.getSortedChildren(ctx).iterator();
while (it.hasNext()) {
Context childrenCtx =
(Context)it.next();
if (childrenCtx.getChildren() ==
null) {
m.addMenuItem(childrenCtx.getValue());
} else {
m.addSubMenu(createMenu(childrenCtx));
}
}
return m;
}
Context menuCtx = Context.getDefault().createSubcontext("/someMenu");
Menu menu = createMenu(menuCtx);
Differences:
The bindingless API is better in handling of order of
items because everything is context. However adequate solution can be
provided for binding based API as well.
Summary
Both solutions are adequate. There are just slight differences.