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 java.io.*;
38 import java.awt.*;
39 import java.awt.event.*;
40 import javax.swing.*;
41 
42 import com.sun.jdi.*;
43 import com.sun.jdi.request.*;
44 
45 import com.sun.tools.example.debug.bdi.*;
46 
47 public class SourceTool extends JPanel {
48 
49     private static final long serialVersionUID = -5461299294186395257L;
50 
51     private Environment env;
52 
53     private ExecutionManager runtime;
54     private ContextManager context;
55     private SourceManager sourceManager;
56 
57     private JList list;
58     private ListModel sourceModel;
59 
60     // Information on source file that is on display, or failed to be
61     // displayed due to inaccessible source.  Used to update display
62     // when sourcepath is changed.
63 
64     private String sourceName;          // relative path name, if showSourceFile
65     private Location sourceLocn;        // location, if showSourceForLocation
66     private CommandInterpreter interpreter;
67 
SourceTool(Environment env)68     public SourceTool(Environment env) {
69 
70         super(new BorderLayout());
71 
72         this.env = env;
73 
74         runtime = env.getExecutionManager();
75         sourceManager = env.getSourceManager();
76         this.context = env.getContextManager();
77         this.interpreter = new CommandInterpreter(env, true);
78 
79         sourceModel = new DefaultListModel();  // empty
80 
81         list = new JList(sourceModel);
82         list.setCellRenderer(new SourceLineRenderer());
83 
84         list.setPrototypeCellValue(SourceModel.prototypeCellValue);
85 
86         SourceToolListener listener = new SourceToolListener();
87         context.addContextListener(listener);
88         runtime.addSpecListener(listener);
89         sourceManager.addSourceListener(listener);
90 
91         MouseListener squeek = new STMouseListener();
92         list.addMouseListener(squeek);
93 
94         add(new JScrollPane(list));
95     }
96 
setTextFont(Font f)97     public void setTextFont(Font f) {
98         list.setFont(f);
99         list.setPrototypeCellValue(SourceModel.prototypeCellValue);
100     }
101 
102     private class SourceToolListener
103                implements ContextListener, SourceListener, SpecListener
104     {
105 
106         // ContextListener
107 
108         @Override
currentFrameChanged(CurrentFrameChangedEvent e)109         public void currentFrameChanged(CurrentFrameChangedEvent e) {
110             showSourceContext(e.getThread(), e.getIndex());
111         }
112 
113             // Clear source view.
114             //      sourceModel = new DefaultListModel();  // empty
115 
116         // SourceListener
117 
118         @Override
sourcepathChanged(SourcepathChangedEvent e)119         public void sourcepathChanged(SourcepathChangedEvent e) {
120             // Reload source view if its contents depend
121             // on the source path.
122             if (sourceName != null) {
123                 showSourceFile(sourceName);
124             } else if (sourceLocn != null) {
125                 showSourceForLocation(sourceLocn);
126             }
127         }
128 
129         // SpecListener
130 
131         @Override
breakpointSet(SpecEvent e)132         public void breakpointSet(SpecEvent e) {
133             breakpointResolved(e);
134         }
135 
136         @Override
breakpointDeferred(SpecEvent e)137         public void breakpointDeferred(SpecEvent e) { }
138 
139         @Override
breakpointDeleted(SpecEvent e)140         public void breakpointDeleted(SpecEvent e) {
141             BreakpointRequest req = (BreakpointRequest)e.getEventRequest();
142             Location loc = req.location();
143             if (loc != null) {
144                 try {
145                     SourceModel sm = sourceManager.sourceForLocation(loc);
146                     sm.showBreakpoint(loc.lineNumber(), false);
147                     showSourceForLocation(loc);
148                 } catch (Exception exc) {
149                 }
150             }
151         }
152 
153         @Override
breakpointResolved(SpecEvent e)154         public void breakpointResolved(SpecEvent e) {
155             BreakpointRequest req = (BreakpointRequest)e.getEventRequest();
156             Location loc = req.location();
157             try {
158                 SourceModel sm = sourceManager.sourceForLocation(loc);
159                 sm.showBreakpoint(loc.lineNumber(), true);
160                 showSourceForLocation(loc);
161             } catch (Exception exc) {
162             }
163         }
164 
165         @Override
breakpointError(SpecErrorEvent e)166         public void breakpointError(SpecErrorEvent e) {
167             breakpointDeleted(e);
168         }
169 
170         @Override
watchpointSet(SpecEvent e)171         public void watchpointSet(SpecEvent e) {
172         }
173         @Override
watchpointDeferred(SpecEvent e)174         public void watchpointDeferred(SpecEvent e) {
175         }
176         @Override
watchpointDeleted(SpecEvent e)177         public void watchpointDeleted(SpecEvent e) {
178         }
179         @Override
watchpointResolved(SpecEvent e)180         public void watchpointResolved(SpecEvent e) {
181         }
182         @Override
watchpointError(SpecErrorEvent e)183         public void watchpointError(SpecErrorEvent e) {
184         }
185 
186         @Override
exceptionInterceptSet(SpecEvent e)187         public void exceptionInterceptSet(SpecEvent e) {
188         }
189         @Override
exceptionInterceptDeferred(SpecEvent e)190         public void exceptionInterceptDeferred(SpecEvent e) {
191         }
192         @Override
exceptionInterceptDeleted(SpecEvent e)193         public void exceptionInterceptDeleted(SpecEvent e) {
194         }
195         @Override
exceptionInterceptResolved(SpecEvent e)196         public void exceptionInterceptResolved(SpecEvent e) {
197         }
198         @Override
exceptionInterceptError(SpecErrorEvent e)199         public void exceptionInterceptError(SpecErrorEvent e) {
200         }
201     }
202 
showSourceContext(ThreadReference thread, int index)203     private void showSourceContext(ThreadReference thread, int index) {
204         //### Should use ThreadInfo here.
205         StackFrame frame = null;
206         if (thread != null) {
207             try {
208                 frame = thread.frame(index);
209             } catch (IncompatibleThreadStateException e) {}
210         }
211         if (frame == null) {
212             return;
213         }
214         Location locn = frame.location();
215         /*****
216         if (!showSourceForLocation(locn)) {
217             env.notice("Could not display source for "
218                        + Utils.locationString(locn));
219         }
220         *****/
221         showSourceForLocation(locn);
222     }
223 
showSourceForLocation(Location locn)224     public boolean showSourceForLocation(Location locn) {
225         sourceName = null;
226         sourceLocn = locn;
227         int lineNo = locn.lineNumber();
228         if (lineNo != -1) {
229             SourceModel source = sourceManager.sourceForLocation(locn);
230             if (source != null) {
231                 showSourceAtLine(source, lineNo-1);
232                 return true;
233             }
234         }
235         // Here if we could not display source.
236         showSourceUnavailable();
237         return false;
238     }
239 
showSourceFile(String fileName)240     public boolean showSourceFile(String fileName) {
241         sourceLocn = null;
242         File file;
243         if (!fileName.startsWith(File.separator)) {
244             sourceName = fileName;
245             SearchPath sourcePath = sourceManager.getSourcePath();
246             file = sourcePath.resolve(fileName);
247             if (file == null) {
248                 //env.failure("Source not found on current source path.");
249                 showSourceUnavailable();
250                 return false;
251             }
252         } else {
253             sourceName = null;  // Absolute pathname does not depend on sourcepath.
254             file = new File(fileName);
255         }
256         SourceModel source = sourceManager.sourceForFile(file);
257         if (source != null) {
258             showSource(source);
259             return true;
260         }
261         showSourceUnavailable();
262         return false;
263     }
264 
showSource(SourceModel model)265     private void showSource(SourceModel model) {
266         setViewModel(model);
267     }
268 
showSourceAtLine(SourceModel model, int lineNo)269     private void showSourceAtLine(SourceModel model, int lineNo) {
270         setViewModel(model);
271         if (model.isActuallySource && (lineNo < model.getSize())) {
272             list.setSelectedIndex(lineNo);
273             if (lineNo+4 < model.getSize()) {
274                 list.ensureIndexIsVisible(lineNo+4);  // give some context
275             }
276             list.ensureIndexIsVisible(lineNo);
277         }
278     }
279 
showSourceUnavailable()280     private void showSourceUnavailable() {
281         SourceModel model = new SourceModel("[Source code is not available]");
282         setViewModel(model);
283     }
284 
setViewModel(SourceModel model)285     private void setViewModel(SourceModel model) {
286         if (model != sourceModel) {
287             // install new model
288             list.setModel(model);
289             sourceModel = model;
290         }
291     }
292 
293     private class SourceLineRenderer extends DefaultListCellRenderer {
294 
295         @Override
getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)296         public Component getListCellRendererComponent(JList list,
297                                                       Object value,
298                                                       int index,
299                                                       boolean isSelected,
300                                                       boolean cellHasFocus) {
301 
302             //### Should set background highlight and/or icon if breakpoint on this line.
303             // Configures "this"
304             super.getListCellRendererComponent(list, value, index,
305                                                isSelected, cellHasFocus);
306 
307             SourceModel.Line line = (SourceModel.Line)value;
308 
309             //### Tab expansion is now done when source file is read in,
310             //### to speed up display.  This costs a lot of space, slows
311             //### down source file loading, and has not been demonstrated
312             //### to yield an observable improvement in display performance.
313             //### Measurements may be appropriate here.
314             //String sourceLine = expandTabs((String)value);
315             setText(line.text);
316             if (line.hasBreakpoint) {
317                 setIcon(Icons.stopSignIcon);
318             } else if (line.isExecutable()) {
319                 setIcon(Icons.execIcon);
320             } else {
321                 setIcon(Icons.blankIcon);
322             }
323 
324 
325             return this;
326         }
327 
328         @Override
getPreferredSize()329         public Dimension getPreferredSize() {
330             Dimension dim = super.getPreferredSize();
331             return new Dimension(dim.width, dim.height-5);
332         }
333 
334     }
335 
336     private class STMouseListener extends MouseAdapter implements MouseListener {
337         @Override
mousePressed(MouseEvent e)338         public void mousePressed(MouseEvent e) {
339             if (e.isPopupTrigger()) {
340                 showPopupMenu((Component)e.getSource(),
341                               e.getX(), e.getY());
342             }
343         }
344 
345         @Override
mouseReleased(MouseEvent e)346         public void mouseReleased(MouseEvent e) {
347             if (e.isPopupTrigger()) {
348                 showPopupMenu((Component)e.getSource(),
349                               e.getX(), e.getY());
350             }
351         }
352 
showPopupMenu(Component invoker, int x, int y)353         private void showPopupMenu(Component invoker, int x, int y) {
354             JList list = (JList)invoker;
355             int ln = list.getSelectedIndex() + 1;
356             SourceModel.Line line =
357                 (SourceModel.Line)list.getSelectedValue();
358             JPopupMenu popup = new JPopupMenu();
359 
360             if (line == null) {
361                 popup.add(new JMenuItem("please select a line"));
362             } else if (line.isExecutable()) {
363                 String className = line.refType.name();
364                 if (line.hasBreakpoint()) {
365                     popup.add(commandItem("Clear Breakpoint",
366                                           "clear " + className +
367                                           ":" + ln));
368                 } else {
369                     popup.add(commandItem("Set Breakpoint",
370                                           "stop at " + className +
371                                           ":" + ln));
372                 }
373             } else {
374                 popup.add(new JMenuItem("not an executable line"));
375             }
376 
377             popup.show(invoker,
378                        x + popup.getWidth()/2, y + popup.getHeight()/2);
379         }
380 
commandItem(String label, final String cmd)381         private JMenuItem commandItem(String label, final String cmd) {
382             JMenuItem item = new JMenuItem(label);
383             item.addActionListener(new ActionListener() {
384                 @Override
385                 public void actionPerformed(ActionEvent e) {
386                     interpreter.executeCommand(cmd);
387                 }
388             });
389             return item;
390         }
391     }
392 }
393