blob: 615a25bb540e259ae2d967a22e2fd85f31362620 [file] [log] [blame]
Some notes about the CNF in attempt to understand things.
30 Nov 08 FRU
Documentation notes:
There is some good text potentially for the docs in:
Navigator Content
The purpose of the navigatorContent extension (NCE) is to bind a content provider
and label provider to the navigator under the right circumstances depending
on what is selected.
A navigator content descriptor has a one-to-one correspondence with the
navigator content extension, which is the higher level object representing
the navigator content.
An NCE may specify that it overrides another through the user of the override
mechanism where it specifies the NCE extension ID of the extension to override.
When doing the processing associated with the label and content provider, it is possible
that multiple NCEs meet the criteria for processing based on their triggerPoints and/or
possibleChildren. When this happens, the NCEs are ordered in a tree based
on the overrides specification, and then with that based on priority.
The first NCE in the tree that provides the answer (label or content) being
sought is used.
When the NCE is bound to the viewer (using the contentExtension element) it may
specify that that NCE serves as "root content" for that viewer, which allows
the viewer to start with an initial set of NCEs to process the root(s) [why exactly --
I understand intuitively but can't put it into words].
In some cases, it is desirable to have multiple NCEs be invoked during
content provider processing to modify the objects to be returned by the
content provider. This is done using the pipelining mechanism where each NCE
in the tree has the opportunity to modify the objects to be returned by the
content provider. There are also hooks for these pipelined NCEs to
be invoked at the Add/Remove/Update/Refresh methods on the viewer.
Selecting a Content Extension (NCE)
This is done either through the possibleChildren or triggerPoints expression
on the NCE. The enablement expression expression specifies the same expression
for both triggerPoints and possibleChildren.
The triggerPoints expression is used:
- by the NavigatorContentServiceContentProvider
to find extensions for getElements() (through the root content extensions)
or getChildren(). Given a parent node, that content extension is used
to determine what children to provide.
- by the NavigatorPipelineService for ???
The possibleChildren expression is used for all other cases, which include:
- Label/icon/description provider
- Selecting the NCE for a drop
The current documentation on these uses is incorrect, in particular for
the label provides, it says that the triggerPoints expression is used, but
it's not.
1) In NavigatorContentServicedLabelProvider, seems that we look for
label providers by looking for matching NCEs by possibleChild.
However the documentation is clear that content and label providers
are found by triggerPoint, possibleChild is used only in the content
provider getParent() case.
<mde>The API documentation for label providers is wrong. It has always been
the case that label providers rely on the possibleChildren; we should
correct the doc since actual implementations are relying on the
behavior of label providers using possible children (but only in
cases where the framework can't determine which extension contributed
the content with its short term memory mechanism).
Other parts of the framework (like the DND Drop Handlers) also rely on
possible children to determine which content extension's handlers
should be invoked when processing a drop operation. </mde>
2) In NavigatorContentServiceLabel provider, there seems to be special
processing to handle overriding instead of using the normal processing
to get only the relevant NCEs. Also, the way to get the NCEs is
different depending on whether doing a getImage() or getForeground()
for example and I don't understand why. Also
NavigatorContentServiceDescriptionProvider does not seem to consider
overrides at all. This all seems wrong.
The content extension has alot of complexity because it has to
determine the overrides for each individual child along the path.
Labels are a bit simpler because only one value can be returned.
The override logic recursively walks the override path by the highest
priority label provider. Overriding label providers can opt not to
return a label (e.g. null), and the framework will fall back on the
base label provider to provide a value.
The getForeground()/getFont() cases weren't retrofitted for
overriding values. This was just a legacy/point in time and
is arguably a bug. The Font and Color providers were added to
support the Team providers (they weren't part of the original product
framework). If anything, the getFont()/Foreground/Background()
methods should be retrofitted to follow the patter in getText/Image().
3) The triggerPoints and possibleChildren descriptions in the exsd are
"The triggerPoints expression defines the nodes in a tree that should
cause this extension to be invoked for children."
"The possibleChildren expression defines the nodes in a tree that
could be contributed by this extension. Clients should describes when
this content extension could provide a parent for elements that match
the expression."
I think a clearer description is:
The triggerPoints expression defines the tree nodes for which this
extension (and associated content/label providers) is to be invoked.
<mde>Remove "label providers" and we're good. </mde>
The possibleChildren expression defines the content provider to be
selected when processing the ITreeContentProvider.getParent() call.
<mde>As well as the DND Drop Handler and Action Providers.</mde>
4) Why is the overridden tree computed only on
findDescriptorsForPossibleChild and not for
findDescriptorsForTriggerPoint? The extension point documentation for
the overrides element says that the overrides only applied to
triggerpoints, it does not mention possibleChildren (except in
reference to the OnlySuppressedifExtVisibleAndActive option is set,
but it still seems to say that only triggerPoints is actually used).
<mde>It might be worth setting up an hour to go through a deep dive
for this material. If I recall correctly, there was a challenge in
computing all of the triggerpoint overrides a priori, but the
overrides are used. The NavigatorContentServiceContentProvider
processes a given element for children (e.g. getChildren()),
and then invokes the overridden providers directly (from within
the Service content provider) to compute the overridden tree.
The possible children through doesn't have a use case like this,
and so these can be computed and returned as needed.</mde>
5) More confusion (from the overrides exsd):
"InvokeAlwaysRegardlessOfSuppressedExt (default): Indicates that this
extension is a first class extension; it should be given opportunities
to contribute content regardless if its suppressedExtensionId is
visible or active to the viewer. Thus, the extension is a first-class
extension and an overriding extension. It will be invoked whenever its
triggerPoints expression is matched. When the suppressed extension and
its triggerPoints extension is matched, it will be invoked with the
contributed items from its suppressed extension; the suppressed
extension will not be given an opportunity to directly contribute."
The last sentence of this is unclear. I think it would be better (and
consistent with the rest of the explanations) if the suppressed
extension was just suppressed, it is never invoked at all.
<mde>The use cases here are as follows:
1. I want to contribute my stuff, and I want to specialize an existing provider
like Java, and
2. I want to just specialize another provider, and if it's not turned on, I don't
even need to be considered.
So in neither case is it completely suppressed, but rather in case 2 it acts
like a domino of its source provider. I just want to make sure we don't
lose this meaning in the update of the documentation </mde>
6) Should the NCE class and NavigatorContentDescriptor classes be
combined? They seem to be one-to-one and there is a lot of code that
seems to convert from one to another for no good reason.
<mde>The Descriptor/Extension pattern is used for every part of the framework, here's why:
A Manager handles Descriptors; Managers are singletons, and each known extension
has exactly one Descriptor. The state associated with a Descriptor is just an API
convenience layer on top of the IConfigurationElement API. These are relatively
lightweight, and their lifecycle is the lifecycle of the Manager, which is
effectively the lifecycle of the workbench.
A Service handles Extensions; Services are 1 instance per Common Navigator viewer
(or whatever client is using the Service). Each Service creates an Extension
and manages a map from Descriptor (1 instance for each extension for all
viewers) to its Extension (1 instance for each instance viewer). The Extension
instance creates instances of classes defined by the plugin extension metadata;
each of these instances has an associated lifecycle (init .. do work .. dispose)
and can hold on to system resources (label providers can hold Fonts or Colors,
content providers might talk to a data source across a network). Most of these
classes aren't necessarily designed to be re-entrant (they make assumptions
about the current state of a given viewer, like whether to show
Packages as hierarchical or flattened). So if I create a Project Explorer,
and then separately create a MyCustomView and bind Java to each of them,
I'll have 1 Java Descriptor, and 2 Java Extensions in memory.
Collapsing Descriptors into Extensions would be a fundamental change to the
framework, eliminate the assumption that there's 1 content
provider/label provider/etc for each view instance, and make it hard or
impossible to know when each of the instantiated classes from each
plugin extension can be disposed. I would not recommend this.
7) There are substantial chunks of duplicate code where only one
line is different between the similar methods.
<mde>If you can provide a few examples, I might be able to provide some
insights here; in many cases, I tried to use the same simple patterns
throughout the frameworks to keep the overall complexity down, which
can lead to this kind of pattern.</mde>