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