站内搜索: 请输入搜索关键词
当前页面: 在线文档首页 > NetBeans API Javadoc (Current Development Version)

Binding based versus Bindingless Context - NetBeans API Javadoc (Current Development Version)

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.