/*******************************************************************************
* Copyright (c) 2005, 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Wind River Systems - adapted to use with DSF
* Dobrin Alexiev (Texas Instruments) - user groups support (bug 240208)
*******************************************************************************/
package org.eclipse.cdt.dsf.ui.viewmodel;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.Viewer;
/**
* This is the default implementation of {@link IModelProxy} interface for
* use by a view model provider. It implements an algorithm to walk the
* tree hierarchy of nodes configured with a provider in order to compose
* an {@link IModelDelta} for a given data model event.
* <p/>
* This class is closely linked with a view model provider which is required
* for the constructor. The view model provider is used to access the correct
* executor and the node hierarchy.
*
* @since 1.0
*/
@ConfinedToDsfExecutor("#getProvider()#getExecutor()")
@SuppressWarnings("restriction")
public class DefaultVMModelProxyStrategy implements IVMModelProxy {
private final AbstractVMProvider fProvider;
private final Object fRootElement;
private IPresentationContext fContext;
private Viewer fViewer;
private boolean fDisposed = false;
private ListenerList fListeners = new ListenerList();
private IDoubleClickListener fDoubleClickListener;
private boolean fAllowRecursiveVMNodes = false;
/**
* Creates this model proxy strategy for the given provider.
*/
public DefaultVMModelProxyStrategy(AbstractVMProvider provider, Object rootElement) {
fProvider = provider;
fRootElement = rootElement;
}
public boolean isDeltaEvent(Object event) {
return getEventDeltaFlags(event) != IModelDelta.NO_CHANGE;
}
public int getEventDeltaFlags(Object event) {
IRootVMNode rootNode = getVMProvider().getRootVMNode();
if (rootNode != null &&
rootNode.isDeltaEvent(getRootElement(), event))
{
return getDeltaFlags(rootNode, null, event);
}
return IModelDelta.NO_CHANGE;
}
/**
* Returns the view model provider that this strategy is configured for.
* @return
*/
protected AbstractVMProvider getVMProvider() {
return fProvider;
}
private Object[] getListeners() {
return fListeners.getListeners();
}
public void addModelChangedListener(IModelChangedListener listener) {
fListeners.add(listener);
}
public void removeModelChangedListener(IModelChangedListener listener) {
fListeners.remove(listener);
}
public Object getRootElement() {
return fRootElement;
}
/** @since 1.1 */
public Object getViewerInput() {
return fRootElement;
}
/** @since 1.1 */
public TreePath getRootPath() {
return TreePath.EMPTY;
}
/**
* Notifies registered listeners of the given delta.
*
* @param delta model delta to broadcast
*/
public void fireModelChanged(IModelDelta delta) {
final IModelDelta root = getRootDelta(delta);
Object[] listeners = getListeners();
for (int i = 0; i < listeners.length; i++) {
final IModelChangedListener listener = (IModelChangedListener) listeners[i];
ISafeRunnable safeRunnable = new ISafeRunnable() {
public void handleException(Throwable exception) {
DebugUIPlugin.log(exception);
}
public void run() throws Exception {
listener.modelChanged(root, DefaultVMModelProxyStrategy.this);
}
};
SafeRunner.run(safeRunnable);
}
}
/**
* Convenience method that returns the root node of the given delta.
*
* @param delta delta node
* @return returns the root of the given delta
*/
protected IModelDelta getRootDelta(IModelDelta delta) {
IModelDelta parent = delta.getParentDelta();
while (parent != null) {
delta = parent;
parent = delta.getParentDelta();
}
return delta;
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.IModelProxy#dispose()
*/
public void dispose() {
fDisposed = true;
if (fViewer instanceof StructuredViewer && fDoubleClickListener != null) {
((StructuredViewer) fViewer).removeDoubleClickListener(fDoubleClickListener);
fDoubleClickListener= null;
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.IModelProxy#init(org.eclipse.debug.internal.ui.viewers.IPresentationContext)
*/
public void init(IPresentationContext context) {
fDisposed = false;
fContext = context;
}
/**
* Returns the context this model proxy is installed in.
*
* @return presentation context, or <code>null</code> if this
* model proxy has been disposed
*/
public IPresentationContext getPresentationContext() {
return fContext;
}
/* (non-Javadoc)
*
* Subclasses should override as required.
*
* @see org.eclipse.debug.internal.ui.viewers.provisional.IModelProxy#installed(org.eclipse.jface.viewers.Viewer)
*/
public void installed(final Viewer viewer) {
fViewer = viewer;
getVMProvider().getExecutor().execute( new DsfRunnable() {
public void run() {
fProvider.handleEvent(new ModelProxyInstalledEvent(DefaultVMModelProxyStrategy.this, viewer, fRootElement));
}
});
if (fViewer instanceof StructuredViewer && fDoubleClickListener == null) {
((StructuredViewer) fViewer).addDoubleClickListener(fDoubleClickListener= new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent e) {
handleDoubleClick(e);
}
});
}
}
/**
* Handle viewer double click.
*
* @param e the event
*
* @since 1.1
*/
protected void handleDoubleClick(final DoubleClickEvent e) {
final AbstractVMProvider vmProvider= getVMProvider();
if (!vmProvider.isDisposed()) {
ISelection selection = e.getSelection();
if (!selection.isEmpty() && selection instanceof ITreeSelection) {
final TreePath path = ((ITreeSelection)selection).getPaths()[0];
final Object input = e.getViewer().getInput();
vmProvider.getExecutor().execute( new DsfRunnable() {
public void run() {
Object rootElement = getRootElement();
boolean eventContainsRootElement = rootElement.equals(input);
for (int i = 0; !eventContainsRootElement && i < path.getSegmentCount(); i++) {
eventContainsRootElement = rootElement.equals(path.getSegment(i));
}
if (eventContainsRootElement) {
vmProvider.handleEvent(e);
}
}
});
}
}
}
/**
* Returns the viewer this proxy is installed in.
*
* @return viewer or <code>null</code> if not installed
*
* @since 1.1
*/
public Viewer getViewer() {
return fViewer;
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy#isDisposed()
*/
public boolean isDisposed() {
return fDisposed;
}
/**
* Recursively calls the VM nodes in the hierarchy of the given node to
* determine how elements of those types may be (or are) affected by
* [event], the answer being a collection of IModelDelta flags.
*
* A response of IModeDelta.CONTENT has a special meaning. If we return that
* flag for an IVMNode, it means the <i>collection</i> of elements of that
* type are affected. It is not a statement on the elements themselves.
*
* Optimization 1: If the first-level child node does not have
* <code>IModelDelta.CONTENT</code> but one of its descendants does, then
* for optimization reasons we return <code>IModelDelta.STATE</code>
* instead. See {@link DefaultVMModelProxyStrategy#buildChildDeltasForAllContexts(IVMNode, Object, VMDelta, int, RequestMonitor)}
*
* Optimization 2: If the parent delta contains
* <code>IModelDelta.CONTENT</code>, we do not need to specify it for its
* children. This can shorten delta processing considerably.
*
* @param node
* the IVMNode whose delta flags (and those of its descendants)
* are being queried
* @param parentDelta
* the base portion of the delta the caller is trying to
* construct; this delta node is specifically the deepest node in
* that chain (i.e., has no children, but may have ancestors)
* @param event
* the event the caller is trying to produce a delta for
* @return the collective set of IModelDelta flags that reflect how [node]
* and its descendants may be (or are) affected by [event]
*/
protected int getDeltaFlags(IVMNode node, ModelDelta parentDelta, Object event) {
int flags = node.getDeltaFlags(event);
for (IVMNode childNode : getVMProvider().getChildVMNodes(node)) {
if (!childNode.equals(node)) {
int childNodeDeltaFlags = getDeltaFlags(childNode, parentDelta, event);
// optimization 1; see above
if ((childNodeDeltaFlags & IModelDelta.CONTENT) != 0) {
childNodeDeltaFlags &= ~IModelDelta.CONTENT;
childNodeDeltaFlags |= IModelDelta.STATE;
}
flags |= childNodeDeltaFlags;
}
}
// optimization 2; see above
while (parentDelta != null) {
if ( (parentDelta.getFlags() & IModelDelta.CONTENT) != 0 ) {
flags = flags & ~IModelDelta.CONTENT & ~IModelDelta.STATE;
break;
}
parentDelta = (ModelDelta)parentDelta.getParentDelta();
}
return flags;
}
/**
* Default implementation creates a delta assuming that the root node
* is the input object into the view.
*/
public void createDelta(final Object event, final DataRequestMonitor<IModelDelta> rm) {
final IRootVMNode rootNode = getVMProvider().getRootVMNode();
// Always create the rootDelta, no matter what delta flags the child nodes have.
rootNode.createRootDelta(
getRootElement(), event,
new DataRequestMonitor<VMDelta>(getVMProvider().getExecutor(), rm) {
@Override
protected void handleSuccess() {
// The resulting delta will have parents if our
// VMProvider is registered to populate only a sub-tree
// of the viewer. Get the root node of the chain--i.e.,
// the delta for the root element of the entire viewer.
final IModelDelta viewRootDelta = getRootDelta(getData());
// Find the child nodes that (may) have deltas for the given event.
final Map<IVMNode,Integer> childNodesWithDeltaFlags = getChildNodesWithDeltaFlags(rootNode, getData(), event);
// If no child nodes have deltas we can stop here.
if (childNodesWithDeltaFlags.size() == 0) {
rm.setData(viewRootDelta);
rm.done();
return;
}
callChildNodesToBuildDelta(
rootNode,
childNodesWithDeltaFlags, getData(), event,
new RequestMonitor(getVMProvider().getExecutor(), rm) {
@Override
protected void handleSuccess() {
// Get rid of redundant CONTENT and STATE flags in delta and prune
// nodes without flags
rm.setData(pruneDelta((VMDelta)viewRootDelta));
rm.done();
}
});
}
});
}
protected VMDelta pruneDelta(VMDelta delta) {
delta.accept(new IModelDeltaVisitor() {
public boolean visit(IModelDelta deltaNode, int depth) {
if ((deltaNode.getFlags() & (IModelDelta.CONTENT | IModelDelta.STATE)) != 0) {
VMDelta parent = (VMDelta)deltaNode.getParentDelta();
while (parent != null) {
if ((parent.getFlags() & IModelDelta.CONTENT) != 0) {
((VMDelta)deltaNode).setFlags(deltaNode.getFlags() & ~(IModelDelta.CONTENT | IModelDelta.STATE));
break;
}
parent = parent.getParentDelta();
}
}
return true;
}
});
return delta;
}
/**
* Base implementation that handles calling child nodes to build
* the model delta. This method delegates to two other methods:
* {@link #buildChildDeltasForEventContext(IVMContext[], IVMNode, Object, VMDelta, int, RequestMonitor)}
* and {@link #buildChildDeltasForAllContexts(IVMNode, Object, VMDelta, int, RequestMonitor)},
* depending on the result of calling{@link IVMNode#getContextsForEvent(VMDelta, Object, DataRequestMonitor)}.
* @see IVMNode#buildDelta(Object, ModelDelta, int, RequestMonitor)
*/
protected void buildChildDeltas(final IVMNode node, final Object event, final VMDelta parentDelta,
final int nodeOffset, final RequestMonitor rm)
{
node.getContextsForEvent(
parentDelta,
event,
new DataRequestMonitor<IVMContext[]>(getVMProvider().getExecutor(), rm) {
@Override
protected void handleCompleted() {
if (isSuccess()) {
assert getData() != null;
buildChildDeltasForEventContext(getData(), node, event, parentDelta, nodeOffset, rm);
}
else if (getStatus().getCode() == IDsfStatusConstants.NOT_SUPPORTED) {
// The DMC for this node was not found in the event. Call the
// super-class to resort to the default behavior which will add a
// delta for every element in this node.
buildChildDeltasForAllContexts(node, event, parentDelta, nodeOffset, rm);
}
else {
super.handleCompleted();
}
}
});
}
/**
* Base implementation that handles calling child nodes to build
* the model delta. This method is called with the context obtained
* by calling{@link IVMNode#getContextsForEvent(VMDelta, Object, DataRequestMonitor)}.
* @see IVMNode#buildDelta(Object, ModelDelta, int, RequestMonitor)
*/
protected void buildChildDeltasForEventContext(final IVMContext[] vmcs, final IVMNode node, final Object event,
final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm)
{
final Map<IVMNode,Integer> childNodesWithDeltaFlags = getChildNodesWithDeltaFlags(node, parentDelta, event);
if (childNodesWithDeltaFlags.size() == 0) {
// There are no child nodes with deltas, just return to parent.
rm.done();
return;
}
// Check if any of the child nodes are will generate IModelDelta.SELECT or
// IModelDelta.EXPAND flags. If so, we must calculate the index for this
// VMC.
boolean calculateIndex = false;
if (nodeOffset >= 0) {
for (int childDelta : childNodesWithDeltaFlags.values()) {
if ( (childDelta & (IModelDelta.SELECT | IModelDelta.EXPAND)) != 0 ) {
calculateIndex = true;
break;
}
}
}
if (calculateIndex) {
// Calculate the index of this node by retrieving all the
// elements and then finding the DMC that the event is for.
getVMProvider().updateNode(
node,
new VMChildrenUpdate(
parentDelta, getVMProvider().getPresentationContext(), -1, -1,
new DataRequestMonitor<List<Object>>(getVMProvider().getExecutor(), rm) {
@Override
protected void handleSuccess() {
// Check for an empty list of elements. If it's empty then we
// don't have to call the children nodes, so return here.
// No need to propagate error, there's no means or need to display it.
if (getData().isEmpty()) {
rm.done();
return;
}
CountingRequestMonitor countingRm =
new CountingRequestMonitor(getVMProvider().getExecutor(), rm);
int count = 0;
for (IVMContext vmc : vmcs) {
// Find the index of the vmc in the full list of elements.
int i;
for (i = 0; i < getData().size(); i++) {
if (vmc.equals(getData().get(i))) break;
}
if (i == getData().size()) {
// Element not found, no need to generate the delta.
continue;
}
// Optimization: Try to find a delta with a matching element, if found use it.
// Otherwise create a new delta for the event element.
int elementIndex = nodeOffset + i;
VMDelta delta = parentDelta.getChildDelta(vmc);
if (delta == null || delta.getIndex() != elementIndex) {
delta = parentDelta.addNode(vmc, elementIndex, IModelDelta.NO_CHANGE);
}
callChildNodesToBuildDelta(
node, childNodesWithDeltaFlags, delta, event, countingRm);
count++;
}
countingRm.setDoneCount(count);
}
}));
} else {
CountingRequestMonitor countingRm =
new CountingRequestMonitor(getVMProvider().getExecutor(), rm);
int count = 0;
for (IVMContext vmc : vmcs) {
// Optimization: Try to find a delta with a matching element, if found use it.
// Otherwise create a new delta for the event element.
VMDelta delta = parentDelta.getChildDelta(vmc);
if (delta == null) {
delta = parentDelta.addNode(vmc, IModelDelta.NO_CHANGE);
}
callChildNodesToBuildDelta(node, childNodesWithDeltaFlags, delta, event, rm);
count++;
}
countingRm.setDoneCount(count);
}
}
/**
* Base implementation that handles calling child nodes to build
* the model delta. The child nodes are called with all the elements
* in this node, which could be very inefficient. This method is only
* called if {@link IVMNode#getContextsForEvent(VMDelta, Object, DataRequestMonitor)}
* is not supported by the node for the given element.
* @see IVMNode#buildDelta(Object, ModelDelta, int, RequestMonitor)
*/
protected void buildChildDeltasForAllContexts(final IVMNode node, final Object event, final VMDelta parentDelta,
final int nodeOffset, final RequestMonitor rm)
{
final Map<IVMNode,Integer> childNodesWithDeltaFlags = getChildNodesWithDeltaFlags(node, parentDelta, event);
if (childNodesWithDeltaFlags.size() == 0) {
// There are no child nodes with deltas, just return to parent.
rm.done();
return;
}
// Check if the child delta only has an IModelDelta.STATE flag. If
// that's the case, we can skip creating a delta for this node, because
// the Debug Platform's handling of that flag does not require ancestor
// deltas (doesn't need to know the path to the element)
//
// We can skip the delta for this node even if a deeper IVMNode child
// calls for an IModelDelta.CONTENT flag, since what we do in that case
// is have the CONTENT flag applied to the first ancestor delta that has
// something other than IModelDelta.STATE.
//
// The main benefit of this optimization is that the viewer is left
// to retrieve the elements that need to be refreshed, rather
// than having this proxy do it. Since the viewer is lazy loading
// it may not need to retrieve as many elements in the hierarchy.
//
// For example: suppose the model looks like:
// A-
// |-1
// |-I
// | |-a
// | |-b
// |-II
// | |-c
// |-III
// |-d
//
// And if VM Node responsible for element a, b, c, d needs a CONTENT
// update, then the delta may look like this:
//
// Element: A
// Flags: CONTENT
// Index: 0 Child Count:-1
//
// Instead of:
//
// Element: A
// Flags: NO_CHANGE
// Index: 0 Child Count: 1
// Element: 1
// Flags: NO_CHANGE
// Index: 0 Child Count: 3
// Element: I
// Flags: CONTENT
// Index: 0 Child Count: 2
// Element: II
// Flags: CONTENT
// Index: 1 Child Count: 1
// Element: III
// Flags: CONTENT
// Index: 2 Child Count: 1
boolean mustGetElements = false;
boolean _updateFlagsOnly = true;
for (int childDelta : childNodesWithDeltaFlags.values()) {
if ((childDelta & ~IModelDelta.STATE) != 0) {
mustGetElements = true;
}
if ((childDelta & ~(IModelDelta.STATE | IModelDelta.CONTENT)) != 0) {
_updateFlagsOnly = false;
}
}
final boolean updateFlagsOnly = _updateFlagsOnly;
if (!mustGetElements) {
callChildNodesToBuildDelta(
node, childNodesWithDeltaFlags, parentDelta, event,
new RequestMonitor(getVMProvider().getExecutor(), rm) {
@Override
protected void handleError() {
super.handleError();
}
});
} else {
// The given child nodes have deltas potentially for all elements
// from this node. Retrieve all elements and call the child nodes with
// each element as the parent of their delta.
getVMProvider().updateNode(
node,
new VMChildrenUpdate(
parentDelta, getVMProvider().getPresentationContext(), -1, -1,
new DataRequestMonitor<List<Object>>(getVMProvider().getExecutor(), rm) {
@Override
protected void handleCompleted() {
if (fDisposed) return;
final List<Object> childElements = getData();
// Check for an empty list of elements. If the list of elements is empty
// still call the child nodes using the parent delta only. Do this only if
// an optimization was used to build the delta, so that the child node can
// adds the optimized delta flags without the full delta (bug 280770).
if (childElements == null || childElements.size() == 0) {
if (updateFlagsOnly) {
callChildNodesToBuildDelta(
node, childNodesWithDeltaFlags, parentDelta, event, rm);
} else {
rm.done();
return;
}
} else {
final CountingRequestMonitor countingRM = new CountingRequestMonitor(getVMProvider().getExecutor(), rm);
int rmCount = 0;
// For each element from this node, create a new delta,
// and then call all the child nodes to build their delta.
for (int i = 0; i < childElements.size(); i++) {
int elementIndex = nodeOffset >= 0 ? nodeOffset + i : -1;
VMDelta delta= parentDelta.getChildDelta(childElements.get(i));
if (delta == null) {
delta= parentDelta.addNode(childElements.get(i), elementIndex, IModelDelta.NO_CHANGE);
}
callChildNodesToBuildDelta(node, childNodesWithDeltaFlags, delta, event, countingRM);
rmCount++;
}
countingRM.setDoneCount(rmCount);
}
}
})
);
}
}
/**
* Calls the specified child nodes (of [node]) to build the delta for the
* given event.
*
* @param childNodes
* Map of nodes to be invoked, and the corresponding delta flags
* that they may (or will) generate. This map is generated with a
* call to {@link #getChildNodesWithDeltaFlags(Object)}.
* @param delta
* The delta object to build on. This delta should have been
* generated by this node, unless the full delta path is not
* being calculated due to an optimization.
* @param event
* The event object that the delta is being built for.
* @param rm
* The result monitor to invoke when the delta is completed.
*/
protected void callChildNodesToBuildDelta(final IVMNode node, final Map<IVMNode,Integer> childNodes, final VMDelta delta,
final Object event, final RequestMonitor rm)
{
assert childNodes.size() != 0;
// Check if any of the child nodes might generate a delta that requires
// us to calculate the index for this VMC.
boolean calculateOffsets = false;
for (int childDelta : childNodes.values()) {
if ( (childDelta & (IModelDelta.SELECT | IModelDelta.EXPAND | IModelDelta.INSERTED | IModelDelta.REMOVED)) != 0 ) {
calculateOffsets = true;
break;
}
}
getChildNodesElementOffsets(
node, delta, calculateOffsets,
new DataRequestMonitor<Map<IVMNode, Integer>>(getVMProvider().getExecutor(), rm) {
@Override
protected void handleSuccess() {
final CountingRequestMonitor multiRm = new CountingRequestMonitor(getVMProvider().getExecutor(), rm);
int multiRmCount = 0;
// Set the total count of number of children in the parent delta.
delta.setChildCount(getData().get(null));
for (final IVMNode childNode : childNodes.keySet()) {
if (node.equals(childNode)) {
// Avoid descending into recursive node hierarchy's when calculating the delta.
// if recursive nodes are not allowed.
if( !allowRecursiveVMNodes())
continue;
// get into recursion to build the delta only if the recursive context is added.
//
// We user current assumption that recursive container can be added as first children
// if the list of VMNodes. If we decide to make the patch more generic ( allow recursive
// node to be at different index) we need to remove this simplification.
//
if( isDeltaElementOfType(delta, childNode)) {
childNode.buildDelta(
event, delta, 0,
new RequestMonitor(getVMProvider().getExecutor(), multiRm) {
@Override
protected void handleSuccess() {
buildChildDeltas(
childNode, event, delta, 0,
new RequestMonitor(getVMProvider().getExecutor(), multiRm) );
}
});
multiRmCount++;
}
continue;
}
final int nodeOffset = getData().get(childNode);
childNode.buildDelta(
event, delta, nodeOffset,
new RequestMonitor(getVMProvider().getExecutor(), multiRm) {
@Override
protected void handleSuccess() {
buildChildDeltas(
childNode, event, delta, nodeOffset,
new RequestMonitor(getVMProvider().getExecutor(), multiRm) );
}
});
multiRmCount++;
}
multiRm.setDoneCount(multiRmCount);
}
});
}
/**
* Calculates the indexes at which the elements of each of the child
* nodes begin. These indexes are necessary to correctly
* calculate the deltas for elements in the child nodes.
* @param delta The delta object to build on. This delta should have been
* generated by this node, unless the full delta path is not being calculated
* due to an optimization.
* @param doCalculdateOffsets If true, the method calls each node to get its
* element count. If false, it causes this method to fill the return data
* structure with dummy values. The dummy values indicate that the indexes
* are not known and are acceptable in the delta if the delta flags being
* generated do not require full index information.
* @param rm Return token containing the results. The result data is a
* mapping between the child nodes and the indexes at which the child nodes'
* elements begin. There is a special value in the map with a <code>null</code>
* key, which contains the full element count for all the nodes.
*/
private void getChildNodesElementOffsets(IVMNode node, IModelDelta delta, boolean calculdateOffsets, final DataRequestMonitor<Map<IVMNode, Integer>> rm) {
final IVMNode[] childNodes = getVMProvider().getChildVMNodes(node);
assert childNodes.length != 0;
if (calculdateOffsets) {
final Integer[] counts = new Integer[childNodes.length];
final CountingRequestMonitor crm = new CountingRequestMonitor(getVMProvider().getExecutor(), rm) {
@Override
protected void handleSuccess() {
Map<IVMNode, Integer> data = new HashMap<IVMNode, Integer>();
int offset = 0;
for (int i = 0; i < childNodes.length; i++) {
data.put(childNodes[i], offset);
offset += counts[i];
}
// As the final value, put the total count in the return map, with null key.
data.put(null, offset);
rm.setData(data);
rm.done();
}
};
int countRM = 0;
for (int i = 0; i < childNodes.length; i++) {
final int nodeIndex = i;
getVMProvider().updateNode(
childNodes[i],
new VMChildrenCountUpdate(
delta, getVMProvider().getPresentationContext(),
new DataRequestMonitor<Integer>(getVMProvider().getExecutor(), crm) {
@Override
protected void handleCompleted() {
counts[nodeIndex] = getData();
crm.done();
}
}
)
);
countRM++;
}
crm.setDoneCount(countRM);
} else {
Map<IVMNode, Integer> data = new HashMap<IVMNode, Integer>();
for (int i = 0; i < childNodes.length; i++) {
data.put(childNodes[i], -1);
}
data.put(null, -1);
rm.setData(data);
rm.done();
}
}
/**
* Convenience method that returns what each of the child nodes returns from
* {@link #getDeltaFlags(IVMNode, ModelDelta, Object)}. Children that return
* IModelDelta.NO_CHANGE are omitted.
*/
protected Map<IVMNode, Integer> getChildNodesWithDeltaFlags(IVMNode node, ModelDelta parentDelta, Object e) {
Map<IVMNode, Integer> nodes = new HashMap<IVMNode, Integer>();
for (final IVMNode childNode : getVMProvider().getChildVMNodes(node)) {
if (!childNode.equals(node) || allowRecursiveVMNodes()) {
int delta = getDeltaFlags(childNode, parentDelta, e);
if (delta != IModelDelta.NO_CHANGE) {
nodes.put(childNode, delta);
}
}
}
return nodes;
}
/**
* Returns whether DefaultVMModelProxyStrategy allows to handles recursive VMNdoes hierarchy.
*
* @see setAllowRecursiveVMNodes()
* @return true if this DefaultVMModelProxyStrategy allows recursive containers.
*/
public boolean allowRecursiveVMNodes() {
return fAllowRecursiveVMNodes;
}
/**
* Allow DefaultVMModelProxyStrategy to handles recursive VMNdoes hierarchy.
*
* For example if the client wants the debug view to display container nodes that
* have containers this flag has to be set.
*
* Launch1
* Container-1
* Container-1.1
* Thread-1
* Container-1.1.1
* Thread-2
* Thread-2
*
* This will allow the client to setup a VMNode to be in the list of its children.
* addChildNodes(containerNode, new IVMNode[] { containerNode, threadsNode });
*
* The client also need to make sure the recursive VMNodes and their immediate children:
* 1. Handles buildDelta() by building one level at a time by examining the delta passed as parameter.
* 2. Returns the correct level container inside getContextsForEvent() based on the delta passed.
*
* See org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch.ContainerVMNode for sample implementation.
*
* @param allow - whether to allow or not recursive containment of VMNodes.
*/
public void setAllowRecursiveVMNodes( boolean allow) {
fAllowRecursiveVMNodes = allow;
}
/**
* Compares if the VMNode of element of the provided delta is the same as the provided IVMNode.
*
* @param delta - delta for which to compare the IDMVMContext
* @param node - the IVMNode we want to compare to.
* @return if the VMNode of element of the provided delta is the same as the provided IVMNode.
*/
protected boolean isDeltaElementOfType( VMDelta delta, IVMNode node) {
if( delta.getElement() instanceof IDMVMContext) {
IDMVMContext dmvmContext = (IDMVMContext)delta.getElement();
return dmvmContext.getVMNode().equals(node);
}
return false;
}
}