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