| /******************************************************************************* |
| * Copyright (c) 2010, 2014 Freescale Semiconductor. and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Freescale Semiconductor - initial API and implementation |
| * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) |
| * Marc Khouzam (Ericsson) - Add support disable "View Memory" action (bug 418710) |
| * Marc Khouzam (Ericsson) - Turn off "watch" action for return values of methods (bug 341731) |
| *******************************************************************************/ |
| package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.cdt.debug.core.ICWatchpointTarget; |
| import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.RequestMonitor; |
| import org.eclipse.cdt.dsf.datamodel.DMContexts; |
| import org.eclipse.cdt.dsf.datamodel.IDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IExpressions; |
| import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress; |
| import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; |
| import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.AbstractExpressionVMNode; |
| import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.SyncVariableDataAccess; |
| import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableVMNode; |
| import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; |
| import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; |
| import org.eclipse.cdt.dsf.mi.service.IMIExpressions; |
| import org.eclipse.cdt.dsf.service.DsfSession; |
| import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; |
| import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; |
| import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate; |
| import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; |
| import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; |
| import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; |
| import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.model.IExpression; |
| 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.ui.actions.IWatchExpressionFactoryAdapter2; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.TreePath; |
| |
| /** |
| * Specialization of DSF's VariableVMNode. See |
| * {@link GdbVariableVMNode#createVMContext(IDMContext)} for why this is needed. |
| */ |
| public class GdbVariableVMNode extends VariableVMNode { |
| |
| // Notes on gdb's pretty printer support (bug 302121): |
| // If |
| // - an expression has children |
| // - and those children are provided by a pretty printer |
| // - and the expression is not yet initialized |
| // the expression might have a large number of children. Asking gdb to |
| // provide all children, or even just the number of all children, will |
| // lead to extremely slow response times. |
| // Furthermore, there are C/C++ data structures (e.g. linked lists) that |
| // can lead to endless loops if not correctly initialized and a pretty |
| // printer tries to obtain the number of children. In this case, gdb |
| // will never return. |
| // |
| // In order to address this problem, IMIExpressions deriving from |
| // IExpressions has been introduced. |
| // It lets the client specify a maximum number of children to be considered, |
| // both when asking the number of sub-expression, or the sub-expressions |
| // itself. |
| // |
| // The algorithm how it is used is as following: |
| // - We don't show all children in the UI, but only up to a certain limit. |
| // A special context type IncompleteChildrenVMC is used to show that |
| // there are more children than those currently visible. |
| // The user can fetch more children on demand. |
| // - updateHasElementsInSessionThread asks only for up to one child. |
| // - updateElementCountInSessionThread checks whether the expression |
| // requires a limit on the child count limit. If yes, it asks |
| // the expression service for up to limit + 1 children. The + 1 |
| // represent the child for the <...more children...> node. I.e., |
| // if the returned number of children is limit + 1, then there is |
| // an <...more_children...> node. Otherwise, there is not. |
| // - updateElementsInSessionThread sooner or later delegates to |
| // fillUpdateWithVMCs. fillUpdateWithVMCs checks whether there are |
| // limit + 1 children, and if so, will create an IncompleteChildrenVMC |
| // for the last child, discarding the original expression context. |
| |
| /** |
| * Specialization of VariableVMNode.VariableExpressionVMC that participates |
| * in the "Add Watchpoint" object contribution action. |
| */ |
| public class GdbVariableExpressionVMC extends VariableVMNode.VariableExpressionVMC implements ICWatchpointTarget { |
| |
| /** |
| * Constructor (passthru) |
| */ |
| public GdbVariableExpressionVMC(IDMContext dmc) { |
| super(dmc); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.internal.core.IWatchpointTarget#getSize() |
| */ |
| @Override |
| public void getSize(final ICWatchpointTarget.GetSizeRequest request) { |
| final IExpressionDMContext exprDmc = DMContexts.getAncestorOfType(getDMContext(), |
| IExpressionDMContext.class); |
| if (exprDmc != null) { |
| getSession().getExecutor().execute(() -> { |
| final IExpressions expressionService = getServicesTracker().getService(IExpressions.class); |
| if (expressionService != null) { |
| final DataRequestMonitor<IExpressionDMAddress> drm = new DataRequestMonitor<>( |
| getSession().getExecutor(), null) { |
| @Override |
| public void handleCompleted() { |
| if (isSuccess()) { |
| request.setSize(getData().getSize()); |
| } |
| request.setStatus(getStatus()); |
| request.done(); |
| } |
| }; |
| |
| expressionService.getExpressionAddressData(exprDmc, drm); |
| } else { |
| request.setStatus(internalError()); |
| request.done(); |
| } |
| }); |
| } else { |
| request.setStatus(internalError()); |
| request.done(); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.internal.core.IWatchpointTarget#canCreateWatchpoint(org.eclipse.cdt.debug.internal.core.IWatchpointTarget.CanCreateWatchpointRequest) |
| */ |
| @Override |
| public void canSetWatchpoint(final ICWatchpointTarget.CanCreateWatchpointRequest request) { |
| // If the expression is an l-value, then we say it supports a |
| // watchpoint. The logic here is basically the same as what's in |
| // getSize(), as the same DSF service method tells us (a) if it's an |
| // lvalue, and (b) its size. |
| final IExpressionDMContext exprDmc = DMContexts.getAncestorOfType(getDMContext(), |
| IExpressionDMContext.class); |
| if (exprDmc != null) { |
| getSession().getExecutor().execute(() -> { |
| final IExpressions expressionService = getServicesTracker().getService(IExpressions.class); |
| if (expressionService != null) { |
| final DataRequestMonitor<IExpressionDMAddress> drm = new DataRequestMonitor<>( |
| getSession().getExecutor(), null) { |
| @Override |
| public void handleCompleted() { |
| if (isSuccess()) { |
| request.setCanCreate(getData().getSize() > 0); |
| } |
| request.setStatus(getStatus()); |
| request.done(); |
| } |
| }; |
| |
| expressionService.getExpressionAddressData(exprDmc, drm); |
| } else { |
| request.setStatus(internalError()); |
| request.done(); |
| } |
| }); |
| } else { |
| request.setStatus(internalError()); |
| request.done(); |
| } |
| } |
| |
| @Override |
| public boolean canViewInMemory() { |
| String expr = getExpression(); |
| if (isConvenienceVariable(expr) || isRegisterExpression(expr)) { |
| return false; |
| } |
| return super.canViewInMemory(); |
| } |
| |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| @Override |
| public Object getAdapter(Class adapter) { |
| if (adapter.isAssignableFrom(IWatchExpressionFactoryAdapter2.class)) { |
| return fGdbVariableExpressionFactory; |
| } |
| return super.getAdapter(adapter); |
| } |
| } |
| |
| private static boolean isConvenienceVariable(String expr) { |
| // GDB convenience variables are variables that start with a $ followed |
| // by at least one digit. |
| // Note that registers also start with a $, so we need to make sure |
| // there is a digit immediately following the $. |
| // Also, the $ may not be at the start of the expression in cases |
| // where we are dealing with children of a convenience variable, |
| // such as ($1).myvar or ((class bar) $1).foo. |
| // So, we look for a $ followed by a number, anywhere in the expression. |
| // Convenience variables are used for return values of methods calls. |
| // see bug 341731 |
| if (expr.matches(".*" + "\\$\\d" + ".*")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean isRegisterExpression(String expr) { |
| // Registers expressions start with a $ followed by a non-digit. |
| // We must check for the non-digit because we need to make sure |
| // we are not dealing with a convenience variable. |
| // Also, the $ may not be at the start of the expression in cases |
| // where we are dealing with a casted register, or children of a register |
| // such as (int)($eax) |
| // So, we look for a $ followed by a non-digit, anywhere in the expression. |
| if (expr.matches(".*" + "\\$\\D" + ".*")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * A factory to control the "Watch" action for GDB variables. |
| */ |
| protected class GdbVariableExpressionFactory extends VariableExpressionFactory { |
| @Override |
| public boolean canCreateWatchExpression(Object element) { |
| if (element instanceof VariableExpressionVMC) { |
| String expression = ((VariableExpressionVMC) element).getExpression(); |
| if (isConvenienceVariable(expression)) { |
| return false; |
| } |
| } |
| |
| return super.canCreateWatchExpression(element); |
| } |
| } |
| |
| final protected VariableExpressionFactory fGdbVariableExpressionFactory = new GdbVariableExpressionFactory(); |
| |
| /** |
| * The special context representing more children to be available. |
| * |
| * @since 3.0 |
| */ |
| public class IncompleteChildrenVMC extends AbstractVMContext { |
| |
| private IExpressionDMContext parentDmc; |
| |
| public IncompleteChildrenVMC(IExpressionDMContext exprDmc, int childCountLimit) { |
| super(GdbVariableVMNode.this); |
| this.parentDmc = exprDmc; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| return obj instanceof IncompleteChildrenVMC && ((IncompleteChildrenVMC) obj).parentDmc.equals(parentDmc); |
| } |
| |
| @Override |
| public int hashCode() { |
| return parentDmc.hashCode(); |
| } |
| |
| public IExpressionDMContext getParentDMContext() { |
| return parentDmc; |
| } |
| } |
| |
| /** |
| * Maps expressions to their current limit on the maximum number of children. |
| */ |
| private Map<IExpressionDMContext, Integer> childCountLimits = new HashMap<>(); |
| |
| /** |
| * Utility method to create an IStatus object for an internal error |
| */ |
| private static Status internalError() { |
| return new Status(Status.ERROR, GdbUIPlugin.getUniqueIdentifier(), Messages.Internal_Error); |
| } |
| |
| /** |
| * Constructor (passthru) |
| */ |
| public GdbVariableVMNode(AbstractDMVMProvider provider, DsfSession session, |
| SyncVariableDataAccess syncVariableDataAccess) { |
| super(provider, session, syncVariableDataAccess); |
| } |
| |
| /** |
| * The primary reason for the specialization of VariableVMNode is to create |
| * a GDB-specific VM context that implements ICWatchpointTarget, so that the |
| * "Add Watchpoint" context menu appears for variables and expressions in |
| * GDB-DSF sessions but not necessarily other DSF-based sessions [bugzilla |
| * 248606] |
| * |
| * @see org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableVMNode#createVMContext(org.eclipse.cdt.dsf.datamodel.IDMContext) |
| */ |
| @Override |
| protected IDMVMContext createVMContext(IDMContext dmc) { |
| return new GdbVariableExpressionVMC(dmc); |
| } |
| |
| @Override |
| protected void updateHasElementsInSessionThread(final IHasChildrenUpdate update) { |
| if (update.getElement() instanceof IncompleteChildrenVMC) { |
| update.setHasChilren(false); |
| update.done(); |
| return; |
| } |
| |
| super.updateHasElementsInSessionThread(update); |
| } |
| |
| @Override |
| protected void updateElementCountInSessionThread(final IChildrenCountUpdate update) { |
| // Get the data model context object for the current node in the hierarchy. |
| |
| final IExpressionDMContext expressionDMC = findDmcInPath(update.getViewerInput(), update.getElementPath(), |
| IExpressionDMContext.class); |
| |
| if (expressionDMC != null) { |
| final IExpressions expressionService = getServicesTracker().getService(IExpressions.class); |
| |
| if (expressionService == null) { |
| handleFailedUpdate(update); |
| return; |
| } |
| |
| if (expressionService instanceof IMIExpressions) { |
| final IMIExpressions miExpressions = (IMIExpressions) expressionService; |
| |
| miExpressions.safeToAskForAllSubExpressions(expressionDMC, |
| new ViewerDataRequestMonitor<Boolean>(getSession().getExecutor(), update) { |
| |
| @Override |
| protected void handleCompleted() { |
| if (!isSuccess()) { |
| handleFailedUpdate(update); |
| return; |
| } |
| |
| boolean limitRequired = !getData().booleanValue(); |
| if (limitRequired) { |
| |
| final int childCountLimit = getOrInitChildCountLimit(expressionDMC); |
| |
| miExpressions.getSubExpressionCount(expressionDMC, childCountLimit + 1, |
| new ViewerDataRequestMonitor<Integer>(getExecutor(), update) { |
| @Override |
| public void handleCompleted() { |
| if (!isSuccess()) { |
| handleFailedUpdate(update); |
| return; |
| } |
| |
| int childCount = getData(); |
| if (childCountLimit < childCount) { |
| childCount = childCountLimit + 1; |
| } |
| |
| update.setChildCount(childCount); |
| update.done(); |
| } |
| }); |
| } else { |
| GdbVariableVMNode.super.updateElementCountInSessionThread(update); |
| } |
| } |
| }); |
| |
| return; |
| } |
| } |
| |
| super.updateElementCountInSessionThread(update); |
| } |
| |
| @Override |
| protected void fillUpdateWithVMCs(IChildrenUpdate update, IDMContext[] dmcs, int firstIndex) { |
| super.fillUpdateWithVMCs(update, dmcs, firstIndex); |
| |
| IExpressionDMContext expressionDMC = findDmcInPath(update.getViewerInput(), update.getElementPath(), |
| IExpressionDMContext.class); |
| |
| if (expressionDMC != null) { |
| int childCountLimit = getChildCountLimit(expressionDMC); |
| int childCount = firstIndex + update.getLength(); |
| if (childCountLimit < childCount) { |
| update.setChild(new IncompleteChildrenVMC(expressionDMC, childCountLimit), childCountLimit); |
| } |
| } |
| } |
| |
| @Override |
| public void update(IPropertiesUpdate[] updates) { |
| List<IPropertiesUpdate> realExpressions = new ArrayList<>(); |
| |
| for (IPropertiesUpdate update : updates) { |
| if (update.getElement() instanceof IncompleteChildrenVMC) { |
| if (update.getProperties().contains(AbstractExpressionVMNode.PROP_ELEMENT_EXPRESSION)) { |
| update.setProperty(AbstractExpressionVMNode.PROP_ELEMENT_EXPRESSION, Messages.More_Children); |
| |
| } |
| |
| if (update.getProperties().contains(PROP_NAME)) { |
| update.setProperty(PROP_NAME, Messages.More_Children); |
| } |
| update.done(); |
| } else { |
| realExpressions.add(update); |
| } |
| } |
| |
| super.update(realExpressions.toArray(new IPropertiesUpdate[realExpressions.size()])); |
| } |
| |
| private int getInitialChildCountLimit() { |
| Object initialLimitProperty = getVMProvider().getPresentationContext() |
| .getProperty(IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS); |
| |
| return (initialLimitProperty instanceof Integer) ? (Integer) initialLimitProperty : 100; |
| } |
| |
| /** |
| * The given expression context requires a child count limit. If a limit |
| * is already available from preceding calls, obtain this limit. Otherwise |
| * calculate the initial value, store it, and return it. |
| * |
| * @param expressionDMC |
| * @return The child count limit to apply for the given expression. |
| * |
| * @since 3.0 |
| */ |
| protected int getOrInitChildCountLimit(IExpressionDMContext expressionDMC) { |
| if (childCountLimits.containsKey(expressionDMC)) { |
| return childCountLimits.get(expressionDMC); |
| } |
| |
| int initialLimit = getInitialChildCountLimit(); |
| childCountLimits.put(expressionDMC, initialLimit); |
| |
| return initialLimit; |
| } |
| |
| /** |
| * @param expressionDMC |
| * @return The currently stored child count limit for the given expression, |
| * or {@link Integer#MAX_VALUE} if no child count limit is currently |
| * stored. |
| * |
| * @since 3.0 |
| */ |
| protected int getChildCountLimit(IExpressionDMContext expressionDMC) { |
| if (childCountLimits.containsKey(expressionDMC)) { |
| return childCountLimits.get(expressionDMC); |
| } |
| return Integer.MAX_VALUE; |
| } |
| |
| private void resetChildCountLimits(IExecutionDMContext execCtx) { |
| int initialLimit = getInitialChildCountLimit(); |
| for (IExpressionDMContext limitCtx : childCountLimits.keySet()) { |
| if (DMContexts.isAncestorOf(limitCtx, execCtx)) { |
| childCountLimits.put(limitCtx, initialLimit); |
| } |
| } |
| } |
| |
| private void resetAllChildCountLimits() { |
| int initialLimit = getInitialChildCountLimit(); |
| for (IExpressionDMContext limitCtx : childCountLimits.keySet()) { |
| childCountLimits.put(limitCtx, initialLimit); |
| } |
| } |
| |
| /** |
| * Increment the child count limit by the default increment. |
| * This implementation doubles the current limit. |
| * |
| * @since 3.0 |
| */ |
| public void incrementChildCountLimit(IExpressionDMContext expressionDMC) { |
| assert (childCountLimits.containsKey(expressionDMC)); |
| |
| int childCountLimit = getChildCountLimit(expressionDMC); |
| if (childCountLimit < Integer.MAX_VALUE / 2) { |
| childCountLimits.put(expressionDMC, childCountLimit * 2); |
| } |
| } |
| |
| @Override |
| public int getDeltaFlags(Object e) { |
| int flags = super.getDeltaFlags(e); |
| |
| if (e instanceof FetchMoreChildrenEvent) { |
| flags |= IModelDelta.CONTENT; |
| } else if (e instanceof ISuspendedDMEvent) { |
| // The child count limit must be reset. |
| flags |= IModelDelta.CONTENT; |
| } else if (e instanceof PropertyChangeEvent) { |
| String property = ((PropertyChangeEvent) e).getProperty(); |
| if (IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS.equals(property)) { |
| flags |= IModelDelta.CONTENT; |
| } |
| } |
| |
| return flags; |
| } |
| |
| @Override |
| public int getDeltaFlagsForExpression(IExpression expression, Object event) { |
| int flags = super.getDeltaFlagsForExpression(expression, event); |
| |
| if (event instanceof FetchMoreChildrenEvent) { |
| flags |= IModelDelta.CONTENT; |
| } else if (event instanceof PropertyChangeEvent) { |
| String property = ((PropertyChangeEvent) event).getProperty(); |
| if (IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS.equals(property)) { |
| flags |= IModelDelta.CONTENT; |
| } |
| } |
| |
| return flags; |
| } |
| |
| @Override |
| public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) { |
| |
| if (e instanceof FetchMoreChildrenEvent) { |
| buildDeltaForFetchMoreChildrenEvent((FetchMoreChildrenEvent) e, parentDelta, rm); |
| return; |
| } else if (e instanceof ISuspendedDMEvent) { |
| resetChildCountLimits(((ISuspendedDMEvent) e).getDMContext()); |
| } else if (e instanceof PropertyChangeEvent) { |
| String property = ((PropertyChangeEvent) e).getProperty(); |
| if (IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS.equals(property)) { |
| resetAllChildCountLimits(); |
| buildDeltaForChildCountLimitPreferenceChangedEvent(parentDelta, rm); |
| return; |
| } |
| } |
| |
| super.buildDelta(e, parentDelta, nodeOffset, rm); |
| } |
| |
| @Override |
| public void buildDeltaForExpressionElement(Object element, int elementIdx, Object event, VMDelta parentDelta, |
| RequestMonitor rm) { |
| |
| if (event instanceof FetchMoreChildrenEvent) { |
| FetchMoreChildrenEvent fetchMoreEvent = (FetchMoreChildrenEvent) event; |
| GdbVariableExpressionVMC topLevelExpressionVMC = (GdbVariableExpressionVMC) element; |
| if (topLevelExpressionVMC.equals(fetchMoreEvent.getPath().getFirstSegment())) { |
| buildDeltaForFetchMoreChildrenEvent(fetchMoreEvent, parentDelta, rm); |
| return; |
| } |
| } else if (event instanceof ISuspendedDMEvent) { |
| resetChildCountLimits(((ISuspendedDMEvent) event).getDMContext()); |
| } else if (event instanceof IContainerSuspendedDMEvent) { |
| resetChildCountLimits(((IContainerSuspendedDMEvent) event).getDMContext()); |
| } else if (event instanceof PropertyChangeEvent) { |
| String property = ((PropertyChangeEvent) event).getProperty(); |
| if (IGdbDebugPreferenceConstants.PREF_INITIAL_CHILD_COUNT_LIMIT_FOR_COLLECTIONS.equals(property)) { |
| resetAllChildCountLimits(); |
| buildDeltaForChildCountLimitPreferenceChangedEvent(parentDelta, rm); |
| return; |
| } |
| } |
| |
| super.buildDeltaForExpressionElement(element, elementIdx, event, parentDelta, rm); |
| } |
| |
| private void buildDeltaForFetchMoreChildrenEvent(FetchMoreChildrenEvent fetchMoreChidrenEvent, VMDelta parentDelta, |
| final RequestMonitor rm) { |
| |
| TreePath path = fetchMoreChidrenEvent.getPath(); |
| |
| // Add all the parents of the expression. Those didn't change, however. |
| for (int i = 0; i < path.getSegmentCount() - 2; ++i) { |
| parentDelta = parentDelta.addNode(path.getSegment(i), IModelDelta.NO_CHANGE); |
| } |
| |
| // Add the node for the expression. This one changed, of course. |
| final VMDelta expressionDelta = parentDelta.addNode(path.getSegment(path.getSegmentCount() - 2), |
| IModelDelta.CONTENT); |
| |
| // Make sure the element formerly know as <...more_children...> is selected |
| // afterwards. |
| |
| final int offset = getChildCountLimit(fetchMoreChidrenEvent.getDMContext()) / 2; |
| // The one trailing element is to see whether there are more children. |
| final int maxLength = offset + 1; |
| getVMProvider().updateNode(this, new VMChildrenUpdate(expressionDelta, getVMProvider().getPresentationContext(), |
| offset, maxLength, new DataRequestMonitor<List<Object>>(getExecutor(), rm) { |
| @Override |
| public void handleCompleted() { |
| |
| // FIXME if the new child has children they do not appear because of this code. |
| // final List<Object> data= getData(); |
| // if (data != null && data.size() != 0) { |
| // expressionDelta.addNode(data.get(0), offset, IModelDelta.SELECT); |
| // } |
| rm.done(); |
| } |
| })); |
| } |
| |
| private void buildDeltaForChildCountLimitPreferenceChangedEvent(final VMDelta parentDelta, |
| final RequestMonitor rm) { |
| parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); |
| rm.done(); |
| } |
| } |