Overview

# Merged context

There exists implementation of `org.netbeans.spi.registry.BasicContext` which provides this basic functionality: merges subcontexts, bindings and context attributes according their names for arbitrary number of delegates and takes precedence in case of name conflict. This implementation of `org.netbeans.spi.registry.BasicContext` and also its API context is bellow called Merged Context.

## API

Two factory methods were added to provide ability to create instance of Merged Context:

- `public static Context` `org.netbeans.api.registry.Context.merge (Context[] delegates)`
  This method doesn't allow to dynamically change delegates.
- `public static BasicContext` `org.netbeans.spi.registry.SpiUtils.merge (MergeContextProvider)`
  This method requires to implement interface `org.netbeans.spi.registry.MergeContextProvider:`

      public interface MergeContextProvider {
        public static final String PROP_DELEGATES_CHANGED = "PROP_DELEGATES_CHANGED"; //NOI18N
        public void addPropertyChangeListener (PropertyChangeListener listener);
        public void removePropertyChangeListener (PropertyChangeListener listener);
        public BasicContext[] getDelegates ();
      }

  Implementation of Merged Context registers its PropertyChangeListener and as soon as`PROP_DELEGATES_CHANGED` is fired, then method `getDelegates ()` is called and this invokes refresh of root context and all live descendant subcontexts. This may cause, that `ContextEvents` are fired.

  Implementation of method `getDelegates ()` requires to return instances of some implementation of `BasicContext`. In ther words it means probably to provide its own implementation of`BasicContext`.
  Return value of this factory method can be passed to method `SpiUtils.createContext`to get `org.netbeans.api.registry.Context.`

## Overview

- #### Lifecycle of objects

  Any call of factory method creates new instance of Merged Context. This instance can be considered as root which means its parent is null.

  All descendant subcontexts of this instance lives so long as some non-Registry code is holding a reference to the object, subsequent calls will return the same object.

  On the other hand Merged Context does not create new instances for bindings, but delegates calls to its delegating contexts and then there is only one object and it should be the same without regard whether it was created from the regular Context or from the Merged Context.

- #### Merging

  Subcontext, bindings and attributes provided by their delegates are merged according their names. There is important that there is taken precedence in case of name conflict. There is true: the lower index in the array the higher priority.

  Merged Context provides subcontext for subcontext name only if at least one of its delegates provides this subcontext.

  Attributes related to bindings are not merged at all. On the other hand attributes related to context are merged from all delegates.

  If name for binding comes from two or more delegates, then the instance from delegate with highest priority is returned from Merged Context and also only this delegate with highest priority provides attributes and attribute names for this binding.

- #### Events

  Merged Context listens on its delegates and Merged Context is also source of events. Every modification on Merged Context fires ContextEvent. But not all events fired originally from delegates are propagated from Merged Context to its listeners, because not all changes on delegates mean modification of Merged Context.
  For example: creation of subcontext on read-only delegate isn't propagated from Merged Context in case, that subcontext with the same name already existed on active delegate.

  Listener registered on Merged Context will receive events about modifications of this context and all its descendant live subcontext instances.

  Destroy of subcontext destroys all descendant subcontexts and naturally all descendant subcontetxs must fire SubcontextEvents.

  There is obvious, that as soon as event is fired, then Merged Context must be in consistent status, which means that added or modified subcontext, binding or attribute must be reachable and on the other hand removed suncontext, binding or attribute can't be reachable.

- #### Delegates & active delegate

  All delegates may come from the same RootContex (in other words from the same implementation of BasicContext and RootContext).

  Active delegate is the one and the only one delegate with special meaning among others: all modifications go into active delegate (newly created, destroyed or modified subcontexts, bindings or attributes). All other delegates are considered as read-only. There is true that active delegate is the one with index zero.

  Delegates of Merged Context may be instances of Merged Context again.

  Method merge with zero delegates returns instance of context impl., that has no content (subcontexts, bindings, attributes). This is true for root context, else there won't be returned no subcontext instance if there is zero delegates. Zero-delegate merged context is automatically read-only.

  Destroyed subcontexts, bindings and attributes from read-only delegates must be masked on active delegate. Merged Context isolates its clients from this mechanism and masks must be considered as implementation detail.

- #### Resettability

  Merged Context provides resettable functionality and there is possible to reset whole context or individual binding in context. (see ResettableContext).

  Binding has default value if there exists binding on one of read-only delegates Binding is modified if this binding comes from active delegate or there doesn't exist default value for this binding. Revert of binding means destroy of binding on active delegate and also destroy its mask.

  In the merged context, Context.hasDefault(null) will be true when at least one read-only delegate has a context with the same path. Context.isModified(null) will be true in case a context with the same path exists on the active delegate (whether or not contexts with that path exist on any of the read-only delegates). Context.revert(null) will remove the context with the same path from the active delegate, if it exists.

## Limitations, problems, open issues

- there should be enhanced context merging with explicit support for overriding. At the moment are things merged according to order in array of delegates
- information about ordering provided by Merge Context is wrong and useless (see default.context.sorting in org.netbeans.core.registry.ContextImpl)
- use javax.swing.event.ChangeListener for one property instead of java.beans.PropertyChangeListener ?
- currently masks are considered to be only on active delegate; masks on read-only delegates aren't taken into consideration
- delegates of Merged Context may be instances of Merged Context again, but this is not true for active delegate
- masked stuff isn't filtered by Registry API (also active delegate shouldn't provide masks)
- lack of right synchronization at the moment