1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 package com.sun.star.script.framework.provider.beanshell; 28 29 import javax.swing.JComponent; 30 import javax.swing.JFrame; 31 import javax.swing.JPanel; 32 import javax.swing.JButton; 33 import javax.swing.JOptionPane; 34 35 import java.awt.FlowLayout; 36 import java.awt.event.ActionListener; 37 import java.awt.event.ActionEvent; 38 import java.awt.event.WindowAdapter; 39 import java.awt.event.WindowEvent; 40 41 import java.io.InputStream; 42 import java.io.OutputStream; 43 import java.io.IOException; 44 import java.net.URL; 45 import java.util.Map; 46 import java.util.HashMap; 47 48 import com.sun.star.script.provider.XScriptContext; 49 import com.sun.star.script.framework.provider.ScriptEditor; 50 import com.sun.star.script.framework.provider.SwingInvocation; 51 import com.sun.star.script.framework.container.ScriptMetaData; 52 import com.sun.star.script.framework.provider.ClassLoaderFactory; 53 54 public class ScriptEditorForBeanShell 55 implements ScriptEditor, ActionListener 56 { 57 private JFrame frame; 58 private String filename; 59 60 private ScriptSourceModel model; 61 private ScriptSourceView view; 62 63 private XScriptContext context; 64 private URL scriptURL = null; 65 private ClassLoader cl = null; 66 67 // global ScriptEditorForBeanShell returned for getEditor() calls 68 private static ScriptEditorForBeanShell theScriptEditorForBeanShell; 69 70 // global list of ScriptEditors, key is URL of file being edited 71 private static Map BEING_EDITED = new HashMap(); 72 73 // template for new BeanShell scripts 74 private static String BSHTEMPLATE; 75 76 // try to load the template for BeanShell scripts 77 static { 78 try { 79 URL url = 80 ScriptEditorForBeanShell.class.getResource("template.bsh"); 81 82 InputStream in = url.openStream(); 83 StringBuffer buf = new StringBuffer(); 84 byte[] b = new byte[1024]; 85 int len = 0; 86 87 while ((len = in.read(b)) != -1) { 88 buf.append(new String(b, 0, len)); 89 } 90 91 in.close(); 92 93 BSHTEMPLATE = buf.toString(); 94 } 95 catch (IOException ioe) { 96 BSHTEMPLATE = "// BeanShell script"; 97 } 98 catch (Exception e) { 99 BSHTEMPLATE = "// BeanShell script"; 100 } 101 } 102 103 /** 104 * Returns the global ScriptEditorForBeanShell instance. 105 */ 106 public static ScriptEditorForBeanShell getEditor() 107 { 108 if (theScriptEditorForBeanShell == null) 109 { 110 synchronized(ScriptEditorForBeanShell.class) 111 { 112 if (theScriptEditorForBeanShell == null) 113 { 114 theScriptEditorForBeanShell = 115 new ScriptEditorForBeanShell(); 116 } 117 } 118 } 119 return theScriptEditorForBeanShell; 120 } 121 122 /** 123 * Get the ScriptEditorForBeanShell instance for this URL 124 * 125 * @param url The URL of the script source file 126 * 127 * @return The ScriptEditorForBeanShell associated with 128 * the given URL if one exists, otherwise null. 129 */ 130 public static ScriptEditorForBeanShell getEditor(URL url) 131 { 132 synchronized (BEING_EDITED) { 133 return (ScriptEditorForBeanShell)BEING_EDITED.get(url); 134 } 135 } 136 137 /** 138 * Returns whether or not the script source being edited in this 139 * ScriptEditorForBeanShell has been modified 140 */ 141 public boolean isModified() 142 { 143 return view.isModified(); 144 } 145 146 /** 147 * Returns the text being displayed in this ScriptEditorForBeanShell 148 * 149 * @return The text displayed in this ScriptEditorForBeanShell 150 */ 151 public String getText() 152 { 153 return view.getText(); 154 } 155 156 /** 157 * Returns the template text for BeanShell scripts 158 * 159 * @return The template text for BeanShell scripts 160 */ 161 public String getTemplate() { 162 return BSHTEMPLATE; 163 } 164 165 /** 166 * Returns the default extension for BeanShell scripts 167 * 168 * @return The default extension for BeanShell scripts 169 */ 170 public String getExtension() { 171 return "bsh"; 172 } 173 174 175 /** 176 * Indicates the line where error occured 177 * 178 */ 179 public void indicateErrorLine( int lineNum ) 180 { 181 model.indicateErrorLine( lineNum ); 182 } 183 /** 184 * Executes the script edited by the editor 185 * 186 */ 187 public Object execute() throws Exception { 188 frame.toFront(); 189 return model.execute( context, cl ); 190 } 191 /** 192 * Opens an editor window for the specified ScriptMetaData. 193 * If an editor window is already open for that data it will be 194 * moved to the front. 195 * 196 * @param metadata The metadata describing the script 197 * @param context The context in which to execute the script 198 * 199 */ 200 public void edit(final XScriptContext context, ScriptMetaData entry) { 201 if (entry != null ) { 202 try { 203 ClassLoader cl = null; 204 try { 205 cl = ClassLoaderFactory.getURLClassLoader( entry ); 206 } 207 catch (Exception ignore) // TODO re-examine error handling 208 { 209 } 210 final ClassLoader theCl = cl; 211 String sUrl = entry.getParcelLocation(); 212 if ( !sUrl.endsWith( "/" ) ) 213 { 214 sUrl += "/"; 215 } 216 sUrl += entry.getLanguageName(); 217 final URL url = entry.getSourceURL(); 218 SwingInvocation.invoke( 219 new Runnable() { 220 public void run() { 221 ScriptEditorForBeanShell editor; 222 synchronized (BEING_EDITED) { 223 editor = (ScriptEditorForBeanShell) 224 BEING_EDITED.get(url); 225 if (editor == null) { 226 editor = new ScriptEditorForBeanShell( 227 context, theCl, url); 228 BEING_EDITED.put(url, editor); 229 } 230 } 231 editor.frame.toFront(); 232 } 233 }); 234 } 235 catch (IOException ioe) { 236 showErrorMessage( "Error loading file: " + ioe.getMessage() ); 237 } 238 } 239 } 240 241 private ScriptEditorForBeanShell() { 242 } 243 244 private ScriptEditorForBeanShell(XScriptContext context, ClassLoader cl, 245 URL url) 246 { 247 this.context = context; 248 this.scriptURL = url; 249 this.model = new ScriptSourceModel(url); 250 this.filename = url.getFile(); 251 this.cl = cl; 252 try { 253 Class c = Class.forName( 254 "org.openoffice.netbeans.editor.NetBeansSourceView"); 255 256 Class[] types = new Class[] { ScriptSourceModel.class }; 257 258 java.lang.reflect.Constructor ctor = c.getConstructor(types); 259 260 if (ctor != null) { 261 Object[] args = new Object[] { this.model }; 262 this.view = (ScriptSourceView) ctor.newInstance(args); 263 } 264 else { 265 this.view = new PlainSourceView(model); 266 } 267 } 268 catch (java.lang.Error err) { 269 this.view = new PlainSourceView(model); 270 } 271 catch (Exception e) { 272 this.view = new PlainSourceView(model); 273 } 274 275 this.model.setView(this.view); 276 initUI(); 277 frame.show(); 278 } 279 280 private void showErrorMessage(String message) { 281 JOptionPane.showMessageDialog(frame, message, 282 "Error", JOptionPane.ERROR_MESSAGE); 283 } 284 285 private void initUI() { 286 frame = new JFrame("BeanShell Debug Window: " + filename); 287 frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 288 289 frame.addWindowListener( 290 new WindowAdapter() 291 { 292 public void windowClosing(WindowEvent e) { 293 doClose(); 294 } 295 } 296 ); 297 298 String[] labels = {"Run", "Clear", "Save", "Close"}; 299 JPanel p = new JPanel(); 300 p.setLayout(new FlowLayout()); 301 302 for (int i = 0; i < labels.length; i++) { 303 JButton b = new JButton(labels[i]); 304 b.addActionListener(this); 305 p.add(b); 306 307 if (labels[i].equals("Save") && filename == null) { 308 b.setEnabled(false); 309 } 310 } 311 312 frame.getContentPane().add((JComponent)view, "Center"); 313 frame.getContentPane().add(p, "South"); 314 frame.pack(); 315 frame.setSize(590, 480); 316 frame.setLocation(300, 200); 317 } 318 319 private void doClose() { 320 if (view.isModified()) { 321 int result = JOptionPane.showConfirmDialog(frame, 322 "The script has been modified. " + 323 "Do you want to save the changes?"); 324 325 if (result == JOptionPane.CANCEL_OPTION) 326 { 327 // don't close the window, just return 328 return; 329 } 330 else if (result == JOptionPane.YES_OPTION) 331 { 332 boolean saveSuccess = saveTextArea(); 333 if (saveSuccess == false) 334 { 335 return; 336 } 337 } 338 } 339 frame.dispose(); 340 shutdown(); 341 } 342 343 private boolean saveTextArea() { 344 boolean result = true; 345 346 if (!view.isModified()) { 347 return true; 348 } 349 350 OutputStream fos = null; 351 try { 352 String s = view.getText(); 353 fos = scriptURL.openConnection().getOutputStream(); 354 if ( fos != null) { 355 fos.write(s.getBytes()); 356 } 357 else 358 { 359 showErrorMessage( 360 "Error saving script: Could not open stream for file" ); 361 result = false; 362 } 363 view.setModified(false); 364 } 365 catch (IOException ioe) { 366 showErrorMessage( "Error saving script: " + ioe.getMessage() ); 367 result = false; 368 } 369 catch (Exception e) { 370 showErrorMessage( "Error saving script: " + e.getMessage() ); 371 result = false; 372 } 373 finally { 374 if (fos != null) { 375 try { 376 fos.flush(); 377 if ( fos != null ) 378 { 379 fos.close(); 380 } 381 } 382 catch (IOException ignore) { 383 } 384 } 385 } 386 return result; 387 } 388 389 private void shutdown() 390 { 391 synchronized (BEING_EDITED) { 392 BEING_EDITED.remove(scriptURL); 393 } 394 } 395 396 public void actionPerformed(ActionEvent e) { 397 if (e.getActionCommand().equals("Run")) { 398 try 399 { 400 execute(); 401 } 402 catch (Exception invokeException ) { 403 showErrorMessage(invokeException.getMessage()); 404 } 405 } 406 else if (e.getActionCommand().equals("Close")) { 407 doClose(); 408 } 409 else if (e.getActionCommand().equals("Save")) { 410 saveTextArea(); 411 } 412 else if (e.getActionCommand().equals("Clear")) { 413 view.clear(); 414 } 415 } 416 } 417