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