From b191075ec4d85f4cc588e7556be42d8f15e4745e Mon Sep 17 00:00:00 2001 From: Pawel Piech Date: Thu, 30 Nov 2006 22:47:43 +0000 Subject: [PATCH] Ported to new flexible hierarchy APIs (bug 164341). --- .../DebugViewSelectionRootLayoutNode.java | 81 ++- .../launch/LaunchViewModelProvider.java | 70 -- .../launch/StackFramesLayoutNode.java | 402 ++++++----- .../launch/StandardLaunchRootLayoutNode.java | 89 ++- .../launch/StandardProcessLayoutNode.java | 121 ++-- .../register/RegisterColumnPresentation.java | 4 +- .../register/RegisterGroupLayoutNode.java | 102 +-- .../register/RegisterLayoutNode.java | 127 ++-- .../register/RegisterVMProvider.java | 26 +- .../META-INF/MANIFEST.MF | 3 +- .../dsf/ui/viewmodel/AbstractVMAdapter.java | 270 ++++---- .../ui/viewmodel/AbstractVMLayoutNode.java | 443 +++++++++--- .../dsf/ui/viewmodel/AbstractVMProvider.java | 648 ++++++++++++++++++ .../viewmodel/AbstractVMRootLayoutNode.java | 117 ++-- .../ui/viewmodel/DMContextVMLayoutNode.java | 360 ---------- .../dd/dsf/ui/viewmodel/IVMAdapter.java | 19 + .../dd/dsf/ui/viewmodel/IVMContext.java | 8 +- .../dd/dsf/ui/viewmodel/IVMLayoutNode.java | 119 ++-- .../dd/dsf/ui/viewmodel/IVMProvider.java | 40 ++ .../dsf/ui/viewmodel/IVMRootLayoutNode.java | 19 +- .../eclipse/dd/dsf/ui/viewmodel/VMDelta.java | 204 +++--- .../dd/dsf/ui/viewmodel/VMProvider.java | 439 ------------ .../ui/viewmodel/dm/AbstractDMVMAdapter.java | 44 ++ .../viewmodel/dm/AbstractDMVMLayoutNode.java | 501 ++++++++++++++ .../ui/viewmodel/dm/AbstractDMVMProvider.java | 139 ++++ .../dd/dsf/concurrent/DefaultDsfExecutor.java | 21 +- .../dd/dsf/concurrent/DoneCollector.java | 40 +- .../dd/dsf/concurrent/DsfExecutable.java | 22 +- .../dd/dsf/datamodel/AbstractDMContext.java | 9 +- .../dd/dsf/datamodel/ServiceDMContext.java | 5 +- .../eclipse/dd/dsf/service/IDsfService.java | 6 +- 31 files changed, 2619 insertions(+), 1879 deletions(-) delete mode 100644 plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/LaunchViewModelProvider.java create mode 100644 plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMProvider.java delete mode 100644 plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/DMContextVMLayoutNode.java create mode 100644 plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMAdapter.java create mode 100644 plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMProvider.java delete mode 100644 plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/VMProvider.java create mode 100644 plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMAdapter.java create mode 100644 plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMLayoutNode.java create mode 100644 plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMProvider.java diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/DebugViewSelectionRootLayoutNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/DebugViewSelectionRootLayoutNode.java index 18d00b8d4b4..a147deafe8f 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/DebugViewSelectionRootLayoutNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/DebugViewSelectionRootLayoutNode.java @@ -10,14 +10,15 @@ *******************************************************************************/ package org.eclipse.dd.dsf.debug.ui.viewmodel; -import org.eclipse.dd.dsf.concurrent.DsfExecutor; -import org.eclipse.dd.dsf.concurrent.DsfRunnable; import org.eclipse.dd.dsf.datamodel.DMContexts; import org.eclipse.dd.dsf.datamodel.IDMContext; import org.eclipse.dd.dsf.datamodel.IDMEvent; +import org.eclipse.dd.dsf.debug.ui.DsfDebugUIPlugin; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider; import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMRootLayoutNode; import org.eclipse.dd.dsf.ui.viewmodel.IVMRootLayoutNode; -import org.eclipse.dd.dsf.ui.viewmodel.DMContextVMLayoutNode.DMContextVMContext; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMLayoutNode.DMVMContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.contexts.DebugContextEvent; import org.eclipse.debug.ui.contexts.IDebugContextListener; @@ -27,21 +28,26 @@ import org.eclipse.ui.IWorkbenchWindow; /** * This is is a standard root node which listens to the selection in Debug View. - * Views such as variables and registers base their content based on the + * Views such as variables and registers base their content on the * selection in Debug view, and this node provides tracking of that selection. + *

+ * Note: The variables/registers views track the selection using the same + * IDebugContextListener interface, but they only use the first element of the + * selection, as in IStructuredSelection.getFirstElement(). Therefore the root + * node also has to use the first element as the root object instead of the + * whole selection. */ +@SuppressWarnings("restriction") public class DebugViewSelectionRootLayoutNode extends AbstractVMRootLayoutNode implements IVMRootLayoutNode, IDebugContextListener { - private RootVMC fRootVMC; + private ISelection fSelection; - public DebugViewSelectionRootLayoutNode(DsfExecutor executor, IWorkbenchWindow window) { - super(executor); - ISelection selection = DebugUITools.getDebugContextManager().getContextService(window).getActiveContext(); - if (selection instanceof IStructuredSelection) { - fRootVMC = new RootVMC( this, ((IStructuredSelection)selection).getFirstElement() ); - } else { - fRootVMC = new RootVMC( this, null ); + public DebugViewSelectionRootLayoutNode(AbstractVMProvider provider) { + super(provider); + IWorkbenchWindow activeWindow = DsfDebugUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow(); + if (activeWindow != null) { + fSelection = DebugUITools.getDebugContextManager().getContextService(activeWindow).getActiveContext(); } DebugUITools.getDebugContextManager().addDebugContextListener(this); } @@ -53,7 +59,7 @@ public class DebugViewSelectionRootLayoutNode extends AbstractVMRootLayoutNode } /** - * If the input object is a DMC-VMC, and the event is a DMC event. + * If the input object is a Data Model context, and the event is a DMC event. * Then we can filter the event to make sure that the view does not * react to events that relate to objects outside this view. * @@ -74,45 +80,58 @@ public class DebugViewSelectionRootLayoutNode extends AbstractVMRootLayoutNode * determine whether a delta is needed. */ @Override - public boolean hasDeltaFlags(Object event) { - if (event instanceof IDMEvent && fRootVMC.getInputObject() instanceof DMContextVMContext) { + public int getDeltaFlags(Object event) { + IDMContext inputDmc = getSelectedDMC(); + if (event instanceof IDMEvent && inputDmc != null) { boolean potentialMatchFound = false; boolean matchFound = false; IDMContext eventDmc = ((IDMEvent)event).getDMContext(); - IDMContext inputDmc = ((DMContextVMContext)fRootVMC.getInputObject()).getDMC(); for (IDMContext eventDmcAncestor : DMContexts.toList(eventDmc)) { IDMContext inputDmcAncestor = DMContexts.getAncestorOfType(inputDmc, eventDmcAncestor.getClass()); if (inputDmcAncestor != null) { potentialMatchFound = true; if (inputDmcAncestor.equals(eventDmcAncestor)) { - matchFound = true; + return super.getDeltaFlags(event); } } } if (potentialMatchFound && !matchFound) { - return false; + return IModelDelta.NO_CHANGE; } } - return super.hasDeltaFlags(event); + return super.getDeltaFlags(event); } - public IRootVMC getRootVMC() { - return fRootVMC; + /** + * Returns the full selection as it came from the DebugContextManager. + * @return + */ + public ISelection getSelection() { + return fSelection; + } + + public Object getRootObject() { + if (fSelection instanceof IStructuredSelection) { + return ((IStructuredSelection)fSelection).getFirstElement(); + } + return null; } public void debugContextChanged(DebugContextEvent event) { - final ISelection selection = event.getContext(); - getExecutor().execute(new DsfRunnable() { - public void run() { - if (selection instanceof IStructuredSelection) { - fRootVMC = new RootVMC( DebugViewSelectionRootLayoutNode.this, - ((IStructuredSelection)selection).getFirstElement() ); - } else { - fRootVMC = new RootVMC( DebugViewSelectionRootLayoutNode.this, null ); - } + fSelection = event.getContext(); + } + + private IDMContext getSelectedDMC() { + Object selection = fSelection; + if (selection instanceof IStructuredSelection) { + IStructuredSelection structSelection = (IStructuredSelection)selection; + if (structSelection.getFirstElement() instanceof DMVMContext) + { + return ((DMVMContext)structSelection.getFirstElement()).getDMC(); } - }); + } + return null; } } diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/LaunchViewModelProvider.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/LaunchViewModelProvider.java deleted file mode 100644 index 1999f647def..00000000000 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/LaunchViewModelProvider.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006 Wind River Systems 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: - * Wind River Systems - initial API and implementation - *******************************************************************************/ -package org.eclipse.dd.dsf.debug.ui.viewmodel.launch; - -import org.eclipse.dd.dsf.concurrent.DsfRunnable; -import org.eclipse.dd.dsf.concurrent.GetDataDone; -import org.eclipse.dd.dsf.concurrent.ThreadSafe; -import org.eclipse.dd.dsf.service.DsfSession; -import org.eclipse.dd.dsf.ui.viewmodel.IVMRootLayoutNode; -import org.eclipse.dd.dsf.ui.viewmodel.VMProvider; -import org.eclipse.debug.core.DebugEvent; -import org.eclipse.debug.core.DebugPlugin; -import org.eclipse.debug.core.IDebugEventSetListener; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; - - -/** - * - */ -@SuppressWarnings("restriction") -public class LaunchViewModelProvider extends VMProvider - implements IDebugEventSetListener -{ - @ThreadSafe - public LaunchViewModelProvider(DsfSession session, IVMRootLayoutNode rootLayoutNode) { - super(session, rootLayoutNode); - DebugPlugin.getDefault().addDebugEventListener(this); - } - - - public void handleDebugEvents(final DebugEvent[] events) { - getSession().getExecutor().execute(new DsfRunnable() { - public void run() { - for (DebugEvent event : events) { - handleDebugEvent(event); - } - } - }); - } - - private void handleDebugEvent(DebugEvent event) { - /* - * Just like with DMC events, go through all the layout nodes and - * collect delta information for the received event. - */ - if (getRootLayoutNode() != null && getRootLayoutNode().hasDeltaFlags(event)) { - getRootLayoutNode().createDelta(event, new GetDataDone() { - public void run() { - if (getStatus().isOK()) { - getModelProxy().fireModelChangedNonDispatch(getData()); - } - } - }); - } - } - - @Override - public void dispose() { - DebugPlugin.getDefault().removeDebugEventListener(this); - super.dispose(); - } -} diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StackFramesLayoutNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StackFramesLayoutNode.java index 91458bd6894..0bd4d240637 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StackFramesLayoutNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StackFramesLayoutNode.java @@ -10,9 +10,11 @@ *******************************************************************************/ package org.eclipse.dd.dsf.debug.ui.viewmodel.launch; -import org.eclipse.core.runtime.MultiStatus; +import java.util.List; + import org.eclipse.dd.dsf.concurrent.Done; import org.eclipse.dd.dsf.concurrent.GetDataDone; +import org.eclipse.dd.dsf.datamodel.IDMContext; import org.eclipse.dd.dsf.datamodel.IDMEvent; import org.eclipse.dd.dsf.debug.service.IRunControl; import org.eclipse.dd.dsf.debug.service.IStack; @@ -21,66 +23,77 @@ import org.eclipse.dd.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.dd.dsf.debug.service.IRunControl.StateChangeReason; import org.eclipse.dd.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.dd.dsf.debug.service.IStack.IFrameDMData; -import org.eclipse.dd.dsf.debug.ui.DsfDebugUIPlugin; import org.eclipse.dd.dsf.service.DsfSession; -import org.eclipse.dd.dsf.ui.viewmodel.DMContextVMLayoutNode; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider; import org.eclipse.dd.dsf.ui.viewmodel.IVMContext; import org.eclipse.dd.dsf.ui.viewmodel.VMDelta; -import org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMLayoutNode; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugUIConstants; -import org.eclipse.jface.resource.ImageDescriptor; - @SuppressWarnings("restriction") -public class StackFramesLayoutNode extends DMContextVMLayoutNode { +public class StackFramesLayoutNode extends AbstractDMVMLayoutNode { - public IVMContext[] fCachedOldFramesVMCs; + public IVMContext[] fCachedOldFrameVMCs; - public StackFramesLayoutNode(DsfSession session) { - super(session, IStack.IFrameDMContext.class); + public StackFramesLayoutNode(AbstractVMProvider provider, DsfSession session) { + super(provider, session, IStack.IFrameDMContext.class); } - public void hasElements(IVMContext parentVmc, final GetDataDone done) { - IExecutionDMContext execDmc = findDmcInVmc(parentVmc, IExecutionDMContext.class); - if (execDmc == null || getServicesTracker().getService(IStack.class) == null || getServicesTracker().getService(IRunControl.class) == null) { - done.setData(false); - getExecutor().execute(done); - return; - } + @Override + protected void updateHasElementsInSessionThread(IHasChildrenUpdate[] updates) { - done.setData(getServicesTracker().getService(IStack.class).isStackAvailable(execDmc)); - getExecutor().execute(done); + for (IHasChildrenUpdate update : updates) { + if (!checkService(IStack.class, null, update)) return; + + IExecutionDMContext execDmc = findDmcInPath(update.getElementPath(), IExecutionDMContext.class); + if (execDmc == null) { + handleFailedUpdate(update); + return; + } + + update.setHasChilren(getServicesTracker().getService(IStack.class).isStackAvailable(execDmc)); + update.done(); + } } - public void getElements(final IVMContext parentVmc, final GetDataDone done) { - final IExecutionDMContext execDmc = findDmcInVmc(parentVmc, IExecutionDMContext.class); - if (execDmc == null || getServicesTracker().getService(IStack.class) == null || getServicesTracker().getService(IRunControl.class) == null) { - done.setData(new IVMContext[0]); - getExecutor().execute(done); + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + if (!checkService(IStack.class, null, update)) return; + + final IExecutionDMContext execDmc = findDmcInPath(update.getElementPath(), IExecutionDMContext.class); + if (execDmc == null) { + handleFailedUpdate(update); return; } getServicesTracker().getService(IStack.class).getFrames( execDmc, - new GetDataDone() { public void run() { - if (!getStatus().isOK()) { - // Failed to retrieve frames. If we are stepping, we - // might still be able to retrieve just the top stack - // frame, which would still be useful in Debug View. - if (getServicesTracker().getService(IRunControl.class).isStepping(execDmc)) { - getElementsTopStackFrameOnly(parentVmc, done); - } else { - propagateError(getExecutor(), done, "Failed retrieving stack frames"); //$NON-NLS-1$ + new GetDataDone() { + public void run() { + if (!getStatus().isOK()) { + // Failed to retrieve frames. If we are stepping, we + // might still be able to retrieve just the top stack + // frame, which would still be useful in Debug View. + if (!checkService(IRunControl.class, null, update)) return; + if (getServicesTracker().getService(IRunControl.class).isStepping(execDmc)) { + getElementsTopStackFrameOnly(update); + } else { + update.done(); + } + return; } - return; + // Store the VMC element array, in case we need to use it when + fCachedOldFrameVMCs = dmcs2vmcs(getData()); + for (int i = 0; i < fCachedOldFrameVMCs.length; i++) update.setChild(fCachedOldFrameVMCs[i], i); + update.done(); } - // Store the VMC element array, in case we need to use it when - fCachedOldFramesVMCs = dmcs2vmcs(parentVmc, getData()); - done.setData(fCachedOldFramesVMCs); - getExecutor().execute(done); - }}); + }); } /** @@ -89,184 +102,197 @@ public class StackFramesLayoutNode extends DMContextVMLayoutNode done) { - final IExecutionDMContext execDmc = findDmcInVmc(parentVmc, IExecutionDMContext.class); + private void getElementsTopStackFrameOnly(final IChildrenUpdate update) { + final IExecutionDMContext execDmc = findDmcInPath(update.getElementPath(), IExecutionDMContext.class); + if (execDmc == null) { + handleFailedUpdate(update); + return; + } getServicesTracker().getService(IStack.class).getTopFrame( execDmc, new GetDataDone() { public void run() { - if (propagateError(getExecutor(), done, "Failed retrieving top stack frame")) return; //$NON-NLS-1$ - IVMContext topFrameVmc = new DMContextVMContext(parentVmc, getData()); + if (!getStatus().isOK()) { + handleFailedUpdate(update); + return; + } + IVMContext topFrameVmc = new DMVMContext(getData()); + + update.setChild(topFrameVmc, 0); // If there are old frames cached, use them and only substitute the top frame object. Otherwise, create // an array of VMCs with just the top frame. - if (fCachedOldFramesVMCs != null && fCachedOldFramesVMCs.length >= 1) { - fCachedOldFramesVMCs[0] = topFrameVmc; - done.setData(fCachedOldFramesVMCs); + if (fCachedOldFrameVMCs != null && fCachedOldFrameVMCs.length >= 1) { + fCachedOldFrameVMCs[0] = topFrameVmc; + for (int i = 0; i < fCachedOldFrameVMCs.length; i++) update.setChild(fCachedOldFrameVMCs[i], i); } else { - done.setData(new IVMContext[] { topFrameVmc }); + update.setChild(topFrameVmc, 0); } - getExecutor().execute(done); + update.done(); }}); } - public void retrieveLabel(IVMContext vmc, final ILabelRequestMonitor result, String[] columns) { - final IExecutionDMContext execDmc = findDmcInVmc(vmc, IExecutionDMContext.class); - if (execDmc == null || getServicesTracker().getService(IStack.class) == null || getServicesTracker().getService(IRunControl.class) == null) { - result.done(); - return; - } - + @Override + protected void fillColumnLabel(IDMContext dmContext, IFrameDMData dmData, String columnId, int idx, + ILabelUpdate update) + { + if (idx != 0) return; + + final IExecutionDMContext execDmc = findDmcInPath(update.getElementPath(), IExecutionDMContext.class); + IRunControl runControlService = getServicesTracker().getService(IRunControl.class); + IStepQueueManager stepQueueMgrService = getServicesTracker().getService(IStepQueueManager.class); + if (execDmc == null || runControlService == null || stepQueueMgrService == null) return; + String imageKey = null; - IRunControl rc = getServicesTracker().getService(IRunControl.class); - if (rc.isSuspended(execDmc) || - (rc.isStepping(execDmc) && !getServicesTracker().getService(IStepQueueManager.class).isSteppingTimedOut(execDmc))) + if (runControlService.isSuspended(execDmc) || + (runControlService.isStepping(execDmc) && !stepQueueMgrService.isSteppingTimedOut(execDmc))) { imageKey = IDebugUIConstants.IMG_OBJS_STACKFRAME; } else { imageKey = IDebugUIConstants.IMG_OBJS_STACKFRAME_RUNNING; } - result.setImageDescriptors(new ImageDescriptor[] { DebugUITools.getImageDescriptor(imageKey) }); + update.setImageDescriptor(DebugUITools.getImageDescriptor(imageKey), 0); - IFrameDMContext frameDmc = (IFrameDMContext)((DMContextVMContext)vmc).getDMC(); - getServicesTracker().getService(IStack.class).getModelData( - frameDmc, - new GetDataDone() { public void run() { - // Check if services are still available. - if (getServicesTracker().getService(IRunControl.class) == null) { - result.done(); - return; - } - - if (!getStatus().isOK()) { - // If failed set a dummy label, and only propagate the - // error if we are not stepping, since that would be a - // common cause of failure. - result.setLabels(new String[] { "..." }); //$NON-NLS-1$ - if (!getServicesTracker().getService(IRunControl.class).isStepping(execDmc)) { - MultiStatus status = new MultiStatus(DsfDebugUIPlugin.PLUGIN_ID, 0, "Failed to retrieve stack frame label", null); //$NON-NLS-1$ - status.add(getStatus()); - result.setStatus(status); - } - result.done(); - return; - } - - // - // Finally, if all goes well, set the label. - // - StringBuilder label = new StringBuilder(); - - // Add frame number (if total number of frames in known) - if (fCachedOldFramesVMCs != null) { - label.append(fCachedOldFramesVMCs.length - getData().getLevel()); - } - - // Add the function name - if (getData().getFunction() != null && getData().getFunction().length() != 0) { - label.append(" "); //$NON-NLS-1$ - label.append(getData().getFunction()); - label.append("()"); //$NON-NLS-1$ - } - - // Add full file name - if (getData().getFile() != null && getData().getFile().length() != 0) { - label.append(" at "); //$NON-NLS-1$ - label.append(getData().getFile()); - } - - // Add line number - if (getData().getLine() >= 0) { - label.append(":"); //$NON-NLS-1$ - label.append(getData().getLine()); - label.append(" "); //$NON-NLS-1$ - } - - // Add the address - label.append(getData().getAddress()); - - // Set the label to the result listener - result.setLabels(new String[] { label.toString() }); - result.done(); - }}); + // + // Finally, if all goes well, set the label. + // + StringBuilder label = new StringBuilder(); + + // Add frame number (if total number of frames in known) + if (fCachedOldFrameVMCs != null) { + label.append(fCachedOldFrameVMCs.length - dmData.getLevel()); + } + + // Add the function name + if (dmData.getFunction() != null && dmData.getFunction().length() != 0) { + label.append(" "); //$NON-NLS-1$ + label.append(dmData.getFunction()); + label.append("()"); //$NON-NLS-1$ + } + + // Add full file name + if (dmData.getFile() != null && dmData.getFile().length() != 0) { + label.append(" at "); //$NON-NLS-1$ + label.append(dmData.getFile()); + } + + // Add line number + if (dmData.getLine() >= 0) { + label.append(":"); //$NON-NLS-1$ + label.append(dmData.getLine()); + label.append(" "); //$NON-NLS-1$ + } + + // Add the address + label.append(dmData.getAddress()); + + // Set the label to the result listener + update.setLabel(label.toString(), 0); } - public boolean hasDeltaFlagsForDMEvent(IDMEvent e) { + protected void handleFailedUpdate(IViewerUpdate update) { + if (update instanceof ILabelUpdate) { + // Avoid repainting the label if it's not available. This only slows + // down the display. + } else { + super.handleFailedUpdate(update); + } + } + + + @Override + protected int getNodeDeltaFlagsForDMEvent(org.eclipse.dd.dsf.datamodel.IDMEvent e) { // This node generates delta if the timers have changed, or if the // label has changed. - return e instanceof IRunControl.ISuspendedDMEvent || - e instanceof IRunControl.IResumedDMEvent || - e instanceof IStepQueueManager.ISteppingTimedOutEvent || - super.hasDeltaFlagsForDMEvent(e); + if (e instanceof IRunControl.ISuspendedDMEvent) { + return IModelDelta.CONTENT | IModelDelta.EXPAND | IModelDelta.SELECT; + } else if (e instanceof IRunControl.IResumedDMEvent) { + if (((IRunControl.IResumedDMEvent)e).getReason() == StateChangeReason.STEP) { + return IModelDelta.STATE; + } else { + return IModelDelta.CONTENT; + } + } else if (e instanceof IStepQueueManager.ISteppingTimedOutEvent) { + return IModelDelta.CONTENT; + } + return 0; } - public void buildDeltaForDMEvent(final IDMEvent e, final VMDelta parent, final Done done) { - if (getServicesTracker().getService(IStack.class) == null || getServicesTracker().getService(IRunControl.class) == null) { + @Override + protected void buildDeltaForDMEvent(final IDMEvent e, final VMDelta parent, final int nodeOffset, final Done done) { + if (e instanceof IRunControl.ISuspendedDMEvent) { + buildDeltaForSuspendedEvent((IRunControl.ISuspendedDMEvent)e, parent, nodeOffset, done); + } else if (e instanceof IRunControl.IResumedDMEvent) { + buildDeltaForResumedEvent((IRunControl.IResumedDMEvent)e, parent, nodeOffset, done); + } else if (e instanceof IStepQueueManager.ISteppingTimedOutEvent) { + buildDeltaForSteppingTimedOutEvent((IStepQueueManager.ISteppingTimedOutEvent)e, parent, nodeOffset, done); + } else { + // Call super-class to build sub-node delta's. + super.buildDeltaForDMEvent(e, parent, nodeOffset, done); + } + } + + private void buildDeltaForSuspendedEvent(final IRunControl.ISuspendedDMEvent e, final VMDelta parent, final int nodeOffset, final Done done) { + IRunControl runControlService = getServicesTracker().getService(IRunControl.class); + IStack stackService = getServicesTracker().getService(IStack.class); + if (stackService == null || runControlService == null) { // Required services have not initialized yet. Ignore the event. - super.buildDeltaForDMEvent(e, parent, done); + super.buildDeltaForDMEvent(e, parent, nodeOffset, done); return; } - if (e instanceof IRunControl.ISuspendedDMEvent) { - IRunControl.ISuspendedDMEvent suspendedEvent = (IRunControl.ISuspendedDMEvent)e; - - // Refresh the whole list of stack frames unless the target is already stepping the next command. In - // which case, the refresh will occur when the stepping sequence slows down or stops. Trying to - // refresh the whole stack trace with every step would slow down stepping too much. - if (!getServicesTracker().getService(IRunControl.class).isStepping(suspendedEvent.getDMContext())) { - parent.addFlags(IModelDelta.CONTENT); - } - - // Always expand the thread node to show the stack frames. - parent.addFlags(IModelDelta.EXPAND); - - // Retrieve the list of stack frames, and mark the top frame to be selected. - getElementsTopStackFrameOnly( - parent.getVMC(), - new GetDataDone() { public void run() { - if (getStatus().isOK() && getData().length != 0) { - parent.addNode( getData()[0], IModelDelta.SELECT | IModelDelta.STATE); - // If second frame is available repaint it, so that a "..." appears. This gives a better - // impression that the frames are not up-to date. - if (getData().length >= 2) { - parent.addNode( getData()[1], IModelDelta.STATE); - } - } - // Even in case of errors, call super-class to complete building of the delta. - StackFramesLayoutNode.super.buildDeltaForDMEvent(e, parent, done); - }}); - - } else if (e instanceof IRunControl.IResumedDMEvent) { - IRunControl.IResumedDMEvent resumedEvent = (IRunControl.IResumedDMEvent)e; - getExecutor().execute(done); - if (resumedEvent.getReason() == StateChangeReason.STEP) { - // TODO: Refreshing the state of the top stack frame is only necessary to re-enable the step button. - // This is because platform disables the step action every time after it is invoked. Need to file - // a bug on this. - getServicesTracker().getService(IStack.class).getTopFrame( - resumedEvent.getDMContext(), - new GetDataDone() { public void run() { - if (getStatus().isOK()) { - parent.addNode(new DMContextVMContext(parent.getVMC(), getData()), IModelDelta.STATE); - } - StackFramesLayoutNode.super.buildDeltaForDMEvent(e, parent, done); - }}); - StackFramesLayoutNode.super.buildDeltaForDMEvent(e, parent, done); - } else { - // Refresh the list of stack frames only if the run operation is not a step. Also, clear the list - // of cached frames. - parent.addFlags(IModelDelta.CONTENT); - fCachedOldFramesVMCs = null; - // Call super-class to build sub-node delta's. - super.buildDeltaForDMEvent(e, parent, done); - } - } else if (e instanceof IStepQueueManager.ISteppingTimedOutEvent) { - // Repaint the stack frame images to have the running symbol. + // Refresh the whole list of stack frames unless the target is already stepping the next command. In + // which case, the refresh will occur when the stepping sequence slows down or stops. Trying to + // refresh the whole stack trace with every step would slow down stepping too much. + if (!runControlService.isStepping(e.getDMContext())) { parent.addFlags(IModelDelta.CONTENT); - super.buildDeltaForDMEvent(e, parent, done); - } else { - // Call super-class to build sub-node delta's. - super.buildDeltaForDMEvent(e, parent, done); } + + // Always expand the thread node to show the stack frames. + parent.addFlags(IModelDelta.EXPAND); + + // Retrieve the list of stack frames, and mark the top frame to be selected. + getElementsTopStackFrameOnly( + new ElementsUpdate( + new GetDataDone>() { + public void run() { + if (getStatus().isOK() && getData().size() != 0) { + parent.addNode( getData().get(0), IModelDelta.SELECT | IModelDelta.STATE); + // If second frame is available repaint it, so that a "..." appears. This gives a better + // impression that the frames are not up-to date. + if (getData().size() >= 2) { + parent.addNode( getData().get(1), IModelDelta.STATE); + } + } + // Even in case of errors, call super-class to complete building of the delta. + StackFramesLayoutNode.super.buildDeltaForDMEvent(e, parent, nodeOffset, done); + } + }, + parent) + ); + } + + private void buildDeltaForResumedEvent(final IRunControl.IResumedDMEvent e, final VMDelta parent, final int nodeOffset, final Done done) { + IStack stackService = getServicesTracker().getService(IStack.class); + if (stackService == null) { + // Required services have not initialized yet. Ignore the event. + super.buildDeltaForDMEvent(e, parent, nodeOffset, done); + return; + } + + IRunControl.IResumedDMEvent resumedEvent = (IRunControl.IResumedDMEvent)e; + if (resumedEvent.getReason() != StateChangeReason.STEP) { + // Refresh the list of stack frames only if the run operation is not a step. Also, clear the list + // of cached frames. + parent.addFlags(IModelDelta.CONTENT); + fCachedOldFrameVMCs = null; + } + super.buildDeltaForDMEvent(e, parent, nodeOffset, done); + } + + private void buildDeltaForSteppingTimedOutEvent(final IStepQueueManager.ISteppingTimedOutEvent e, final VMDelta parent, final int nodeOffset, final Done done) { + // Repaint the stack frame images to have the running symbol. + parent.addFlags(IModelDelta.CONTENT); + super.buildDeltaForDMEvent(e, parent, nodeOffset, done); } } diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StandardLaunchRootLayoutNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StandardLaunchRootLayoutNode.java index ac19bf205f3..e45c8623912 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StandardLaunchRootLayoutNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StandardLaunchRootLayoutNode.java @@ -10,20 +10,24 @@ *******************************************************************************/ package org.eclipse.dd.dsf.debug.ui.viewmodel.launch; -import org.eclipse.dd.dsf.concurrent.DoneCollector; -import org.eclipse.dd.dsf.concurrent.DsfExecutor; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.eclipse.dd.dsf.concurrent.Done; import org.eclipse.dd.dsf.concurrent.GetDataDone; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider; import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMRootLayoutNode; -import org.eclipse.dd.dsf.ui.viewmodel.IVMContext; import org.eclipse.dd.dsf.ui.viewmodel.IVMLayoutNode; import org.eclipse.dd.dsf.ui.viewmodel.IVMRootLayoutNode; import org.eclipse.dd.dsf.ui.viewmodel.VMDelta; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.IProcess; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; /** * Layout node for the standard ILaunch object. This node can only be used at @@ -34,34 +38,29 @@ import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; public class StandardLaunchRootLayoutNode extends AbstractVMRootLayoutNode implements IVMRootLayoutNode { - final private RootVMC fRootVMC; - final IVMContext[] fElements; + final private ILaunch fLaunch; - public StandardLaunchRootLayoutNode(DsfExecutor executor, ILaunch launch) { - super(executor); - fRootVMC = new RootVMC(this, launch); - fElements = new IVMContext[] { fRootVMC }; + public StandardLaunchRootLayoutNode(AbstractVMProvider provider, ILaunch launch) { + super(provider); + fLaunch = launch; } @Override - public boolean hasDeltaFlags(Object e) { - /* - * Launch deltas for standard platform DebugEvent events are handled by - * org.eclipse.debug.internal.ui.viewers.update.LaunchManagerProxy. - * Here only control the deltas of child nodes. This is necessary to avoid - * IProcess layout node from processing events that are intended for a - * different launch. This is not a problem with DMC events, because the - * full path of the DMC object is built into the DMC itself. - */ + public int getDeltaFlags(Object e) { if (e instanceof DebugEvent) { DebugEvent de = (DebugEvent)e; - if (de.getSource() instanceof IProcess) { - return ((IProcess)de.getSource()).getLaunch().equals(getRootVMC().getInputObject()) && super.hasDeltaFlags(e); - } else if (de.getSource() instanceof IDebugElement) { - return ((IDebugElement)de.getSource()).getLaunch().equals(getRootVMC().getInputObject()) && super.hasDeltaFlags(e); + if (de.getSource() instanceof IProcess && + !((IProcess)de.getSource()).getLaunch().equals(fLaunch) ) + { + return IModelDelta.NO_CHANGE; + } + else if (de.getSource() instanceof IDebugElement && + !((IDebugElement)de.getSource()).getLaunch().equals(fLaunch) ) + { + return IModelDelta.NO_CHANGE; } } - return super.hasDeltaFlags(e); + return super.getDeltaFlags(e); } public void createDelta(Object event, final GetDataDone done) { @@ -70,34 +69,26 @@ public class StandardLaunchRootLayoutNode extends AbstractVMRootLayoutNode * root of the view, create the delta with the path to the launch, then * pass that to the child layout nodes. */ - final VMDelta delta = new VMDelta(DebugPlugin.getDefault().getLaunchManager(), null); - done.setData(delta); - final VMDelta rootDelta = delta.addNode(getRootVMC().getInputObject(), getRootVMC()); + ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); + List launchList = Arrays.asList(manager.getLaunches()); + final VMDelta viewRootDelta = new VMDelta(manager, 0, IModelDelta.NO_CHANGE, launchList.size()); + final VMDelta rootDelta = viewRootDelta.addNode(getRootObject(), launchList.indexOf(fLaunch), IModelDelta.NO_CHANGE); - final IVMLayoutNode[] childNodes = getChildNodesWithDeltas(event); - if (childNodes.length == 0) { - done.setData(delta); - getExecutor().execute(done); - return; - } + final Map childNodeDeltas = getChildNodesWithDeltas(event); + assert childNodeDeltas.size() != 0 : "Caller should make sure that there are deltas for given event."; //$NON-NLS-1$ - /* - * The execution for this node is not done until all the child nodes - * are done. Use the tracker to wait for all children to complete. - */ - final DoneCollector doneCollector = new DoneCollector(getExecutor()) { - public void run() { - if (propagateError(getExecutor(), done, "Failed to generate child deltas.")) return; //$NON-NLS-1$ - done.setData(delta); - getExecutor().execute(done); - } - }; - for (final IVMLayoutNode childNode : childNodes) { - childNode.buildDelta(event, rootDelta, doneCollector.addNoActionDone()); - } + callChildNodesToBuildDelta( + childNodeDeltas, rootDelta, event, + new Done() { + public void run() { + if (propagateError(getExecutor(), done, "Failed to create delta.")); //$NON-NLS-1$ + done.setData(viewRootDelta); + getExecutor().execute(done); + } + }); } - public IRootVMC getRootVMC() { - return fRootVMC; + public Object getRootObject() { + return fLaunch; } } diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StandardProcessLayoutNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StandardProcessLayoutNode.java index 652034f9416..78f34c3eaa9 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StandardProcessLayoutNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/launch/StandardProcessLayoutNode.java @@ -10,25 +10,23 @@ *******************************************************************************/ package org.eclipse.dd.dsf.debug.ui.viewmodel.launch; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; import org.eclipse.dd.dsf.concurrent.Done; -import org.eclipse.dd.dsf.concurrent.DsfExecutor; -import org.eclipse.dd.dsf.concurrent.GetDataDone; -import org.eclipse.dd.dsf.debug.ui.DsfDebugUIPlugin; -import org.eclipse.dd.dsf.service.IDsfService; import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMLayoutNode; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider; import org.eclipse.dd.dsf.ui.viewmodel.IVMContext; import org.eclipse.dd.dsf.ui.viewmodel.IVMLayoutNode; import org.eclipse.dd.dsf.ui.viewmodel.VMDelta; -import org.eclipse.dd.dsf.ui.viewmodel.IVMRootLayoutNode.IRootVMC; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.IStreamsProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; +import org.eclipse.jface.viewers.TreePath; /** * Layout node for the standard platform debug model IProcess object. This @@ -43,19 +41,20 @@ public class StandardProcessLayoutNode extends AbstractVMLayoutNode { * VMC element implementation, it is a proxy for the IProcess class, to * allow the standard label adapter to be used with this object. */ - private class VMC implements IVMContext, IProcess + private class VMC extends AbstractVMContext + implements IProcess { - private final IVMContext fParentVmc; private final IProcess fProcess; - VMC(IVMContext parentVmc, IProcess process) { - fParentVmc = parentVmc; + VMC(IProcess process) { + super(getVMProvider().getVMAdapter(), StandardProcessLayoutNode.this); fProcess = process; } - public IVMContext getParent() { return fParentVmc; } public IVMLayoutNode getLayoutNode() { return StandardProcessLayoutNode.this; } - @SuppressWarnings("unchecked") public Object getAdapter(Class adapter) { return fProcess.getAdapter(adapter); } + @SuppressWarnings("unchecked") public Object getAdapter(Class adapter) { + return fProcess.getAdapter(adapter); + } public String toString() { return "IProcess " + fProcess.toString(); } //$NON-NLS-1$ public String getAttribute(String key) { return fProcess.getAttribute(key); } @@ -72,21 +71,17 @@ public class StandardProcessLayoutNode extends AbstractVMLayoutNode { public int hashCode() { return fProcess.hashCode(); } } - public StandardProcessLayoutNode(DsfExecutor executor) { - super(executor); + public StandardProcessLayoutNode(AbstractVMProvider provider) { + super(provider); } - // @see org.eclipse.dd.dsf.ui.viewmodel.IViewModelLayoutNode#getElements(org.eclipse.dd.dsf.ui.viewmodel.IVMContext, org.eclipse.dd.dsf.concurrent.GetDataDone) - public void getElements(IVMContext parentVmc, GetDataDone done) { - ILaunch launch = findLaunch(parentVmc); + public void updateElements(IChildrenUpdate update) { + ILaunch launch = findLaunch(update.getElementPath()); if (launch == null) { - /* - * There is no launch in the parent of this node. This means that the - * layout is misconfigured. - */ + // There is no launch in the parent of this node. This means that the + // layout is misconfigured. assert false; - done.setStatus(new Status(IStatus.ERROR, DsfDebugUIPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR, "Can't get list of processes, because there is no launch.", null)); //$NON-NLS-1$ - getExecutor().execute(done); + update.done(); return; } @@ -95,30 +90,43 @@ public class StandardProcessLayoutNode extends AbstractVMLayoutNode { * retrieve them on dispatch thread. */ IProcess[] processes = launch.getProcesses(); - IVMContext[] processVmcs = new IVMContext[processes.length]; for (int i = 0; i < processes.length; i++) { - processVmcs[i] = new VMC(parentVmc, processes[i]); + update.setChild(new VMC(processes[i]), i); } - done.setData(processVmcs); - getExecutor().execute(done); + update.done(); } - - // @see org.eclipse.dd.dsf.ui.viewmodel.IViewModelLayoutNode#hasElements(org.eclipse.dd.dsf.ui.viewmodel.IVMContext, org.eclipse.dd.dsf.concurrent.GetDataDone) - public void hasElements(IVMContext parentVmc, GetDataDone done) { - ILaunch launch = findLaunch(parentVmc); + + public void updateElementCount(IChildrenCountUpdate update) { + ILaunch launch = findLaunch(update.getElementPath()); if (launch == null) { - assert false; - done.setStatus(new Status(IStatus.ERROR, DsfDebugUIPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR, "Can't get list of processes, because there is no launch.", null)); //$NON-NLS-1$ - getExecutor().execute(done); + assert false; + update.setChildCount(0); + update.done(); return; } - done.setData(launch.getProcesses().length != 0); - getExecutor().execute(done); + update.setChildCount(launch.getProcesses().length); + update.done(); + } + + // @see org.eclipse.dd.dsf.ui.viewmodel.IViewModelLayoutNode#hasElements(org.eclipse.dd.dsf.ui.viewmodel.IVMContext, org.eclipse.dd.dsf.concurrent.GetDataDone) + public void updateHasElements(IHasChildrenUpdate[] updates) { + for (IHasChildrenUpdate update : updates) { + ILaunch launch = findLaunch(update.getElementPath()); + if (launch == null) { + assert false; + update.setHasChilren(false); + update.done(); + return; + } + + update.setHasChilren(launch.getProcesses().length != 0); + update.done(); + } } // @see org.eclipse.dd.dsf.ui.viewmodel.IViewModelLayoutNode#retrieveLabel(org.eclipse.dd.dsf.ui.viewmodel.IVMContext, org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor) - public void retrieveLabel(IVMContext vmc, ILabelRequestMonitor result, String[] columns) { + public void updateLabel(IVMContext vmc, ILabelRequestMonitor result, String[] columns) { /* * The implementation of IAdapterFactory that uses this node should not @@ -134,30 +142,33 @@ public class StandardProcessLayoutNode extends AbstractVMLayoutNode { * Recursively searches the VMC for Launch VMC, and returns its ILaunch. * Returns null if an ILaunch is not found. */ - private ILaunch findLaunch(IVMContext vmc) { - if (vmc == null) { - return null; - } else if (vmc instanceof IRootVMC || ((IRootVMC)vmc).getInputObject() instanceof ILaunch) { - return (ILaunch)(((IRootVMC)vmc)).getInputObject(); - } else { - return findLaunch(vmc.getParent()); + private ILaunch findLaunch(TreePath path) { + for (int i = path.getSegmentCount() - 1; i >= 0; i--) { + if (path.getSegment(i) instanceof ILaunch) { + return (ILaunch)path.getSegment(i); + } } + return null; } @Override - public boolean hasDeltaFlags(Object e) { + public int getDeltaFlags(Object e) { + int myFlags = 0; if (e instanceof DebugEvent) { DebugEvent de = (DebugEvent)e; - return de.getSource() instanceof IProcess && - (de.getKind() == DebugEvent.CHANGE || - de.getKind() == DebugEvent.CREATE || - de.getKind() == DebugEvent.TERMINATE); + if ( de.getSource() instanceof IProcess && + (de.getKind() == DebugEvent.CHANGE || + de.getKind() == DebugEvent.CREATE || + de.getKind() == DebugEvent.TERMINATE) ) + { + myFlags = IModelDelta.STATE; + } } - return super.hasDeltaFlags(e); + return myFlags | super.getDeltaFlags(e); } @Override - public void buildDelta(Object e, VMDelta parent, Done done) { + public void buildDelta(Object e, VMDelta parent, int nodeOffset, Done done) { if (e instanceof DebugEvent && ((DebugEvent)e).getSource() instanceof IProcess) { DebugEvent de = (DebugEvent)e; if (de.getKind() == DebugEvent.CHANGE) { @@ -173,12 +184,12 @@ public class StandardProcessLayoutNode extends AbstractVMLayoutNode { */ getExecutor().execute(done); } else { - super.buildDelta(e, parent, done); + super.buildDelta(e, parent, nodeOffset, done); } } protected void handleChange(DebugEvent event, VMDelta parent) { - parent.addNode(new VMC(parent.getVMC(), (IProcess)event.getSource()), IModelDelta.STATE); + parent.addNode(new VMC((IProcess)event.getSource()), IModelDelta.STATE); } protected void handleCreate(DebugEvent event, VMDelta parent) { diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterColumnPresentation.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterColumnPresentation.java index c259d78b3a1..779980b0586 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterColumnPresentation.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterColumnPresentation.java @@ -11,8 +11,8 @@ package org.eclipse.dd.dsf.debug.ui.viewmodel.register; import org.eclipse.dd.dsf.debug.ui.DsfDebugUIPlugin; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation; -import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.jface.resource.ImageDescriptor; /** diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterGroupLayoutNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterGroupLayoutNode.java index ef96b6ac340..f924d815f3b 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterGroupLayoutNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterGroupLayoutNode.java @@ -10,84 +10,58 @@ *******************************************************************************/ package org.eclipse.dd.dsf.debug.ui.viewmodel.register; -import org.eclipse.dd.dsf.concurrent.Done; import org.eclipse.dd.dsf.concurrent.GetDataDone; import org.eclipse.dd.dsf.datamodel.IDMContext; -import org.eclipse.dd.dsf.datamodel.IDMEvent; import org.eclipse.dd.dsf.debug.service.IRegisters; import org.eclipse.dd.dsf.debug.service.IRegisters.IRegisterGroupDMContext; import org.eclipse.dd.dsf.debug.service.IRegisters.IRegisterGroupData; import org.eclipse.dd.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.dd.dsf.service.DsfSession; -import org.eclipse.dd.dsf.ui.viewmodel.DMContextVMLayoutNode; -import org.eclipse.dd.dsf.ui.viewmodel.IVMContext; -import org.eclipse.dd.dsf.ui.viewmodel.VMDelta; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.swt.graphics.FontData; -import org.eclipse.swt.graphics.RGB; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMLayoutNode; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; @SuppressWarnings("restriction") -public class RegisterGroupLayoutNode extends DMContextVMLayoutNode { +public class RegisterGroupLayoutNode extends AbstractDMVMLayoutNode { - public RegisterGroupLayoutNode(DsfSession session) { - super(session, IRegisters.IRegisterGroupDMContext.class); - } - - public void hasElements( final IVMContext parentVmc , final GetDataDone done ) { - final IExecutionDMContext execDmc = findDmcInVmc(parentVmc, IExecutionDMContext.class); - - if (execDmc == null || getServicesTracker().getService(IRegisters.class) == null) { - done.setData(Boolean.FALSE); - getExecutor().execute(done); - return; - } - - getServicesTracker().getService(IRegisters.class).getRegisterGroups( - execDmc, null, - new GetDataDone() { public void run() { - if (propagateError(getExecutor(), done, "Failed to retrieve register groups")) return; //$NON-NLS-1$ - done.setData(getData().length != 0); - getExecutor().execute(done); - }}); - } - - public void getElements( final IVMContext parentVmc, final GetDataDone done ) { - final IExecutionDMContext execDmc = findDmcInVmc(parentVmc, IExecutionDMContext.class) ; - - if ( execDmc == null || getServicesTracker().getService( IRegisters.class ) == null ) { - done.setData(new IVMContext[0]); - getExecutor().execute( done ); - return; - } - - getServicesTracker().getService(IRegisters.class).getRegisterGroups( - execDmc, null, - new GetDataDone() { public void run() { - if (propagateError(getExecutor(), done, "Failed to retrieve register groups")) return; //$NON-NLS-1$ - done.setData(dmcs2vmcs(parentVmc, getData())); - getExecutor().execute(done); - }}); + public RegisterGroupLayoutNode(AbstractVMProvider provider, DsfSession session) { + super(provider, session, IRegisters.IRegisterGroupDMContext.class); } @Override - protected void fillColumnLabel(IDMContext dmContext, IRegisterGroupData dmData, - String columnId, int idx, String[] text, ImageDescriptor[] image, - FontData[] fontData, RGB[] foreground, RGB[] background) - { - if (RegisterColumnPresentation.COL_NAME.equals(columnId)) { - text[idx] = dmData.getName(); - } else if (RegisterColumnPresentation.COL_VALUE.equals(columnId)) { - text[idx] = ""; //$NON-NLS-1$ - } else if (RegisterColumnPresentation.COL_DESCRIPTION.equals(columnId)) { - text[idx] = dmData.getDescription(); - } + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + if (!checkService(IRegisters.class, null, update)) return; + + final IExecutionDMContext execDmc = findDmcInPath(update.getElementPath(), IExecutionDMContext.class) ; + + if (execDmc != null) { + getServicesTracker().getService(IRegisters.class).getRegisterGroups( + execDmc, null, + new GetDataDone() { public void run() { + if (!getStatus().isOK()) { + update.done(); + return; + } + fillUpdateWithVMCs(update, getData()); + update.done(); + }}); + } else { + handleFailedUpdate(update); + } + } - public boolean hasDeltaFlagsForDMEvent(IDMEvent e) { - return super.hasDeltaFlagsForDMEvent(e); - } - - public void buildDeltaForDMEvent(final IDMEvent e, final VMDelta parent, final Done done) { - super.buildDeltaForDMEvent(e, parent, done); + @Override + protected void fillColumnLabel(IDMContext dmContext, IRegisterGroupData dmData, + String columnId, int idx, ILabelUpdate update) + { + if (RegisterColumnPresentation.COL_NAME.equals(columnId)) { + update.setLabel(dmData.getName(), idx); + } else if (RegisterColumnPresentation.COL_VALUE.equals(columnId)) { + update.setLabel("", idx); //$NON-NLS-1$ + } else if (RegisterColumnPresentation.COL_DESCRIPTION.equals(columnId)) { + update.setLabel(dmData.getDescription(), idx); + } } } diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterLayoutNode.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterLayoutNode.java index dce21df6358..dfdf80b19fd 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterLayoutNode.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterLayoutNode.java @@ -12,6 +12,7 @@ package org.eclipse.dd.dsf.debug.ui.viewmodel.register; import org.eclipse.dd.dsf.concurrent.Done; import org.eclipse.dd.dsf.concurrent.GetDataDone; +import org.eclipse.dd.dsf.datamodel.IDMContext; import org.eclipse.dd.dsf.datamodel.IDMEvent; import org.eclipse.dd.dsf.debug.service.IRegisters; import org.eclipse.dd.dsf.debug.service.IRunControl; @@ -19,112 +20,86 @@ import org.eclipse.dd.dsf.debug.service.IRegisters.IRegisterDMContext; import org.eclipse.dd.dsf.debug.service.IRegisters.IRegisterDMData; import org.eclipse.dd.dsf.debug.service.IRegisters.IRegisterGroupDMContext; import org.eclipse.dd.dsf.service.DsfSession; -import org.eclipse.dd.dsf.service.IDsfService; -import org.eclipse.dd.dsf.ui.viewmodel.DMContextVMLayoutNode; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider; import org.eclipse.dd.dsf.ui.viewmodel.IVMContext; import org.eclipse.dd.dsf.ui.viewmodel.VMDelta; -import org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMLayoutNode; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; @SuppressWarnings("restriction") -public class RegisterLayoutNode extends DMContextVMLayoutNode { +public class RegisterLayoutNode extends AbstractDMVMLayoutNode { public IVMContext[] fCachedRegisterVMCs; - public RegisterLayoutNode(DsfSession session) { - super(session, IRegisters.IRegisterDMContext.class); + public RegisterLayoutNode(AbstractVMProvider provider, DsfSession session) { + super(provider, session, IRegisters.IRegisterDMContext.class); } - public void hasElements( final IVMContext parentVmc , final GetDataDone done ) { - final IRegisterGroupDMContext registerGroupDmc = findDmcInVmc(parentVmc, IRegisterGroupDMContext.class); + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + final IRegisterGroupDMContext execDmc = findDmcInPath(update.getElementPath(), IRegisterGroupDMContext.class); - if (registerGroupDmc == null || getServicesTracker().getService(IRegisters.class) == null) { - done.setData(Boolean.FALSE); - getExecutor().execute(done); + if (execDmc == null) { + handleFailedUpdate(update); return; } - getServicesTracker().getService( IRegisters.class ).getRegisters( - registerGroupDmc, + getServicesTracker().getService(IRegisters.class).getRegisters( + execDmc, new GetDataDone() { public void run() { - if (propagateError(getExecutor(), done, "Failed to retrieve registers")) return; //$NON-NLS-1$ - done.setData(getData().length != 0); - getExecutor().execute(done); + if (!getStatus().isOK()) { + handleFailedUpdate(update); + } + fillUpdateWithVMCs(update, getData()); + update.done(); } }); } - - public void getElements( final IVMContext parentVmc , final GetDataDone done ) { - final IRegisterGroupDMContext execDmc = findDmcInVmc(parentVmc, IRegisterGroupDMContext.class); - - if (execDmc == null || getServicesTracker().getService(IRegisters.class) == null) { - getExecutor().execute(done); - return; - } - - getServicesTracker().getService( IRegisters.class ).getRegisters( - execDmc, - new GetDataDone() { public void run() { - if (propagateError(getExecutor(), done, "Failed to retrieve registers")) return; //$NON-NLS-1$ - done.setData( dmcs2vmcs( parentVmc, getData()) ); - getExecutor().execute(done); - }}); - } - public void retrieveLabel(final IVMContext vmc , final ILabelRequestMonitor result, String[] columns) { - - if ( getServicesTracker().getService( IRegisters.class ) == null ) { - result.done(); - return; - } - - final IRegisterDMContext registerDmc = (IRegisterDMContext) ( (DMContextVMContext) vmc ).getDMC() ; - - getServicesTracker().getService( IRegisters.class ).getModelData( - registerDmc , - new GetDataDone() { - public void run() { - if ( !getStatus().isOK() ) { - assert getStatus().getCode() == IDsfService.INVALID_STATE || getStatus().getCode() == IDsfService.INVALID_HANDLE : getStatus().toString(); - // Some error conditions are expected. - result.setLabels( new String[] { "...", "...", "..." } ) ; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } else { - String size = getData().getDescription(); - String value = getData().getHexValue(); - if ("".equals(size)) { //$NON-NLS-1$ - if ( value.contains( "uint64" ) ) { //$NON-NLS-1$ - size = "64 bit register" ; //$NON-NLS-1$ - } else if ( value.contains( "v4_float" ) ) { //$NON-NLS-1$ - size = "128 bit register" ; //$NON-NLS-1$ - } - } - - result.setLabels(new String[] { getData().getName(), getData().getHexValue(), size }); - } - - result.done() ; - return ; + @Override + protected void fillColumnLabel(IDMContext dmContext, IRegisterDMData dmData, String columnId, + int idx, ILabelUpdate update) + { + if (RegisterColumnPresentation.COL_NAME.equals(columnId)) { + update.setLabel(dmData.getName(), idx); + } else if (RegisterColumnPresentation.COL_VALUE.equals(columnId)) { + update.setLabel(dmData.getHexValue(), idx); + } else if (RegisterColumnPresentation.COL_DESCRIPTION.equals(columnId)) { + String size = dmData.getDescription(); + String value = dmData.getHexValue(); + if ("".equals(size)) { //$NON-NLS-1$ + if ( value.contains( "uint64" ) ) { //$NON-NLS-1$ + size = "64 bit register" ; //$NON-NLS-1$ + } else if ( value.contains( "v4_float" ) ) { //$NON-NLS-1$ + size = "128 bit register" ; //$NON-NLS-1$ } } - ) ; + update.setLabel(size, idx); + } } - public boolean hasDeltaFlagsForDMEvent( IDMEvent e ) { - return (e instanceof IRunControl.ISuspendedDMEvent) || super.hasDeltaFlagsForDMEvent(e) ; + @Override + protected int getNodeDeltaFlagsForDMEvent(IDMEvent e) { + if (e instanceof IRunControl.ISuspendedDMEvent) { + return IModelDelta.CONTENT; + } else if (e instanceof IRegisters.IRegisterChangedDMEvent) { + return IModelDelta.STATE; + } + return IModelDelta.NO_CHANGE; } - public void buildDeltaForDMEvent( final IDMEvent e, final VMDelta parent, final Done done ) { + @Override + protected void buildDeltaForDMEvent(IDMEvent e, VMDelta parent, int nodeOffset, Done done) { if (e instanceof IRunControl.ISuspendedDMEvent) { // Create a delta that the whole register group has changed. parent.addFlags(IModelDelta.CONTENT); } if (e instanceof IRegisters.IRegisterChangedDMEvent) { - parent.addNode( - new DMContextVMContext(parent.getVMC(), ((IRegisters.IRegisterChangedDMEvent)e).getDMContext()), - IModelDelta.STATE); + parent.addNode( new DMVMContext(((IRegisters.IRegisterChangedDMEvent)e).getDMContext()), IModelDelta.STATE ); } - - super.buildDeltaForDMEvent(e, parent, done); + super.buildDeltaForDMEvent(e, parent, nodeOffset, done); } } diff --git a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterVMProvider.java b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterVMProvider.java index 24e9527c19a..f40284a2fc2 100644 --- a/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterVMProvider.java +++ b/plugins/org.eclipse.dd.dsf.debug.ui/src/org/eclipse/dd/dsf/debug/ui/viewmodel/register/RegisterVMProvider.java @@ -12,35 +12,35 @@ package org.eclipse.dd.dsf.debug.ui.viewmodel.register; import org.eclipse.dd.dsf.debug.ui.viewmodel.DebugViewSelectionRootLayoutNode; import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMAdapter; import org.eclipse.dd.dsf.ui.viewmodel.IVMLayoutNode; import org.eclipse.dd.dsf.ui.viewmodel.IVMRootLayoutNode; -import org.eclipse.dd.dsf.ui.viewmodel.VMProvider; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation; -import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; /** * */ @SuppressWarnings("restriction") -public class RegisterVMProvider extends VMProvider { - public RegisterVMProvider(DsfSession session, IPresentationContext context) { - super(session, null); - IVMRootLayoutNode debugViewSelection = new DebugViewSelectionRootLayoutNode( - getSession().getExecutor(), context.getPart().getSite().getWorkbenchWindow() ); - IVMLayoutNode registerGroupNode = new RegisterGroupLayoutNode(getSession()); +public class RegisterVMProvider extends AbstractDMVMProvider { + public RegisterVMProvider(AbstractVMAdapter adapter, IPresentationContext context, DsfSession session) { + super(adapter, context, session); + IVMRootLayoutNode debugViewSelection = new DebugViewSelectionRootLayoutNode(this); + IVMLayoutNode registerGroupNode = new RegisterGroupLayoutNode(this, getSession()); debugViewSelection.setChildNodes(new IVMLayoutNode[] { registerGroupNode }); - IVMLayoutNode registerNode = new RegisterLayoutNode(getSession()); + IVMLayoutNode registerNode = new RegisterLayoutNode(this, getSession()); registerGroupNode.setChildNodes(new IVMLayoutNode[] { registerNode }); setRootLayoutNode(debugViewSelection); } - + @Override - public IColumnPresentation createColumnPresentation(Object element) { + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { return new RegisterColumnPresentation(); } @Override - public String getColumnPresentationId(Object element) { + public String getColumnPresentationId(IPresentationContext context, Object element) { return RegisterColumnPresentation.ID; } } diff --git a/plugins/org.eclipse.dd.dsf.ui/META-INF/MANIFEST.MF b/plugins/org.eclipse.dd.dsf.ui/META-INF/MANIFEST.MF index bc80cddac17..6070a58d0f0 100644 --- a/plugins/org.eclipse.dd.dsf.ui/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.dd.dsf.ui/META-INF/MANIFEST.MF @@ -11,5 +11,6 @@ Require-Bundle: org.eclipse.ui, org.eclipse.dd.dsf, org.eclipse.debug.ui Eclipse-LazyStart: true -Export-Package: org.eclipse.dd.dsf.ui.viewmodel +Export-Package: org.eclipse.dd.dsf.ui.viewmodel, + org.eclipse.dd.dsf.ui.viewmodel.dm Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMAdapter.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMAdapter.java index a759c952ec6..163e67a9593 100644 --- a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMAdapter.java +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMAdapter.java @@ -13,46 +13,39 @@ package org.eclipse.dd.dsf.ui.viewmodel; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; -import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor; import org.eclipse.dd.dsf.concurrent.DsfRunnable; import org.eclipse.dd.dsf.concurrent.ThreadSafe; -import org.eclipse.dd.dsf.service.DsfSession; import org.eclipse.dd.dsf.service.IDsfService; import org.eclipse.dd.dsf.ui.DsfUIPlugin; -import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousContentAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousLabelAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.IChildrenRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditor; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditorFactoryAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentationFactoryAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.IContainerRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelProxy; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelProxyFactoryAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +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.IViewerUpdate; /** - * Base implementation for DSF-based view model adapters. + * Base implementation for View Model Adapters. The implementation uses + * its own single-thread executor for communicating with providers and + * layout nodes. */ @ThreadSafe @SuppressWarnings("restriction") -abstract public class AbstractVMAdapter - implements IAsynchronousLabelAdapter, - IAsynchronousContentAdapter, - IModelProxyFactoryAdapter, - IColumnPresentationFactoryAdapter, - IColumnEditorFactoryAdapter +abstract public class AbstractVMAdapter implements IVMAdapter { - private final DsfSession fSession; + private final ExecutorService fExecutor; + private boolean fDisposed; - @ConfinedToDsfExecutor("getSession().getExecutor()") - private final Map fViewModelProviders = - Collections.synchronizedMap( new HashMap() ); + private final Map fViewModelProviders = + Collections.synchronizedMap( new HashMap() ); /** * Constructor for the View Model session. It is tempting to have the @@ -62,21 +55,32 @@ abstract public class AbstractVMAdapter * to have the owner of this object register it with the session. * @param session */ - public AbstractVMAdapter(DsfSession session) { - fSession = session; + public AbstractVMAdapter() { + fExecutor = new DefaultDsfExecutor(); } - @ThreadSafe - abstract protected VMProvider createViewModelProvider(IPresentationContext context); - - protected DsfSession getSession() { return fSession; } + /** + * Returns the executor that will be used to communicate with the providers + * and the layout nodes. + * @return + */ + public Executor getExecutor() { + return fExecutor; + } + /** + * Creates a new View Model Provider for given presentation context. Returns null + * if the presentation context is not supported. + */ + @ThreadSafe + abstract protected IVMProvider createViewModelProvider(IPresentationContext context); + @ThreadSafe - private VMProvider getViewModelProvider(IPresentationContext context) { - assert DsfSession.isSessionActive(getSession().getId()); - + private IVMProvider getViewModelProvider(IPresentationContext context) { synchronized(fViewModelProviders) { - VMProvider provider = fViewModelProviders.get(context); + if (fDisposed) return null; + + IVMProvider provider = fViewModelProviders.get(context); if (provider == null) { provider = createViewModelProvider(context); if (provider != null) { @@ -87,129 +91,113 @@ abstract public class AbstractVMAdapter } } - @ConfinedToDsfExecutor("getSession().getExecutor()") - public void install(IPresentationContext context) { - } - - @ConfinedToDsfExecutor("getSession().getExecutor()") public void dispose() { - assert getSession().getExecutor().isInExecutorThread(); + // Execute the shutdown in adapter's dispatch thread. + getExecutor().execute(new DsfRunnable() { + public void run() { + synchronized(fViewModelProviders) { + fDisposed = true; + for (IVMProvider provider : fViewModelProviders.values()) { + provider.dispose(); + } + fViewModelProviders.clear(); + } + fExecutor.shutdown(); + } + }); + } + + public void update(IHasChildrenUpdate[] updates) { + handleUpdates(updates); + } + + public void update(IChildrenCountUpdate[] updates) { + handleUpdates(updates); + } + + public void update(final IChildrenUpdate[] updates) { + handleUpdates(updates); + } + + private void handleUpdates(final IViewerUpdate[] updates) { + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + IPresentationContext context = null; + int firstIdx = 0; + int curIdx = 0; + for (curIdx = 0; curIdx < updates.length; curIdx++) { + if (!updates[curIdx].getPresentationContext().equals(context)) { + if (context != null) { + callProviderWithUpdate(updates, firstIdx, curIdx); + } + context = updates[curIdx].getPresentationContext(); + firstIdx = curIdx; + } + } + callProviderWithUpdate(updates, firstIdx, curIdx); + } + }); + } catch(RejectedExecutionException e) { + } + } + + private void callProviderWithUpdate(IViewerUpdate[] updates, int startIdx, int endIdx) { + final IVMProvider provider = getViewModelProvider(updates[0].getPresentationContext()); + if (provider == null) { + for (IViewerUpdate update : updates) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR, + "No model provider for update " + update, null)); //$NON-NLS-1$ + update.done(); + } + return; + } + if (startIdx == 0 && endIdx == updates.length) { + if (updates instanceof IHasChildrenUpdate[]) provider.update((IHasChildrenUpdate[])updates); + else if (updates instanceof IChildrenCountUpdate[]) provider.update((IChildrenCountUpdate[])updates); + else if (updates instanceof IChildrenUpdate[]) provider.update((IChildrenUpdate[])updates); + } else { + if (updates instanceof IHasChildrenUpdate[]) { + IHasChildrenUpdate[] providerUpdates = new IHasChildrenUpdate[endIdx - startIdx]; + System.arraycopy(updates, startIdx, providerUpdates, 0, endIdx - startIdx); + provider.update(providerUpdates); + } + else if (updates instanceof IChildrenCountUpdate[]) { + IChildrenCountUpdate[] providerUpdates = new IChildrenCountUpdate[endIdx - startIdx]; + System.arraycopy(updates, startIdx, providerUpdates, 0, endIdx - startIdx); + provider.update(providerUpdates); + } + else if (updates instanceof IChildrenUpdate[]) { + IChildrenUpdate[] providerUpdates = new IChildrenUpdate[endIdx - startIdx]; + System.arraycopy(updates, startIdx, providerUpdates, 0, endIdx - startIdx); + provider.update(providerUpdates); + } + } + } - for (VMProvider provider : fViewModelProviders.values()) { - provider.dispose(); - } - fViewModelProviders.clear(); - } - - public void retrieveLabel(final Object object, final IPresentationContext context, final ILabelRequestMonitor result) { - try { - getSession().getExecutor().execute(new DsfRunnable() { - public void run() { - if (result.isCanceled()) return; - - VMProvider provider = getViewModelProvider(context); - if (provider == null) { - result.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR, "No model provider for object: " + object.toString(), null)); //$NON-NLS-1$ - result.done(); - } - provider.retrieveLabel(object, result, context.getColumns()); - } - @Override - public String toString() { return "Switch to dispatch thread to execute retrieveLabel()"; } //$NON-NLS-1$ - }); - } catch(RejectedExecutionException e) { - // This can happen if session is being shut down. - result.done(); - } - } - - public void isContainer(final Object element, final IPresentationContext context, final IContainerRequestMonitor result) { - try { - getSession().getExecutor().execute(new DsfRunnable() { - public void run() { - if (result.isCanceled()) return; - - VMProvider provider = getViewModelProvider(context); - if (provider == null) { - result.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR, "No model provider for object: " + element.toString(), null)); //$NON-NLS-1$ - result.done(); - } - provider.isContainer(element, result); - } - public String toString() { return "Switch to dispatch thread to execute isContainer()"; } //$NON-NLS-1$ - }); - } catch(RejectedExecutionException e) { - // This can happen if session is being shut down. - result.done(); - } - } - - public void retrieveChildren(final Object element, final IPresentationContext context, final IChildrenRequestMonitor result) { - try { - getSession().getExecutor().execute(new DsfRunnable() { - public void run() { - if (result.isCanceled()) return; - - VMProvider provider = getViewModelProvider(context); - if (provider == null) { - result.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR, "No model provider for object: " + element.toString(), null)); //$NON-NLS-1$ - result.done(); - } - provider.retrieveChildren(element, result); - } - public String toString() { return "Switch to dispatch thread to execute retrieveChildren()"; } //$NON-NLS-1$ - }); - } catch(RejectedExecutionException e) { - // This can happen if session is being shut down. - result.done(); - } - } - public IModelProxy createModelProxy(Object element, IPresentationContext context) { - /* - * Model proxy is the object that correlates events from the data model - * into view model deltas that the view can process. We only need to - * create a proxy for the root element of the tree. - */ - VMProvider provider = getViewModelProvider(context); - if (provider != null && - provider.getRootLayoutNode() != null && - element.equals(provider.getRootLayoutNode().getRootVMC().getInputObject())) - { - return provider.getModelProxy(); - } - return null; - } - - public String getColumnPresentationId(IPresentationContext context, Object element) { - VMProvider provider = getViewModelProvider(context); + IVMProvider provider = getViewModelProvider(context); if (provider != null) { - return provider.getColumnPresentationId(element); + return provider.createModelProxy(element, context); } return null; } public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { - VMProvider provider = getViewModelProvider(context); + final IVMProvider provider = getViewModelProvider(context); if (provider != null) { - return provider.createColumnPresentation(element); + return provider.createColumnPresentation(context, element); } return null; } - public IColumnEditor createColumnEditor(IPresentationContext context, Object element) { - VMProvider provider = getViewModelProvider(context); + public String getColumnPresentationId(IPresentationContext context, Object element) { + final IVMProvider provider = getViewModelProvider(context); if (provider != null) { - return provider.createColumnEditor(element); + return provider.getColumnPresentationId(context, element); } return null; } - public String getColumnEditorId(IPresentationContext context, Object element) { - VMProvider provider = getViewModelProvider(context); - if (provider != null) { - return provider.getColumnEditorId(element); - } - return null; - } + } diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMLayoutNode.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMLayoutNode.java index 3fca9124448..2d1b36ab055 100644 --- a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMLayoutNode.java +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMLayoutNode.java @@ -11,39 +11,57 @@ package org.eclipse.dd.dsf.ui.viewmodel; import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; +import org.eclipse.core.runtime.IStatus; import org.eclipse.dd.dsf.concurrent.Done; import org.eclipse.dd.dsf.concurrent.DoneCollector; -import org.eclipse.dd.dsf.concurrent.DsfExecutor; import org.eclipse.dd.dsf.concurrent.GetDataDone; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditor; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditorFactoryAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.jface.viewers.TreePath; /** - * + * Base implementation of the View Model layout node. + * The main functionality implemented here is for building the view model + * deltas (IModelDelta), based on the flags returned by child nodes. */ @SuppressWarnings("restriction") abstract public class AbstractVMLayoutNode implements IVMLayoutNode { - private final DsfExecutor fExecutor; + private final AbstractVMProvider fProvider; + private boolean fDisposed = false; /** Child schema nodes of this node. */ private IVMLayoutNode[] fChildNodes = new IVMLayoutNode[0]; - public AbstractVMLayoutNode(DsfExecutor executor) { - fExecutor = executor; + public AbstractVMLayoutNode(AbstractVMProvider provider) { + fProvider = provider; } /** * Accessor method for sub-classes. */ - protected DsfExecutor getExecutor() { - return fExecutor; + protected Executor getExecutor() { + return fProvider.getExecutor(); + } + + protected IVMProvider getVMProvider() { + return fProvider; } public void setChildNodes(IVMLayoutNode[] childNodes) { @@ -55,6 +73,7 @@ abstract public class AbstractVMLayoutNode implements IVMLayoutNode { } public void dispose() { + fDisposed = true; for (IVMLayoutNode childNode : getChildLayoutNodes()) { childNode.dispose(); } @@ -64,11 +83,12 @@ abstract public class AbstractVMLayoutNode implements IVMLayoutNode { * If any of the children nodes have delta flags, that means that this * node has to generate a delta as well. */ - public boolean hasDeltaFlags(Object e) { + public int getDeltaFlags(Object e) { + int retVal = 0; for (IVMLayoutNode childNode : getChildLayoutNodes()) { - if (childNode.hasDeltaFlags(e)) return true; + retVal |= childNode.getDeltaFlags(e); } - return false; + return retVal; } /** @@ -76,72 +96,159 @@ abstract public class AbstractVMLayoutNode implements IVMLayoutNode { * schema nodes have deltas, this schema node has to provide the * IModelDelta objects that the child shema node can build on. */ - public void buildDelta(final Object e, final VMDelta parent, final Done done) { - /* - * Find the child nodes that have deltas for the given event. If no - * child layout nodes have deltas, just invoke the done. - */ - final IVMLayoutNode[] childNodes = getChildNodesWithDeltas(e); - if (childNodes.length == 0) { + public void buildDelta(final Object event, final VMDelta parentDelta, final int nodeOffset, final Done done) { + // Find the child nodes that have deltas for the given event. + final Map childNodeDeltas = getChildNodesWithDeltas(event); + + // If no child layout nodes have deltas we can stop here. + if (childNodeDeltas.size() == 0) { getExecutor().execute(done); return; } + + // Check if the child delta only has an IModelDelta.CONTENT or + // IModelDelta.STATE flags. If that's the case, we can skip + // creating a delta for this node, because the TreeUpdatePolicy does not + // use the full path from the delta to handle these flags. + // Similarly, the index argument is not necessary either. + boolean mustGetElements = false; + for (int childDelta : childNodeDeltas.values()) { + if ((childDelta & ~IModelDelta.CONTENT & ~IModelDelta.STATE) != 0) { + mustGetElements = true; + } + } + if (!mustGetElements) { + callChildNodesToBuildDelta(childNodeDeltas, parentDelta, event, done); + } else { + // The given child layout 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. + updateElements(new ElementsUpdate( + new GetDataDone>() { + public void run() { + if (fDisposed) return; + + // 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 (!getStatus().isOK() || getData().size() == 0) { + getExecutor().execute(done); + return; + } + + final DoneCollector elementsDeltasDoneCollector = new DoneCollector() { + public void run() { + if (isDisposed()) return; + getExecutor().execute(done); + } + }; - /* - * The given child layout 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. - */ - getElements( - parent.getVMC(), - new GetDataDone() { - public void run() { - if (propagateError(getExecutor(), done, "Failed to retrieve elements in layout node " + AbstractVMLayoutNode.this)) return; //$NON-NLS-1$ - - /* - * Check for an empty list of elements. If it's empty then we - * don't have to call the children nodes, so return here. - */ - if (getData().length == 0) { - getExecutor().execute(done); - } - - /* - * The execution for this node is not done until all the child nodes - * are done. Use the tracker to wait for all children to complete. - */ - final DoneCollector doneCollector = new DoneCollector(getExecutor()) { public void run() { - getExecutor().execute(done); - }}; - for (IVMContext element : getData()) { - for (final IVMLayoutNode childNode : childNodes) { - childNode.buildDelta( - e, - parent.addNode(element, IModelDelta.NO_CHANGE), - doneCollector.addNoActionDone()); + // 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 < getData().size(); i++) { + VMDelta delta = parentDelta.addNode((IVMContext)getData().get(i), nodeOffset + i, IModelDelta.NO_CHANGE); + callChildNodesToBuildDelta( + childNodeDeltas, delta, event, + elementsDeltasDoneCollector.add(new Done() { + public void run() { + elementsDeltasDoneCollector.doneDone(this); + } + })); } } - } - }); + }, + parentDelta)); + } } - /** - * Default implementation of the IColumnEditorFactoryAdapter delegate. It - * returns null, which means that no cell editor is configured. - * @see IColumnEditorFactoryAdapter#createColumnEditor(IPresentationContext, Object) - */ - public IColumnEditor createColumnEditor(IVMContext vmc) { - return null; + protected void callChildNodesToBuildDelta(final Map nodes, final VMDelta delta, final Object event, final Done done) { + assert nodes.size() != 0; + + // Check if any of the child nodes are will generate IModelDelta.SELECT or + // IModelDelta.EXPAND flags. If so, we must calcuate the index for this + // VMC. + boolean calculateOffsets = false; + for (int childDelta : nodes.values()) { + if ( (childDelta & (IModelDelta.SELECT | IModelDelta.EXPAND)) != 0 ) { + calculateOffsets = true; + break; + } + } + + getChildNodesElementOffsets(delta, !calculateOffsets, new GetDataDone>() { + public void run() { + final DoneCollector childrenBuildDeltaDoneCollector = new DoneCollector() { + public void run() { + if (isDisposed()) return; + getExecutor().execute(done); + } + }; + + // Set the total count of number of children in the parent delta. + delta.setChildCount(getData().get(null)); + + for (IVMLayoutNode node : nodes.keySet()) { + node.buildDelta( + event, delta, getData().get(node), + childrenBuildDeltaDoneCollector.add(new Done() { + public void run() { + childrenBuildDeltaDoneCollector.doneDone(this); + }}) + ); + } + } + }); } - /** - * Default implementation of the IColumnEditorFactoryAdapter delegate. It - * returns null, which means that no cell editor is configured. - * @see IColumnEditorFactoryAdapter#getColumnEditorId(IPresentationContext, Object) - */ - public String getColumnEditorId(IVMContext vmc) { - return null; + private void getChildNodesElementOffsets(IModelDelta delta, boolean fakeIt, final GetDataDone> done) { + assert getChildLayoutNodes().length != 0; + + if (!fakeIt) { + final Integer[] counts = new Integer[getChildLayoutNodes().length]; + final DoneCollector childrenCountDoneCollector = new DoneCollector() { + public void run() { + if (isDisposed()) return; + if (propagateError(getExecutor(), done, "")) return; //$NON-NLS-1$ + Map data = new HashMap(); + int offset = 0; + for (int i = 0; i < getChildLayoutNodes().length; i++) { + data.put(getChildLayoutNodes()[i], offset); + offset += counts[i]; + } + // As the final value, put the total count in the return map, with null key. + data.put(null, offset); + done.setData(data); + getExecutor().execute(done); + } + }; + + for (int i = 0; i < getChildLayoutNodes().length; i++) { + final int nodeIndex = i; + getChildLayoutNodes()[i].updateElementCount( + new ElementsCountUpdate( + childrenCountDoneCollector.add(new GetDataDone() { + public void run() { + counts[nodeIndex] = getData(); + childrenCountDoneCollector.doneDone(this); + } + }), + delta) + ); + } + } else { + Map data = new HashMap(); + for (int i = 0; i < getChildLayoutNodes().length; i++) { + data.put(getChildLayoutNodes()[i], -1); + } + data.put(null, -1); + done.setData(data); + getExecutor().execute(done); + } + } + + protected boolean isDisposed() { + return fDisposed; } /** @@ -149,41 +256,173 @@ abstract public class AbstractVMLayoutNode implements IVMLayoutNode { * true to the hasDeltaFlags() test for the given * event. */ - protected IVMLayoutNode[] getChildNodesWithDeltas(Object e) { - List nodes = new ArrayList(); + protected Map getChildNodesWithDeltas(Object e) { + Map nodes = new HashMap(); for (final IVMLayoutNode childNode : getChildLayoutNodes()) { - if (childNode.hasDeltaFlags(e)) { - nodes.add(childNode); + int delta = childNode.getDeltaFlags(e); + if (delta != IModelDelta.NO_CHANGE) { + nodes.put(childNode, delta); } } - return nodes.toArray(new IVMLayoutNode[nodes.size()]); - } - - /** - * Convenience method that returns a token value in case when the services - * that the layout node depends on, are not available. - */ - protected void handleFailedHasElements(GetDataDone done) { - done.setData(false); - getExecutor().execute(done); - } - - /** - * Convenience method that returns a token value in case when the services - * that the layout node depends on, are not available. - */ - protected void handleFailedGetElements(GetDataDone done) { - done.setData(new IVMContext[0]); - getExecutor().execute(done); - } - - /** - * Convenience method that returns a token value in case when the services - * that the layout node depends on, are not available. - */ - protected void handleFailedRetrieveLabel(ILabelRequestMonitor result) { - result.setLabels(new String[] { "..."} ); //$NON-NLS-1$ - result.done(); + return nodes; } + /** + * Convenience method that returns a token value in case when the services + * that the layout node depends on, are not available. + */ + protected boolean checkUpdate(IViewerUpdate update) { + if (update.isCanceled()) return false; + if (fDisposed) { + handleFailedUpdate(update); + return false; + } + return true; + } + + protected void handleFailedUpdate(IViewerUpdate update) { + if (update instanceof IHasChildrenUpdate) { + ((IHasChildrenUpdate)update).setHasChilren(false); + } else if (update instanceof IChildrenCountUpdate) { + ((IChildrenCountUpdate)update).setChildCount(0); + } else if (update instanceof ILabelUpdate) { + ILabelUpdate labelUpdate = (ILabelUpdate)update; + String[] columns = labelUpdate.getPresentationContext().getColumns(); + for (int i = 0; i < (columns != null ? columns.length : 1); i++) { + labelUpdate.setLabel("...", i); //$NON-NLS-1$ + } + } + update.done(); + } + + public static class AbstractVMContext implements IVMContext { + protected final IVMAdapter fVMAdapter; + protected final IVMLayoutNode fLayoutNode; + + public AbstractVMContext(IVMAdapter adapter, IVMLayoutNode node) { + fVMAdapter = adapter; + fLayoutNode = node; + } + + public IVMLayoutNode getLayoutNode() { return fLayoutNode; } + + /** + * IAdapter implementation returns the IVMAdapter instance for the + * interfaces that are actually implemented by the VM Adapter. These + * should at least include {@link IElementContentProvider}, + * {@link IModelProxyFactoryAdapter}, and + * {@link IColumnPresentationFactoryAdapter}. + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if (adapter.isInstance(fVMAdapter)) { + return fVMAdapter; + } else if (adapter.isInstance(fLayoutNode)) { + return fLayoutNode; + } + return null; + } + } + + protected class ViewerUpdate implements IViewerUpdate { + final private Done fDone; + private boolean fDoneInvoked = false; + final private TreePath fTreePath; + private IStatus fStatus; + private boolean fCancelled = false; + + public ViewerUpdate(Done done, IModelDelta delta) { + fDone = done; + List elementList = new LinkedList(); + IModelDelta listDelta = delta; + elementList.add(0, listDelta.getElement()); + while (listDelta.getParentDelta() != null) { + elementList.add(0, listDelta.getElement()); + listDelta = listDelta.getParentDelta(); + } + fTreePath = new TreePath(elementList.toArray()); + } + + public ViewerUpdate(Done done, TreePath path) { + fDone = done; + fTreePath = path; + } + + public Object getElement(TreePath path) { + // If not asking for root, just return the last segment in path. + if (path.getSegmentCount() > 0) { + return path.getLastSegment(); + } + // Calculate the root of the viewer. + return getVMProvider().getRootLayoutNode().getRootObject(); + } + + public Object getElement() { + return getElementPath().getLastSegment(); + } + + public IPresentationContext getPresentationContext() { return getVMProvider().getPresentationContext(); } + public TreePath getElementPath() { return fTreePath; } + public IStatus getStatus() { return fStatus; } + public void setStatus(IStatus status) { fStatus = status; } + public void beginTask(String name, int totalWork) {} + public void internalWorked(double work) {} + public boolean isCanceled() { return fCancelled; } + public void setCanceled(boolean value) { fCancelled = value; } + public void setTaskName(String name) {} + public void subTask(String name) {} + public void worked(int work) {} + + public void done() { + assert !fDoneInvoked; + fDoneInvoked = true; + try { + getExecutor().execute(fDone); + } catch (RejectedExecutionException e) { + } + } + } + + protected class ElementsCountUpdate extends ViewerUpdate implements IChildrenCountUpdate { + private final GetDataDone fDone; + + public ElementsCountUpdate(GetDataDone done, IModelDelta delta) { + super(done, delta); + fDone = done; + } + + public void setChildCount(int numChildren) { + fDone.setData(numChildren); + } + } + + protected class ElementsUpdate extends ViewerUpdate implements IChildrenUpdate { + private final List fChildren = new ArrayList(); + private GetDataDone> fDone; + + public ElementsUpdate(GetDataDone> done, IModelDelta delta) { + super(done, delta); + fDone = done; + fDone.setData(fChildren); + } + + public ElementsUpdate(GetDataDone> done, TreePath path) { + super(done, path); + fDone = done; + fDone.setData(fChildren); + } + + public int getOffset() { + return -1; + } + + public int getLength() { + return -1; + } + + public void setChild(Object child, int offset) { + fChildren.add(offset, child); + } + } + } diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMProvider.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMProvider.java new file mode 100644 index 00000000000..821b1e1184c --- /dev/null +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMProvider.java @@ -0,0 +1,648 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.dd.dsf.ui.viewmodel; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.dd.dsf.concurrent.Done; +import org.eclipse.dd.dsf.concurrent.DoneCollector; +import org.eclipse.dd.dsf.concurrent.GetDataDone; +import org.eclipse.dd.dsf.concurrent.ThreadSafe; +import org.eclipse.dd.dsf.datamodel.IDMEvent; +import org.eclipse.dd.dsf.service.DsfServiceEventHandler; +import org.eclipse.dd.dsf.service.IDsfService; +import org.eclipse.dd.dsf.ui.DsfUIPlugin; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +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.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy; +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousContentAdapter; +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousLabelAdapter; +import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditorFactoryAdapter; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.Viewer; + +/** + * View model provider implements the asynchronous view model functionality for + * a single view. This provider is just a holder which further delegates the + * model provider functionality to the view model layout nodes that need + * to be configured with each provider. + *

+ * The view model provider, often does not provide the model for the entire + * view. Rather, it needs to be able to plug in at any level in the viewer's + * content model and provide data for a sub-tree. + * + * @see IAsynchronousContentAdapter + * @see IAsynchronousLabelAdapter + * @see IModelProxy + * @see IVMLayoutNode + */ +@ConfinedToDsfExecutor("getVMAdapter#getExecutor") +@SuppressWarnings("restriction") +abstract public class AbstractVMProvider implements IVMProvider +{ + private final AbstractVMAdapter fVMAdapter; + private final IPresentationContext fPresentationContext; + private final ModelProxy fModelProxy = new ModelProxy(); + private boolean fDisposed = false; + + /** + * The root node for this model provider. The root layout node could be + * null when first created, to allow sub-classes to prorperly configure the + * root node in the sub-class constructor. + */ + private AtomicReference fRootLayoutNodeRef = new AtomicReference(); + + /** + * Constructs the view model provider for given DSF session. The + * constructor is thread-safe to allow VM provider to be constructed + * synchronously when a call to getAdapter() is made on an element + * in a view. + */ + public AbstractVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext) { + fVMAdapter = adapter; + fPresentationContext = presentationContext; + } + + public IVMAdapter getVMAdapter() { + return fVMAdapter; + } + + public IPresentationContext getPresentationContext() { + return fPresentationContext; + } + + /** + * Sets the root node for this provider. + */ + @ThreadSafe + protected void setRootLayoutNode(IVMRootLayoutNode rootLayoutNode) { + final IVMRootLayoutNode oldRootLayoutNode = fRootLayoutNodeRef.getAndSet(rootLayoutNode); + if (oldRootLayoutNode != null) { + oldRootLayoutNode.dispose(); + } + } + + protected ModelProxy getModelProxy() { + return fModelProxy; + } + + @ThreadSafe + public IVMRootLayoutNode getRootLayoutNode() { + return fRootLayoutNodeRef.get(); + } + + /** Called to dispose the provider. */ + public void dispose() { + fDisposed = true; + if (fRootLayoutNodeRef.get() != null) { + fRootLayoutNodeRef.get().dispose(); + } + } + + protected boolean isDisposed() { + return fDisposed; + } + + /** + * Convenience method to access the View Model's executor. + */ + public Executor getExecutor() { return fVMAdapter.getExecutor(); } + + + public void update(IHasChildrenUpdate[] updates) { + // Sort the updates by the layout node. + Map> nodeUpdatesMap = new HashMap>(); + for (IHasChildrenUpdate update : updates) { + // Get the VM Context for last element in path. + IVMLayoutNode layoutNode = getLayoutNodeObject(update.getElement()); + if (layoutNode == null) { + // Stale update, most likely as a result of the layout nodes being + // changed. Just ignore it. + if (!update.isCanceled()) update.done(); + continue; + } + if (!nodeUpdatesMap.containsKey(layoutNode)) { + nodeUpdatesMap.put(layoutNode, new ArrayList()); + } + nodeUpdatesMap.get(layoutNode).add(update); + } + + // Iterate through the nodes in the sorted map. + for (IVMLayoutNode node : nodeUpdatesMap.keySet()) { + updateNode(node, nodeUpdatesMap.get(node).toArray(new IHasChildrenUpdate[nodeUpdatesMap.get(node).size()])); + } + } + + private void updateNode(IVMLayoutNode node, final IHasChildrenUpdate[] updates) { + // If parent element's layout node has no children, just set the + // result and coninue to next element. + if (node.getChildLayoutNodes().length == 0) { + for (IHasChildrenUpdate update : updates) { + update.setHasChilren(false); + } + return; + } + + HasElementsUpdate[][] elementsUpdates = + new HasElementsUpdate[node.getChildLayoutNodes().length][updates.length]; + for (int i = 0; i < updates.length; i ++) + { + final IHasChildrenUpdate update = updates[i]; + for (int j = 0; j < node.getChildLayoutNodes().length; j++) + { + final DoneCollector> hasChildrenDoneCollector = + new DoneCollector>() { + public void run() { + // Status is OK, only if all dones are OK. + if (getStatus().isOK()) { + boolean isContainer = false; + for (GetDataDone hasElementsDone : getDones().keySet()) { + isContainer |= hasElementsDone.getStatus().isOK() && + hasElementsDone.getData().booleanValue(); + } + update.setHasChilren(isContainer); + update.done(); + } + } + }; + + elementsUpdates[j][i] = new HasElementsUpdate( + update, + hasChildrenDoneCollector.add(new GetDataDone() { + public void run() { + hasChildrenDoneCollector.doneDone(this); + } + })); + } + } + + for (int j = 0; j < node.getChildLayoutNodes().length; j++) { + node.getChildLayoutNodes()[j].updateHasElements(elementsUpdates[j]); + } + } + + + public void update(final IChildrenCountUpdate[] updates) { + for (final IChildrenCountUpdate update : updates) { + if (update.isCanceled()) continue; + + getChildrenCountsForNode( + update, update.getElementPath(), + new GetDataDone() { + public void run() { + if (getStatus().isOK()) { + int numChildren = 0; + for (Integer count : getData()) { + numChildren += count.intValue(); + } + update.setChildCount(numChildren); + } else { + update.setChildCount(0); + } + update.done(); + } + }); + } + } + + public void update(IChildrenUpdate[] updates) { + for (final IChildrenUpdate update : updates) { + getChildrenCountsForNode( + update, update.getElementPath(), + new GetDataDone() { + public void run() { + if (!getStatus().isOK()) { + update.done(); + return; + } + updateChildrenWithCounts(update, getData()); + } + }); + } + } + + + private void getChildrenCountsForNode(IViewerUpdate update, TreePath elementPath, final GetDataDone done) { + if (isDisposed()) return; + + // Get the VM Context for last element in path. + final IVMLayoutNode layoutNode = getLayoutNodeObject(update.getElement()); + if (layoutNode == null) { + // Stale update. Just ignore. + done.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfService.INVALID_HANDLE, + "Stale update.", null)); //$NON-NLS-1$ + getExecutor().execute(done); + return; + } + + IVMLayoutNode[] childNodes = layoutNode.getChildLayoutNodes(); + + // If parent element's layout node has no children, just mark done and + // return. + if (childNodes.length == 0) { + done.setData(new Integer[0]); + getExecutor().execute(done); + return; + } + + + // Get the mapping of all the counts. + final Integer[] counts = new Integer[childNodes.length]; + final DoneCollector childrenCountDoneCollector = + new DoneCollector() { + public void run() { + if (fDisposed) return; + if (propagateError(getExecutor(), done, "")) return; //$NON-NLS-1$ + done.setData(counts); + getExecutor().execute(done); + } + }; + + for (int i = 0; i < childNodes.length; i++) { + final int nodeIndex = i; + childNodes[i].updateElementCount( + new ElementsCountUpdate( + update, + childrenCountDoneCollector.add(new GetDataDone() { + public void run() { + if (getStatus().isOK()) { + assert getData() != null; + counts[nodeIndex] = getData(); + } + childrenCountDoneCollector.doneDone(this); + } + }), + elementPath) + ); + } + } + + private void updateChildrenWithCounts(final IChildrenUpdate update, Integer[] nodeElementCounts) { + final IVMLayoutNode layoutNode = getLayoutNodeObject(update.getElement()); + if (layoutNode == null) { + // Stale update. Just ignore. + if (!update.isCanceled()) update.done(); + } + + // Create the done collector to mark update when querying all children nodes is finished. + final DoneCollector elementsDoneCollector = + new DoneCollector() { + public void run() { + if (!update.isCanceled()) update.done(); + } + }; + + // Iterate through all child nodes and if requested range matches, call them to + // get their elements. + int updateStartIdx = update.getOffset(); + int updateEndIdx = update.getOffset() + update.getLength(); + int idx = 0; + IVMLayoutNode[] layoutNodes = layoutNode.getChildLayoutNodes(); + for (int i = 0; i < layoutNodes.length; i++) { + final int nodeStartIdx = idx; + final int nodeEndIdx = idx + nodeElementCounts[i]; + idx = nodeEndIdx; + + // Check if update range overlaps the node's range. + if (updateStartIdx <= nodeEndIdx && updateEndIdx > nodeStartIdx) { + final int elementsStartIdx = Math.max(updateStartIdx - nodeStartIdx, 0); + final int elementsEndIdx = Math.min(updateEndIdx - nodeStartIdx, nodeElementCounts[i]); + + layoutNodes[i].updateElements( + new ElementsUpdate( + update, + elementsDoneCollector.add(new Done() { + public void run() { + elementsDoneCollector.doneDone(this); + } + }), + nodeStartIdx, elementsStartIdx, elementsEndIdx - elementsStartIdx) + ); + } + } + + // Guard against invalid queries. + if (elementsDoneCollector.getDones().isEmpty()) { + update.done(); + } + + } + + public ModelProxy createModelProxy(Object element, IPresentationContext context) { + /* + * Model proxy is the object that correlates events from the data model + * into view model deltas that the view can process. We only need to + * create a proxy for the root element of the tree. + */ + if (getRootLayoutNode() != null && + element.equals(getRootLayoutNode().getRootObject())) + { + return fModelProxy; + } + return null; + } + + /** + * Creates the column presentation for the given object. This method is meant + * to be overriden by deriving class to provide view-specific functionality. + * The default is to return null, meaning no columns. + *

+ * The viewer only reads the column presentation for the root/input element of + * the tree/table, so the VMProvider must be configured to own the root element + * in the view in order for this setting to be effective. + *

+ * Note: since the IColumnEditorFactoryAdapter interface is synchronous, and since + * column info is fairly static, this method is thread-safe, and it will + * not be called on the executor thread. + * + * @see IColumnPresentationFactoryAdapter#createColumnPresentation(IPresentationContext, Object) + */ + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { + return null; + } + + /** + * Returns the ID of the column presentation for the given object. This method + * is meant to be overriden by deriving class to provide view-specific + * functionality. The default is to return null, meaning no columns. + *

+ * The viewer only reads the column presentation for the root/input element of + * the tree/table, so the VMProvider must be configured to own the root element + * in the view in order for this setting to be effective. + *

+ * Note: since the IColumnEditorFactoryAdapter interface is synchronous, and since + * column info is fairly static, this method is thread-safe, and it will + * not be called on the executor thread. + * + * @see IColumnEditorFactoryAdapter#getColumnEditorId(IPresentationContext, Object) + */ + public String getColumnPresentationId(IPresentationContext context, Object element) { + return null; + } + + + /** + * Convenience method that finds the VMC corresponding to given parent + * argument given to isContainer() or retrieveChildren(). + * @param object Object to find the VMC for. + * @return parent VMC, if null it indicates that the object did not originate + * from this view or is stale. + */ + private IVMLayoutNode getLayoutNodeObject(Object element) { + /* + * First check to see if the parent object is the root object of the + * hierarchy. If that's the case, then retrieve the correcponding + * root VMC from the root node, and pass this root vmc to the root's + * child layout nodes. + */ + IVMRootLayoutNode rootLayoutNode = getRootLayoutNode(); + if (rootLayoutNode == null) { + return null; + } + else if (element.equals(rootLayoutNode.getRootObject())) { + return rootLayoutNode; + } + else if (element instanceof IVMContext){ + /* + * The parent is a VMC. Check to make sure that the VMC + * originated from a node in this ViewModelProvider. If it didn't + * it is most likely a result of a change in view layout, and this + * request is a stale request. So just ignore it. + */ + if (isOurLayoutNode( ((IVMContext)element).getLayoutNode(), + new IVMLayoutNode[] { rootLayoutNode } )) + { + return ((IVMContext)element).getLayoutNode(); + } + } + return null; + } + + /** + * Convenience method which checks whether given layout node is a node + * that is configured in this ViewModelProvider. Implementation + * recursively walks the layout hierarchy, and returns true if it finds + * the node. + */ + private boolean isOurLayoutNode(IVMLayoutNode layoutNode, IVMLayoutNode[] nodesToSearch) { + for (IVMLayoutNode node : nodesToSearch) { + if (node == layoutNode) return true; + if (isOurLayoutNode(layoutNode, node.getChildLayoutNodes())) return true; + } + return false; + } + + /** + * Handle "data model changed" event by generating a delta object for each + * view and passing it to the corresponding view model provider. The view + * model provider is then responsible for filling-in and sending the delta + * to the viewer. + * @param e + */ + @DsfServiceEventHandler + public void eventDispatched(final IDMEvent event) { + IVMRootLayoutNode rootLayoutNode = getRootLayoutNode(); + + if (rootLayoutNode != null && rootLayoutNode.getDeltaFlags(event) != 0) { + rootLayoutNode.createDelta(event, new GetDataDone() { + public void run() { + if (getStatus().isOK()) { + fModelProxy.fireModelChangedNonDispatch(getData()); + } + } + @Override public String toString() { + return "Result of a delta for event: '" + event.toString() + "' in VMP: '" + AbstractVMProvider.this + "'" + "\n" + getData().toString(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + }); + } + } + + @ThreadSafe + protected class ModelProxy extends AbstractModelProxy { + /** + * Counter for whether the model proxy is currently installed in the viewer. + * Data model events are processed only if the model proxy is active. + */ + private int fProxyActive = 0; + + /** + * Scheduling rule for running the update jobs. + */ + private ISchedulingRule fModelChangeRule = new ISchedulingRule() { + public boolean contains(ISchedulingRule rule) { return this == rule; } + public boolean isConflicting(ISchedulingRule rule) { return rule == this; } + }; + + public void installed(Viewer viewer) { + fProxyActive++; + } + + public void dispose() { + fProxyActive--; + super.dispose(); + } + + /** + * Fires given delta using a job. Processing the delta on the dispatch + * thread can lead to dead-locks. + * @param delta + */ + public void fireModelChangedNonDispatch(final IModelDelta delta) { + if (fProxyActive <= 0) return; + + Job job = new Job("Processing view model delta.") { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + fireModelChanged(delta); + return Status.OK_STATUS; + } + }; + job.setPriority(Job.INTERACTIVE); + job.setRule(fModelChangeRule); + job.schedule(); + } + + } + + class ViewerUpdate implements IViewerUpdate { + private IStatus fStatus; + private boolean fDoneInvoked = false; + final private Done fDone; + final protected IViewerUpdate fClientUpdate; + + public ViewerUpdate(IViewerUpdate clientUpdate, Done done) { + fDone = done; + fClientUpdate = clientUpdate; + } + + public Object getElement() { return fClientUpdate.getElement(); } + public TreePath getElementPath() { return fClientUpdate.getElementPath(); } + public IPresentationContext getPresentationContext() { return fClientUpdate.getPresentationContext(); } + + public IStatus getStatus() { return fStatus; } + public void setStatus(IStatus status) { fStatus = status; } + public void beginTask(String name, int totalWork) {} + public void internalWorked(double work) {} + public boolean isCanceled() { return fClientUpdate.isCanceled(); } + public void setCanceled(boolean value) { fClientUpdate.setCanceled(value); } + public void setTaskName(String name) {} + public void subTask(String name) {} + public void worked(int work) {} + + public void done() { + assert !fDoneInvoked; + fDoneInvoked = true; + try { + getExecutor().execute(fDone); + } catch (RejectedExecutionException e) { // Ignore + } + } + + } + + class HasElementsUpdate extends ViewerUpdate implements IHasChildrenUpdate { + + final private GetDataDone fDone; + + HasElementsUpdate(IHasChildrenUpdate clientUpdate, GetDataDone done) { + super(clientUpdate, done); + fDone = done; + } + + public TreePath getElementPath() { + return ((IHasChildrenUpdate)fClientUpdate).getElementPath(); + } + + public void setHasChilren(boolean hasChildren) { + fDone.setData(hasChildren); + } + + public void done() { + assert fDone.getData() != null || !fDone.getStatus().isOK(); + super.done(); + } + } + + class ElementsCountUpdate extends ViewerUpdate implements IChildrenCountUpdate { + final private GetDataDone fDone; + final private TreePath fElementPath; + + ElementsCountUpdate(IViewerUpdate clientUpdate, GetDataDone done, TreePath elementPath) { + super(clientUpdate, done); + fElementPath = elementPath; + fDone = done; + } + + public TreePath getElementPath() { + return fElementPath; + } + + public void setChildCount(int count) { + fDone.setData(count); + } + + public void done() { + assert fDone.getData() != null || !fDone.getStatus().isOK(); + super.done(); + } + + } + + class ElementsUpdate extends ViewerUpdate implements IChildrenUpdate { + private final int fClientOffset; + private final int fOffset; + private final int fLength; + + ElementsUpdate(IChildrenUpdate clientUpdate, Done done, int clientOffset, int offset, int length) { + super(clientUpdate, done); + fClientOffset = clientOffset; + fOffset = offset; + fLength = length; + } + + public int getOffset() { + return fOffset; + } + + public int getLength() { + return fLength; + } + + public TreePath getElementPath() { + return ((IChildrenUpdate)fClientUpdate).getElementPath(); + } + + public void setChild(Object child, int offset) { + if (offset >= fOffset && offset < (fOffset + fLength)) { + ((IChildrenUpdate)fClientUpdate).setChild(child, fClientOffset + offset); + } + } + + } +} diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMRootLayoutNode.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMRootLayoutNode.java index baacc1cfc7c..53d5650d917 100644 --- a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMRootLayoutNode.java +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/AbstractVMRootLayoutNode.java @@ -10,12 +10,15 @@ *******************************************************************************/ package org.eclipse.dd.dsf.ui.viewmodel; -import org.eclipse.core.runtime.IAdaptable; -import org.eclipse.dd.dsf.concurrent.DoneCollector; -import org.eclipse.dd.dsf.concurrent.DsfExecutor; +import java.util.Map; + +import org.eclipse.dd.dsf.concurrent.Done; import org.eclipse.dd.dsf.concurrent.GetDataDone; -import org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; /** * @@ -23,44 +26,8 @@ import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; @SuppressWarnings("restriction") abstract public class AbstractVMRootLayoutNode extends AbstractVMLayoutNode implements IVMRootLayoutNode { - protected static class RootVMC implements IRootVMC { - private final V fInputObject; - private final IVMRootLayoutNode fVMRootLayoutNode; - - public RootVMC(IVMRootLayoutNode vmRootLayoutNode, V inputObject) { - fVMRootLayoutNode = vmRootLayoutNode; - fInputObject = inputObject; - } - - /** Returns the ILaunch object belonging to this launch VMC. */ - public V getInputObject() { return fInputObject; } - - public IVMContext getParent() { return null; } - - public IVMLayoutNode getLayoutNode() { return fVMRootLayoutNode; } - - @SuppressWarnings("unchecked") - public Object getAdapter(Class adapter) { - if (fInputObject instanceof IAdaptable) { - return ((IAdaptable)fInputObject).getAdapter(adapter); - } - return null; - } - - public boolean equals(Object other) { - return getClass().equals( other.getClass() ) && - fInputObject.equals( ((RootVMC)other).getInputObject() ); - } - - public int hashCode() { - return fInputObject.hashCode(); - } - - public String toString() { return "Root VMC for " + fInputObject.toString(); } //$NON-NLS-1$ - } - - public AbstractVMRootLayoutNode(DsfExecutor executor) { - super(executor); + public AbstractVMRootLayoutNode(AbstractVMProvider provider) { + super(provider); } /** @@ -68,9 +35,20 @@ abstract public class AbstractVMRootLayoutNode extends AbstractVMLayoutNode impl * There is no use case for a root node implementing this method, but its * easier to just impelemnt it for sake of uniformity of model. */ - public void getElements(IVMContext parentVmc, GetDataDone done) { - done.setData(new IVMContext[] { getRootVMC() }); - getExecutor().execute(done); + public void updateElements(IChildrenUpdate update) { + // Ignore startIdx, endIdx, since there's only one element to be had. + update.setChild(getRootObject(), 0); + update.done(); + } + + /** + * This implementation only fulfils the requirements of the super-interface. + * There is no use case for a root node implementing this method, but its + * easier to just impelemnt it for sake of uniformity of model. + */ + public void updateElementCount(IChildrenCountUpdate update) { + update.setChildCount(1); + update.done(); } /** @@ -78,9 +56,11 @@ abstract public class AbstractVMRootLayoutNode extends AbstractVMLayoutNode impl * There is no use case for a root node implementing this method, but its * easier to just impelemnt it for sake of uniformity of model. */ - public void hasElements(IVMContext parentVmc, GetDataDone done) { - done.setData(true); - getExecutor().execute(done); + public void updateHasElements(IHasChildrenUpdate[] updates) { + for (IHasChildrenUpdate update : updates) { + update.setHasChilren(true); + update.done(); + } } /** @@ -88,8 +68,8 @@ abstract public class AbstractVMRootLayoutNode extends AbstractVMLayoutNode impl * There is no use case for a root node implementing this method, but its * easier to just impelemnt it for sake of uniformity of model. */ - public void retrieveLabel(IVMContext vmc, ILabelRequestMonitor result, String[] columns) { - result.done(); + public void updateLabel(IVMContext vmc, ILabelUpdate update) { + update.done(); } /** @@ -97,28 +77,21 @@ abstract public class AbstractVMRootLayoutNode extends AbstractVMLayoutNode impl * is the input object into the view. */ public void createDelta(Object event, final GetDataDone done) { - final VMDelta rootDelta = new VMDelta(getRootVMC().getInputObject(), getRootVMC()); + final Map childNodeDeltas = getChildNodesWithDeltas(event); + assert childNodeDeltas.size() != 0 : "Caller should make sure that there are deltas for given event."; //$NON-NLS-1$ - final IVMLayoutNode[] childNodes = getChildNodesWithDeltas(event); - if (childNodes.length == 0) { - done.setData(rootDelta); - getExecutor().execute(done); - return; - } + // Always create the rootDelta, no matter what delta flags the child nodes have. + final VMDelta rootDelta = new VMDelta(getRootObject(), IModelDelta.NO_CHANGE); - /* - * The execution for this node is not done until all the child nodes - * are done. Use the tracker to wait for all children to complete. - */ - final DoneCollector doneCollector = new DoneCollector(getExecutor()) { - public void run() { - if (propagateError(getExecutor(), done, "Failed to generate child deltas.")) return; //$NON-NLS-1$ - done.setData(rootDelta); - getExecutor().execute(done); - } - }; - for (final IVMLayoutNode childNode : childNodes) { - childNode.buildDelta(event, rootDelta, doneCollector.addNoActionDone()); - } + callChildNodesToBuildDelta( + childNodeDeltas, rootDelta, event, + new Done() { + public void run() { + if (isDisposed()) return; + if (propagateError(getExecutor(), done, "Failed to create delta.")); //$NON-NLS-1$ + done.setData(rootDelta); + getExecutor().execute(done); + } + }); } } diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/DMContextVMLayoutNode.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/DMContextVMLayoutNode.java deleted file mode 100644 index eefeebaa56f..00000000000 --- a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/DMContextVMLayoutNode.java +++ /dev/null @@ -1,360 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006 Wind River Systems 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: - * Wind River Systems - initial API and implementation - *******************************************************************************/ -package org.eclipse.dd.dsf.ui.viewmodel; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.dd.dsf.concurrent.Done; -import org.eclipse.dd.dsf.concurrent.DoneCollector; -import org.eclipse.dd.dsf.concurrent.GetDataDone; -import org.eclipse.dd.dsf.concurrent.Immutable; -import org.eclipse.dd.dsf.datamodel.DMContexts; -import org.eclipse.dd.dsf.datamodel.IDMContext; -import org.eclipse.dd.dsf.datamodel.IDMData; -import org.eclipse.dd.dsf.datamodel.IDMEvent; -import org.eclipse.dd.dsf.datamodel.IDMService; -import org.eclipse.dd.dsf.service.DsfServicesTracker; -import org.eclipse.dd.dsf.service.DsfSession; -import org.eclipse.dd.dsf.service.IDsfService; -import org.eclipse.dd.dsf.ui.DsfUIPlugin; -import org.eclipse.dd.dsf.ui.viewmodel.IVMRootLayoutNode.IRootVMC; -import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousLabelAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentationFactoryAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.swt.graphics.FontData; -import org.eclipse.swt.graphics.RGB; - - -/** - * View model layout node based on a single Data Model Context type. - * The assumption in this implementation is that elements of this node have - * a single IDMContext associated with them, and all of these contexts - * are of the same class type. - */ -@SuppressWarnings("restriction") -abstract public class DMContextVMLayoutNode extends AbstractVMLayoutNode { - - /** - * IVMContext implementation used for this schema node. - */ - @Immutable - public class DMContextVMContext implements IVMContext { - private final IVMContext fParent; - private final IDMContext fDmc; - - public DMContextVMContext(IVMContext parent, IDMContext dmc) { - fParent = parent; - fDmc = dmc; - } - - public IDMContext getDMC() { return fDmc; } - public IVMContext getParent() { return fParent; } - public IVMLayoutNode getLayoutNode() { return DMContextVMLayoutNode.this; } - - /** - * The IAdaptable implementation. If the adapter is the DM context, - * return the context, otherwise delegate to IDMContext.getAdapter(). - */ - @SuppressWarnings("unchecked") - public Object getAdapter(Class adapter) { - if (adapter.isInstance(fDmc)) { - return fDmc; - } else { - return fDmc.getAdapter(adapter); - } - } - - public boolean equals(Object other) { - if (!(other instanceof DMContextVMLayoutNode.DMContextVMContext)) return false; - DMContextVMLayoutNode.DMContextVMContext otherVmc = (DMContextVMLayoutNode.DMContextVMContext)other; - return DMContextVMLayoutNode.this.equals(otherVmc.getLayoutNode()) && - fParent.equals(otherVmc.fParent) && - fDmc.equals(otherVmc.fDmc); - } - - public int hashCode() { - return DMContextVMLayoutNode.this.hashCode() + fParent.hashCode() + fDmc.hashCode(); - } - - public String toString() { - return fParent.toString() + "->" + fDmc.toString(); //$NON-NLS-1$ - } - } - - /** Service tracker to be used by sub-classes */ - private DsfServicesTracker fServices; - - private DsfSession fSession; - - /** - * Concrete class type that the elements of this schema node are based on. - * Even though the data model type is a parameter the DMContextVMLayoutNode, - * this type is erased at runtime, so a concrete class typs of the DMC - * is needed for instanceof chacks. - */ - private Class> fDMCClassType; - - /** - * Constructor initializes instance data, except for the child nodes. - * Child nodes must be initialized by calling setChildNodes() - * @param session - * @param dmcClassType - * @see #setChildNodes(IVMLayoutNode[]) - */ - public DMContextVMLayoutNode(DsfSession session, Class> dmcClassType) { - super(session.getExecutor()); - fSession = session; - fServices = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); - fDMCClassType = dmcClassType; - } - - /** - * Returns the session for use by sub-classes. - */ - protected DsfSession getSession() { - return fSession; - } - - /** - * Returns the services tracker for sub-class use. - */ - protected DsfServicesTracker getServicesTracker() { - return fServices; - } - - /** - * The default implementation of the retrieve label method. It acquires - * the service, using parameters in the DMC, then it fetches the model - * data from the service, and then it calls the protected method - * fillColumnLabel() for each column. The deriving classes should override - * this method if a different method of computing the label is needed. - * - * @see #fillColumnLabel(IDMData, String, int, String[], ImageDescriptor[], FontData[], RGB[], RGB[]) - */ - @SuppressWarnings("unchecked") - public void retrieveLabel(IVMContext vmc, final ILabelRequestMonitor result, final String[] columns) { - /* - * Extract the DMContext from the VMContext, see DMContextVMContext.getAdapter(). - * Since the VMContext is supplied by this node, the DMContext should never be null. - * Note: had to suppress type cast warnings here, because getAdapter() does not support - * generics, and even if it did, I'm not sure it would help. - */ - final IDMContext dmc = (IDMContext)(vmc).getAdapter(IDMContext.class); - if (dmc == null) { - assert false; - result.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR, "Invalid VMC type", null)); //$NON-NLS-1$ - result.done(); - return; - } - - /* - * Get the instance of the service using the service filter in the DMContext - * If null it could mean that the service already shut down, and the view - * is holding stale elements which will be cleaned up shortly. - */ - IDMService dmService = (IDMService)getServicesTracker().getService(null, dmc.getServiceFilter()); - if (dmService == null) { - handleFailedRetrieveLabel(result); - return; - } - - dmService.getModelData( - dmc, - new GetDataDone() { - public void run() { - /* - * Check that the request was evaluated and data is still - * valid. The request could fail if the state of the - * service changed during the request, but the view model - * has not been updated yet. - */ - if (!getStatus().isOK() || !getData().isValid()) { - assert getStatus().isOK() || - getStatus().getCode() != IDsfService.INTERNAL_ERROR || - getStatus().getCode() != IDsfService.NOT_SUPPORTED; - handleFailedRetrieveLabel(result); - return; - } - - /* - * If columns are configured, call the protected methods to - * fill in column values. - */ - String[] localColumns = columns; - if (localColumns == null) localColumns = new String[] { null }; - - String[] text = new String[localColumns.length]; - ImageDescriptor[] image = new ImageDescriptor[localColumns.length]; - FontData[] fontData = new FontData[localColumns.length]; - RGB[] foreground = new RGB[localColumns.length]; - RGB[] background = new RGB[localColumns.length]; - for (int i = 0; i < localColumns.length; i++) { - fillColumnLabel(dmc, getData(), localColumns[i], i, text, image, fontData, foreground, background); - } - result.setLabels(text); - result.setImageDescriptors(image); - result.setFontDatas(fontData); - result.setBackgrounds(background); - result.setForegrounds(foreground); - result.done(); - } - }); - } - - /** - * Fills in label information for given column. This method is intended to - * be overriden by deriving classes, to supply label information specific - * to the node.
- * The implementation should fill in the correct value in each array at the - * given index. - * @param dmContext Data Model Context object for which the label is generated. - * @param dmData Data Model Data object retrieved from the model service. - * for the DM Context supplied to the retrieveLabel() call. - * @param columnId Name of the column to fill in, null if no columns specified. - * @param idx Index to fill in in the label arrays. - * @param text - * @param image - * @param fontData - * @param foreground - * @param background - * - * @see IAsynchronousLabelAdapter - * @see IColumnPresentationFactoryAdapter - */ - protected void fillColumnLabel(IDMContext dmContext, V dmData, String columnId, int idx, String[] text, - ImageDescriptor[] image, FontData[] fontData, RGB[] foreground, RGB[] background ) - { - text[idx] = ""; //$NON-NLS-1$ - } - - @Override - public boolean hasDeltaFlags(Object e) { - if (e instanceof IDMEvent) { - return hasDeltaFlagsForDMEvent((IDMEvent)e); - } else { - return super.hasDeltaFlags(e); - } - } - - /** - * DMC-specific version of {@link IVMLayoutNode#hasDeltaFlags(Object)}. - * By default, it falls back on the super-class implementation. - */ - protected boolean hasDeltaFlagsForDMEvent(IDMEvent e) { - return super.hasDeltaFlags(e); - } - - @Override - public void buildDelta(Object e, VMDelta parent, Done done) { - if (e instanceof IDMEvent) { - buildDeltaForDMEvent((IDMEvent)e, parent, done); - } else { - super.buildDelta(e, parent, done); - } - } - - /** - * Adds an optimization (over the AbstractViewModelLayoutNode) which - * narrows down the list of children based on the DMC within the event. - */ - public void buildDeltaForDMEvent(final IDMEvent e, final VMDelta parent, final Done done) { - /* - * Take the IDMContext (DMC) that the event is based on, and - * search its ancestors. Look for the DMC class typs that this schema - * node is based on. If its found, then only one IModelDelta needs to - * be generated for this schema node. Otherwise, resort to the default - * behavior and generate a IModelDelta for every element in this schema - * node. - */ - IDMContext dmc = DMContexts.getAncestorOfType(e.getDMContext(), fDMCClassType); - if (dmc != null) { - IVMLayoutNode[] childNodes = getChildNodesWithDeltas(e); - if (childNodes.length == 0) { - // There are no child nodes with deltas, just return to parent. - getExecutor().execute(done); - return; - } - - /* - * This execution for this node is not done until all the child nodes - * are done. Use the tracker to wait for all children to complete. - */ - DoneCollector childDoneTracker = new DoneCollector(getExecutor()) { - public void run() { - getExecutor().execute(done); - } - }; - for (final IVMLayoutNode childNode : getChildLayoutNodes()) { - /* - * Create a delta corresponding to the DMC from the event and pass - * it as parent VMC to the child node. The child node will build - * its delta on top of this delta. - */ - childNode.buildDelta( - e, - parent.addNode(new DMContextVMContext(parent.getVMC(), dmc), IModelDelta.NO_CHANGE), - childDoneTracker.addNoActionDone()); - } - } else { - super.buildDelta(e, parent, done); - } - } - - /** - * Utility method that takes an array of DMC object and creates a - * corresponding array of IVMContext elements base on that. - * @param parent The parent for generated IVMContext elements. - * @param dmcs Array of DMC objects to build return array on. - * @return Array of IVMContext objects. - */ - protected IVMContext[] dmcs2vmcs(IVMContext parent, IDMContext[] dmcs) { - IVMContext[] vmContexts = new IVMContext[dmcs.length]; - for (int i = 0; i < dmcs.length; i++) { - vmContexts[i] = new DMContextVMContext(parent, dmcs[i]); - } - return vmContexts; - } - - /** - * Searches for a DMC of given type in the tree patch contained in given - * VMC. VMCs keep a reference to the parent node that contain them in the - * tree. This method recursively looks compares the parent until root is - * reached, or the DMC is found. If the root is reached, and the root's - * input is also a VMC (which comes from another view), then the hierarchy - * of the input object will be searched as well. - * @param Type of the DMC that will be returned. - * @param vmc VMC element to search. - * @param dmcType Class object for matching the type. - * @return DMC, or null if not found. - */ - @SuppressWarnings("unchecked") - public static V findDmcInVmc(IVMContext vmc, Class dmcType) { - if (vmc instanceof IRootVMC && ((IRootVMC)vmc).getInputObject() instanceof IVMContext) { - vmc = (IVMContext)((IRootVMC)vmc).getInputObject(); - } - - if (vmc instanceof DMContextVMLayoutNode.DMContextVMContext && - dmcType.isAssignableFrom( ((DMContextVMLayoutNode.DMContextVMContext)vmc).getDMC().getClass() )) - { - return (V)((DMContextVMLayoutNode.DMContextVMContext)vmc).getDMC(); - } else if (vmc.getParent() != null) { - return findDmcInVmc(vmc.getParent(), dmcType); - } - return null; - } - - public void dispose() { - fServices.dispose(); - super.dispose(); - } -} diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMAdapter.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMAdapter.java new file mode 100644 index 00000000000..2d1c9dfaa05 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMAdapter.java @@ -0,0 +1,19 @@ +package org.eclipse.dd.dsf.ui.viewmodel; + +import org.eclipse.dd.dsf.concurrent.ThreadSafe; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactoryAdapter; + +/** + * The View Model adapter handles the layout of a given data model within a + * set of viewers. This adapter should be returned by an adapter factory for + * the input object of the viewer, and this adapter implementation will then + * populate the view contents. + */ +@ThreadSafe +@SuppressWarnings("restriction") +public interface IVMAdapter + extends IElementContentProvider, IModelProxyFactoryAdapter, IColumnPresentationFactoryAdapter +{ +} diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMContext.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMContext.java index 9c31035105a..aeacc41e922 100644 --- a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMContext.java +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMContext.java @@ -14,7 +14,7 @@ import org.eclipse.core.runtime.IAdaptable; import org.eclipse.dd.dsf.concurrent.Immutable; /** - * View model element which is stored as the data object of nodes in the viewer. + * View Model element which is stored as the data object of nodes in the viewer. */ @Immutable public interface IVMContext extends IAdaptable { @@ -23,10 +23,4 @@ public interface IVMContext extends IAdaptable { * Returns the layout node that originated this element. */ public IVMLayoutNode getLayoutNode(); - - /** - * Returns the parent of this element in the viewer layout. - * @return - */ - public IVMContext getParent(); } diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMLayoutNode.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMLayoutNode.java index 0d2d3f3014f..883d0e23d0d 100644 --- a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMLayoutNode.java +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMLayoutNode.java @@ -12,55 +12,67 @@ package org.eclipse.dd.dsf.ui.viewmodel; import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.dd.dsf.concurrent.Done; -import org.eclipse.dd.dsf.concurrent.GetDataDone; -import org.eclipse.dd.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditor; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditorFactoryAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; /** * View model layout nodes are combined together into a tree, to collectively - * define the layout of a view. Each schema node generates elements of type - * IVMContext, and provide label information about these elements. + * define the layout of a view. Each layout node generates elements of type + * IVMContext which are then stored in the viewer. *

* Besides the standard Data Model Context based implementation, this * node could be implemented to present data from any source, not necessarily * DSF services. It could also define a static node which operates on basis * of other data in the view tree. - * @see VMProvider + * @see AbstractDMVMProvider */ @ConfinedToDsfExecutor("") @SuppressWarnings("restriction") -public interface IVMLayoutNode { +public interface IVMLayoutNode +{ /** - * Retrieves information whether for the given parent node, there are any elements - * available. - * @param parentVmc Parent node, for which to calculate elements at the - * current level. - * @param done The data return token. + * Retrieves information whether for a given path in the viewer, + * there are any elements available in this node. + * + * @param updates The update objects which need to be filled in with results + * calculated by this method. + * Even though the "children" interface is reused, the updates refer to the + * elements of this layout node, and not it's children. */ - public void hasElements(IVMContext parentVmc, GetDataDone done); - - /** - * Retrieves the list of elements. - * @param parentVmc Parent node, for which to calculate elements at the - * current level. - * @param done The data return token. - */ - public void getElements(IVMContext parentVmc, GetDataDone done); - - /** - * Retrieves the label for the given element. - * @param vmc Element for which to retrieve label information. - * @param result Monitor which accepts the data. - * @param columns Currently configured columns in view. - * @see IPresentationContext - */ - public void retrieveLabel(IVMContext vmc, ILabelRequestMonitor result, String[] columns); + public void updateHasElements(IHasChildrenUpdate[] updates); + /** + * Retrieves the number of available elements in this node for the given + * path in the viewer. + * + * @param updates The update object which needs to be filled in with result + * calculated by this method. + * Even though the "children" interface is reused, the updates refer to the + * elements of this layout node, and not it's children. + */ + public void updateElementCount(IChildrenCountUpdate update); + + + /** + * Retrieves the element objects of this node for the given path in the + * viewer, and for the given range of indexes.
+ * Note: the range of children, denoted by ILabelUpdate.getOffset() + * and ILabelUpdate.getLength(), may not be specified, in which case these + * methods may return -1. This means that all the elements should be + * retrieved for this node.
+ * + * @param updates The update object which needs to be filled in with result + * calculated by this method. + * Even though the "children" interface is reused, the updates refer to the + * elements of this layout node, and not it's children. + */ + public void updateElements(IChildrenUpdate update); + + /** * Configures the child layout nodes for this node. * @param childNodes @@ -73,51 +85,28 @@ public interface IVMLayoutNode { public IVMLayoutNode[] getChildLayoutNodes(); /** - * Returns true/false indicating whether the given even will cause this - * schema node to generate a model delta. + * Returns the potential delta flags that would be generated by this node + * for the given event. * @param event Event to process. - * @return True if this node (or its children) would generate delta data - * due to this event. + * @return IModelDelta flags * @see IModelDelta */ - public boolean hasDeltaFlags(Object event); + public int getDeltaFlags(Object event); /** * Builds model delta information based on the given event. * @param event Event to process. * @param parent Parent model delta node that this object should add delta * data to. + * @param nodeOffset The offset of the first element in this node. This offset + * depends on the elements returned by the siblings of this layout node. * @param done Return token, which notifies the caller that the calculation is * complete. */ - public void buildDelta(Object event, VMDelta parent, Done done); + public void buildDelta(Object event, VMDelta parent, int nodeOffset, Done done); /** - * Creates a column editor for the given element. The interface that this - * method is delegated to is synchronous, therefore it also needs to be thread - * safe. - * - * @see IColumnEditorFactoryAdapter#createColumnEditor(IPresentationContext, Object) - * @param vmc VM Context to return the editor for - * @return - */ - @ThreadSafeAndProhibitedFromDsfExecutor("") - public IColumnEditor createColumnEditor(IVMContext vmc); - - /** - * Returns the ID of the editor for the given element. The interface that this - * method is delegated to is synchronous, therefore it also needs to be thread - * safe. - * - * @see IColumnEditorFactoryAdapter#getColumnEditorId(IPresentationContext, Object) - * @param vmc VM Context to return the editor ID for - * @return - */ - @ThreadSafeAndProhibitedFromDsfExecutor("") - public String getColumnEditorId(IVMContext vmc); - - /** - * Disposes the resources held by this node. + * Releases the resources held by this node. */ public void dispose(); } \ No newline at end of file diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMProvider.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMProvider.java new file mode 100644 index 00000000000..44b2a3e6504 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMProvider.java @@ -0,0 +1,40 @@ +package org.eclipse.dd.dsf.ui.viewmodel; + +import org.eclipse.dd.dsf.concurrent.ThreadSafe; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * The View Model Provider handles the layout of a given model within a + * single viewer. The View Model Adapter delegates calls for view content to + * this object for a view that this provider handles. + */ +@ThreadSafe +@SuppressWarnings("restriction") +public interface IVMProvider + extends IElementContentProvider, IModelProxyFactoryAdapter, IColumnPresentationFactoryAdapter +{ + /** + * Returns the View Model Adapter that this provider belongs to. + */ + public IVMAdapter getVMAdapter(); + + /** + * Returns the root layout node that is configured in this provider. + * It may return null, if a root node is not yet configured. + */ + public IVMRootLayoutNode getRootLayoutNode(); + + /** + * Returns the presentation context of the viewer that this provider + * is configured for. + */ + public IPresentationContext getPresentationContext(); + + /** + * Cleans up the resources associated with this provider. + */ + public void dispose(); +} diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMRootLayoutNode.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMRootLayoutNode.java index 251d74c7574..500ff56c64f 100644 --- a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMRootLayoutNode.java +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/IVMRootLayoutNode.java @@ -11,7 +11,7 @@ package org.eclipse.dd.dsf.ui.viewmodel; import org.eclipse.dd.dsf.concurrent.GetDataDone; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; /** * Special type of the view model layout node, which can be used as a root node @@ -21,28 +21,13 @@ import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; @SuppressWarnings("restriction") public interface IVMRootLayoutNode extends IVMLayoutNode{ - /** - * The root VMC object of this root layout node. There can be only element - * in the root node at a time, and this element must implement this - * interface. - */ - public interface IRootVMC extends IVMContext { - /** - * Returns the view's "input" object. This could be the actual input - * object for the whole view, if this view model hierarchy fills the - * whole view. Or this could an element in the tree from which this - * hierarchy starts. This is the case - */ - Object getInputObject(); - } - /** * Returns the single element of this node. Root layout node can only have * one element, and this is a convenience method to access this element. * Alternatively getElements() could be used. * @return */ - public IRootVMC getRootVMC(); + public Object getRootObject(); /** * Version of the {@link IVMLayoutNode#buildDelta(Object, ViewModelDelta, org.eclipse.dd.dsf.concurrent.Done)} diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/VMDelta.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/VMDelta.java index 09c2c953bd2..6c524768b3f 100644 --- a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/VMDelta.java +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/VMDelta.java @@ -11,8 +11,9 @@ *******************************************************************************/ package org.eclipse.dd.dsf.ui.viewmodel; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.provisional.ModelDelta; +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.ModelDelta; /** * This delta class mostly just duplicates the ModelDelta implemention, but @@ -25,13 +26,13 @@ import org.eclipse.debug.internal.ui.viewers.provisional.ModelDelta; public class VMDelta extends ModelDelta { private VMDelta fParent; - private IVMContext fVmcElement; private Object fElement; private int fFlags; private VMDelta[] fNodes = EMPTY_NODES; private Object fReplacement; private int fIndex; private static final VMDelta[] EMPTY_NODES = new VMDelta[0]; + private int fChildCount = -1; /** * Constructs a new delta for the given element. @@ -39,9 +40,9 @@ public class VMDelta extends ModelDelta { * @param vmcElement model element * @param flags change flags */ - public VMDelta(IVMContext vmcElement, int flags) { - super(vmcElement, flags); - fVmcElement = vmcElement; + public VMDelta(Object element, int flags) { + super(element, flags); + fElement = element; fFlags = flags; } @@ -53,9 +54,9 @@ public class VMDelta extends ModelDelta { * @param replacement replacement element * @param flags change flags */ - public VMDelta(IVMContext vmcElement, Object replacement, int flags) { - super(vmcElement, replacement, flags); - fVmcElement = vmcElement; + public VMDelta(Object element, Object replacement, int flags) { + super(element, replacement, flags); + fElement = element; fReplacement = replacement; fFlags = flags; } @@ -68,40 +69,44 @@ public class VMDelta extends ModelDelta { * @param index insertion position * @param flags change flags */ - public VMDelta(IVMContext vmcElement, int index, int flags) { - super(vmcElement, index, flags); - fVmcElement = vmcElement; + public VMDelta(Object element, int index, int flags) { + super(element, index, flags); + fElement = element; fIndex = index; fFlags = flags; } /** - * Constructor for model delta based on non-VMC element. This delta is - * only needed for creating delta nodes for parent elements in the tree - * if the VMC elements are not at the root of the tree. - * @param element Element to create the delta for. - * @param vmcElement Optional VMC element for this node, it can be used - * by other nodes in the delta to set their VMC parent element correctly. + * Constructs a new delta for the given element at the specified index + * relative to its parent with the given number of children. + * + * @param element model element + * @param index insertion position + * @param flags change flags + * @param childCount number of children this node has */ - public VMDelta(Object element, IVMContext vmcElement) { - super(element, IModelDelta.NO_CHANGE); + public VMDelta(Object element, int index, int flags, int childCount) { + super(element, index, flags, childCount); fElement = element; - fVmcElement = vmcElement; + fIndex = index; + fFlags = flags; + fChildCount = childCount; } - /** * Returns the non-VMC element if one is set, otherwise returns the VMC * element of this delta node. * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getElement() */ + @Override public Object getElement() { - return fElement != null ? fElement : fVmcElement; + return fElement; } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getFlags() */ + @Override public int getFlags() { return fFlags; } @@ -110,7 +115,9 @@ public class VMDelta extends ModelDelta { fFlags |= flags; } - public IVMContext getVMC() { return fVmcElement; } + public void setChildCount(int count) { + fChildCount = count; + } /** * Adds a child node to this delta with the given element and change flags, @@ -120,7 +127,7 @@ public class VMDelta extends ModelDelta { * @param flags change flags for child * @return newly created child delta */ - public VMDelta addNode(IVMContext element, int flags) { + public VMDelta addNode(Object element, int flags) { VMDelta node = new VMDelta(element, flags); node.setParent(this); addDelta(node); @@ -137,7 +144,7 @@ public class VMDelta extends ModelDelta { * @param flags change flags * @return newly created child delta */ - public VMDelta addNode(IVMContext element, Object replacement, int flags) { + public VMDelta addNode(Object element, Object replacement, int flags) { VMDelta node = new VMDelta(element, replacement, flags); node.setParent(this); addDelta(node); @@ -153,7 +160,7 @@ public class VMDelta extends ModelDelta { * @param flags change flags * @return newly created child delta */ - public VMDelta addNode(IVMContext element, int index, int flags) { + public VMDelta addNode(Object element, int index, int flags) { VMDelta node = new VMDelta(element, index, flags); node.setParent(this); addDelta(node); @@ -161,21 +168,22 @@ public class VMDelta extends ModelDelta { } /** - * Adds a node to the delta for a non-VMC element. This is used to - * construct the root branch of the delta before it is handed off to - * ViewModelProvider.handleDataModelEvent() - * @param element Element in the asynchronous view to create the new node for. - * @param vmcElement Optional VMC element for this node, it can be used - * by other nodes in the delta to set their VMC parent element correctly. - * @return Returns the added delta node. + * Adds a child delta to this delta at the specified index with the + * given number of children, and returns the newly created child delta. + * + * @param element child element in insert + * @param index index of the element relative to parent + * @param flags change flags + * @param numChildren the number of children the element has + * @return newly created child delta */ - public VMDelta addNode(Object element, IVMContext vmcElement) { - VMDelta node = new VMDelta(element, vmcElement); + public ModelDelta addNode(Object element, int index, int flags, int numChildren) { + VMDelta node = new VMDelta(element, index, flags, numChildren); node.setParent(this); addDelta(node); return node; } - + /** * Sets the parent delta of this delta * @@ -184,17 +192,19 @@ public class VMDelta extends ModelDelta { void setParent(VMDelta node) { fParent = node; } - + /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getParent() */ - public IModelDelta getParent() { + @Override + public IModelDelta getParentDelta() { return fParent; } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getReplacementElement() */ + @Override public Object getReplacementElement() { return fReplacement; } @@ -202,6 +212,7 @@ public class VMDelta extends ModelDelta { /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getIndex() */ + @Override public int getIndex() { return fIndex; } @@ -209,7 +220,8 @@ public class VMDelta extends ModelDelta { /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getNodes() */ - public VMDelta[] getNodes() { + @Override + public VMDelta[] getChildDeltas() { return fNodes; } @@ -224,6 +236,7 @@ public class VMDelta extends ModelDelta { } } + @Override public String toString() { StringBuffer buf = new StringBuffer(); buf.append("Model Delta Start\n"); //$NON-NLS-1$ @@ -233,43 +246,78 @@ public class VMDelta extends ModelDelta { } private void appendDetail(StringBuffer buf, VMDelta delta) { - buf.append("\tElement: "); //$NON-NLS-1$ - buf.append(delta.getElement()); - buf.append('\n'); - buf.append("\t\tFlags: "); //$NON-NLS-1$ - int flags = delta.getFlags(); - if (flags == 0) { - buf.append("NO_CHANGE"); //$NON-NLS-1$ - } else { - if ((flags & IModelDelta.ADDED) > 0) { - buf.append("ADDED | "); //$NON-NLS-1$ - } - if ((flags & IModelDelta.CONTENT) > 0) { - buf.append("CONTENT | "); //$NON-NLS-1$ - } - if ((flags & IModelDelta.EXPAND) > 0) { - buf.append("EXPAND | "); //$NON-NLS-1$ - } - if ((flags & IModelDelta.INSERTED) > 0) { - buf.append("INSERTED | "); //$NON-NLS-1$ - } - if ((flags & IModelDelta.REMOVED) > 0) { - buf.append("REMOVED | "); //$NON-NLS-1$ - } - if ((flags & IModelDelta.REPLACED) > 0) { - buf.append("REPLACED | "); //$NON-NLS-1$ - } - if ((flags & IModelDelta.SELECT) > 0) { - buf.append("SELECT | "); //$NON-NLS-1$ - } - if ((flags & IModelDelta.STATE) > 0) { - buf.append("STATE | "); //$NON-NLS-1$ - } - } - buf.append('\n'); - VMDelta[] nodes = delta.getNodes(); - for (int i = 0; i < nodes.length; i++) { - appendDetail(buf, nodes[i]); - } + buf.append("\tElement: "); //$NON-NLS-1$ + buf.append(delta.getElement()); + buf.append('\n'); + buf.append("\t\tFlags: "); //$NON-NLS-1$ + int flags = delta.getFlags(); + if (flags == 0) { + buf.append("NO_CHANGE"); //$NON-NLS-1$ + } else { + if ((flags & IModelDelta.ADDED) > 0) { + buf.append("ADDED | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.CONTENT) > 0) { + buf.append("CONTENT | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.EXPAND) > 0) { + buf.append("EXPAND | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.INSERTED) > 0) { + buf.append("INSERTED | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.REMOVED) > 0) { + buf.append("REMOVED | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.REPLACED) > 0) { + buf.append("REPLACED | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.SELECT) > 0) { + buf.append("SELECT | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.STATE) > 0) { + buf.append("STATE | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.INSTALL) > 0) { + buf.append("INSTALL | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.UNINSTALL) > 0) { + buf.append("UNINSTALL | "); //$NON-NLS-1$ + } + } + buf.append('\n'); + buf.append("\t\tIndex: "); //$NON-NLS-1$ + buf.append(delta.fIndex); + buf.append(" Child Count: "); //$NON-NLS-1$ + buf.append(delta.fChildCount); + buf.append('\n'); + IModelDelta[] nodes = delta.getChildDeltas(); + for (int i = 0; i < nodes.length; i++) { + appendDetail(buf, (VMDelta)nodes[i]); + } } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta#getChildCount() + */ + public int getChildCount() { + return fChildCount; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta#accept(org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor) + */ + public void accept(IModelDeltaVisitor visitor) { + doAccept(visitor, 0); + } + + protected void doAccept(IModelDeltaVisitor visitor, int depth) { + if (visitor.visit(this, depth)) { + ModelDelta[] childDeltas = getChildDeltas(); + for (int i = 0; i < childDeltas.length; i++) { + ((VMDelta)childDeltas[i]).doAccept(visitor, depth+1); + } + } + } + } diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/VMProvider.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/VMProvider.java deleted file mode 100644 index 029b25ff52b..00000000000 --- a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/VMProvider.java +++ /dev/null @@ -1,439 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006 Wind River Systems 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: - * Wind River Systems - initial API and implementation - *******************************************************************************/ -package org.eclipse.dd.dsf.ui.viewmodel; - -import java.util.concurrent.atomic.AtomicReference; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.ISchedulingRule; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor; -import org.eclipse.dd.dsf.concurrent.Done; -import org.eclipse.dd.dsf.concurrent.DoneCollector; -import org.eclipse.dd.dsf.concurrent.GetDataDone; -import org.eclipse.dd.dsf.concurrent.ThreadSafe; -import org.eclipse.dd.dsf.datamodel.IDMEvent; -import org.eclipse.dd.dsf.service.DsfServiceEventHandler; -import org.eclipse.dd.dsf.service.DsfSession; -import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy; -import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousContentAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousLabelAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.IChildrenRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditor; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditorFactoryAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation; -import org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentationFactoryAdapter; -import org.eclipse.debug.internal.ui.viewers.provisional.IContainerRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.ILabelRequestMonitor; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.provisional.IModelProxy; -import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; - -/** - * View model provider implements the asynchronous view model functionality for - * a single view. This provider is just a holder which further delegates the - * model provider functionality to the view model layout nodes that need - * to be configured with each provider. - *

- * The view model provider, often does not provide the model for the entire - * view. Rather, it needs to be able to plug in at any level in the viewer's - * content model and provide data for a sub-tree. - * - * @see IAsynchronousContentAdapter - * @see IAsynchronousLabelAdapter - * @see IModelProxy - * @see IVMLayoutNode - */ -@ConfinedToDsfExecutor("fSession#getExecutor") -@SuppressWarnings("restriction") -public class VMProvider -{ - private final DsfSession fSession; - private final ModelProxy fModelProxy = new ModelProxy(); - - /** - * It is theoretically possible for a VMProvider to be disposed before it - * has a chance to register itself as event listener. This flag is used - * to avoid removing itself as listener in such situation. - */ - private boolean fRegisteredAsEventListener = false; - - /** - * The root node for this model provider. The root layout node could be - * null when first created, to allow sub-classes to prorperly configure the - * root node in the sub-class constructor. - */ - private AtomicReference fRootLayoutNodeRef = new AtomicReference(); - - /** - * Constructs the view model provider for given DSF session. The - * constructor is thread-safe to allow VM provider to be constructed - * synchronously when a call to getAdapter() is made on an element - * in a view. - */ - @ThreadSafe - public VMProvider(DsfSession session, IVMRootLayoutNode rootLayoutNode) { - fSession = session; - fRootLayoutNodeRef.set(rootLayoutNode); - // Add ourselves as listener for DM events events. - session.getExecutor().execute(new Runnable() { - public void run() { - if (DsfSession.isSessionActive(getSession().getId())) { - getSession().addServiceEventListener(VMProvider.this, null); - fRegisteredAsEventListener = true; - } - } - }); - } - - /** - * Sets the layout nodes. This method is thread-safe, because it might - * be called fromthe constructor, which itself is thread-safe. - */ - @ThreadSafe - public void setRootLayoutNode(IVMRootLayoutNode rootLayoutNode) { - final IVMRootLayoutNode oldRootLayoutNode = fRootLayoutNodeRef.getAndSet(rootLayoutNode); - if (oldRootLayoutNode != null) { - // IVMLayoutNode has to be called on dispatch thread... for now at least. - getSession().getExecutor().execute( new Runnable() { - public void run() { - oldRootLayoutNode.dispose(); - } - }); - } - } - - public IVMRootLayoutNode getRootLayoutNode() { - return fRootLayoutNodeRef.get(); - } - - /** Called to dispose the provider. */ - public void dispose() { - if (fRegisteredAsEventListener) { - fSession.removeServiceEventListener(this); - } - if (fRootLayoutNodeRef != null) { - fRootLayoutNodeRef.get().dispose(); - } - } - - protected DsfSession getSession() { return fSession; } - - /** - * Performs the query to determine if given VNC is a container. - * Note: this method must be called on the provider's dispatch thread. - * @see IAsynchronousContentAdapter#isContainer(Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext, IContainerRequestMonitor) - */ - @SuppressWarnings("unchecked") - public void isContainer(Object parent, final IContainerRequestMonitor monitor) - { - assert fSession.getExecutor().isInExecutorThread(); - - IVMContext parentVmc = getVmcForObject(parent); - if (parentVmc == null) { - monitor.done(); - return; - } - - /* - * If the element's model node has no child layout nodes, it's not a - * container. - */ - if (parentVmc.getLayoutNode().getChildLayoutNodes().length == 0) { - monitor.setIsContainer(false); - monitor.done(); - return; - } - - /* - * For each child layout node, retrieve the list of elements. When - * all are done, If any of the child nodes have elements, notify the - * monitor that there are children. - */ - final DoneCollector doneCollector = new DoneCollector(fSession.getExecutor()) { - public void run() { - if (monitor.isCanceled()) return; - - boolean isContainer = false; - for (Done hasElementsDone : getDones().keySet()) { - isContainer |= hasElementsDone.getStatus().isOK() && - ((GetDataDone)hasElementsDone).getData().booleanValue(); - } - monitor.setIsContainer(isContainer); - monitor.done(); - } - }; - for (IVMLayoutNode childNode : parentVmc.getLayoutNode().getChildLayoutNodes()) { - childNode.hasElements( - parentVmc, - doneCollector.add( new GetDataDone() { public void run() { - doneCollector.doneDone(this); - }})); - } - } - - /** - * Performs the query to retrieve children for the given VMC. - * Note: this method must be called on the provider's dispatch thread. - * @see IAsynchronousContentAdapter#retrieveChildren(Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext, IChildrenRequestMonitor) - */ - @SuppressWarnings("unchecked") - public void retrieveChildren(final Object parent, final IChildrenRequestMonitor monitor) - { - assert fSession.getExecutor().isInExecutorThread(); - - IVMContext parentVmc = getVmcForObject(parent); - if (parentVmc == null) { - monitor.done(); - return; - } - - /* - * If the element's model node has no child layout nodes. There is - * nothing to do, just mark the monitor done. - */ - if (parentVmc.getLayoutNode().getChildLayoutNodes().length == 0) { - assert false : "We should never get here, because isContainer() should have returned false"; //$NON-NLS-1$ - monitor.done(); - return; - } - - /* - * Iterate through the child layout nodes, and request their elements. - * Requests are async, so use a tracker for the results. - */ - final DoneCollector doneCollector = new DoneCollector(fSession.getExecutor()) { - public void run() { - if (monitor.isCanceled()) return; - monitor.done(); - } - }; - for (IVMLayoutNode childNode : parentVmc.getLayoutNode().getChildLayoutNodes()) { - childNode.getElements( - parentVmc, - doneCollector.add( new GetDataDone() { - public void run() { - if (getStatus().isOK()) { - monitor.addChildren(getData()); - } - doneCollector.doneDone(this); - } - })); - } - } - - /** - * Retrieves the label information for given object. - * The implementation converts the object into a VM-Context, then delegates - * to the context's layout node. - * Note: this method must be called on the provider's dispatch thread. - - * @see IAsynchronousLabelAdapter#retrieveLabel(Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext, ILabelRequestMonitor) - */ - public void retrieveLabel(Object object, ILabelRequestMonitor result, String[] columns) - { - IVMContext vmc = getVmcForObject(object); - if (vmc == null) { - result.done(); - return; - } - - vmc.getLayoutNode().retrieveLabel(vmc, result, columns); - } - - public ModelProxy getModelProxy() { - return fModelProxy; - } - - - /** - * Creates the column presentation for the given object. This method is meant - * to be overriden by deriving class to provide view-specific functionality. - * The default is to return null, meaning no columns. - *

- * The viewer only reads the column presentation for the root/input element of - * the tree/table, so the VMProvider must be configured to own the root element - * in the view in order for this setting to be effective. - *

- * Note: since the IColumnEditorFactoryAdapter interface is synchronous, and since - * column info is fairly static, this method is thread-safe, and it will - * not be called on the executor thread. - * - * @see IColumnPresentationFactoryAdapter#createColumnPresentation(IPresentationContext, Object) - */ - @ThreadSafe - public IColumnPresentation createColumnPresentation(Object element) { - return null; - } - - /** - * Returns the ID of the column presentation for the given object. This method - * is meant to be overriden by deriving class to provide view-specific - * functionality. The default is to return null, meaning no columns. - *

- * The viewer only reads the column presentation for the root/input element of - * the tree/table, so the VMProvider must be configured to own the root element - * in the view in order for this setting to be effective. - *

- * Note: since the IColumnEditorFactoryAdapter interface is synchronous, and since - * column info is fairly static, this method is thread-safe, and it will - * not be called on the executor thread. - * - * @see IColumnEditorFactoryAdapter#getColumnEditorId(IPresentationContext, Object) - */ - @ThreadSafe - public String getColumnPresentationId(Object element) { - return null; - } - - public IColumnEditor createColumnEditor(Object element) { - IVMContext vmc = getVmcForObject(element); - if (vmc == null) { - return null; - } - - return vmc.getLayoutNode().createColumnEditor(vmc); - } - - public String getColumnEditorId(Object element) { - IVMContext vmc = getVmcForObject(element); - if (vmc == null) { - return null; - } - - return vmc.getLayoutNode().getColumnEditorId(vmc); - } - - - /** - * Convenience method that finds the VMC corresponding to given parent - * argument given to isContainer() or retrieveChildren(). - * @param object Object to find the VMC for. - * @return parent VMC, if null it indicates that the object did not originate - * from this view or is stale. - */ - private IVMContext getVmcForObject(Object parent) { - /* - * First check to see if the parent object is the root object of the - * hierarchy. If that's the case, then retrieve the correcponding - * root VMC from the root node, and pass this root vmc to the root's - * child layout nodes. - */ - IVMRootLayoutNode rootLayoutNode = getRootLayoutNode(); - if (rootLayoutNode == null) { - return null; - } - else if (parent.equals(rootLayoutNode.getRootVMC().getInputObject())) { - return rootLayoutNode.getRootVMC(); - } - else if (parent instanceof IVMContext){ - /* - * The parent is a VMC. Check to make sure that the VMC - * originated from a node in this ViewModelProvider. If it didn't - * it is most likely a result of a change in view layout, and this - * request is a stale request. So just ignore it. - */ - if (isOurLayoutNode( ((IVMContext)parent).getLayoutNode(), - new IVMLayoutNode[] { rootLayoutNode } )) - { - return (IVMContext)parent; - } - } - return null; - } - - /** - * Convenience method which checks whether given layout node is a node - * that is configured in this ViewModelProvider. Implementation - * recursively walks the layout hierarchy, and returns true if it finds - * the node. - */ - private boolean isOurLayoutNode(IVMLayoutNode layoutNode, IVMLayoutNode[] nodesToSearch) { - for (IVMLayoutNode node : nodesToSearch) { - if (node == layoutNode) return true; - if (isOurLayoutNode(layoutNode, node.getChildLayoutNodes())) return true; - } - return false; - } - - /** - * Handle "data model changed" event by generating a delta object for each - * view and passing it to the corresponding view model provider. The view - * model provider is then responsible for filling-in and sending the delta - * to the viewer. - * @param e - */ - @DsfServiceEventHandler - public void eventDispatched(final IDMEvent event) { - IVMRootLayoutNode rootLayoutNode = getRootLayoutNode(); - - if (rootLayoutNode != null && rootLayoutNode.hasDeltaFlags(event)) { - rootLayoutNode.createDelta(event, new GetDataDone() { - public void run() { - if (getStatus().isOK()) { - fModelProxy.fireModelChangedNonDispatch(getData()); - } - } - @Override public String toString() { - return "Result of a delta for event: '" + event.toString() + "' in VMP: '" + VMProvider.this + "'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - }); - } - } - - @ThreadSafe - public class ModelProxy extends AbstractModelProxy { - /** - * Counter for whether the model proxy is currently installed in the viewer. - * Data model events are processed only if the model proxy is active. - */ - private int fProxyActive = 0; - - /** - * Scheduling rule for running the update jobs. - */ - private ISchedulingRule fModelChangeRule = new ISchedulingRule() { - public boolean contains(ISchedulingRule rule) { return this == rule; } - public boolean isConflicting(ISchedulingRule rule) { return rule == this; } - }; - - public void installed() { - fProxyActive++; - } - - public void dispose() { - fProxyActive--; - super.dispose(); - } - - /** - * Fires given delta using a job. Processing the delta on the dispatch - * thread can lead to dead-locks. - * @param delta - */ - public void fireModelChangedNonDispatch(final IModelDelta delta) { - if (fProxyActive <= 0) return; - - Job job = new Job("Processing view model delta.") { //$NON-NLS-1$ - protected IStatus run(IProgressMonitor monitor) { - fireModelChanged(delta); - return Status.OK_STATUS; - } - }; - job.setPriority(Job.INTERACTIVE); - job.setRule(fModelChangeRule); - job.schedule(); - } - - } -} diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMAdapter.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMAdapter.java new file mode 100644 index 00000000000..5035419519a --- /dev/null +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMAdapter.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.dd.dsf.ui.viewmodel.dm; + +import org.eclipse.dd.dsf.concurrent.ThreadSafe; +import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMAdapter; + +/** + * Base implementation for DSF-based view model adapters. + */ +@ThreadSafe +abstract public class AbstractDMVMAdapter extends AbstractVMAdapter +{ + private final DsfSession fSession; + + /** + * Constructor for the View Model session. It is tempting to have the + * adapter register itself here with the session as the model adapter, but + * that would mean that the adapter might get accessed on another thread + * even before the deriving class is fully constructed. So it it better + * to have the owner of this object register it with the session. + * @param session + */ + public AbstractDMVMAdapter(DsfSession session) { + super(); + fSession = session; + } + + /** + * Returns the DSF session that this adapter is associated with. + * @return + */ + protected DsfSession getSession() { return fSession; } + +} diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMLayoutNode.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMLayoutNode.java new file mode 100644 index 00000000000..775ecdf61ab --- /dev/null +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMLayoutNode.java @@ -0,0 +1,501 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.dd.dsf.ui.viewmodel.dm; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.dd.dsf.concurrent.Done; +import org.eclipse.dd.dsf.concurrent.DsfRunnable; +import org.eclipse.dd.dsf.concurrent.GetDataDone; +import org.eclipse.dd.dsf.concurrent.Immutable; +import org.eclipse.dd.dsf.datamodel.DMContexts; +import org.eclipse.dd.dsf.datamodel.IDMContext; +import org.eclipse.dd.dsf.datamodel.IDMData; +import org.eclipse.dd.dsf.datamodel.IDMEvent; +import org.eclipse.dd.dsf.datamodel.IDMService; +import org.eclipse.dd.dsf.service.DsfServicesTracker; +import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.dd.dsf.service.IDsfService; +import org.eclipse.dd.dsf.ui.DsfUIPlugin; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMLayoutNode; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.dd.dsf.ui.viewmodel.IVMContext; +import org.eclipse.dd.dsf.ui.viewmodel.IVMLayoutNode; +import org.eclipse.dd.dsf.ui.viewmodel.VMDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; + + +/** + * View model layout node based on a single Data Model Context type. + * The assumption in this implementation is that elements of this node have + * a single IDMContext associated with them, and all of these contexts + * are of the same class type. + */ +@SuppressWarnings("restriction") +abstract public class AbstractDMVMLayoutNode extends AbstractVMLayoutNode + implements IElementLabelProvider +{ + + /** + * IVMContext implementation used for this schema node. + */ + @Immutable + public class DMVMContext extends AbstractVMContext { + private final IDMContext fDmc; + + public DMVMContext(IDMContext dmc) { + super(getVMProvider().getVMAdapter(), AbstractDMVMLayoutNode.this); + fDmc = dmc; + } + + public IDMContext getDMC() { return fDmc; } + + /** + * The IAdaptable implementation. If the adapter is the DM context, + * return the context, otherwise delegate to IDMContext.getAdapter(). + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + Object superAdapter = super.getAdapter(adapter); + if (superAdapter != null) { + return superAdapter; + } else if (adapter.isInstance(fDmc)) { + return fDmc; + } else { + return fDmc.getAdapter(adapter); + } + } + + public boolean equals(Object other) { + if (!(other instanceof AbstractDMVMLayoutNode.DMVMContext)) return false; + DMVMContext otherVmc = (DMVMContext)other; + return AbstractDMVMLayoutNode.this.equals(otherVmc.getLayoutNode()) && + fDmc.equals(otherVmc.fDmc); + } + + public int hashCode() { + return AbstractDMVMLayoutNode.this.hashCode() + fDmc.hashCode(); + } + + public String toString() { + return fDmc.toString(); + } + } + + private DsfSession fSession; + + private DsfServicesTracker fServicesTracker; + + /** + * Concrete class type that the elements of this schema node are based on. + * Even though the data model type is a parameter the DMContextVMLayoutNode, + * this type is erased at runtime, so a concrete class typs of the DMC + * is needed for instanceof chacks. + */ + private Class> fDMCClassType; + + /** + * Constructor initializes instance data, except for the child nodes. + * Child nodes must be initialized by calling setChildNodes() + * @param session + * @param dmcClassType + * @see #setChildNodes(IVMLayoutNode[]) + */ + public AbstractDMVMLayoutNode(AbstractVMProvider provider, DsfSession session, Class> dmcClassType) { + super(provider); + fSession = session; + fServicesTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + fDMCClassType = dmcClassType; + } + + + public void dispose() { + fServicesTracker.dispose(); + super.dispose(); + } + + protected DsfSession getSession() { + return fSession; + } + + protected DsfServicesTracker getServicesTracker() { + return fServicesTracker; + } + + @Override + protected boolean checkUpdate(IViewerUpdate update) { + if (!super.checkUpdate(update)) return false; + + // Extract the VMC from the update (whatever the update sub-class. + Object element = update.getElement(); + if (element instanceof AbstractDMVMLayoutNode.DMVMContext) { + // If update element is a DMC, check if session is still alive. + IDMContext dmc = ((DMVMContext)element).getDMC(); + if (dmc.getSessionId() != getSession().getId() || !DsfSession.isSessionActive(dmc.getSessionId())) { + handleFailedUpdate(update); + return false; + } + } + return true; + } + + protected boolean checkService(Class serviceClass, String filter, IViewerUpdate update) { + if (getServicesTracker().getService(serviceClass, filter) == null) { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfService.INVALID_STATE, + "Service " + serviceClass.getName() + " not available.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + handleFailedUpdate(update); + return false; + } + return true; + } + + public void updateHasElements(final IHasChildrenUpdate[] updates) { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + updateHasElementsInSessionThread(updates); + }}); + } + + protected void updateHasElementsInSessionThread(IHasChildrenUpdate[] updates) { + for (final IHasChildrenUpdate update : updates) { + if (!checkUpdate(update)) return; + + updateElementsInSessionThread( + new ElementsUpdate( + new GetDataDone>() { + public void run() { + if (!checkUpdate(update)) return; + if (getStatus().isOK()) { + update.setHasChilren(getData().size() != 0); + } else { + update.setHasChilren(false); + } + update.done(); + } + }, + update.getElementPath()) + ); + } + } + + public void updateElementCount(final IChildrenCountUpdate update) { + if (!checkUpdate(update)) return; + + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + // After every dispatch, must check if update still valid. + if (!checkUpdate(update)) return; + updateElementCountInSessionThread(update); + }}); + } + + protected void updateElementCountInSessionThread(final IChildrenCountUpdate update) { + updateElementsInSessionThread( + new ElementsUpdate( + new GetDataDone>() { + public void run() { + if (!checkUpdate(update)) return; + if (getStatus().isOK()) { + update.setChildCount(getData().size()); + } else { + update.setChildCount(0); + } + update.done(); + } + }, + update.getElementPath()) + ); + } + + public void updateElements(final IChildrenUpdate update) { + if (!checkUpdate(update)) return; + + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + // After every dispatch, must check if update still valid. + if (!checkUpdate(update)) return; + updateElementsInSessionThread(update); + }}); + } + + abstract protected void updateElementsInSessionThread(IChildrenUpdate update); + + public void update(final ILabelUpdate[] updates) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + // After every dispatch, must check if update still valid. + updateLabelInSessionThread(updates); + }}); + } catch (RejectedExecutionException e) { + for (ILabelUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + /** + * The default implementation of the retrieve label method. It acquires + * the service, using parameters in the DMC, then it fetches the model + * data from the service, and then it calls the protected method + * fillColumnLabel() for each column. The deriving classes should override + * this method if a different method of computing the label is needed. + * + * @see #fillColumnLabel(IDMData, String, int, String[], ImageDescriptor[], FontData[], RGB[], RGB[]) + */ + protected void updateLabelInSessionThread(ILabelUpdate[] updates) { + for (final ILabelUpdate update : updates) { + final DMVMContext vmc = (DMVMContext)update.getElement(); + if (!checkService(null, vmc.getDMC().getServiceFilter(), update)) return; + + final IDMContext dmc = findDmcInPath(update.getElementPath(), fDMCClassType); + + ((IDMService)getServicesTracker().getService(null, vmc.getDMC().getServiceFilter())).getModelData( + dmc, + new GetDataDone() { + public void run() { + /* + * Check that the request was evaluated and data is still + * valid. The request could fail if the state of the + * service changed during the request, but the view model + * has not been updated yet. + */ + if (!getStatus().isOK() || !getData().isValid()) { + assert getStatus().isOK() || + getStatus().getCode() != IDsfService.INTERNAL_ERROR || + getStatus().getCode() != IDsfService.NOT_SUPPORTED; + handleFailedUpdate(update); + return; + } + + /* + * If columns are configured, call the protected methods to + * fill in column values. + */ + String[] localColumns = update.getPresentationContext().getColumns(); + if (localColumns == null) localColumns = new String[] { null }; + + for (int i = 0; i < localColumns.length; i++) { + fillColumnLabel(dmc, getData(), localColumns[i], i, update); + } + update.done(); + } + }); + } + } + + /** + * Fills in label information for given column. This method is intended to + * be overriden by deriving classes, to supply label information specific + * to the node.
+ * The implementation should fill in the correct value in each array at the + * given index. + * @param dmContext Data Model Context object for which the label is generated. + * @param dmData Data Model Data object retrieved from the model service. + * for the DM Context supplied to the retrieveLabel() call. + * @param columnId Name of the column to fill in, null if no columns specified. + * @param idx Index to fill in in the label arrays. + * @param update Update object to fill information to + * + * @see IElementLabelProvider + * @see IColumnPresentationFactoryAdapter + */ + protected void fillColumnLabel(IDMContext dmContext, V dmData, String columnId, int idx, ILabelUpdate update) { + update.setLabel("", idx); //$NON-NLS-1$ + } + + @Override + public int getDeltaFlags(Object e) { + int flags = 0; + if (e instanceof IDMEvent) { + flags = getNodeDeltaFlagsForDMEvent((IDMEvent)e); + } + return flags | super.getDeltaFlags(e); + } + + protected int getNodeDeltaFlagsForDMEvent(IDMEvent e) { + return IModelDelta.NO_CHANGE; + } + + @Override + public void buildDelta(final Object e, final VMDelta parentDelta, final int nodeOffset, final Done done) { + if (e instanceof IDMEvent) { + // Call handler for Data Model events. But check to make sure + // that session is still active. + if (DsfSession.isSessionActive(getSession().getId())) { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + buildDeltaForDMEvent((IDMEvent)e, parentDelta, nodeOffset, done); + } + }); + } else { + if (isDisposed()) return; + getExecutor().execute(done); + } + } else { + super.buildDelta(e, parentDelta, nodeOffset, done); + } + } + + /** + * Handle all Data Model events. If a DM context in the event contains + * a context of the type tracked by this node, then this base implementation + * will only create a delta node for this one element. + */ + protected void buildDeltaForDMEvent(final IDMEvent event, final VMDelta parentDelta, final int nodeOffset, final Done done) { + IDMContext dmc = DMContexts.getAncestorOfType(event.getDMContext(), fDMCClassType); + + if (dmc != null) { + // Create the VM context based on the DM context from the DM event. + final IVMContext vmc = new DMVMContext(DMContexts.getAncestorOfType(event.getDMContext(), fDMCClassType)); + + final Map childNodeDeltas = getChildNodesWithDeltas(event); + if (childNodeDeltas.size() == 0) { + // There are no child nodes with deltas, just return to parent. + getExecutor().execute(done); + return; + } + + // Check if any of the child nodes are will generate IModelDelta.SELECT or + // IModelDelta.EXPAND flags. If so, we must calcuate the index for this + // VMC. + boolean calculateIndex = false; + for (int childDelta : childNodeDeltas.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. + updateElements(new ElementsUpdate( + new GetDataDone>() { + public void run() { + if (isDisposed()) return; + + // 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 (!getStatus().isOK()) { + getExecutor().execute(done); + return; + } + + // Find the index. + 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. + getExecutor().execute(done); + } + + VMDelta delta = parentDelta.addNode(vmc, nodeOffset + i, IModelDelta.NO_CHANGE); + callChildNodesToBuildDelta(childNodeDeltas, delta, event, done); + } + }, + parentDelta)); + } else { + VMDelta delta = parentDelta.addNode(vmc, IModelDelta.NO_CHANGE); + callChildNodesToBuildDelta(childNodeDeltas, delta, event, done); + } + } else { + // The for this node was not found in the event. Call the + // super-class to resort to the default behavior which may add a + // delta node for every element in this node. + super.buildDelta(event, parentDelta, nodeOffset, done); + } + } + + /** + * Utility method that takes an array of DMC object and creates a + * corresponding array of IVMContext elements base on that. + * @param parent The parent for generated IVMContext elements. + * @param dmcs Array of DMC objects to build return array on. + * @return Array of IVMContext objects. + */ + protected IVMContext[] dmcs2vmcs(IDMContext[] dmcs) { + IVMContext[] vmContexts = new IVMContext[dmcs.length]; + for (int i = 0; i < dmcs.length; i++) { + vmContexts[i] = new DMVMContext(dmcs[i]); + } + return vmContexts; + } + + protected void fillUpdateWithVMCs(IChildrenUpdate update, IDMContext[] dmcs) { + int startIdx = update.getOffset() != -1 ? update.getOffset() : 0; + int endIdx = update.getLength() != -1 ? startIdx + update.getLength() : dmcs.length; + for (int i = startIdx; i < endIdx; i++) { + update.setChild(new DMVMContext(dmcs[i]), i); + } + } + + /** + * Searches for a DMC of given type in the tree patch contained in given + * VMC. Only a DMC in the same session will be returned. + * @param Type of the DMC that will be returned. + * @param vmc VMC element to search. + * @param dmcType Class object for matching the type. + * @return DMC, or null if not found. + */ + @SuppressWarnings("unchecked") + public T findDmcInPath(TreePath path, Class dmcType) { + T retVal = null; + for (int i = path.getSegmentCount() - 1; i >= 0; i--) { + if (path.getSegment(i) instanceof AbstractDMVMLayoutNode.DMVMContext) { + IDMContext dmc = ((DMVMContext)path.getSegment(i)).getDMC(); + if ( dmc.getSessionId().equals(getSession().getId()) ) { + retVal = DMContexts.getAncestorOfType(dmc, dmcType); + if (retVal != null) break; + } + } + } + // Search the root object of the layout hierarchy. + if (retVal == null) { + Object inputObject = getVMProvider().getRootLayoutNode().getRootObject(); + if (inputObject instanceof ITreeSelection) { + ITreeSelection inputSelection = (ITreeSelection)inputObject; + if (inputSelection.getPaths().length == 1) { + retVal = findDmcInPath(inputSelection.getPaths()[0], dmcType); + } + } else if (inputObject instanceof IStructuredSelection) { + Object rootElement = ((IStructuredSelection)inputObject).getFirstElement(); + if (rootElement instanceof AbstractDMVMLayoutNode.DMVMContext) { + retVal = DMContexts.getAncestorOfType(((DMVMContext)rootElement).getDMC(), dmcType); + } + } else if (inputObject instanceof AbstractDMVMLayoutNode.DMVMContext) { + retVal = DMContexts.getAncestorOfType(((DMVMContext)inputObject).getDMC(), dmcType); + } + } + + return retVal; + } +} diff --git a/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMProvider.java b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMProvider.java new file mode 100644 index 00000000000..788b370ea09 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf.ui/src/org/eclipse/dd/dsf/ui/viewmodel/dm/AbstractDMVMProvider.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.dd.dsf.ui.viewmodel.dm; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.dd.dsf.concurrent.GetDataDone; +import org.eclipse.dd.dsf.datamodel.IDMEvent; +import org.eclipse.dd.dsf.service.DsfServiceEventHandler; +import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.dd.dsf.ui.viewmodel.IVMLayoutNode; +import org.eclipse.dd.dsf.ui.viewmodel.IVMRootLayoutNode; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +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.provisional.IAsynchronousContentAdapter; +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousLabelAdapter; + +/** + * View model provider implements the asynchronous view model functionality for + * a single view. This provider is just a holder which further delegates the + * model provider functionality to the view model layout nodes that need + * to be configured with each provider. + *

+ * The view model provider, often does not provide the model for the entire + * view. Rather, it needs to be able to plug in at any level in the viewer's + * content model and provide data for a sub-tree. + * + * @see IAsynchronousContentAdapter + * @see IAsynchronousLabelAdapter + * @see IModelProxy + * @see IVMLayoutNode + */ +@ConfinedToDsfExecutor("fSession#getExecutor") +@SuppressWarnings("restriction") +abstract public class AbstractDMVMProvider extends AbstractVMProvider +{ + private final DsfSession fSession; + + /** + * It is theoretically possible for a VMProvider to be disposed before it + * has a chance to register itself as event listener. This flag is used + * to avoid removing itself as listener in such situation. + */ + private boolean fRegisteredAsEventListener = false; + + /** + * Constructs the view model provider for given DSF session. The + * constructor is thread-safe to allow VM provider to be constructed + * synchronously when a call to getAdapter() is made on an element + * in a view. + */ + public AbstractDMVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, DsfSession session) { + super(adapter, presentationContext); + fSession = session; + // Add ourselves as listener for DM events events. + try { + session.getExecutor().execute(new Runnable() { + public void run() { + if (DsfSession.isSessionActive(getSession().getId())) { + getSession().addServiceEventListener(AbstractDMVMProvider.this, null); + fRegisteredAsEventListener = true; + } + } + }); + } catch (RejectedExecutionException e) { + // Session shut down, not much we can do but wait to be disposed. + } + } + + /** Called to dispose the provider. */ + public void dispose() { + try { + getSession().getExecutor().execute(new Runnable() { + public void run() { + if (DsfSession.isSessionActive(getSession().getId()) && fRegisteredAsEventListener ) { + fSession.removeServiceEventListener(AbstractDMVMProvider.this); + } + } + }); + } catch (RejectedExecutionException e) { + // Session shut down. + } + super.dispose(); + } + + protected DsfSession getSession() { return fSession; } + + + + /** + * Handle "data model changed" event by generating a delta object for each + * view and passing it to the corresponding view model provider. The view + * model provider is then responsible for filling-in and sending the delta + * to the viewer. + * @param e + */ + @DsfServiceEventHandler + public void eventDispatched(final IDMEvent event) { + if (isDisposed()) return; + + // We're in session's executor thread. Re-dispach to VM Adapter + // executor thread and then call root layout node. + try { + getExecutor().execute(new Runnable() { + public void run() { + if (isDisposed()) return; + + IVMRootLayoutNode rootLayoutNode = getRootLayoutNode(); + if (rootLayoutNode != null && rootLayoutNode.getDeltaFlags(event) != 0) { + rootLayoutNode.createDelta(event, new GetDataDone() { + public void run() { + if (getStatus().isOK()) { + getModelProxy().fireModelChangedNonDispatch(getData()); + } + } + @Override public String toString() { + return "Result of a delta for event: '" + event.toString() + "' in VMP: '" + AbstractDMVMProvider.this + "'" + "\n" + getData().toString(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + }); + } + }}); + } catch (RejectedExecutionException e) { + // Ignore. This exception could be thrown if the provider is being + // shut down. + } + } +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java index d4feb038cb5..7c212d3734b 100644 --- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java @@ -86,7 +86,10 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor } static void logException(Throwable t) { - ILog log = DsfPlugin.getDefault().getLog(); + DsfPlugin plugin = DsfPlugin.getDefault(); + if (plugin == null) return; + + ILog log = plugin.getLog(); if (log != null) { log.log(new Status( IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Uncaught exception in DSF executor thread", t)); //$NON-NLS-1$ @@ -222,6 +225,12 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor public TracingWrapperRunnable(Runnable runnable, int offset) { super(offset); fRunnable = runnable; + + // Check if executable wasn't executed already. + if (DEBUG_EXECUTOR && fRunnable instanceof DsfExecutable) { + assert !((DsfExecutable)fRunnable).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$ + ((DsfExecutable)fRunnable).setSubmitted(); + } } protected Object getExecutable() { return fRunnable; } @@ -229,11 +238,6 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor public void run() { traceExecution(); - // If debugging a DSF exeutable, mark that it was executed. - if (DEBUG_EXECUTOR && fRunnable instanceof DsfExecutable) { - ((DsfExecutable)fRunnable).setExecuted(); - } - // Finally invoke the runnable code. fRunnable.run(); } @@ -251,11 +255,6 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor public T call() throws Exception { traceExecution(); - // If debugging a DSF exeutable, mark that it was executed. - if (DEBUG_EXECUTOR && fCallable instanceof DsfExecutable) { - ((DsfExecutable)fCallable).setExecuted(); - } - // Finally invoke the runnable code. return fCallable.call(); } diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DoneCollector.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DoneCollector.java index 8fce539f983..e21dc82d494 100644 --- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DoneCollector.java +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DoneCollector.java @@ -35,9 +35,8 @@ import org.eclipse.dd.dsf.DsfPlugin; * } * */ -public abstract class DoneCollector extends Done { - private final DsfExecutor fExecutor; - private Map fDones = new HashMap(); +public abstract class DoneCollector extends Done { + private Map fDones = new HashMap(); private int fDoneCounter; /** @@ -47,39 +46,25 @@ public abstract class DoneCollector extends Done { * execution of the last done, and in the same dispatch loop. * */ - public DoneCollector(DsfExecutor executor) { + public DoneCollector() { setStatus(new MultiStatus(DsfPlugin.PLUGIN_ID, 0, "Collective status for set of sub-operations.", null)); //$NON-NLS-1$ - fExecutor = executor; } /** * Adds a new Done callback to this tracker's list. - * @param Service-specific class of the Done callback, to avoid an - * unnecessary cast. + * @param Client-specific class of the Done callback, it's used here to avoid an + * unnecessary cast by the client. * @param done callback object to add to the tracker * @return the done that was just added, it allows this method to be used * inlined in service method calls */ - public V add(V done) { + public T add(T done) { assert !fDones.containsKey(done); fDones.put(done, false); fDoneCounter++; return done; } - /** - * Adds a Done which performs no actions. This is useful if all work - * is performed inside DoneCollector.run(). - * @return Done which is to be passed as an argument to a service method. - */ - public Done addNoActionDone() { - return add(new Done() { - public void run() { - doneDone(this); - } - }); - } - /** * Marks the given Done callback as completed. Client implementations of * the Done callback have to call this method in order for the tracker @@ -88,13 +73,15 @@ public abstract class DoneCollector extends Done { * Note: funniest method signature ever! * @param done */ - public void doneDone(Done done) { + public void doneDone(V done) { ((MultiStatus)getStatus()).merge(done.getStatus()); + assert fDones.containsKey(done); fDones.put(done, true); + assert fDoneCounter > 0; fDoneCounter--; if (fDoneCounter == 0) { assert !fDones.containsValue(false); - fExecutor.execute(this); + run(); } } @@ -104,10 +91,15 @@ public abstract class DoneCollector extends Done { * done callbacks. * @return map of the done callbacks. */ - public Map getDones() { return fDones; } + public Map getDones() { return fDones; } @Override public String toString() { return "Done Collector: " + getStatus().toString(); //$NON-NLS-1$ } + + @Override + protected boolean isExecutionRequired() { + return false; + } } diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutable.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutable.java index 748aa8d2772..7620478eca9 100644 --- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutable.java +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutable.java @@ -62,7 +62,7 @@ public class DsfExecutable { * Flag indicating whether this executable was ever executed by an * executor. Used for tracing only. */ - private boolean fExecuted = false; + private boolean fSubmitted = false; @SuppressWarnings("unchecked") public DsfExecutable() { @@ -97,17 +97,31 @@ public class DsfExecutable { } } + boolean getSubmitted() { + return fSubmitted; + } + /** * Marks this executable to indicate that it has been executed by the * executor. To be invoked only by DsfExecutor. */ - void setExecuted() { - fExecuted = true; + void setSubmitted() { + fSubmitted = true; + } + + /** + * Returns whether the runnable/callable is expected to be always executed. + * Overriding classes can implement this method and return false, to avoid + * unnecessary trace output. + * @return true if this runnable is expected to run. + */ + protected boolean isExecutionRequired() { + return true; } @Override protected void finalize() { - if (DEBUG_EXECUTOR && !fExecuted) { + if (DEBUG_EXECUTOR && !fSubmitted && isExecutionRequired()) { StringBuilder traceBuilder = new StringBuilder(); // Record the time diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/AbstractDMContext.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/AbstractDMContext.java index b428ba83337..778295eea21 100644 --- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/AbstractDMContext.java +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/AbstractDMContext.java @@ -12,7 +12,6 @@ package org.eclipse.dd.dsf.datamodel; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.dd.dsf.concurrent.Immutable; -import org.eclipse.dd.dsf.service.AbstractDsfService; import org.eclipse.dd.dsf.service.DsfSession; /** @@ -40,12 +39,10 @@ abstract public class AbstractDMContext extends PlatformObjec } /** Convenience constructor */ - public AbstractDMContext(AbstractDsfService service, IDMContext parent) { - this(service.getSession().getId(), - service.getServiceFilter(), - parent == null ? new IDMContext[] {} : new IDMContext[] { parent }); + public AbstractDMContext(IDMService service, IDMContext[] parents) { + this(service.getSession().getId(), service.getServiceFilter(), parents); } - + /** * Should be used by the deriving class to compare the basic context object * information. diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/ServiceDMContext.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/ServiceDMContext.java index 1f71becf043..481abf680f9 100644 --- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/ServiceDMContext.java +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/ServiceDMContext.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.dd.dsf.datamodel; -import org.eclipse.dd.dsf.service.AbstractDsfService; /** * The Data Model Context representing the owner service. The service DM Context @@ -22,8 +21,8 @@ import org.eclipse.dd.dsf.service.AbstractDsfService; public class ServiceDMContext extends AbstractDMContext { String fServiceDMID; - public ServiceDMContext(AbstractDsfService service, String serviceDMID) { - super(service, null); + public ServiceDMContext(IDMService service, String serviceDMID) { + super(service, new IDMContext[0]); fServiceDMID = serviceDMID; } diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/IDsfService.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/IDsfService.java index 77bd7654078..a5b527ac02a 100644 --- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/IDsfService.java +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/IDsfService.java @@ -74,10 +74,14 @@ public interface IDsfService { */ final static int INTERNAL_ERROR = 10005; + /** + * Returns the DSF Session that this service belongs to. + */ + DsfSession getSession(); /** * Returns the executor that should be used to call methods of this service. - * @return + * This method is equivalent to calling getSession().getExecutor() */ DsfExecutor getExecutor();