diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java index be9e3af4d4c2..762282dd22d1 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java @@ -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). + *
+ * 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. + * + * Note: we can't just use checking like'{@link #getRenderer(DebuggerTreeNode, DebuggerContextImpl)} == null'
+ * 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 true if current action can be applied to the value referenced by the given descriptor;
+ * false 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;
+ }
+ }
+ }
}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java
index ab731f3e0daf..dabec03d00c0 100644
--- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java
@@ -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 'true', 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) {
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenRenderer.java
index 838749e8f401..969635a20446 100644
--- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenRenderer.java
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenRenderer.java
@@ -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);
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/DebuggerRendererUtil.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/DebuggerRendererUtil.java
new file mode 100644
index 000000000000..4e4ac43f060e
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/DebuggerRendererUtil.java
@@ -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