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) { buf.append(new String(b, 0, len))84 buf.append(new String(b, 0, len)); 85 } 86 in.close()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 */ getEditor()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 */ getEditor(URL url)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 */ isModified()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 */ getText()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 */ getTemplate()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 */ getExtension()166 public String getExtension() { 167 return "bsh"; 168 } 169 170 171 /** 172 * Indicates the line where error occurred 173 * 174 */ indicateErrorLine( int lineNum )175 public void indicateErrorLine( int lineNum ) 176 { 177 model.indicateErrorLine( lineNum ); 178 } 179 /** 180 * Executes the script edited by the editor 181 * 182 */ execute()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 context The context in which to execute the script 193 * @param entry The metadata describing the script 194 */ edit(final XScriptContext context, ScriptMetaData entry)195 public void edit(final XScriptContext context, ScriptMetaData entry) { 196 if (entry != null ) { 197 try { 198 ClassLoader cl = null; 199 try { 200 cl = ClassLoaderFactory.getURLClassLoader( entry ); 201 } 202 catch (Exception ignore) // TODO re-examine error handling 203 { 204 } 205 final ClassLoader theCl = cl; 206 String sUrl = entry.getParcelLocation(); 207 if ( !sUrl.endsWith( "/" ) ) 208 { 209 sUrl += "/"; 210 } 211 sUrl += entry.getLanguageName(); 212 final URL url = entry.getSourceURL(); 213 SwingInvocation.invoke( 214 new Runnable() { 215 public void run() { 216 ScriptEditorForBeanShell editor; 217 synchronized (BEING_EDITED) { 218 editor = (ScriptEditorForBeanShell) 219 BEING_EDITED.get(url); 220 if (editor == null) { 221 editor = new ScriptEditorForBeanShell( 222 context, theCl, url); 223 BEING_EDITED.put(url, editor); 224 } 225 } 226 editor.frame.toFront(); 227 } 228 }); 229 } 230 catch (IOException ioe) { 231 showErrorMessage( "Error loading file: " + ioe.getMessage() ); 232 } 233 } 234 } 235 ScriptEditorForBeanShell()236 private ScriptEditorForBeanShell() { 237 } 238 ScriptEditorForBeanShell(XScriptContext context, ClassLoader cl, URL url)239 private ScriptEditorForBeanShell(XScriptContext context, ClassLoader cl, 240 URL url) 241 { 242 this.context = context; 243 this.scriptURL = url; 244 this.model = new ScriptSourceModel(url); 245 this.filename = url.getFile(); 246 this.cl = cl; 247 try { 248 Class c = Class.forName( 249 "org.openoffice.netbeans.editor.NetBeansSourceView"); 250 251 Class[] types = new Class[] { ScriptSourceModel.class }; 252 253 java.lang.reflect.Constructor ctor = c.getConstructor(types); 254 255 if (ctor != null) { 256 Object[] args = new Object[] { this.model }; 257 this.view = (ScriptSourceView) ctor.newInstance(args); 258 } 259 else { 260 this.view = new PlainSourceView(model); 261 } 262 } 263 catch (java.lang.Error err) { 264 this.view = new PlainSourceView(model); 265 } 266 catch (Exception e) { 267 this.view = new PlainSourceView(model); 268 } 269 270 this.model.setView(this.view); 271 initUI(); 272 frame.show(); 273 } 274 showErrorMessage(String message)275 private void showErrorMessage(String message) { 276 JOptionPane.showMessageDialog(frame, message, 277 "Error", JOptionPane.ERROR_MESSAGE); 278 } 279 initUI()280 private void initUI() { 281 frame = new JFrame("BeanShell Debug Window: " + filename); 282 frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 283 284 frame.addWindowListener( 285 new WindowAdapter() 286 { 287 public void windowClosing(WindowEvent e) { 288 doClose(); 289 } 290 } 291 ); 292 293 String[] labels = {"Run", "Clear", "Save", "Close"}; 294 JPanel p = new JPanel(); 295 p.setLayout(new FlowLayout()); 296 297 for (int i = 0; i < labels.length; i++) { 298 JButton b = new JButton(labels[i]); 299 b.addActionListener(this); 300 p.add(b); 301 302 if (labels[i].equals("Save") && filename == null) { 303 b.setEnabled(false); 304 } 305 } 306 307 frame.getContentPane().add((JComponent)view, "Center"); 308 frame.getContentPane().add(p, "South"); 309 frame.pack(); 310 frame.setSize(590, 480); 311 frame.setLocation(300, 200); 312 } 313 doClose()314 private void doClose() { 315 if (view.isModified()) { 316 int result = JOptionPane.showConfirmDialog(frame, 317 "The script has been modified. " + 318 "Do you want to save the changes?"); 319 320 if (result == JOptionPane.CANCEL_OPTION) 321 { 322 // don't close the window, just return 323 return; 324 } 325 else if (result == JOptionPane.YES_OPTION) 326 { 327 boolean saveSuccess = saveTextArea(); 328 if (saveSuccess == false) 329 { 330 return; 331 } 332 } 333 } 334 frame.dispose(); 335 shutdown(); 336 } 337 saveTextArea()338 private boolean saveTextArea() { 339 boolean result = true; 340 341 if (!view.isModified()) { 342 return true; 343 } 344 345 OutputStream fos = null; 346 try { 347 String s = view.getText(); 348 fos = scriptURL.openConnection().getOutputStream(); 349 if ( fos != null) { 350 fos.write(s.getBytes()); 351 } 352 else 353 { 354 showErrorMessage( 355 "Error saving script: Could not open stream for file" ); 356 result = false; 357 } 358 view.setModified(false); 359 } 360 catch (IOException ioe) { 361 showErrorMessage( "Error saving script: " + ioe.getMessage() ); 362 result = false; 363 } 364 catch (Exception e) { 365 showErrorMessage( "Error saving script: " + e.getMessage() ); 366 result = false; 367 } 368 finally { 369 if (fos != null) { 370 try { 371 fos.flush(); 372 if ( fos != null ) 373 { 374 fos.close(); 375 } 376 } 377 catch (IOException ignore) { 378 } 379 } 380 } 381 return result; 382 } 383 shutdown()384 private void shutdown() 385 { 386 synchronized (BEING_EDITED) { 387 BEING_EDITED.remove(scriptURL); 388 } 389 } 390 actionPerformed(ActionEvent e)391 public void actionPerformed(ActionEvent e) { 392 if (e.getActionCommand().equals("Run")) { 393 try 394 { 395 execute(); 396 } 397 catch (Exception invokeException ) { 398 showErrorMessage(invokeException.getMessage()); 399 } 400 } 401 else if (e.getActionCommand().equals("Close")) { 402 doClose(); 403 } 404 else if (e.getActionCommand().equals("Save")) { 405 saveTextArea(); 406 } 407 else if (e.getActionCommand().equals("Clear")) { 408 view.clear(); 409 } 410 } 411 } 412