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