bump product version to 5.0.4.1
[LibreOffice.git] / scripting / examples / java / debugger / OOBeanShellDebugger.java
blob68a508784a0103923c17e503629248e02a19ddd0
1 /*
2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 package org.libreoffice.example.java_scripts;
21 import javax.swing.JFrame;
22 import javax.swing.JTextArea;
23 import javax.swing.JPanel;
24 import javax.swing.JScrollPane;
25 import javax.swing.JButton;
26 import javax.swing.JComponent;
27 import javax.swing.JFileChooser;
28 import javax.swing.JOptionPane;
29 import javax.swing.text.Document;
30 import javax.swing.event.DocumentListener;
31 import javax.swing.event.DocumentEvent;
33 import java.awt.FlowLayout;
34 import java.awt.Graphics;
35 import java.awt.Color;
36 import java.awt.Font;
37 import java.awt.FontMetrics;
38 import java.awt.Polygon;
39 import java.awt.Rectangle;
40 import java.awt.Dimension;
41 import java.awt.event.ActionListener;
42 import java.awt.event.ActionEvent;
44 import java.io.File;
45 import java.io.InputStream;
46 import java.io.FileInputStream;
47 import java.io.FileOutputStream;
48 import java.io.IOException;
50 import com.sun.star.script.provider.XScriptContext;
51 import bsh.Interpreter;
53 public class OOBeanShellDebugger implements OOScriptDebugger, ActionListener,
54 DocumentListener {
56 private JFrame frame;
57 private JTextArea ta;
58 private GlyphGutter gg;
59 private XScriptContext context;
60 private int currentPosition = -1;
61 private int linecount;
62 private Interpreter sessionInterpreter;
63 private Thread execThread = null;
64 private String filename = null;
66 /* Entry point for script execution */
67 public void go(XScriptContext context, String filename) {
68 if (filename != null && filename != "") {
69 try {
70 FileInputStream fis = new FileInputStream(filename);
71 this.filename = filename;
72 go(context, fis);
73 } catch (IOException ioe) {
74 JOptionPane.showMessageDialog(frame,
75 "Error loading file: " + ioe.getMessage(),
76 "Error", JOptionPane.ERROR_MESSAGE);
81 /* Entry point for script execution */
82 public void go(XScriptContext context, InputStream in) {
83 this.context = context;
84 initUI();
86 if (in != null) {
87 try {
88 loadFile(in);
89 } catch (IOException ioe) {
90 JOptionPane.showMessageDialog(frame,
91 "Error loading stream: " + ioe.getMessage(),
92 "Error", JOptionPane.ERROR_MESSAGE);
97 public void loadFile(InputStream in) throws IOException {
99 /* Remove ourselves as a DocumentListener while loading the file
100 so we don't get a storm of DocumentEvents during loading */
101 ta.getDocument().removeDocumentListener(this);
103 byte[] contents = new byte[1024];
104 int len = 0, pos = 0;
106 while ((len = in.read(contents, 0, 1024)) != -1) {
107 ta.insert(new String(contents, 0, len), pos);
108 pos += len;
111 try {
112 in.close();
113 } catch (IOException ignore) {
116 /* Update the GlyphGutter and add back the DocumentListener */
117 gg.update();
118 ta.getDocument().addDocumentListener(this);
121 private void initUI() {
122 frame = new JFrame("BeanShell Debug Window");
123 ta = new JTextArea();
124 ta.setRows(15);
125 ta.setColumns(40);
126 ta.setLineWrap(false);
127 linecount = ta.getLineCount();
129 gg = new GlyphGutter(this);
131 final JScrollPane sp = new JScrollPane();
132 sp.setViewportView(ta);
133 sp.setRowHeaderView(gg);
135 ta.getDocument().addDocumentListener(this);
136 String[] labels = {"Run", "Clear", "Save", "Close"};
137 JPanel p = new JPanel();
138 p.setLayout(new FlowLayout());
140 for (int i = 0; i < labels.length; i++) {
141 JButton b = new JButton(labels[i]);
142 b.addActionListener(this);
143 p.add(b);
145 if (labels[i].equals("Save") && filename == null) {
146 b.setEnabled(false);
150 frame.getContentPane().add(sp, "Center");
151 frame.getContentPane().add(p, "South");
152 frame.pack();
153 frame.show();
156 /* Implementation of DocumentListener interface */
157 public void insertUpdate(DocumentEvent e) {
158 doChanged(e);
161 public void removeUpdate(DocumentEvent e) {
162 doChanged(e);
165 public void changedUpdate(DocumentEvent e) {
166 doChanged(e);
169 /* If the number of lines in the JTextArea has changed then update the
170 GlyphGutter */
171 public void doChanged(DocumentEvent e) {
172 if (linecount != ta.getLineCount()) {
173 gg.update();
174 linecount = ta.getLineCount();
178 private void startExecution() {
179 execThread = new Thread() {
180 public void run() {
181 Interpreter interpreter = new Interpreter();
182 interpreter.getNameSpace().clear();
184 // reset position and repaint gutter so no red arrow appears
185 currentPosition = -1;
186 gg.repaint();
188 try {
189 interpreter.set("context", context);
190 interpreter.eval(ta.getText());
191 } catch (bsh.EvalError err) {
192 currentPosition = err.getErrorLineNumber() - 1;
194 try {
195 // scroll to line of the error
196 int line = ta.getLineStartOffset(currentPosition);
197 Rectangle rect = ta.modelToView(line);
198 ta.scrollRectToVisible(rect);
199 } catch (Exception e) {
200 // couldn't scroll to line, do nothing
203 gg.repaint();
205 JOptionPane.showMessageDialog(frame, "Error at line " +
206 String.valueOf(err.getErrorLineNumber()) +
207 "\n\n: " + err.getErrorText(),
208 "Error", JOptionPane.ERROR_MESSAGE);
209 } catch (Exception e) {
210 JOptionPane.showMessageDialog(frame,
211 "Error: " + e.getMessage(),
212 "Error", JOptionPane.ERROR_MESSAGE);
216 execThread.start();
219 private void promptForSaveName() {
220 JFileChooser chooser = new JFileChooser();
221 chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {
222 public boolean accept(File f) {
223 if (f.isDirectory() || f.getName().endsWith(".bsh")) {
224 return true;
227 return false;
230 public String getDescription() {
231 return ("BeanShell files: *.bsh");
235 int ret = chooser.showSaveDialog(frame);
237 if (ret == JFileChooser.APPROVE_OPTION) {
238 filename = chooser.getSelectedFile().getAbsolutePath();
240 if (!filename.endsWith(".bsh")) {
241 filename += ".bsh";
247 private void saveTextArea() {
248 if (filename == null) {
249 promptForSaveName();
252 FileOutputStream fos = null;
254 if (filename != null) {
255 try {
256 File f = new File(filename);
257 fos = new FileOutputStream(f);
258 String s = ta.getText();
259 fos.write(s.getBytes(), 0, s.length());
260 } catch (IOException ioe) {
261 JOptionPane.showMessageDialog(frame,
262 "Error saving file: " + ioe.getMessage(),
263 "Error", JOptionPane.ERROR_MESSAGE);
264 } finally {
265 if (fos != null) {
266 try {
267 fos.close();
268 } catch (IOException ignore) {
275 public void actionPerformed(ActionEvent e) {
276 if (e.getActionCommand().equals("Run")) {
277 startExecution();
278 } else if (e.getActionCommand().equals("Close")) {
279 frame.dispose();
280 } else if (e.getActionCommand().equals("Save")) {
281 saveTextArea();
282 } else if (e.getActionCommand().equals("Clear")) {
283 ta.setText("");
287 public JTextArea getTextArea() {
288 return ta;
291 public int getCurrentPosition() {
292 return currentPosition;
296 class GlyphGutter extends JComponent {
298 private OOBeanShellDebugger debugger;
299 private final String DUMMY_STRING = "99";
301 GlyphGutter(OOBeanShellDebugger debugger) {
302 this.debugger = debugger;
303 update();
306 public void update() {
307 JTextArea textArea = debugger.getTextArea();
308 Font font = textArea.getFont();
309 setFont(font);
311 FontMetrics metrics = getFontMetrics(font);
312 int h = metrics.getHeight();
313 int lineCount = textArea.getLineCount() + 1;
315 String dummy = Integer.toString(lineCount);
317 if (dummy.length() < 2) {
318 dummy = DUMMY_STRING;
321 Dimension d = new Dimension();
322 d.width = metrics.stringWidth(dummy) + 16;
323 d.height = lineCount * h + 100;
324 setPreferredSize(d);
325 setSize(d);
328 public void paintComponent(Graphics g) {
329 JTextArea textArea = debugger.getTextArea();
331 Font font = textArea.getFont();
332 g.setFont(font);
334 FontMetrics metrics = getFontMetrics(font);
335 Rectangle clip = g.getClipBounds();
337 g.setColor(getBackground());
338 g.fillRect(clip.x, clip.y, clip.width, clip.height);
340 int ascent = metrics.getMaxAscent();
341 int h = metrics.getHeight();
342 int lineCount = textArea.getLineCount() + 1;
344 int startLine = clip.y / h;
345 int endLine = (clip.y + clip.height) / h + 1;
346 int width = getWidth();
348 if (endLine > lineCount) {
349 endLine = lineCount;
352 for (int i = startLine; i < endLine; i++) {
353 String text;
354 text = Integer.toString(i + 1) + " ";
355 int w = metrics.stringWidth(text);
356 int y = i * h;
357 g.setColor(Color.blue);
358 g.drawString(text, 0, y + ascent);
359 int x = width - ascent;
361 // if currentPosition is not -1 then a red arrow will be drawn
362 if (i == debugger.getCurrentPosition()) {
363 drawArrow(g, ascent, x, y);
368 private void drawArrow(Graphics g, int ascent, int x, int y) {
369 Polygon arrow = new Polygon();
370 int dx = x;
371 y += ascent - 10;
372 int dy = y;
373 arrow.addPoint(dx, dy + 3);
374 arrow.addPoint(dx + 5, dy + 3);
376 for (x = dx + 5; x <= dx + 10; x++, y++) {
377 arrow.addPoint(x, y);
380 for (x = dx + 9; x >= dx + 5; x--, y++) {
381 arrow.addPoint(x, y);
384 arrow.addPoint(dx + 5, dy + 7);
385 arrow.addPoint(dx, dy + 7);
387 g.setColor(Color.red);
388 g.fillPolygon(arrow);
389 g.setColor(Color.black);
390 g.drawPolygon(arrow);