DSF Data Model

DSF Data Model

Version 1.0
Pawel Piech
© 2006, Wind River Systems.  Release under EPL version 1.0.

Overview

The data model aspect of DSF is only partially complete as compared to the Concurrency and Services Models.  The goals for its design are:

  1. Separate the structure of the data in the services from the model used for presentation in views.  This seems like a basic model-viewer separation, which is something that we theoretically have already.  But in reality the current platform debug model APIs closely correspond to how the data is laid out in debug views, and even with the flexible hierarchy views it is difficult to provide alternative layouts.
  2. Allow for a modular implementation of services that contribute to the data model.  
  3. Perform well with large data sets.
  4. Make the data model interfaces convenient to use by other services as well as by views.  Some interim designs of DSF data model APIs were very well suited for populating views (though asynchronous) content and label provider, but were very difficult to use for other purposes, such as by another service, or a client that creates a dialog.  This led to services implementing two sets of interfaces for the same data, which was more expensive to develop and maintain.
  5. Allow for easy changes to the layout of data in views.  This is from the point of view of a debugger implementer that would like to modify the standard layout of debugger data.
  6. Allow the users to modify the layout of data in views.  And this is a logical extension of the previous goal.

 

That's a pretty ambitious set of goals to keep in mind, which partly explains why the design is not fully complete yet.  In particular, the last goal doesn't have any implementation at this point.  But other than that the, we believe that our current design mostly meets the other goals.  It remains to be seen how well it will hold up beyond a prototype implementation.

The DSF data model is divided into two parts: a non-UI part that helps services expose data in a consistent form, and a UI part that helps viewers present the data.  They are described separately in the two sections below.

Timers Example

A "timers example" is included with the DSF plugins which demonstrates the use of data model and view model APIs.   It is probably much easier to digest this document when referring to this example for usage.

Data Model API (org.eclipse.dd.dsf.model)

As stated before, the aim of this API is to allow services to provide data with just enough common information, so that it can be easily presented in the view, but with a simple enough design, so that the data can be accessed by non-viewer clients.  The type of data in services can vary greatly from service to service, some data for example:
  • service data might be extremely large and thus may only be retrieved from a back end process in small chunks, while some service data might be always stored locally in the service
  • data might take a very long time to retrieve, or it could be instantaneous
  • some services might support canceling of the request while it is being processed, while other services might not
  • some data may change very frequently, other data may not change at all
The data model API tries to find a common denominator for these divergent properties and imposes the following restrictions:
  1. Each "chunk" of data that comes from a service has a corresponding IDataModelContext (Data Model Context) object.
  2. The DM-Context objects are to be generated by the data model services (IDataModelService) with either synchronous or asynchronous methods, and taking whatever arguments are needed.  Put differently, how DM-Contexts are created is up to the service.
  3. The service will provide a method for retrieving each "chunk" of model data (IDataModelData) using a method that requires no other arguments besides the DM-Contexts.
DM-Context (IMContext)
The DM-Contexts are the most important part of this design, so they warrant a closer look.  The interface is listed below:
    public interface IDataModelContext<V extends IDataModelData> extends IAdaptable {
public String getSessionId();
public String getServiceFilter();
public IDataModelContext[] getParents();
}
First of all the object extends IAdaptable, which allows clients to use these objects as handles that are stored with UI components.  However the implementation of IDataModelData.getAdapter()  presents a particular challenge.  If the standard platform method of retrieving an adapter is used ( PlatformObject.getAdapter()), then there can only be one adapter registered for a given DM-Context class, which has to be shared by all the DSF sessions that are running concurrently.  Thus one debugger that implements a IStack.IFrameDMContext, would have to have the same instance of IAsynchronousLabelAdapter as another debugger implementation that is running at the same time.  To overcome this problem, DSF provides a method for registering adapters with a session using DsfSession.registerModelAdapter(), instead of with the platform ( Platform.getAdapterManager().registerAdapters()). 

The getSessionId() method serves two purposes.  First, it allows the IAdapter.getAdapter() implementation to work as described above. Second, it allows clients to access the correct dispatch thread (DsfSession.getSession(id).getExecutor()) for calling the service that the DM-Context originated from. 

The getServiceFilter() method is actually included to allow future development.  It is intended to allow the client to precisely identify the service that the DM-Context originated from, without having to examine the exact class type of the DM-Context.  But this functionality will not really be needed until we start writing generic/data-driven clients.

The getParents() method allows the DM-Context to be connected together into something that can be considered a "model".  Of course, most debugger data objects, require the context of other objects in order to make sense: stack frame is meaningless without the thread, debug symbols belong to a module, which belongs to a process, etc.  In other words, there is some natural hierarchy to the data in debug services which needs to be accessible through the data model APIs.  This hierarchy may be the same hierarchy that is to be shown in some debug views, but it doesn't have to be.  More importantly, this hierarchy should allow for a clean separation of debug services, and for a clear dependency graph between these services.

View Model API (org.eclipse.dd.dsf.ui.model)

This is the component which allows the DSF data model to be presented in the views with different/configurable layouts.  It is tightly integrated with the recently added (and still provisional) flexible-hierarchy viewers in the org.eclipse.debug.ui plugin (see EclipseCon 2006 presentation for more details).  Actually, the platform flexible hierarchy framework already provides all the adapter interfaces needed to present the DSF data model in the viewers, and it is possible to do that.  However the flexible hierarchy views were not specifically designed for DSF, and there are a few ugly patterns that emerge when using them with DSF data model interfaces directly:
  • Because of the nature of IAdaptable pattern, the flexible hierarchy label and content adapters have to have a single instance that works for all views that the objects appear in.  This leads to a lot of if-else statements, which make the implementation difficult to follow.
  • There is a single adapter for all DSF data model elements in the tree (from the same session), so the adapters have even more if-else statements to handle the different elements in the viewer.
  • Most of DSF adapter work needs to be performed in the dispatch thread, so each handler starts with a re-dispatch call.
  • In all of this, the logic which determines the hierarchy of elements in the viewer is very hard to follow.
The view model API tries to address these issues in the following way:
  1. It divides the adapter work for different views in separate ViewModelProvider objects.
  2. It defines the view layout in an object-oriented manner using the IViewModelLayoutNode objects.
  3. It consolidates the logic of switching to dispatch thread in one place, and allows the ViewModelProvider objects to work only in dispatch thread.
IViewModelLayoutNode
The core of the logic in this design lies in the implementation of the IViewModelLayoutNode objects. This interface is listed below:
public interface IViewModelLayoutNode {
public IViewModelLayoutNode[] getChildNodes();
public void hasElements(IViewModelContext parentVmc, GetDataDone<Boolean> done);
public void getElements(final IViewModelContext parentVmc, GetDataDone<IViewModelContext[]> done);
public void retrieveLabel(IViewModelContext vmc, final ILabelRequestMonitor result);
public boolean hasDeltaFlags(IDataModelEvent e);
public void buildDelta(IDataModelEvent e, ViewModelDelta parent, Done done);
public void sessionDispose();
}
The getChildNodes() method allows these layout nodes to be combined into a tree structure, which mimics the layout of elements in the view.  What the children are depends on the implementation, some may be configurable and some may be fixed.

The hasElements() and getElements() methods generate the actual elements that will appear in the view.  The methods are analogous to the flexible hierarchy API methods: IAsynchronousContentAdapter.isContainer() and IAsynchronousContentAdapter.retrieveChildren() and are pretty straightforward to implement. Also retrieveLabel() is directly analogous to IAsynchronousLabelAdapter.retrieveLabel(). 

The hasDeltaFlags() and buildDelta() are used to generate model deltas in response to service events. These are discussed in the next section.

Finally, in most cases the elements in the views correspond directly to an IDataModelContext (DM-Context) objects of a specific type.  In those cases, the DMContextVMLayoutNode abstract class implements the common functionality in that pattern.
Model deltas
The hasDeltaFlags() and buildDelta() methods are used to implement the IModelProxy adapter, and are the most tricky aspect of this design.  The difficulty is that the flexible hierarchy views require that the IModelProxy translate data model-specific events, into generic model deltas that can be interpreted by the viewer.  The deltas ( IModelDelta) are tree structures which are supposed to mirror the structure of nodes in the tree, and which contain flags that tell the viewer what has changed in the view and how. *  This means that if the model proxy receives an event for some IDataModelContext (DM-Context) object, it needs to know if this object is in the viewer's tree, and what is the full path (or paths) that leads to this object. 

The model delta is generated by first calling the top layout node's hasDeltaFlags() with the received event, which then can either return true or ask any of its children if they have deltas (which in turn returns true or calls its children, etc).  If a node returns true for hasDeltaFlags(), then the asynchronous buildDelta() is called with the event and a parent delta node, to generate the delta elements and flags for its node.  Once the layout node generates its delta objects, it still needs to call its children, which in turn add their delta information, and so on.

* It's not strictly true that a full path to an element always has to be present for model delta's to work.  If the full path is not present, the viewer will try to find the element using an internal map that it keeps of all of the elements it knows.  But since the viewer is lazy loading, it is possible (and likely) that the element affected by an event is not even known to the viewer at time of the event, and for some delta actions, IModelDelta.SELECT and IModelDelta.EXPAND, this is not acceptable.

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值