xref: /AOO41X/main/scripting/examples/java/debugger/OOBeanShellDebugger.java (revision cd519653a6b6a9e2ff38774da567b1ae7cbeddbb)
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