IDEA-79921 Suspend one thread while debugging
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / ui / impl / watch / DebuggerTree.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
18  * Class DebuggerTree
19  * @author Jeka
20  */
21 package com.intellij.debugger.ui.impl.watch;
22
23 import com.intellij.debugger.DebuggerBundle;
24 import com.intellij.debugger.DebuggerInvocationUtil;
25 import com.intellij.debugger.engine.*;
26 import com.intellij.debugger.engine.evaluation.EvaluateException;
27 import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
28 import com.intellij.debugger.engine.events.DebuggerCommandImpl;
29 import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
30 import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
31 import com.intellij.debugger.impl.DebuggerContextImpl;
32 import com.intellij.debugger.impl.DebuggerSession;
33 import com.intellij.debugger.impl.DebuggerUtilsEx;
34 import com.intellij.debugger.jdi.*;
35 import com.intellij.debugger.settings.NodeRendererSettings;
36 import com.intellij.debugger.settings.ThreadsViewSettings;
37 import com.intellij.debugger.ui.breakpoints.Breakpoint;
38 import com.intellij.debugger.ui.impl.DebuggerTreeBase;
39 import com.intellij.debugger.ui.impl.tree.TreeBuilder;
40 import com.intellij.debugger.ui.impl.tree.TreeBuilderNode;
41 import com.intellij.debugger.ui.tree.DebuggerTreeNode;
42 import com.intellij.debugger.ui.tree.NodeDescriptor;
43 import com.intellij.debugger.ui.tree.render.ArrayRenderer;
44 import com.intellij.debugger.ui.tree.render.ChildrenBuilder;
45 import com.intellij.debugger.ui.tree.render.ClassRenderer;
46 import com.intellij.debugger.ui.tree.render.NodeRenderer;
47 import com.intellij.openapi.actionSystem.DataKey;
48 import com.intellij.openapi.actionSystem.DataProvider;
49 import com.intellij.openapi.application.ApplicationManager;
50 import com.intellij.openapi.diagnostic.Logger;
51 import com.intellij.openapi.project.Project;
52 import com.intellij.openapi.util.Key;
53 import com.intellij.openapi.util.Pair;
54 import com.intellij.openapi.util.text.StringUtil;
55 import com.intellij.ui.SpeedSearchComparator;
56 import com.intellij.ui.TreeSpeedSearch;
57 import com.intellij.xdebugger.settings.XDebuggerSettingsManager;
58 import com.sun.jdi.*;
59 import com.sun.jdi.event.Event;
60 import com.sun.jdi.event.ExceptionEvent;
61
62 import javax.swing.*;
63 import javax.swing.event.TreeModelEvent;
64 import javax.swing.event.TreeModelListener;
65 import javax.swing.tree.TreePath;
66 import java.awt.*;
67 import java.awt.event.MouseEvent;
68 import java.util.*;
69 import java.util.List;
70
71 public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvider {
72   private static final Logger LOG = Logger.getInstance(DebuggerTree.class);
73   protected static final Key<Rectangle> VISIBLE_RECT = Key.create("VISIBLE_RECT");
74
75   public static final DataKey<DebuggerTree> DATA_KEY = DataKey.create("DebuggerTree"); 
76
77   protected final NodeManagerImpl myNodeManager;
78
79   private DebuggerContextImpl myDebuggerContext = DebuggerContextImpl.EMPTY_CONTEXT;
80
81   private DebuggerTreeNodeImpl myEditedNode;
82
83   public DebuggerTree(Project project) {
84     super(null, project);
85     setScrollsOnExpand(false);
86     myNodeManager = createNodeManager(project);
87
88     final TreeBuilder model = new TreeBuilder(this) {
89       @Override
90       public void buildChildren(TreeBuilderNode node) {
91         final DebuggerTreeNodeImpl debuggerTreeNode = (DebuggerTreeNodeImpl)node;
92         if (debuggerTreeNode.getDescriptor() instanceof DefaultNodeDescriptor) {
93           return;
94         }
95         buildNode(debuggerTreeNode);
96       }
97
98       @Override
99       public boolean isExpandable(TreeBuilderNode builderNode) {
100         return DebuggerTree.this.isExpandable((DebuggerTreeNodeImpl)builderNode);
101       }
102     };
103     model.setRoot(getNodeFactory().getDefaultNode());
104     model.addTreeModelListener(new TreeModelListener() {
105       @Override
106       public void treeNodesChanged(TreeModelEvent event) {
107         hideTooltip();
108       }
109
110       @Override
111       public void treeNodesInserted(TreeModelEvent event) {
112         hideTooltip();
113       }
114
115       @Override
116       public void treeNodesRemoved(TreeModelEvent event) {
117         hideTooltip();
118       }
119
120       @Override
121       public void treeStructureChanged(TreeModelEvent event) {
122         hideTooltip();
123       }
124     });
125
126     setModel(model);
127
128     final TreeSpeedSearch search = new TreeSpeedSearch(this);
129     search.setComparator(new SpeedSearchComparator(false));
130   }
131
132   protected NodeManagerImpl createNodeManager(Project project) {
133     return new NodeManagerImpl(project, this);
134   }
135
136   @Override
137   public void dispose() {
138     myNodeManager.dispose();
139     myDebuggerContext = DebuggerContextImpl.EMPTY_CONTEXT;
140     super.dispose();
141   }
142
143   protected boolean isExpandable(DebuggerTreeNodeImpl node) {
144     NodeDescriptorImpl descriptor = node.getDescriptor();
145     return descriptor.isExpandable();
146   }
147
148   @Override
149   public Object getData(String dataId) {
150     if (DATA_KEY.is(dataId)) {
151       return this;
152     }
153     return null;
154   }
155
156
157   private void buildNode(final DebuggerTreeNodeImpl node) {
158     if (node == null || node.getDescriptor() == null) {
159       return;
160     }
161     final DebugProcessImpl debugProcess = getDebuggerContext().getDebugProcess();
162     if (debugProcess != null) {
163       DebuggerCommandImpl command = getBuildNodeCommand(node);
164       if (command != null) {
165         node.add(myNodeManager.createMessageNode(MessageDescriptor.EVALUATING));
166         debugProcess.getManagerThread().schedule(command);
167       }
168     }
169   }
170
171   // todo: convert "if" into instance method call
172   protected DebuggerCommandImpl getBuildNodeCommand(final DebuggerTreeNodeImpl node) {
173     if (node.getDescriptor() instanceof StackFrameDescriptorImpl) {
174       return new BuildStackFrameCommand(node);
175     }
176     else if (node.getDescriptor() instanceof ValueDescriptorImpl) {
177       return new BuildValueNodeCommand(node);
178     }
179     else if (node.getDescriptor() instanceof StaticDescriptorImpl) {
180       return new BuildStaticNodeCommand(node);
181     }
182     else if (node.getDescriptor() instanceof ThreadDescriptorImpl) {
183       return new BuildThreadCommand(node);
184     }
185     else if (node.getDescriptor() instanceof ThreadGroupDescriptorImpl) {
186       return new BuildThreadGroupCommand(node);
187     }
188     LOG.assertTrue(false);
189     return null;
190   }
191
192   public void saveState(DebuggerTreeNodeImpl node) {
193     if (node.getDescriptor() != null) {
194       TreePath path = new TreePath(node.getPath());
195       node.getDescriptor().myIsExpanded = isExpanded(path);
196       node.getDescriptor().myIsSelected = getSelectionModel().isPathSelected(path);
197       Rectangle rowBounds = getRowBounds(getRowForPath(path));
198       if (rowBounds != null && getVisibleRect().contains(rowBounds)) {
199         node.getDescriptor().putUserData(VISIBLE_RECT, getVisibleRect());
200         node.getDescriptor().myIsVisible = true;
201       }
202       else {
203         node.getDescriptor().putUserData(VISIBLE_RECT, null);
204         node.getDescriptor().myIsVisible = false;
205       }
206     }
207
208     for (Enumeration e = node.rawChildren(); e.hasMoreElements();) {
209       DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)e.nextElement();
210       saveState(child);
211     }
212   }
213
214   public void restoreState(DebuggerTreeNodeImpl node) {
215     restoreStateImpl(node);
216     scrollToVisible(node);
217   }
218
219   protected final void scrollToVisible(DebuggerTreeNodeImpl scopeNode) {
220     final TreePath rootPath = new TreePath(scopeNode.getPath());
221     final int rowCount = getRowCount();
222     for (int idx = rowCount - 1; idx >= 0; idx--) {
223       final TreePath treePath = getPathForRow(idx);
224       if (treePath != null) {
225         if (!rootPath.isDescendant(treePath)) {
226           continue;
227         }
228         final DebuggerTreeNodeImpl pathNode = (DebuggerTreeNodeImpl)treePath.getLastPathComponent();
229         final NodeDescriptorImpl descriptor = pathNode.getDescriptor();
230
231         if (descriptor != null && descriptor.myIsVisible) {
232           final Rectangle visibleRect = descriptor.getUserData(VISIBLE_RECT);
233           if (visibleRect != null) {
234             // prefer visible rect
235             scrollRectToVisible(visibleRect);
236           }
237           else {
238             scrollPathToVisible(treePath);
239           }
240           break;
241         }
242       }
243     }
244   }
245
246   @Override
247   public void scrollRectToVisible(Rectangle aRect) {
248     // see IDEADEV-432
249     aRect.width += aRect.x;
250     aRect.x = 0;
251     super.scrollRectToVisible(aRect);
252   }
253
254   private void restoreStateImpl(DebuggerTreeNodeImpl node) {
255     restoreNodeState(node);
256     if (node.getDescriptor().myIsExpanded) {
257       for (Enumeration e = node.rawChildren(); e.hasMoreElements();) {
258         DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)e.nextElement();
259         restoreStateImpl(child);
260       }
261     }
262   }
263
264   public void restoreState() {
265     clearSelection();
266     DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getModel().getRoot();
267     if (root != null) {
268       restoreState(root);
269     }
270   }
271
272   protected void restoreNodeState(DebuggerTreeNodeImpl node) {
273     final NodeDescriptorImpl descriptor = node.getDescriptor();
274     if (descriptor != null) {
275       if (node.getParent() == null) {
276         descriptor.myIsExpanded = true;
277       }
278
279       TreePath path = new TreePath(node.getPath());
280       if (descriptor.myIsExpanded) {
281         expandPath(path);
282       }
283       if (descriptor.myIsSelected) {
284         addSelectionPath(path);
285       }
286     }
287   }
288
289   public NodeManagerImpl getNodeFactory() {
290     return myNodeManager;
291   }
292
293   public TreeBuilder getMutableModel() {
294     return (TreeBuilder)getModel();
295   }
296
297   public void removeAllChildren() {
298     DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getModel().getRoot();
299     root.removeAllChildren();
300     treeChanged();
301   }
302
303   public void showMessage(MessageDescriptor messageDesc) {
304     DebuggerTreeNodeImpl root = getNodeFactory().getDefaultNode();
305     getMutableModel().setRoot(root);
306     DebuggerTreeNodeImpl message = root.add(messageDesc);
307     treeChanged();
308     expandPath(new TreePath(message.getPath()));
309   }
310
311   public void showMessage(String messageText) {
312     showMessage(new MessageDescriptor(messageText));
313   }
314
315   public final void treeChanged() {
316     DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl)getModel().getRoot();
317     if (node != null) {
318       getMutableModel().nodeStructureChanged(node);
319       restoreState();
320     }
321   }
322
323   protected abstract void build(DebuggerContextImpl context);
324
325   protected final void buildWhenPaused(DebuggerContextImpl context, RefreshDebuggerTreeCommand command) {
326     DebuggerSession session = context.getDebuggerSession();
327
328     if (ApplicationManager.getApplication().isUnitTestMode() || (session != null && session.getState() == DebuggerSession.STATE_PAUSED)) {
329       showMessage(MessageDescriptor.EVALUATING);
330       context.getDebugProcess().getManagerThread().schedule(command);
331     }
332     else {
333       showMessage(session != null? session.getStateDescription() : DebuggerBundle.message("status.debug.stopped"));
334       if (session == null || session.isStopped()) {
335         getNodeFactory().clearHistory(); // save memory by clearing references on JDI objects
336       }
337     }
338   }
339
340   public void rebuild(final DebuggerContextImpl context) {
341     ApplicationManager.getApplication().assertIsDispatchThread();
342     final DebugProcessImpl process = context.getDebugProcess();
343     if (process == null) {
344       return; // empty context, no process available yet
345     }
346     myDebuggerContext = context;
347     saveState();
348     process.getManagerThread().schedule(new DebuggerCommandImpl() {
349       @Override
350       protected void action() throws Exception {
351         getNodeFactory().setHistoryByContext(context);
352       }
353       @Override
354       public Priority getPriority() {
355         return Priority.NORMAL;
356       }
357     });
358
359     build(context);
360   }
361
362   public void saveState() {
363     saveState((DebuggerTreeNodeImpl)getModel().getRoot());
364   }
365
366   public void onEditorShown(DebuggerTreeNodeImpl node) {
367     myEditedNode = node;
368     hideTooltip();
369   }
370
371   public void onEditorHidden(DebuggerTreeNodeImpl node) {
372     if (myEditedNode != null) {
373       assert myEditedNode == node;
374       myEditedNode = null;
375     }
376   }
377
378   @Override
379   public JComponent createToolTip(MouseEvent e) {
380     return myEditedNode != null ? null : super.createToolTip(e);
381   }
382
383   protected abstract static class RefreshDebuggerTreeCommand extends SuspendContextCommandImpl {
384     private final DebuggerContextImpl myDebuggerContext;
385
386     @Override
387     public Priority getPriority() {
388       return Priority.NORMAL;
389     }
390
391     public RefreshDebuggerTreeCommand(DebuggerContextImpl context) {
392       super(context.getSuspendContext());
393       myDebuggerContext = context;
394     }
395
396     public final DebuggerContextImpl getDebuggerContext() {
397       return myDebuggerContext;
398     }
399   }
400
401   public DebuggerContextImpl getDebuggerContext() {
402     return myDebuggerContext;
403   }
404
405   public abstract class BuildNodeCommand extends DebuggerContextCommandImpl {
406     private final DebuggerTreeNodeImpl myNode;
407
408     protected final List<DebuggerTreeNodeImpl> myChildren = new LinkedList<DebuggerTreeNodeImpl>();
409
410     protected BuildNodeCommand(DebuggerTreeNodeImpl node) {
411       super(DebuggerTree.this.getDebuggerContext());
412       myNode = node;
413     }
414
415     @Override
416     public Priority getPriority() {
417       return Priority.NORMAL;
418     }
419
420     public DebuggerTreeNodeImpl getNode() {
421       return myNode;
422     }
423
424     protected void updateUI(final boolean scrollToVisible) {
425       DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
426         @Override
427         public void run() {
428           myNode.removeAllChildren();
429           for (DebuggerTreeNodeImpl debuggerTreeNode : myChildren) {
430             myNode.add(debuggerTreeNode);
431           }
432           myNode.childrenChanged(scrollToVisible);
433         }
434       });
435     }
436   }
437
438   protected class BuildStackFrameCommand extends BuildNodeCommand {
439     public BuildStackFrameCommand(DebuggerTreeNodeImpl stackNode) {
440       super(stackNode);
441     }
442
443     @Override
444     public void threadAction() {
445       try {
446         final StackFrameDescriptorImpl stackDescriptor = (StackFrameDescriptorImpl)getNode().getDescriptor();
447         final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy();
448
449         final DebuggerContextImpl debuggerContext = getDebuggerContext();
450         final EvaluationContextImpl evaluationContext = debuggerContext.createEvaluationContext();
451         if (!debuggerContext.isEvaluationPossible()) {
452           myChildren.add(myNodeManager.createNode(MessageDescriptor.EVALUATION_NOT_POSSIBLE, evaluationContext));
453         }
454
455         final Location location = frame.location();
456         LOG.assertTrue(location != null);
457
458         final ObjectReference thisObjectReference = frame.thisObject();
459         final NodeDescriptor descriptor;
460         if (thisObjectReference != null) {
461           descriptor = myNodeManager.getThisDescriptor(stackDescriptor, thisObjectReference);
462         }
463         else {
464           descriptor = myNodeManager.getStaticDescriptor(stackDescriptor, location.method().declaringType());
465         }
466         myChildren.add(myNodeManager.createNode(descriptor, evaluationContext));
467
468         final ClassRenderer classRenderer = NodeRendererSettings.getInstance().getClassRenderer();
469         if (classRenderer.SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES) {
470           if (thisObjectReference != null && evaluationContext.getDebugProcess().getVirtualMachineProxy().canGetSyntheticAttribute())  {
471             final ReferenceType thisRefType = thisObjectReference.referenceType();
472             if (thisRefType instanceof ClassType && thisRefType.equals(location.declaringType()) && thisRefType.name().contains("$")) { // makes sense for nested classes only
473               final ClassType clsType = (ClassType)thisRefType;
474               for (Field field : clsType.fields()) {
475                 if (DebuggerUtils.isSynthetic(field) && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) {
476                   final FieldDescriptorImpl fieldDescriptor = myNodeManager.getFieldDescriptor(stackDescriptor, thisObjectReference, field);
477                   myChildren.add(myNodeManager.createNode(fieldDescriptor, evaluationContext));
478                 }
479               }
480             }
481           }
482         }
483
484         try {
485           buildVariables(stackDescriptor, evaluationContext);
486           if (XDebuggerSettingsManager.getInstance().getDataViewSettings().isSortValues()) {
487             Collections.sort(myChildren, NodeManagerImpl.getNodeComparator());
488           }
489         }
490         catch (EvaluateException e) {
491           myChildren.add(myNodeManager.createMessageNode(new MessageDescriptor(e.getMessage())));
492         }
493         // add last method return value if any
494         final Pair<Method, Value> methodValuePair = debuggerContext.getDebugProcess().getLastExecutedMethod();
495         if (methodValuePair != null) {
496           ValueDescriptorImpl returnValueDescriptor = myNodeManager.getMethodReturnValueDescriptor(stackDescriptor, methodValuePair.getFirst(), methodValuePair.getSecond());
497           myChildren.add(1, myNodeManager.createNode(returnValueDescriptor, evaluationContext));
498         }
499         // add context exceptions
500         for (Pair<Breakpoint, Event> pair : DebuggerUtilsEx.getEventDescriptors(getSuspendContext())) {
501           final Event debugEvent = pair.getSecond();
502           if (debugEvent instanceof ExceptionEvent) {
503             final ObjectReference exception = ((ExceptionEvent)debugEvent).exception();
504             if (exception != null) {
505               final ValueDescriptorImpl exceptionDescriptor = myNodeManager.getThrownExceptionObjectDescriptor(stackDescriptor, exception);
506               final DebuggerTreeNodeImpl exceptionNode = myNodeManager.createNode(exceptionDescriptor, evaluationContext);
507               myChildren.add(1, exceptionNode);
508             }
509           }
510         }
511
512       }
513       catch (EvaluateException e) {
514         myChildren.clear();
515         myChildren.add(myNodeManager.createMessageNode(new MessageDescriptor(e.getMessage())));
516       }
517       catch (InvalidStackFrameException e) {
518         LOG.info(e);
519         myChildren.clear();
520         notifyCancelled();
521       }
522       catch (InternalException e) {
523         if (e.errorCode() == 35) {
524           myChildren.add(
525             myNodeManager.createMessageNode(new MessageDescriptor(DebuggerBundle.message("error.corrupt.debug.info", e.getMessage()))));
526         }
527         else {
528           throw e;
529         }
530       }
531
532       updateUI(true);
533     }
534
535     protected void buildVariables(final StackFrameDescriptorImpl stackDescriptor, final EvaluationContextImpl evaluationContext) throws EvaluateException {
536       final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy();
537       for (final LocalVariableProxyImpl local : frame.visibleVariables()) {
538         final LocalVariableDescriptorImpl localVariableDescriptor = myNodeManager.getLocalVariableDescriptor(stackDescriptor, local);
539         final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(localVariableDescriptor, evaluationContext);
540         myChildren.add(variableNode);
541       }
542     }
543   }
544
545   private class BuildValueNodeCommand extends BuildNodeCommand implements ChildrenBuilder {
546     public BuildValueNodeCommand(DebuggerTreeNodeImpl node) {
547       super(node);
548     }
549
550     @Override
551     public void threadAction() {
552       final DebuggerTreeNodeImpl node = getNode();
553       ValueDescriptorImpl descriptor = (ValueDescriptorImpl)node.getDescriptor();
554       try {
555         final NodeRenderer renderer = descriptor.getRenderer(getSuspendContext().getDebugProcess());
556         renderer.buildChildren(descriptor.getValue(), this, getDebuggerContext().createEvaluationContext());
557       }
558       catch (ObjectCollectedException e) {
559         final String message = e.getMessage();
560         DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
561           @Override
562           public void run() {
563             node.removeAllChildren();
564             node.add(getNodeFactory().createMessageNode(
565               new MessageDescriptor(DebuggerBundle.message("error.cannot.build.node.children.object.collected", message)))
566             );
567             node.childrenChanged(false);
568           }
569         });
570       }
571     }
572
573     @Override
574     public NodeManagerImpl getNodeManager() {
575
576       return myNodeManager;
577     }
578
579     @Override
580     public NodeManagerImpl getDescriptorManager() {
581       return myNodeManager;
582     }
583
584     @Override
585     public ValueDescriptorImpl getParentDescriptor() {
586       return (ValueDescriptorImpl)getNode().getDescriptor();
587     }
588
589     @Override
590     public void setRemaining(int remaining) {}
591
592     @Override
593     public void initChildrenArrayRenderer(ArrayRenderer renderer) {}
594
595     @Override
596     public void setChildren(final List<DebuggerTreeNode> children) {
597       for (DebuggerTreeNode child : children) {
598         if (child instanceof DebuggerTreeNodeImpl) {
599           myChildren.add(((DebuggerTreeNodeImpl)child));
600         }
601       }
602       updateUI(false);
603     }
604   }
605
606   private class BuildStaticNodeCommand extends BuildNodeCommand {
607     public BuildStaticNodeCommand(DebuggerTreeNodeImpl node) {
608       super(node);
609     }
610
611     @Override
612     public void threadAction() {
613       final StaticDescriptorImpl sd = (StaticDescriptorImpl)getNode().getDescriptor();
614       final ReferenceType refType = sd.getType();
615       List<Field> fields = refType.allFields();
616       for (Field field : fields) {
617         if (field.isStatic()) {
618           final FieldDescriptorImpl fieldDescriptor = myNodeManager.getFieldDescriptor(sd, null, field);
619           final EvaluationContextImpl evaluationContext = getDebuggerContext().createEvaluationContext();
620           final DebuggerTreeNodeImpl node = myNodeManager.createNode(fieldDescriptor, evaluationContext);
621           myChildren.add(node);
622         }
623       }
624
625       updateUI(true);
626     }
627   }
628
629   private class BuildThreadCommand extends BuildNodeCommand {
630     public BuildThreadCommand(DebuggerTreeNodeImpl threadNode) {
631       super(threadNode);
632     }
633
634     @Override
635     public void threadAction() {
636       ThreadDescriptorImpl threadDescriptor = ((ThreadDescriptorImpl)getNode().getDescriptor());
637       ThreadReferenceProxyImpl threadProxy = threadDescriptor.getThreadReference();
638       if (!threadProxy.isCollected() && getDebuggerContext().getDebugProcess().getSuspendManager().isSuspended(threadProxy)) {
639         int status = threadProxy.status();
640         if (!(status == ThreadReference.THREAD_STATUS_UNKNOWN) &&
641             !(status == ThreadReference.THREAD_STATUS_NOT_STARTED) &&
642             !(status == ThreadReference.THREAD_STATUS_ZOMBIE)) {
643           try {
644             for (StackFrameProxyImpl stackFrame : threadProxy.frames()) {
645               //Method method = stackFrame.location().method();
646               //ToDo :check whether is synthetic if (shouldDisplay(method)) {
647               myChildren.add(myNodeManager.createNode(myNodeManager.getStackFrameDescriptor(threadDescriptor, stackFrame),
648                                                       getDebuggerContext().createEvaluationContext()));
649             }
650           }
651           catch (EvaluateException e) {
652             myChildren.clear();
653             myChildren.add(myNodeManager.createMessageNode(e.getMessage()));
654             LOG.debug(e);
655             //LOG.assertTrue(false);
656             // if we pause during evaluation of this method the exception is thrown
657             //  private static void longMethod(){
658             //    try {
659             //      Thread.sleep(100000);
660             //    } catch (InterruptedException e) {
661             //      e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
662             //    }
663             //  }
664           }
665         }
666       }
667       updateUI(true);
668     }
669   }
670
671   private class BuildThreadGroupCommand extends DebuggerCommandImpl {
672     private final DebuggerTreeNodeImpl myNode;
673     protected final List<DebuggerTreeNodeImpl> myChildren = new LinkedList<DebuggerTreeNodeImpl>();
674
675     public BuildThreadGroupCommand(DebuggerTreeNodeImpl node) {
676       myNode = node;
677     }
678
679     @Override
680     protected void action() throws Exception {
681       ThreadGroupDescriptorImpl groupDescriptor = (ThreadGroupDescriptorImpl)myNode.getDescriptor();
682       ThreadGroupReferenceProxyImpl threadGroup = groupDescriptor.getThreadGroupReference();
683
684       List<ThreadReferenceProxyImpl> threads = new ArrayList<ThreadReferenceProxyImpl>(threadGroup.threads());
685       Collections.sort(threads, ThreadReferenceProxyImpl.ourComparator);
686
687       final DebuggerContextImpl debuggerContext = getDebuggerContext();
688       final SuspendContextImpl suspendContext = debuggerContext.getSuspendContext();
689       final EvaluationContextImpl evaluationContext = suspendContext != null && !suspendContext.isResumed()? debuggerContext.createEvaluationContext() : null;
690
691       boolean showCurrent = ThreadsViewSettings.getInstance().SHOW_CURRENT_THREAD;
692
693       for (final ThreadGroupReferenceProxyImpl group : threadGroup.threadGroups()) {
694         if (group != null) {
695           DebuggerTreeNodeImpl threadNode =
696             myNodeManager.createNode(myNodeManager.getThreadGroupDescriptor(groupDescriptor, group), evaluationContext);
697
698           if (showCurrent && ((ThreadGroupDescriptorImpl)threadNode.getDescriptor()).isCurrent()) {
699             myChildren.add(0, threadNode);
700           }
701           else {
702             myChildren.add(threadNode);
703           }
704         }
705       }
706
707       ArrayList<DebuggerTreeNodeImpl> threadNodes = new ArrayList<DebuggerTreeNodeImpl>();
708
709       for (ThreadReferenceProxyImpl thread : threads) {
710         if (thread != null) {
711           final DebuggerTreeNodeImpl threadNode = myNodeManager.createNode(myNodeManager.getThreadDescriptor(groupDescriptor, thread), evaluationContext);
712           if (showCurrent && ((ThreadDescriptorImpl)threadNode.getDescriptor()).isCurrent()) {
713             threadNodes.add(0, threadNode);
714           }
715           else {
716             threadNodes.add(threadNode);
717           }
718         }
719       }
720
721       myChildren.addAll(threadNodes);
722
723       updateUI(true);
724     }
725
726     protected void updateUI(final boolean scrollToVisible) {
727       DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
728         @Override
729         public void run() {
730           myNode.removeAllChildren();
731           for (DebuggerTreeNodeImpl debuggerTreeNode : myChildren) {
732             myNode.add(debuggerTreeNode);
733           }
734           myNode.childrenChanged(scrollToVisible);
735         }
736       });
737     }
738   }
739
740   public void hideTooltip() {
741     myTipManager.hideTooltip();
742   }
743 }