- make MarkObject dialog appear faster
[fedora-idea.git] / java / debugger / impl / src / com / intellij / debugger / actions / MarkObjectAction.java
blobd7f4e3a68825732a30a58d2e7f900a623c2ea351
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.debugger.actions;
18 import com.intellij.debugger.engine.DebugProcessImpl;
19 import com.intellij.debugger.engine.DebuggerUtils;
20 import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
21 import com.intellij.debugger.impl.DebuggerContextImpl;
22 import com.intellij.debugger.impl.DebuggerSession;
23 import com.intellij.debugger.ui.impl.watch.DebuggerTree;
24 import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
25 import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
26 import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
27 import com.intellij.debugger.ui.tree.ValueDescriptor;
28 import com.intellij.debugger.ui.tree.ValueMarkup;
29 import com.intellij.idea.ActionsBundle;
30 import com.intellij.openapi.actionSystem.AnActionEvent;
31 import com.intellij.openapi.actionSystem.Presentation;
32 import com.intellij.openapi.diagnostic.Logger;
33 import com.intellij.openapi.util.Pair;
34 import com.intellij.openapi.util.Ref;
35 import com.intellij.util.containers.HashMap;
36 import com.sun.jdi.*;
38 import javax.swing.*;
39 import java.awt.*;
40 import java.lang.reflect.InvocationTargetException;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.Map;
46 * Class SetValueAction
47 * @author Jeka
49 public class MarkObjectAction extends DebuggerAction {
50 private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.actions.MarkObjectAction");
51 private final String MARK_TEXT = ActionsBundle.message("action.Debugger.MarkObject.text");
52 private final String UNMARK_TEXT = ActionsBundle.message("action.Debugger.MarkObject.unmark.text");
54 public void actionPerformed(final AnActionEvent event) {
55 final DebuggerTreeNodeImpl node = getSelectedNode(event.getDataContext());
56 if (node == null) {
57 return;
59 final NodeDescriptorImpl descriptor = node.getDescriptor();
60 if (!(descriptor instanceof ValueDescriptorImpl)) {
61 return;
64 final DebuggerTree tree = node.getTree();
65 tree.saveState(node);
67 final ValueDescriptorImpl valueDescriptor = ((ValueDescriptorImpl)descriptor);
68 final DebuggerContextImpl debuggerContext = tree.getDebuggerContext();
69 final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
70 final ValueMarkup markup = valueDescriptor.getMarkup(debugProcess);
71 debugProcess.getManagerThread().invoke(new DebuggerContextCommandImpl(debuggerContext) {
72 public Priority getPriority() {
73 return Priority.HIGH;
75 public void threadAction() {
76 boolean sessionRefreshNeeded = true;
77 try {
78 if (markup != null) {
79 valueDescriptor.setMarkup(debugProcess, null);
81 else {
82 final ValueMarkup suggestedMarkup = new ValueMarkup(valueDescriptor.getName(), Color.RED);
83 final Ref<Pair<ValueMarkup,Boolean>> result = new Ref<Pair<ValueMarkup, Boolean>>(null);
84 try {
85 final boolean suggestAdditionalMarkup = canSuggestAdditionalMarkup(debugProcess, valueDescriptor.getValue());
86 SwingUtilities.invokeAndWait(new Runnable() {
87 public void run() {
88 result.set(ObjectMarkupPropertiesDialog.chooseMarkup(suggestedMarkup, suggestAdditionalMarkup));
90 });
92 catch (InterruptedException ignored) {
94 catch (InvocationTargetException e) {
95 LOG.error(e);
97 final Pair<ValueMarkup, Boolean> pair = result.get();
98 if (pair != null) {
99 valueDescriptor.setMarkup(debugProcess, pair.first);
100 if (pair.second) {
101 final Value value = valueDescriptor.getValue();
102 final Map<Long, ValueMarkup> additionalMarkup = suggestMarkup((ObjectReference)value);
103 if (!additionalMarkup.isEmpty()) {
104 final Map<Long, ValueMarkup> map = NodeDescriptorImpl.getMarkupMap(debugProcess);
105 if (map != null) {
106 for (Map.Entry<Long,ValueMarkup> entry : additionalMarkup.entrySet()) {
107 final Long key = entry.getKey();
108 if (!map.containsKey(key)) {
109 map.put(key, entry.getValue());
116 else {
117 sessionRefreshNeeded = false;
121 finally {
122 final boolean _sessionRefreshNeeded = sessionRefreshNeeded;
123 SwingUtilities.invokeLater(new Runnable() {
124 public void run() {
125 tree.restoreState(node);
126 node.labelChanged();
127 if (_sessionRefreshNeeded) {
128 final DebuggerSession session = debuggerContext.getDebuggerSession();
129 if (session != null) {
130 session.refresh(true);
140 private static boolean canSuggestAdditionalMarkup(DebugProcessImpl debugProcess, Value value) {
141 if (!debugProcess.getVirtualMachineProxy().canGetInstanceInfo()) {
142 return false;
144 if (!(value instanceof ObjectReference)) {
145 return false;
147 final ObjectReference objRef = (ObjectReference)value;
148 if (objRef instanceof ArrayReference || objRef instanceof ClassObjectReference || objRef instanceof ThreadReference || objRef instanceof ThreadGroupReference || objRef instanceof ClassLoaderReference) {
149 return false;
151 return true;
154 private static Map<Long, ValueMarkup> suggestMarkup(ObjectReference objRef) {
155 final Map<Long, ValueMarkup> result = new HashMap<Long, ValueMarkup>();
156 for (ObjectReference ref : getReferringObjects(objRef)) {
157 if (!(ref instanceof ClassObjectReference)) {
158 // consider references from statisc fields only
159 continue;
161 final ReferenceType refType = ((ClassObjectReference)ref).reflectedType();
162 if (!refType.isAbstract()) {
163 continue;
165 for (Field field : refType.visibleFields()) {
166 if (!(field.isStatic() && field.isFinal())) {
167 continue;
169 if (DebuggerUtils.isPrimitiveType(field.typeName())) {
170 continue;
172 final Value fieldValue = refType.getValue(field);
173 if (!(fieldValue instanceof ObjectReference)) {
174 continue;
176 final long id = ((ObjectReference)fieldValue).uniqueID();
177 final ValueMarkup markup = result.get(id);
179 final String fieldName = field.name();
180 final Color autoMarkupColor = ValueMarkup.getAutoMarkupColor();
181 if (markup == null) {
182 result.put(id, new ValueMarkup(fieldName, autoMarkupColor, createMarkupTooltipText(null, refType, fieldName)));
184 else {
185 final String currentText = markup.getText();
186 if (!currentText.contains(fieldName)) {
187 final String currentTooltip = markup.getToolTipText();
188 final String tooltip = createMarkupTooltipText(currentTooltip, refType, fieldName);
189 result.put(id, new ValueMarkup(currentText + ", " + fieldName, autoMarkupColor, tooltip));
194 return result;
197 private static List<ObjectReference> getReferringObjects(ObjectReference value) {
198 // invoke the following method using Reflection in order to remain compilable on jdk 1.5
199 // java.util.List<com.sun.jdi.ObjectReference> referringObjects(long l);
200 try {
201 final java.lang.reflect.Method apiMethod = ObjectReference.class.getMethod("referringObjects", long.class);
202 return (List<ObjectReference>)apiMethod.invoke(value, ValueMarkup.AUTO_MARKUP_REFERRING_OBJECTS_LIMIT);
204 catch (IllegalAccessException e) {
205 LOG.error(e); // should not happen
207 catch (InvocationTargetException e) {
208 throw new RuntimeException(e);
210 catch (NoSuchMethodException ignored) {
212 return Collections.emptyList();
215 private static String createMarkupTooltipText(String prefix, ReferenceType refType, String fieldName) {
216 final StringBuilder builder = new StringBuilder();
217 if (prefix == null) {
218 builder.append("Value referenced from:");
220 else {
221 builder.append(prefix);
223 return builder.append("<br><b>").append(refType.name()).append(".").append(fieldName).append("</b>").toString();
227 public void update(AnActionEvent e) {
228 boolean enable = false;
229 String text = MARK_TEXT;
230 final DebuggerTreeNodeImpl node = getSelectedNode(e.getDataContext());
231 if (node != null) {
232 final NodeDescriptorImpl descriptor = node.getDescriptor();
233 enable = (descriptor instanceof ValueDescriptor);
234 if (enable) {
235 final ValueMarkup markup = ((ValueDescriptor)descriptor).getMarkup(node.getTree().getDebuggerContext().getDebugProcess());
236 if (markup != null) { // already exists
237 text = UNMARK_TEXT;
241 final Presentation presentation = e.getPresentation();
242 presentation.setVisible(enable);
243 presentation.setText(text);