mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-71882 Debugger: Improve usability for checking data sub-range
1. 'Adjust range' action operates on lists as well now; 2. Adjusted range is preserved for local variables delivered at another stack frame; 3. Node representation is updated on target range change now (cached values were used before);
This commit is contained in:
@@ -16,46 +16,79 @@
|
||||
package com.intellij.debugger.actions;
|
||||
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.DebuggerUtils;
|
||||
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
|
||||
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl;
|
||||
import com.intellij.debugger.settings.ArrayRendererConfigurable;
|
||||
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
|
||||
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
|
||||
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
|
||||
import com.intellij.debugger.ui.tree.render.ArrayRenderer;
|
||||
import com.intellij.debugger.ui.tree.DebuggerTreeNode;
|
||||
import com.intellij.debugger.ui.tree.NodeDescriptor;
|
||||
import com.intellij.debugger.ui.tree.render.*;
|
||||
import com.intellij.debugger.ui.tree.render.Renderer;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.ex.SingleConfigurableEditor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.sun.jdi.Type;
|
||||
import com.sun.jdi.Value;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
public class AdjustArrayRangeAction extends DebuggerAction {
|
||||
|
||||
private static final AdjustedRendererManager[] MANAGERS = { new ArrayRendererManager(), new ListRendererManager() };
|
||||
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
|
||||
final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
|
||||
if(debuggerContext == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
|
||||
final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
|
||||
if(debugProcess == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Project project = debuggerContext.getProject();
|
||||
|
||||
final DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
|
||||
if (selectedNode == null) {
|
||||
return;
|
||||
}
|
||||
NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
|
||||
if(!(descriptor instanceof ValueDescriptorImpl && ((ValueDescriptorImpl)descriptor).isArray())) {
|
||||
|
||||
debugProcess.getManagerThread().invoke(new DebuggerCommandImpl() {
|
||||
@Override
|
||||
protected void action() throws Exception {
|
||||
adjustRange(debuggerContext, selectedNode, debugProcess);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void adjustRange(@NotNull final DebuggerContextImpl debuggerContext, @NotNull final DebuggerTreeNodeImpl selectedNode,
|
||||
@NotNull final DebugProcessImpl debugProcess)
|
||||
{
|
||||
final Project project = debuggerContext.getProject();
|
||||
final ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)selectedNode.getDescriptor();
|
||||
ArrayRenderer renderer = null;
|
||||
AdjustedRendererManager manager = null;
|
||||
for (AdjustedRendererManager m : MANAGERS) {
|
||||
renderer = m.getRenderer(selectedNode, debuggerContext);
|
||||
if (renderer != null) {
|
||||
manager = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (renderer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayRenderer renderer = (ArrayRenderer)((ValueDescriptorImpl)selectedNode.getDescriptor()).getLastRenderer();
|
||||
|
||||
String title = createNodeTitle("", selectedNode);
|
||||
String label = selectedNode.toString();
|
||||
int index = label.indexOf('=');
|
||||
@@ -63,31 +96,49 @@ public class AdjustArrayRangeAction extends DebuggerAction {
|
||||
title = title + " " + label.substring(index);
|
||||
}
|
||||
final ArrayRenderer cloneRenderer = renderer.clone();
|
||||
SingleConfigurableEditor editor = new SingleConfigurableEditor(project, new NamedArrayConfigurable(title, cloneRenderer)) {
|
||||
protected Action[] createActions() {
|
||||
final String helpTopic = getConfigurable().getHelpTopic();
|
||||
return (helpTopic != null)?
|
||||
new Action[]{getOKAction(), getCancelAction(), getHelpAction()} :
|
||||
new Action[]{getOKAction(), getCancelAction()};
|
||||
}
|
||||
};
|
||||
editor.show();
|
||||
final AdjustedRendererManager m = manager;
|
||||
|
||||
if(editor.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
|
||||
debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(debuggerContext.getSuspendContext()) {
|
||||
public void contextAction() throws Exception {
|
||||
selectedNode.setRenderer(cloneRenderer);
|
||||
final String finalTitle = title;
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SingleConfigurableEditor editor = new SingleConfigurableEditor(project, new NamedArrayConfigurable(finalTitle, cloneRenderer)) {
|
||||
protected Action[] createActions() {
|
||||
final String helpTopic = getConfigurable().getHelpTopic();
|
||||
return (helpTopic != null) ?
|
||||
new Action[]{getOKAction(), getCancelAction(), getHelpAction()} :
|
||||
new Action[]{getOKAction(), getCancelAction()};
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
editor.show();
|
||||
if(editor.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
|
||||
debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(debuggerContext.getSuspendContext()) {
|
||||
public void contextAction() throws Exception {
|
||||
m.apply(cloneRenderer, valueDescriptor, debuggerContext);
|
||||
selectedNode.clear();
|
||||
selectedNode.calcRepresentation();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void update(AnActionEvent e) {
|
||||
boolean enable = false;
|
||||
DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
|
||||
if(selectedNode != null) {
|
||||
NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
|
||||
enable = descriptor instanceof ValueDescriptorImpl && ((ValueDescriptorImpl)descriptor).isArray() && ((ValueDescriptorImpl) descriptor).getLastRenderer() instanceof ArrayRenderer;
|
||||
final NodeDescriptorImpl d = selectedNode.getDescriptor();
|
||||
if (d instanceof ValueDescriptorImpl) {
|
||||
ValueDescriptorImpl descriptor = (ValueDescriptorImpl)d;
|
||||
for (AdjustedRendererManager manager : MANAGERS) {
|
||||
if (manager.canBeApplied(descriptor)) {
|
||||
enable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
e.getPresentation().setVisible(enable);
|
||||
}
|
||||
@@ -126,4 +177,140 @@ public class AdjustArrayRangeAction extends DebuggerAction {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates details of array renderer retrieval and appliance (e.g. different logic is used for array references and list references).
|
||||
* <p/>
|
||||
* Implementations of this interface are assumed to be thread-safe.
|
||||
*/
|
||||
private interface AdjustedRendererManager {
|
||||
|
||||
/**
|
||||
* Allows to answer if current action can be applied to the value referenced by the given descriptor.
|
||||
* <p/>
|
||||
* <b>Note:</b> we can't just use checking like <code>'{@link #getRenderer(DebuggerTreeNode, DebuggerContextImpl)} == null'</code>
|
||||
* because this method is called outside of debug manager thread context, i.e. it's assumed to be called on
|
||||
* {@link AnAction#update(AnActionEvent)} and perform light-weight check from EDT.
|
||||
*
|
||||
* @param descriptor target value descriptor
|
||||
* @return <code>true</code> if current action can be applied to the value referenced by the given descriptor;
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
boolean canBeApplied(@NotNull ValueDescriptorImpl descriptor);
|
||||
|
||||
/**
|
||||
* @param node target node
|
||||
* @param context current debug context process
|
||||
* @return array-like data renderer used at the moment (if any)
|
||||
*/
|
||||
@Nullable
|
||||
ArrayRenderer getRenderer(@NotNull DebuggerTreeNode node, @NotNull DebuggerContextImpl context);
|
||||
|
||||
/**
|
||||
* Asks to use given renderer for array-like data.
|
||||
*
|
||||
* @param renderer renderer to use
|
||||
* @param descriptor target node descriptor
|
||||
* @param context target debugging context
|
||||
*/
|
||||
void apply(@NotNull ArrayRenderer renderer, @NotNull ValueDescriptorImpl descriptor, @NotNull DebuggerContextImpl context);
|
||||
}
|
||||
|
||||
private static abstract class AbstractAdjustedRendererManager implements AdjustedRendererManager {
|
||||
|
||||
@Override
|
||||
public ArrayRenderer getRenderer(@NotNull final DebuggerTreeNode node, @NotNull final DebuggerContextImpl context) {
|
||||
final NodeDescriptor descriptor = node.getDescriptor();
|
||||
if (!(descriptor instanceof ValueDescriptorImpl)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getRenderer(node, (ValueDescriptorImpl)descriptor, context);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract ArrayRenderer getRenderer(@NotNull DebuggerTreeNode node, @NotNull ValueDescriptorImpl descriptor,
|
||||
@NotNull DebuggerContextImpl context);
|
||||
}
|
||||
|
||||
private static class ArrayRendererManager extends AbstractAdjustedRendererManager {
|
||||
|
||||
@Override
|
||||
public boolean canBeApplied(@NotNull ValueDescriptorImpl descriptor) {
|
||||
return descriptor.getLastRenderer() instanceof ArrayRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayRenderer getRenderer(@NotNull DebuggerTreeNode node, @NotNull ValueDescriptorImpl descriptor,
|
||||
@NotNull DebuggerContextImpl context)
|
||||
{
|
||||
final Renderer result = descriptor.getLastRenderer();
|
||||
if (result instanceof ArrayRenderer) {
|
||||
return (ArrayRenderer)result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(@NotNull ArrayRenderer renderer, @NotNull ValueDescriptorImpl valueDescriptor, @NotNull DebuggerContextImpl context) {
|
||||
valueDescriptor.setRenderer(renderer, context.getDebugProcess(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ListRendererManager extends AbstractAdjustedRendererManager {
|
||||
|
||||
@Override
|
||||
public boolean canBeApplied(@NotNull ValueDescriptorImpl descriptor) {
|
||||
final Value value = descriptor.getValue();
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
final Type type = value.type();
|
||||
if (type == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DebuggerUtils.instanceOf(type, List.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayRenderer getRenderer(@NotNull DebuggerTreeNode node, @NotNull final ValueDescriptorImpl descriptor,
|
||||
@NotNull final DebuggerContextImpl context)
|
||||
{
|
||||
final DelegatingChildrenRenderer delegatingRenderer = getDelegateRenderer(descriptor, context.getDebugProcess());
|
||||
if (delegatingRenderer == null) {
|
||||
return null;
|
||||
}
|
||||
final ChildrenRenderer result = delegatingRenderer.getDelegate(context.createEvaluationContext(), descriptor, descriptor.getValue());
|
||||
if (result instanceof ArrayRenderer) {
|
||||
return (ArrayRenderer)result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(@NotNull ArrayRenderer renderer, @NotNull ValueDescriptorImpl descriptor, @NotNull DebuggerContextImpl context) {
|
||||
final DelegatingChildrenRenderer delegateRenderer = getDelegateRenderer(descriptor, context.getDebugProcess());
|
||||
if (delegateRenderer != null) {
|
||||
delegateRenderer.setDelegate(context.createEvaluationContext(), descriptor, descriptor.getValue(), renderer);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static DelegatingChildrenRenderer getDelegateRenderer(ValueDescriptorImpl descriptor, DebugProcessImpl process) {
|
||||
final NodeRenderer renderer = descriptor.getRenderer(process);
|
||||
if (!(renderer instanceof CompoundReferenceRenderer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CompoundReferenceRenderer compoundReferenceRenderer = (CompoundReferenceRenderer)renderer;
|
||||
final ChildrenRenderer result = compoundReferenceRenderer.getChildrenRenderer();
|
||||
if (result instanceof DelegatingChildrenRenderer) {
|
||||
return (DelegatingChildrenRenderer)result;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-4
@@ -29,9 +29,7 @@ import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
|
||||
import com.intellij.debugger.settings.NodeRendererSettings;
|
||||
import com.intellij.debugger.ui.tree.NodeDescriptor;
|
||||
import com.intellij.debugger.ui.tree.ValueDescriptor;
|
||||
import com.intellij.debugger.ui.tree.render.ClassRenderer;
|
||||
import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
|
||||
import com.intellij.debugger.ui.tree.render.NodeRenderer;
|
||||
import com.intellij.debugger.ui.tree.render.*;
|
||||
import com.intellij.debugger.ui.tree.render.Renderer;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -111,7 +109,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements
|
||||
}
|
||||
|
||||
public Value getValue() {
|
||||
// the following code makes sence only if we do not use ObjectReference.enableCollection() / disableCollection()
|
||||
// the following code makes sense only if we do not use ObjectReference.enableCollection() / disableCollection()
|
||||
// to keep temporary objects
|
||||
if (Patches.IBM_JDK_DISABLE_COLLECTION_BUG && myStoredEvaluationContext != null && !myStoredEvaluationContext.getSuspendContext().isResumed() &&
|
||||
myValue instanceof ObjectReference && VirtualMachineProxyImpl.isCollected((ObjectReference)myValue)) {
|
||||
@@ -168,6 +166,9 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements
|
||||
}
|
||||
myValue = value;
|
||||
myValueException = null;
|
||||
if (myRenderer == null) {
|
||||
myRenderer = DebuggerRendererUtil.getCustomRenderer(evaluationContext.getDebugProcess(), myValue, NodeRenderer.class);
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
myValueException = e;
|
||||
@@ -342,6 +343,22 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements
|
||||
myAutoRenderer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to apply given renderer to the value referenced by the current descriptor.
|
||||
*
|
||||
* @param renderer custom renderer to use
|
||||
* @param process current debug process
|
||||
* @param remember flag that indicates if given renderer should be used at stack frames over than the current
|
||||
* one (e.g. there is a possible case that custom renderer is applied to the local variable. If given flag
|
||||
* is <code>'true'</code>, the renderer is applied down at stack as well)
|
||||
*/
|
||||
public void setRenderer(NodeRenderer renderer, @Nullable DebugProcess process, boolean remember) {
|
||||
setRenderer(renderer);
|
||||
if (remember) {
|
||||
DebuggerRendererUtil.setCustomRenderer(myValue, process, renderer);
|
||||
}
|
||||
}
|
||||
|
||||
//returns expression that evaluates tree to this descriptor
|
||||
public PsiExpression getTreeEvaluation(DebuggerTreeNodeImpl debuggerTreeNode, DebuggerContextImpl context) throws EvaluateException {
|
||||
if(debuggerTreeNode.getParent() != null && debuggerTreeNode.getParent().getDescriptor() instanceof ValueDescriptor) {
|
||||
|
||||
@@ -15,16 +15,13 @@
|
||||
*/
|
||||
package com.intellij.debugger.ui.tree.render;
|
||||
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContext;
|
||||
import com.intellij.debugger.DebuggerContext;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.StackFrameContext;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContext;
|
||||
import com.intellij.debugger.ui.tree.DebuggerTreeNode;
|
||||
import com.intellij.debugger.ui.tree.NodeDescriptor;
|
||||
import com.intellij.debugger.DebuggerContext;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.sun.jdi.Value;
|
||||
import com.sun.tools.corba.se.idl.constExpr.EvaluationException;
|
||||
|
||||
public interface ChildrenRenderer extends Renderer {
|
||||
void buildChildren(Value value, ChildrenBuilder builder, EvaluationContext evaluationContext);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.debugger.ui.tree.render;
|
||||
|
||||
import com.intellij.debugger.engine.DebugProcess;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.util.containers.WeakHashMap;
|
||||
import com.sun.jdi.Value;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/18/11 2:43 PM
|
||||
*/
|
||||
public class DebuggerRendererUtil {
|
||||
|
||||
/**
|
||||
* Holds mappings between particular values and their custom renderers.
|
||||
*/
|
||||
private static final Key<Map<Value, Renderer>> RENDERERS_MAP_KEY = new Key<Map<Value, Renderer>>("ValueRendererMap");
|
||||
|
||||
private DebuggerRendererUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks for custom renderer registered for the given value at the given debug process (if any).
|
||||
*
|
||||
* @param process target debug process
|
||||
* @param value target value which custom renderer is being retrieved
|
||||
* @param clazz class that specifies IS-A constraint for the desired renderer
|
||||
* @return <code>'value - custom renderer'</code> registered for the given value at the given debug process (if any);
|
||||
* <code>null</code> otherwise
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public static <T extends Renderer> T getCustomRenderer(@Nullable final DebugProcess process, @NotNull Value value,
|
||||
@NotNull Class<T> clazz)
|
||||
{
|
||||
if (process == null) {
|
||||
return null;
|
||||
}
|
||||
Map<Value, Renderer> map = process.getUserData(RENDERERS_MAP_KEY);
|
||||
if (map == null) {
|
||||
process.putUserData(RENDERERS_MAP_KEY, map = new WeakHashMap<Value, Renderer>());
|
||||
}
|
||||
final Renderer renderer = map.get(value);
|
||||
if (clazz.isInstance(renderer)) {
|
||||
return (T)renderer;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to store given custom renderer for the given value within the given debug process.
|
||||
*
|
||||
* @param value target value
|
||||
* @param process target debug process
|
||||
* @param renderer target custom renderer to store
|
||||
* @return <code>true</code> if the mapping is successfully stored; <code>false</code> otherwise
|
||||
*/
|
||||
public static boolean setCustomRenderer(@NotNull Value value, @Nullable DebugProcess process, @NotNull Renderer renderer) {
|
||||
if (process == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<Value, Renderer> map = process.getUserData(RENDERERS_MAP_KEY);
|
||||
if (map == null) {
|
||||
process.putUserData(RENDERERS_MAP_KEY, map = new WeakHashMap<Value, Renderer>());
|
||||
}
|
||||
map.put(value, renderer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.debugger.ui.tree.render;
|
||||
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContext;
|
||||
import com.intellij.debugger.ui.tree.NodeDescriptor;
|
||||
import com.sun.jdi.Value;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Defines contract for renderer that performs the job (rendering) by delegating to another object.
|
||||
* <p/>
|
||||
* Example: we can render {@link ArrayList} by delegating to its backed array. The array is a <code>'delegate object'</code> here
|
||||
* and its renderer is exposed via the current interface.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/18/11 12:14 PM
|
||||
*/
|
||||
public interface DelegatingChildrenRenderer extends ChildrenRenderer {
|
||||
|
||||
/**
|
||||
* @param evaluationContext evaluation context to use
|
||||
* @param descriptor target node descriptor
|
||||
* @param value target value for which <code>'delegate object'</code> can be determined
|
||||
* @return renderer fo the <code>'delegate object'</code> if the one exists; <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
ChildrenRenderer getDelegate(@NotNull EvaluationContext evaluationContext, @NotNull NodeDescriptor descriptor, @NotNull Value value);
|
||||
|
||||
/**
|
||||
* Instructs to use given renderer when <code>'delegate object'</code> has given type.
|
||||
*
|
||||
* @param evaluationContext evaluation context to use
|
||||
* @param descriptor target node descriptor
|
||||
* @param value target value for which <code>'delegate object'</code> given custom renderer should be used
|
||||
* @param renderer delegating renderer to use
|
||||
* @return <code>true</code> if given custom renderer is applied; <code>false</code> otherwise
|
||||
*/
|
||||
boolean setDelegate(@NotNull EvaluationContext evaluationContext, @NotNull NodeDescriptor descriptor, @NotNull Value value,
|
||||
@NotNull ChildrenRenderer renderer);
|
||||
}
|
||||
+43
-3
@@ -34,9 +34,11 @@ import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.WriteExternalException;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.sun.jdi.BooleanValue;
|
||||
import com.sun.jdi.Type;
|
||||
import com.sun.jdi.Value;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -46,7 +48,8 @@ import java.util.List;
|
||||
* Date: Sep 17, 2003
|
||||
* Time: 2:04:00 PM
|
||||
*/
|
||||
public class ExpressionChildrenRenderer extends ReferenceRenderer implements ChildrenRenderer {
|
||||
public class ExpressionChildrenRenderer extends ReferenceRenderer implements ChildrenRenderer, DelegatingChildrenRenderer {
|
||||
|
||||
public static final @NonNls String UNIQUE_ID = "ExpressionChildrenRenderer";
|
||||
private static final Key<Value> EXPRESSION_VALUE = new Key<Value>("EXPRESSION_VALUE");
|
||||
|
||||
@@ -78,8 +81,8 @@ public class ExpressionChildrenRenderer extends ReferenceRenderer implements Chi
|
||||
evaluationContext.createEvaluationContext(value), builder.getParentDescriptor()
|
||||
);
|
||||
|
||||
final ChildrenRenderer defaultRenderer = ((DebugProcessImpl)evaluationContext.getDebugProcess()).getDefaultRenderer(childrenValue != null? childrenValue.type() : null);
|
||||
defaultRenderer.buildChildren(childrenValue, builder, evaluationContext);
|
||||
final ChildrenRenderer delegateRenderer = doGetDelegate(evaluationContext, builder.getParentDescriptor(), value);
|
||||
delegateRenderer.buildChildren(childrenValue, builder, evaluationContext);
|
||||
}
|
||||
catch (final EvaluateException e) {
|
||||
List<DebuggerTreeNode> errorChildren = new ArrayList<DebuggerTreeNode>();
|
||||
@@ -88,6 +91,43 @@ public class ExpressionChildrenRenderer extends ReferenceRenderer implements Chi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChildrenRenderer getDelegate(@NotNull EvaluationContext evaluationContext, @NotNull NodeDescriptor descriptor,
|
||||
@NotNull Value value)
|
||||
{
|
||||
try {
|
||||
return doGetDelegate(evaluationContext, descriptor, value);
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ChildrenRenderer doGetDelegate(@NotNull EvaluationContext evaluationContext, @NotNull NodeDescriptor descriptor,
|
||||
@NotNull Value value) throws EvaluateException
|
||||
{
|
||||
final Value childrenValue = evaluateChildren(evaluationContext.createEvaluationContext(value), descriptor);
|
||||
if (childrenValue == null) {
|
||||
return ((DebugProcessImpl)evaluationContext.getDebugProcess()).getDefaultRenderer((Type)null);
|
||||
}
|
||||
ChildrenRenderer result = DebuggerRendererUtil.getCustomRenderer(evaluationContext.getDebugProcess(), value,
|
||||
ChildrenRenderer.class);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return ((DebugProcessImpl)evaluationContext.getDebugProcess()).getDefaultRenderer(childrenValue.type());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setDelegate(@NotNull EvaluationContext evaluationContext,
|
||||
@NotNull NodeDescriptor descriptor,
|
||||
@NotNull Value value,
|
||||
@NotNull ChildrenRenderer renderer)
|
||||
{
|
||||
return DebuggerRendererUtil.setCustomRenderer(value, evaluationContext.getDebugProcess(), renderer);
|
||||
}
|
||||
|
||||
private Value evaluateChildren(EvaluationContext context, NodeDescriptor descriptor) throws EvaluateException {
|
||||
final ExpressionEvaluator evaluator = myChildrenExpression.getEvaluator(context.getProject());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user