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