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
;
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
;
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
,
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
!= "") {
70 FileInputStream fis
= new FileInputStream(filename
);
71 this.filename
= filename
;
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
;
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
);
113 } catch (IOException ignore
) {
116 /* Update the GlyphGutter and add back the DocumentListener */
118 ta
.getDocument().addDocumentListener(this);
121 private void initUI() {
122 frame
= new JFrame("BeanShell Debug Window");
123 ta
= new JTextArea();
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);
145 if (labels
[i
].equals("Save") && filename
== null) {
150 frame
.getContentPane().add(sp
, "Center");
151 frame
.getContentPane().add(p
, "South");
156 /* Implementation of DocumentListener interface */
157 public void insertUpdate(DocumentEvent e
) {
161 public void removeUpdate(DocumentEvent e
) {
165 public void changedUpdate(DocumentEvent e
) {
169 /* If the number of lines in the JTextArea has changed then update the
171 public void doChanged(DocumentEvent e
) {
172 if (linecount
!= ta
.getLineCount()) {
174 linecount
= ta
.getLineCount();
178 private void startExecution() {
179 execThread
= new Thread() {
181 Interpreter interpreter
= new Interpreter();
182 interpreter
.getNameSpace().clear();
184 // reset position and repaint gutter so no red arrow appears
185 currentPosition
= -1;
189 interpreter
.set("context", context
);
190 interpreter
.eval(ta
.getText());
191 } catch (bsh
.EvalError err
) {
192 currentPosition
= err
.getErrorLineNumber() - 1;
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
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
);
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")) {
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")) {
247 private void saveTextArea() {
248 if (filename
== null) {
252 FileOutputStream fos
= null;
254 if (filename
!= null) {
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
);
268 } catch (IOException ignore
) {
275 public void actionPerformed(ActionEvent e
) {
276 if (e
.getActionCommand().equals("Run")) {
278 } else if (e
.getActionCommand().equals("Close")) {
280 } else if (e
.getActionCommand().equals("Save")) {
282 } else if (e
.getActionCommand().equals("Clear")) {
287 public JTextArea
getTextArea() {
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
;
306 public void update() {
307 JTextArea textArea
= debugger
.getTextArea();
308 Font font
= textArea
.getFont();
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;
328 public void paintComponent(Graphics g
) {
329 JTextArea textArea
= debugger
.getTextArea();
331 Font font
= textArea
.getFont();
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
) {
352 for (int i
= startLine
; i
< endLine
; i
++) {
354 text
= Integer
.toString(i
+ 1) + " ";
355 int w
= metrics
.stringWidth(text
);
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();
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
);