001    package nl.cwi.sen1.tide.tool.stackviewer;
002    
003    //{{{ imports
004    
005    import java.awt.BorderLayout;
006    import java.awt.Dimension;
007    import java.awt.GridLayout;
008    import java.awt.event.ActionEvent;
009    import java.util.Iterator;
010    import java.util.Vector;
011    
012    import javax.swing.AbstractAction;
013    import javax.swing.Action;
014    import javax.swing.JLabel;
015    import javax.swing.JList;
016    import javax.swing.JPanel;
017    import javax.swing.JScrollPane;
018    import javax.swing.JSplitPane;
019    import javax.swing.JToolBar;
020    import javax.swing.ListSelectionModel;
021    import javax.swing.event.ListSelectionEvent;
022    import javax.swing.event.ListSelectionListener;
023    
024    import nl.cwi.sen1.tide.tool.ProcessTool;
025    import nl.cwi.sen1.tide.tool.ToolManager;
026    import nl.cwi.sen1.tide.tool.support.DebugAdapter;
027    import nl.cwi.sen1.tide.tool.support.DebugAdapterListener;
028    import nl.cwi.sen1.tide.tool.support.DebugProcess;
029    import nl.cwi.sen1.tide.tool.support.DebugProcessListener;
030    import nl.cwi.sen1.tide.tool.support.Expr;
031    import nl.cwi.sen1.tide.tool.support.Port;
032    import nl.cwi.sen1.tide.tool.support.ProcessStatusChangeListener;
033    import nl.cwi.sen1.tide.tool.support.Rule;
034    
035    //}}}
036    
037    public class StackViewer
038            extends ProcessTool
039            implements
040                    DebugProcessListener,
041                    ProcessStatusChangeListener,
042                    DebugAdapterListener,
043                    ListSelectionListener {
044            //{{{ Constants
045    
046            private static final String TAG_STACK_TRACE = "stack-trace";
047            private static final String TAG_STACK_UNWIND = "stack-unwind";
048    
049            //}}}
050    
051            //{{{ Attributes
052    
053            private JList trace;
054    
055            private JToolBar tools;
056            private JLabel frameName;
057            private JLabel frameDepth;
058            private JLabel frameLocation;
059            private JList frameVars;
060    
061            private String tag_stack_trace;
062            private String tag_stack_unwind;
063    
064            private Action unwind;
065            private Action viewSource;
066            private Action inspectVar;
067    
068            private DebugProcess process;
069            private Rule ruleStackTrace;
070            private Rule ruleStackUnwind;
071    
072            private StackFrame selectedFrame;
073    
074            //}}}
075    
076            //{{{ public StackViewer(final DebugProcess process, ToolManager manager)
077    
078            public StackViewer(ToolManager manager, final DebugProcess process) {
079                    super(manager, process);
080    
081                    //{{{ Build tags
082    
083                    tag_stack_trace = TAG_STACK_TRACE + "-" + getId();
084                    tag_stack_unwind = TAG_STACK_UNWIND + "-" + getId();
085    
086                    //}}}
087                    //{{{ Build actions
088    
089                    unwind = new AbstractAction("Unwind", loadIcon("unwind.gif")) {
090                            public void actionPerformed(ActionEvent event) {
091                                    if (selectedFrame == null) {
092                                            getManager().displayError("Select a stackframe.");
093                                    }
094                                    else if (process.isStopped()) {
095                                            process.requestRuleModification(
096                                                    ruleStackUnwind,
097                                                    Port.makeStep(),
098                                                    Expr.make(
099                                                            "higher-equal("
100                                                                    + selectedFrame.getDepth()
101                                                                    + ",stack-level)"),
102                                                    Expr.makeBreak(),
103                                                    true);
104    
105                                            process.requestResume();
106                                    }
107                            }
108                    };
109    
110                    viewSource =
111                            new AbstractAction("View Source", loadIcon("view-source.gif")) {
112                            public void actionPerformed(ActionEvent event) {
113                                    getManager().displayError("Not implemented yet!");
114                            }
115                    };
116    
117                    inspectVar =
118                            new AbstractAction("Inspect Variable", loadIcon("inspect-var.gif")) {
119                            public void actionPerformed(ActionEvent event) {
120                                    getManager().displayError("Not implemented yet!");
121                            }
122                    };
123    
124                    unwind.setEnabled(false);
125                    viewSource.setEnabled(false);
126                    inspectVar.setEnabled(false);
127    
128                    tools = new JToolBar();
129                    tools.add(unwind).setToolTipText("Unwind stack upto selected frame");
130                    tools.add(viewSource).setToolTipText(
131                            "View source associated with selected frame");
132                    tools.add(inspectVar).setToolTipText("Inspect selected variable");
133    
134                    //}}}
135                    //{{{ Build UI
136    
137                    setLayout(new BorderLayout());
138    
139                    trace = new JList();
140                    trace.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
141                    trace.setPreferredSize(new Dimension(120, 100));
142                    JPanel framePanel = new JPanel();
143    
144                    JSplitPane pane =
145                            new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, trace, framePanel);
146                    add("Center", pane);
147    
148                    trace.addListSelectionListener(this);
149    
150                    framePanel.setLayout(new BorderLayout());
151                    Dimension dim = new Dimension(100, 100);
152    
153                    framePanel.setMinimumSize(dim);
154                    framePanel.setPreferredSize(dim);
155                    trace.setMinimumSize(dim);
156    
157                    frameName = new JLabel("-");
158                    frameDepth = new JLabel("-");
159                    frameLocation = new JLabel("-");
160    
161                    JPanel frameLabels = new JPanel();
162                    frameLabels.setLayout(new BorderLayout());
163                    JPanel leftLabels = new JPanel();
164                    leftLabels.setLayout(new GridLayout(0, 1));
165                    leftLabels.add(new JLabel("Selected:"));
166                    leftLabels.add(new JLabel("Depth:"));
167                    leftLabels.add(new JLabel("Location:"));
168    
169                    JPanel rightLabels = new JPanel();
170                    rightLabels.setLayout(new GridLayout(0, 1));
171                    rightLabels.add(frameName);
172                    rightLabels.add(frameDepth);
173                    rightLabels.add(frameLocation);
174    
175                    frameLabels.add("South", tools);
176                    frameLabels.add("West", leftLabels);
177                    frameLabels.add("Center", rightLabels);
178    
179            frameVars = new JList();
180            JScrollPane scrollFrameVars = new JScrollPane(frameVars);
181    
182                    framePanel.add("North", frameLabels);
183                    framePanel.add("Center", scrollFrameVars);
184    
185                    //}}}
186    
187                    //{{{ Listen to process events
188    
189                    this.process = process;
190                    process.addDebugProcessListener(this);
191                    process.addProcessStatusChangeListener(this);
192    
193                    DebugAdapter adapter = process.getAdapter();
194                    adapter.addDebugAdapterListener(this);
195    
196                    //}}}
197                    //{{{ Create appropriate debugging events
198    
199                    process.requestRuleCreation(
200                            Port.makeStopped(),
201                            Expr.makeTrue(),
202                            Expr.makeStackTrace(),
203                            tag_stack_trace,
204                            true);
205    
206                    process.requestRuleCreation(
207                            Port.makeStep(),
208                            Expr.makeTrue(),
209                            Expr.makeBreak(),
210                            tag_stack_unwind,
211                            false);
212    
213                    process.requestEvaluation(Expr.makeStackTrace(), tag_stack_trace);
214    
215                    //}}}
216            }
217    
218            //}}}
219    
220            //{{{ public void processDestroyed(DebugAdapter adapter, DebugProcess
221            // proc)
222    
223            public void processDestroyed(DebugAdapter adapter, DebugProcess proc) {
224                    if (proc == process) {
225                            // Rules do not need to be removed!
226                            ruleStackTrace = null;
227                            ruleStackUnwind = null;
228                            destroy();
229                    }
230            }
231    
232            //}}}
233            //{{{ public void processCreated(DebugAdapter adapter, DebugProcess proc)
234    
235            public void processCreated(DebugAdapter adapter, DebugProcess proc) {}
236    
237            //}}}
238    
239            //{{{ public void displayStackTrace(Expr stackTrace)
240    
241            public void displayStackTrace(Expr stackTrace) {
242                    Vector<StackFrame> frames = new Vector<StackFrame>();
243    
244                    if (!stackTrace.isStackTrace()) {
245                            getManager().displayError("not a stacktrace: " + stackTrace);
246                            return;
247                    }
248    
249                    Iterator<Expr> iter = stackTrace.frameIterator();
250                    while (iter.hasNext()) {
251                            Expr frame = iter.next();
252    
253                            if (!frame.isStackFrame()) {
254                                    getManager().displayError("not a stackframe: " + frame);
255                                    continue;
256                            }
257                            int depth = frame.getFrameDepth();
258                            String name = frame.getFrameName();
259                            Expr location = frame.getFrameLocation();
260                            Expr variables = frame.getFrameVariables();
261                            StackFrame stackFrame =
262                                    new StackFrame(depth, name, location, variables);
263                            frames.addElement(stackFrame);
264                    }
265                    displayStackFrame(frames.firstElement());
266                    trace.setListData(frames);
267            }
268    
269            //}}}
270            //{{{ public void displayStackFrame(StackFrame frame)
271    
272            public void displayStackFrame(StackFrame frame) {
273                    selectedFrame = frame;
274    
275                    if (frame == null) {
276                            frameName.setText("-");
277                            frameDepth.setText("-");
278                            frameLocation.setText("-");
279                            unwind.setEnabled(false);
280                            viewSource.setEnabled(false);
281                            inspectVar.setEnabled(false);
282                            frameVars.removeAll();
283                    } else {
284                            String name = frame.getName();
285                            int depth = frame.getDepth();
286                            Expr location = frame.getLocation();
287                            frameName.setText(name);
288                            frameDepth.setText(String.valueOf(depth));
289                            frameLocation.setText(location.toString());
290                            unwind.setEnabled(true);
291                            viewSource.setEnabled(!location.isLocationUnknown());
292                            inspectVar.setEnabled(false);
293    
294                            Vector<Expr> variables = new Vector<Expr>();
295                            Iterator<Expr> iter = frame.variableIterator();
296                            while (iter.hasNext()) {
297                                    Expr var = iter.next();
298                                    variables.addElement(var);
299                            }
300                            frameVars.setListData(variables);
301                    }
302            }
303    
304            //}}}
305    
306            //{{{ public void ruleCreated(DebugProcess process, Rule rule)
307    
308            public void ruleCreated(DebugProcess process, Rule rule) {
309                    if (rule.getTag().equals(tag_stack_trace)) {
310                            ruleStackTrace = rule;
311                    } else if (rule.getTag().equals(tag_stack_unwind)) {
312                            ruleStackUnwind = rule;
313                    }
314            }
315    
316            //}}}
317            //{{{ public void ruleDeleted(DebugProcess process, Rule rule)
318    
319            public void ruleDeleted(DebugProcess process, Rule rule) {
320                    if (rule == ruleStackTrace) {
321                            ruleStackTrace = null;
322                    }
323    
324                    if (rule == ruleStackUnwind) {
325                            ruleStackUnwind = null;
326                    }
327            }
328    
329            //}}}
330            //{{{ public void ruleModified(DebugProcess process, Rule rule)
331    
332            public void ruleModified(DebugProcess process, Rule rule) {}
333    
334            //}}}
335            //{{{ public void ruleTriggered(DebugProcess process, Rule rule, Expr
336            // value)
337    
338            public void ruleTriggered(DebugProcess process, Rule rule, Expr value) {
339                    if (rule == ruleStackTrace) {
340                            displayStackTrace(value);
341                    }
342            }
343    
344            //}}}
345            //{{{ public void evaluationResult(process, expr, value, tag)
346    
347            public void evaluationResult(DebugProcess process, Expr expr, Expr value, String tag) {
348                    if (tag.equals(tag_stack_trace)) {
349                            displayStackTrace(value);
350                    } else if (tag.equals(tag_stack_unwind)) {}
351            }
352    
353            //}}}
354    
355            //{{{ public void processStatusChanged(DebugProcess process)
356    
357            public void processStatusChanged(DebugProcess process) {
358                    boolean stopped = process.isStopped();
359    
360                    unwind.setEnabled(stopped);
361    
362                    if (stopped) {
363                            if (ruleStackUnwind.isEnabled()) {
364                                    process.requestRuleEnabling(ruleStackUnwind, false);
365                            }
366                    }
367            }
368    
369            //}}}
370            //{{{ public void valueChanged(ListSelectionEvent evt)
371    
372            public void valueChanged(ListSelectionEvent evt) {
373                    if (evt.getSource() == trace) {
374                            displayStackFrame((StackFrame) trace.getSelectedValue());
375                    }
376            }
377    }
378    
379    class StackFrame {
380            private int depth;
381            private String name;
382            private Expr location;
383            private Expr vars;
384    
385            //{{{ public StackFrame(int depth, String name, Expr location, Expr vars)
386    
387            public StackFrame(int depth, String name, Expr location, Expr vars) {
388                    this.depth = depth;
389                    this.name = name;
390                    this.location = location;
391                    this.vars = vars;
392            }
393    
394            //}}}
395    
396            //{{{ public String toString()
397    
398            public String toString() {
399                    return String.valueOf(depth) + " " + name;
400            }
401    
402            //}}}
403            //{{{ public int getDepth()
404    
405            public int getDepth() {
406                    return depth;
407            }
408    
409            //}}}
410            //{{{ public String getName()
411    
412            public String getName() {
413                    return name;
414            }
415    
416            //}}}
417            //{{{ public Expr getLocation()
418    
419            public Expr getLocation() {
420                    return location;
421            }
422    
423            //}}}
424            //{{{ public Iterator variableIterator()
425    
426            public Iterator<Expr> variableIterator() {
427                    return vars.iterator();
428            }
429    
430            //}}}
431    }