1 /*
   2  * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * This source code is provided to illustrate the usage of a given feature
  28  * or technique and has been deliberately simplified. Additional steps
  29  * required for a production-quality application, such as security checks,
  30  * input validation and proper error handling, might not be present in
  31  * this sample code.
  32  */
  33 
  34 
  35 package com.sun.tools.example.debug.gui;
  36 
  37 import javax.swing.*;
  38 import javax.swing.event.*;
  39 import java.awt.*;
  40 import com.sun.jdi.*;
  41 import com.sun.tools.example.debug.bdi.*;
  42 
  43 public class StackTraceTool extends JPanel {
  44 
  45     private static final long serialVersionUID = 9140041989427965718L;
  46 
  47     private Environment env;
  48 
  49     private ExecutionManager runtime;
  50     private ContextManager context;
  51 
  52     private ThreadInfo tinfo;
  53 
  54     private JList list;
  55     private ListModel stackModel;
  56 
  57     public StackTraceTool(Environment env) {
  58 
  59         super(new BorderLayout());
  60 
  61         this.env = env;
  62         this.runtime = env.getExecutionManager();
  63         this.context = env.getContextManager();
  64 
  65         stackModel = new DefaultListModel();  // empty
  66 
  67         list = new JList(stackModel);
  68         list.setCellRenderer(new StackFrameRenderer());
  69 
  70         JScrollPane listView = new JScrollPane(list);
  71         add(listView);
  72 
  73         // Create listener.
  74         StackTraceToolListener listener = new StackTraceToolListener();
  75         context.addContextListener(listener);
  76         list.addListSelectionListener(listener);
  77 
  78         //### remove listeners on exit!
  79     }
  80 
  81     private class StackTraceToolListener
  82         implements ContextListener, ListSelectionListener
  83     {
  84 
  85         // ContextListener
  86 
  87         // If the user selects a new current frame, display it in
  88         // this view.
  89 
  90         //### I suspect we handle the case badly that the VM is not interrupted.
  91 
  92         @Override
  93         public void currentFrameChanged(CurrentFrameChangedEvent e) {
  94             // If the current frame of the thread appearing in this
  95             // view is changed, move the selection to track it.
  96             int frameIndex = e.getIndex();
  97             ThreadInfo ti = e.getThreadInfo();
  98             if (e.getInvalidate() || tinfo != ti) {
  99                 tinfo = ti;
 100                 showStack(ti, frameIndex);
 101             } else {
 102                 if (frameIndex < stackModel.getSize()) {
 103                     list.setSelectedIndex(frameIndex);
 104                     list.ensureIndexIsVisible(frameIndex);
 105                 }
 106             }
 107         }
 108 
 109         // ListSelectionListener
 110 
 111         @Override
 112         public void valueChanged(ListSelectionEvent e) {
 113             int index = list.getSelectedIndex();
 114             if (index != -1) {
 115                 //### should use listener?
 116                 try {
 117                     context.setCurrentFrameIndex(index);
 118                 } catch (VMNotInterruptedException exc) {
 119                 }
 120             }
 121         }
 122     }
 123 
 124     private class StackFrameRenderer extends DefaultListCellRenderer {
 125 
 126         @Override
 127         public Component getListCellRendererComponent(JList list,
 128                                                       Object value,
 129                                                       int index,
 130                                                       boolean isSelected,
 131                                                       boolean cellHasFocus) {
 132 
 133             //### We should indicate the current thread independently of the
 134             //### selection, e.g., with an icon, because the user may change
 135             //### the selection graphically without affecting the current
 136             //### thread.
 137 
 138             super.getListCellRendererComponent(list, value, index,
 139                                                isSelected, cellHasFocus);
 140             if (value == null) {
 141                 this.setText("<unavailable>");
 142             } else {
 143                 StackFrame frame = (StackFrame)value;
 144                 Location loc = frame.location();
 145                 Method meth = loc.method();
 146                 String methName =
 147                     meth.declaringType().name() + '.' + meth.name();
 148                 String position = "";
 149                 if (meth.isNative()) {
 150                     position = " (native method)";
 151                 } else if (loc.lineNumber() != -1) {
 152                     position = ":" + loc.lineNumber();
 153                 } else {
 154                     long pc = loc.codeIndex();
 155                     if (pc != -1) {
 156                         position = ", pc = " + pc;
 157                     }
 158                 }
 159                 // Indices are presented to the user starting from 1, not 0.
 160                 this.setText("[" + (index+1) +"] " + methName + position);
 161             }
 162             return this;
 163         }
 164     }
 165 
 166     // Point this view at the given thread and frame.
 167 
 168     private void showStack(ThreadInfo tinfo, int selectFrame) {
 169         StackTraceListModel model = new StackTraceListModel(tinfo);
 170         stackModel = model;
 171         list.setModel(stackModel);
 172         list.setSelectedIndex(selectFrame);
 173         list.ensureIndexIsVisible(selectFrame);
 174     }
 175 
 176     private static class StackTraceListModel extends AbstractListModel {
 177 
 178         private final ThreadInfo tinfo;
 179 
 180         public StackTraceListModel(ThreadInfo tinfo) {
 181             this.tinfo = tinfo;
 182         }
 183 
 184         @Override
 185         public Object getElementAt(int index) {
 186             try {
 187                 return tinfo == null? null : tinfo.getFrame(index);
 188             } catch (VMNotInterruptedException e) {
 189                 //### Is this the right way to handle this?
 190                 //### Would happen if user scrolled stack trace
 191                 //### while not interrupted -- should probably
 192                 //### block user interaction in this case.
 193                 return null;
 194             }
 195         }
 196 
 197         @Override
 198         public int getSize() {
 199             try {
 200                 return tinfo == null? 1 : tinfo.getFrameCount();
 201             } catch (VMNotInterruptedException e) {
 202                 //### Is this the right way to handle this?
 203                 return 0;
 204             }
 205         }
 206     }
 207 }