1*cdf0e10cSrcweir import javax.swing.JFrame; 2*cdf0e10cSrcweir import javax.swing.JTextArea; 3*cdf0e10cSrcweir import javax.swing.JPanel; 4*cdf0e10cSrcweir import javax.swing.JScrollPane; 5*cdf0e10cSrcweir import javax.swing.JButton; 6*cdf0e10cSrcweir import javax.swing.JComponent; 7*cdf0e10cSrcweir import javax.swing.JFileChooser; 8*cdf0e10cSrcweir import javax.swing.JOptionPane; 9*cdf0e10cSrcweir import javax.swing.text.Document; 10*cdf0e10cSrcweir import javax.swing.event.DocumentListener; 11*cdf0e10cSrcweir import javax.swing.event.DocumentEvent; 12*cdf0e10cSrcweir 13*cdf0e10cSrcweir import java.awt.FlowLayout; 14*cdf0e10cSrcweir import java.awt.Graphics; 15*cdf0e10cSrcweir import java.awt.Color; 16*cdf0e10cSrcweir import java.awt.Font; 17*cdf0e10cSrcweir import java.awt.FontMetrics; 18*cdf0e10cSrcweir import java.awt.Polygon; 19*cdf0e10cSrcweir import java.awt.Rectangle; 20*cdf0e10cSrcweir import java.awt.Dimension; 21*cdf0e10cSrcweir import java.awt.event.ActionListener; 22*cdf0e10cSrcweir import java.awt.event.ActionEvent; 23*cdf0e10cSrcweir 24*cdf0e10cSrcweir import java.io.File; 25*cdf0e10cSrcweir import java.io.InputStream; 26*cdf0e10cSrcweir import java.io.FileInputStream; 27*cdf0e10cSrcweir import java.io.FileOutputStream; 28*cdf0e10cSrcweir import java.io.IOException; 29*cdf0e10cSrcweir 30*cdf0e10cSrcweir import drafts.com.sun.star.script.framework.runtime.XScriptContext; 31*cdf0e10cSrcweir import bsh.Interpreter; 32*cdf0e10cSrcweir 33*cdf0e10cSrcweir public class OOBeanShellDebugger implements OOScriptDebugger, ActionListener, DocumentListener { 34*cdf0e10cSrcweir 35*cdf0e10cSrcweir private JFrame frame; 36*cdf0e10cSrcweir private JTextArea ta; 37*cdf0e10cSrcweir private GlyphGutter gg; 38*cdf0e10cSrcweir private XScriptContext context; 39*cdf0e10cSrcweir private int currentPosition = -1; 40*cdf0e10cSrcweir private int linecount; 41*cdf0e10cSrcweir private Interpreter sessionInterpreter; 42*cdf0e10cSrcweir private Thread execThread = null; 43*cdf0e10cSrcweir private String filename = null; 44*cdf0e10cSrcweir 45*cdf0e10cSrcweir /* Entry point for script execution */ 46*cdf0e10cSrcweir public void go(XScriptContext context, String filename) { 47*cdf0e10cSrcweir if (filename != null && filename != "") { 48*cdf0e10cSrcweir try { 49*cdf0e10cSrcweir FileInputStream fis = new FileInputStream(filename); 50*cdf0e10cSrcweir this.filename = filename; 51*cdf0e10cSrcweir go(context, fis); 52*cdf0e10cSrcweir } 53*cdf0e10cSrcweir catch (IOException ioe) { 54*cdf0e10cSrcweir JOptionPane.showMessageDialog(frame, 55*cdf0e10cSrcweir "Error loading file: " + ioe.getMessage(), 56*cdf0e10cSrcweir "Error", JOptionPane.ERROR_MESSAGE); 57*cdf0e10cSrcweir } 58*cdf0e10cSrcweir } 59*cdf0e10cSrcweir } 60*cdf0e10cSrcweir 61*cdf0e10cSrcweir /* Entry point for script execution */ 62*cdf0e10cSrcweir public void go(XScriptContext context, InputStream in) { 63*cdf0e10cSrcweir this.context = context; 64*cdf0e10cSrcweir initUI(); 65*cdf0e10cSrcweir 66*cdf0e10cSrcweir if (in != null) { 67*cdf0e10cSrcweir try { 68*cdf0e10cSrcweir loadFile(in); 69*cdf0e10cSrcweir } 70*cdf0e10cSrcweir catch (IOException ioe) { 71*cdf0e10cSrcweir JOptionPane.showMessageDialog(frame, 72*cdf0e10cSrcweir "Error loading stream: " + ioe.getMessage(), 73*cdf0e10cSrcweir "Error", JOptionPane.ERROR_MESSAGE); 74*cdf0e10cSrcweir } 75*cdf0e10cSrcweir } 76*cdf0e10cSrcweir } 77*cdf0e10cSrcweir 78*cdf0e10cSrcweir public void loadFile(InputStream in) throws IOException { 79*cdf0e10cSrcweir 80*cdf0e10cSrcweir /* Remove ourselves as a DocumentListener while loading the file 81*cdf0e10cSrcweir so we don't get a storm of DocumentEvents during loading */ 82*cdf0e10cSrcweir ta.getDocument().removeDocumentListener(this); 83*cdf0e10cSrcweir 84*cdf0e10cSrcweir byte[] contents = new byte[1024]; 85*cdf0e10cSrcweir int len = 0, pos = 0; 86*cdf0e10cSrcweir 87*cdf0e10cSrcweir while ((len = in.read(contents, 0, 1024)) != -1) { 88*cdf0e10cSrcweir ta.insert(new String(contents, 0, len), pos); 89*cdf0e10cSrcweir pos += len; 90*cdf0e10cSrcweir } 91*cdf0e10cSrcweir 92*cdf0e10cSrcweir try { 93*cdf0e10cSrcweir in.close(); 94*cdf0e10cSrcweir } 95*cdf0e10cSrcweir catch (IOException ignore) { 96*cdf0e10cSrcweir } 97*cdf0e10cSrcweir 98*cdf0e10cSrcweir /* Update the GlyphGutter and add back the DocumentListener */ 99*cdf0e10cSrcweir gg.update(); 100*cdf0e10cSrcweir ta.getDocument().addDocumentListener(this); 101*cdf0e10cSrcweir } 102*cdf0e10cSrcweir 103*cdf0e10cSrcweir private void initUI() { 104*cdf0e10cSrcweir frame = new JFrame("BeanShell Debug Window"); 105*cdf0e10cSrcweir ta = new JTextArea(); 106*cdf0e10cSrcweir ta.setRows(15); 107*cdf0e10cSrcweir ta.setColumns(40); 108*cdf0e10cSrcweir ta.setLineWrap(false); 109*cdf0e10cSrcweir linecount = ta.getLineCount(); 110*cdf0e10cSrcweir 111*cdf0e10cSrcweir gg = new GlyphGutter(this); 112*cdf0e10cSrcweir 113*cdf0e10cSrcweir final JScrollPane sp = new JScrollPane(); 114*cdf0e10cSrcweir sp.setViewportView(ta); 115*cdf0e10cSrcweir sp.setRowHeaderView(gg); 116*cdf0e10cSrcweir 117*cdf0e10cSrcweir ta.getDocument().addDocumentListener(this); 118*cdf0e10cSrcweir String[] labels = {"Run", "Clear", "Save", "Close"}; 119*cdf0e10cSrcweir JPanel p = new JPanel(); 120*cdf0e10cSrcweir p.setLayout(new FlowLayout()); 121*cdf0e10cSrcweir 122*cdf0e10cSrcweir for (int i = 0; i < labels.length; i++) { 123*cdf0e10cSrcweir JButton b = new JButton(labels[i]); 124*cdf0e10cSrcweir b.addActionListener(this); 125*cdf0e10cSrcweir p.add(b); 126*cdf0e10cSrcweir 127*cdf0e10cSrcweir if (labels[i].equals("Save") && filename == null) { 128*cdf0e10cSrcweir b.setEnabled(false); 129*cdf0e10cSrcweir } 130*cdf0e10cSrcweir } 131*cdf0e10cSrcweir 132*cdf0e10cSrcweir frame.getContentPane().add(sp, "Center"); 133*cdf0e10cSrcweir frame.getContentPane().add(p, "South"); 134*cdf0e10cSrcweir frame.pack(); 135*cdf0e10cSrcweir frame.show(); 136*cdf0e10cSrcweir } 137*cdf0e10cSrcweir 138*cdf0e10cSrcweir /* Implementation of DocumentListener interface */ 139*cdf0e10cSrcweir public void insertUpdate(DocumentEvent e) { 140*cdf0e10cSrcweir doChanged(e); 141*cdf0e10cSrcweir } 142*cdf0e10cSrcweir 143*cdf0e10cSrcweir public void removeUpdate(DocumentEvent e) { 144*cdf0e10cSrcweir doChanged(e); 145*cdf0e10cSrcweir } 146*cdf0e10cSrcweir 147*cdf0e10cSrcweir public void changedUpdate(DocumentEvent e) { 148*cdf0e10cSrcweir doChanged(e); 149*cdf0e10cSrcweir } 150*cdf0e10cSrcweir 151*cdf0e10cSrcweir /* If the number of lines in the JTextArea has changed then update the 152*cdf0e10cSrcweir GlyphGutter */ 153*cdf0e10cSrcweir public void doChanged(DocumentEvent e) { 154*cdf0e10cSrcweir if (linecount != ta.getLineCount()) { 155*cdf0e10cSrcweir gg.update(); 156*cdf0e10cSrcweir linecount = ta.getLineCount(); 157*cdf0e10cSrcweir } 158*cdf0e10cSrcweir } 159*cdf0e10cSrcweir 160*cdf0e10cSrcweir private void startExecution() { 161*cdf0e10cSrcweir execThread = new Thread() { 162*cdf0e10cSrcweir public void run() { 163*cdf0e10cSrcweir Interpreter interpreter = new Interpreter(); 164*cdf0e10cSrcweir interpreter.getNameSpace().clear(); 165*cdf0e10cSrcweir 166*cdf0e10cSrcweir // reset position and repaint gutter so no red arrow appears 167*cdf0e10cSrcweir currentPosition = -1; 168*cdf0e10cSrcweir gg.repaint(); 169*cdf0e10cSrcweir 170*cdf0e10cSrcweir try { 171*cdf0e10cSrcweir interpreter.set("context", context); 172*cdf0e10cSrcweir interpreter.eval(ta.getText()); 173*cdf0e10cSrcweir } 174*cdf0e10cSrcweir catch (bsh.EvalError err) { 175*cdf0e10cSrcweir currentPosition = err.getErrorLineNumber() - 1; 176*cdf0e10cSrcweir try { 177*cdf0e10cSrcweir // scroll to line of the error 178*cdf0e10cSrcweir int line = ta.getLineStartOffset(currentPosition); 179*cdf0e10cSrcweir Rectangle rect = ta.modelToView(line); 180*cdf0e10cSrcweir ta.scrollRectToVisible(rect); 181*cdf0e10cSrcweir } 182*cdf0e10cSrcweir catch (Exception e) { 183*cdf0e10cSrcweir // couldn't scroll to line, do nothing 184*cdf0e10cSrcweir } 185*cdf0e10cSrcweir gg.repaint(); 186*cdf0e10cSrcweir 187*cdf0e10cSrcweir JOptionPane.showMessageDialog(frame, "Error at line " + 188*cdf0e10cSrcweir String.valueOf(err.getErrorLineNumber()) + 189*cdf0e10cSrcweir "\n\n: " + err.getErrorText(), 190*cdf0e10cSrcweir "Error", JOptionPane.ERROR_MESSAGE); 191*cdf0e10cSrcweir } 192*cdf0e10cSrcweir catch (Exception e) { 193*cdf0e10cSrcweir JOptionPane.showMessageDialog(frame, 194*cdf0e10cSrcweir "Error: " + e.getMessage(), 195*cdf0e10cSrcweir "Error", JOptionPane.ERROR_MESSAGE); 196*cdf0e10cSrcweir } 197*cdf0e10cSrcweir } 198*cdf0e10cSrcweir }; 199*cdf0e10cSrcweir execThread.start(); 200*cdf0e10cSrcweir } 201*cdf0e10cSrcweir 202*cdf0e10cSrcweir private void promptForSaveName() { 203*cdf0e10cSrcweir JFileChooser chooser = new JFileChooser(); 204*cdf0e10cSrcweir chooser.setFileFilter(new javax.swing.filechooser.FileFilter() { 205*cdf0e10cSrcweir public boolean accept(File f) { 206*cdf0e10cSrcweir if (f.isDirectory() || f.getName().endsWith(".bsh")) { 207*cdf0e10cSrcweir return true; 208*cdf0e10cSrcweir } 209*cdf0e10cSrcweir return false; 210*cdf0e10cSrcweir } 211*cdf0e10cSrcweir 212*cdf0e10cSrcweir public String getDescription() { 213*cdf0e10cSrcweir return ("BeanShell files: *.bsh"); 214*cdf0e10cSrcweir } 215*cdf0e10cSrcweir }); 216*cdf0e10cSrcweir 217*cdf0e10cSrcweir int ret = chooser.showSaveDialog(frame); 218*cdf0e10cSrcweir 219*cdf0e10cSrcweir if (ret == JFileChooser.APPROVE_OPTION) { 220*cdf0e10cSrcweir filename = chooser.getSelectedFile().getAbsolutePath(); 221*cdf0e10cSrcweir if (!filename.endsWith(".bsh")) { 222*cdf0e10cSrcweir filename += ".bsh"; 223*cdf0e10cSrcweir } 224*cdf0e10cSrcweir } 225*cdf0e10cSrcweir 226*cdf0e10cSrcweir } 227*cdf0e10cSrcweir 228*cdf0e10cSrcweir private void saveTextArea() { 229*cdf0e10cSrcweir if (filename == null) { 230*cdf0e10cSrcweir promptForSaveName(); 231*cdf0e10cSrcweir } 232*cdf0e10cSrcweir 233*cdf0e10cSrcweir FileOutputStream fos = null; 234*cdf0e10cSrcweir if (filename != null) { 235*cdf0e10cSrcweir try { 236*cdf0e10cSrcweir File f = new File(filename); 237*cdf0e10cSrcweir fos = new FileOutputStream(f); 238*cdf0e10cSrcweir String s = ta.getText(); 239*cdf0e10cSrcweir fos.write(s.getBytes(), 0, s.length()); 240*cdf0e10cSrcweir } 241*cdf0e10cSrcweir catch (IOException ioe) { 242*cdf0e10cSrcweir JOptionPane.showMessageDialog(frame, 243*cdf0e10cSrcweir "Error saving file: " + ioe.getMessage(), 244*cdf0e10cSrcweir "Error", JOptionPane.ERROR_MESSAGE); 245*cdf0e10cSrcweir } 246*cdf0e10cSrcweir finally { 247*cdf0e10cSrcweir if (fos != null) { 248*cdf0e10cSrcweir try { 249*cdf0e10cSrcweir fos.close(); 250*cdf0e10cSrcweir } 251*cdf0e10cSrcweir catch (IOException ignore) { 252*cdf0e10cSrcweir } 253*cdf0e10cSrcweir } 254*cdf0e10cSrcweir } 255*cdf0e10cSrcweir } 256*cdf0e10cSrcweir } 257*cdf0e10cSrcweir 258*cdf0e10cSrcweir public void actionPerformed(ActionEvent e) { 259*cdf0e10cSrcweir if (e.getActionCommand().equals("Run")) { 260*cdf0e10cSrcweir startExecution(); 261*cdf0e10cSrcweir } 262*cdf0e10cSrcweir else if (e.getActionCommand().equals("Close")) { 263*cdf0e10cSrcweir frame.dispose(); 264*cdf0e10cSrcweir } 265*cdf0e10cSrcweir else if (e.getActionCommand().equals("Save")) { 266*cdf0e10cSrcweir saveTextArea(); 267*cdf0e10cSrcweir } 268*cdf0e10cSrcweir else if (e.getActionCommand().equals("Clear")) { 269*cdf0e10cSrcweir ta.setText(""); 270*cdf0e10cSrcweir } 271*cdf0e10cSrcweir } 272*cdf0e10cSrcweir 273*cdf0e10cSrcweir public JTextArea getTextArea() { 274*cdf0e10cSrcweir return ta; 275*cdf0e10cSrcweir } 276*cdf0e10cSrcweir 277*cdf0e10cSrcweir public int getCurrentPosition() { 278*cdf0e10cSrcweir return currentPosition; 279*cdf0e10cSrcweir } 280*cdf0e10cSrcweir } 281*cdf0e10cSrcweir 282*cdf0e10cSrcweir class GlyphGutter extends JComponent { 283*cdf0e10cSrcweir 284*cdf0e10cSrcweir private OOBeanShellDebugger debugger; 285*cdf0e10cSrcweir private final String DUMMY_STRING = "99"; 286*cdf0e10cSrcweir 287*cdf0e10cSrcweir GlyphGutter(OOBeanShellDebugger debugger) { 288*cdf0e10cSrcweir this.debugger = debugger; 289*cdf0e10cSrcweir update(); 290*cdf0e10cSrcweir } 291*cdf0e10cSrcweir 292*cdf0e10cSrcweir public void update() { 293*cdf0e10cSrcweir JTextArea textArea = debugger.getTextArea(); 294*cdf0e10cSrcweir Font font = textArea.getFont(); 295*cdf0e10cSrcweir setFont(font); 296*cdf0e10cSrcweir 297*cdf0e10cSrcweir FontMetrics metrics = getFontMetrics(font); 298*cdf0e10cSrcweir int h = metrics.getHeight(); 299*cdf0e10cSrcweir int lineCount = textArea.getLineCount() + 1; 300*cdf0e10cSrcweir 301*cdf0e10cSrcweir String dummy = Integer.toString(lineCount); 302*cdf0e10cSrcweir if (dummy.length() < 2) { 303*cdf0e10cSrcweir dummy = DUMMY_STRING; 304*cdf0e10cSrcweir } 305*cdf0e10cSrcweir 306*cdf0e10cSrcweir Dimension d = new Dimension(); 307*cdf0e10cSrcweir d.width = metrics.stringWidth(dummy) + 16; 308*cdf0e10cSrcweir d.height = lineCount * h + 100; 309*cdf0e10cSrcweir setPreferredSize(d); 310*cdf0e10cSrcweir setSize(d); 311*cdf0e10cSrcweir } 312*cdf0e10cSrcweir 313*cdf0e10cSrcweir public void paintComponent(Graphics g) { 314*cdf0e10cSrcweir JTextArea textArea = debugger.getTextArea(); 315*cdf0e10cSrcweir 316*cdf0e10cSrcweir Font font = textArea.getFont(); 317*cdf0e10cSrcweir g.setFont(font); 318*cdf0e10cSrcweir 319*cdf0e10cSrcweir FontMetrics metrics = getFontMetrics(font); 320*cdf0e10cSrcweir Rectangle clip = g.getClipBounds(); 321*cdf0e10cSrcweir 322*cdf0e10cSrcweir g.setColor(getBackground()); 323*cdf0e10cSrcweir g.fillRect(clip.x, clip.y, clip.width, clip.height); 324*cdf0e10cSrcweir 325*cdf0e10cSrcweir int ascent = metrics.getMaxAscent(); 326*cdf0e10cSrcweir int h = metrics.getHeight(); 327*cdf0e10cSrcweir int lineCount = textArea.getLineCount() + 1; 328*cdf0e10cSrcweir 329*cdf0e10cSrcweir int startLine = clip.y / h; 330*cdf0e10cSrcweir int endLine = (clip.y + clip.height) / h + 1; 331*cdf0e10cSrcweir int width = getWidth(); 332*cdf0e10cSrcweir if (endLine > lineCount) { 333*cdf0e10cSrcweir endLine = lineCount; 334*cdf0e10cSrcweir } 335*cdf0e10cSrcweir 336*cdf0e10cSrcweir for (int i = startLine; i < endLine; i++) { 337*cdf0e10cSrcweir String text; 338*cdf0e10cSrcweir text = Integer.toString(i + 1) + " "; 339*cdf0e10cSrcweir int w = metrics.stringWidth(text); 340*cdf0e10cSrcweir int y = i * h; 341*cdf0e10cSrcweir g.setColor(Color.blue); 342*cdf0e10cSrcweir g.drawString(text, 0, y + ascent); 343*cdf0e10cSrcweir int x = width - ascent; 344*cdf0e10cSrcweir 345*cdf0e10cSrcweir // if currentPosition is not -1 then a red arrow will be drawn 346*cdf0e10cSrcweir if (i == debugger.getCurrentPosition()) { 347*cdf0e10cSrcweir drawArrow(g, ascent, x, y); 348*cdf0e10cSrcweir } 349*cdf0e10cSrcweir } 350*cdf0e10cSrcweir } 351*cdf0e10cSrcweir 352*cdf0e10cSrcweir private void drawArrow(Graphics g, int ascent, int x, int y) { 353*cdf0e10cSrcweir Polygon arrow = new Polygon(); 354*cdf0e10cSrcweir int dx = x; 355*cdf0e10cSrcweir y += ascent - 10; 356*cdf0e10cSrcweir int dy = y; 357*cdf0e10cSrcweir arrow.addPoint(dx, dy + 3); 358*cdf0e10cSrcweir arrow.addPoint(dx + 5, dy + 3); 359*cdf0e10cSrcweir for (x = dx + 5; x <= dx + 10; x++, y++) { 360*cdf0e10cSrcweir arrow.addPoint(x, y); 361*cdf0e10cSrcweir } 362*cdf0e10cSrcweir for (x = dx + 9; x >= dx + 5; x--, y++) { 363*cdf0e10cSrcweir arrow.addPoint(x, y); 364*cdf0e10cSrcweir } 365*cdf0e10cSrcweir arrow.addPoint(dx + 5, dy + 7); 366*cdf0e10cSrcweir arrow.addPoint(dx, dy + 7); 367*cdf0e10cSrcweir 368*cdf0e10cSrcweir g.setColor(Color.red); 369*cdf0e10cSrcweir g.fillPolygon(arrow); 370*cdf0e10cSrcweir g.setColor(Color.black); 371*cdf0e10cSrcweir g.drawPolygon(arrow); 372*cdf0e10cSrcweir } 373*cdf0e10cSrcweir }; 374*cdf0e10cSrcweir 375