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 24 package com.sun.star.script.framework.provider.javascript; 25 26 import org.mozilla.javascript.Context; 27 import org.mozilla.javascript.Scriptable; 28 import org.mozilla.javascript.ImporterTopLevel; 29 import org.mozilla.javascript.tools.debugger.Main; 30 import org.mozilla.javascript.tools.debugger.ScopeProvider; 31 32 import com.sun.star.script.provider.XScriptContext; 33 import com.sun.star.script.framework.container.ScriptMetaData; 34 import com.sun.star.script.framework.provider.ScriptEditor; 35 import com.sun.star.script.framework.provider.SwingInvocation; 36 import com.sun.star.script.framework.log.LogUtils; 37 38 import java.io.InputStream; 39 import java.io.IOException; 40 import java.net.URL; 41 42 import java.util.Map; 43 import java.util.HashMap; 44 45 import java.awt.event.WindowAdapter; 46 import java.awt.event.WindowEvent; 47 48 public class ScriptEditorForJavaScript implements ScriptEditor 49 { 50 // global ScriptEditorForJavaScript instance 51 private static ScriptEditorForJavaScript theScriptEditorForJavaScript; 52 53 // template for JavaScript scripts 54 private static String JSTEMPLATE; 55 56 static private Main rhinoWindow; 57 private URL scriptURL; 58 // global list of ScriptEditors, key is URL of file being edited 59 private static Map BEING_EDITED = new HashMap(); 60 61 static { 62 try { 63 URL url = 64 ScriptEditorForJavaScript.class.getResource("template.js"); 65 66 InputStream in = url.openStream(); 67 StringBuffer buf = new StringBuffer(); 68 byte[] b = new byte[1024]; 69 int len = 0; 70 71 while ((len = in.read(b)) != -1) { buf.append(new String(b, 0, len))72 buf.append(new String(b, 0, len)); 73 } 74 in.close()75 in.close(); 76 77 JSTEMPLATE = buf.toString(); 78 } 79 catch (IOException ioe) { 80 JSTEMPLATE = "// JavaScript script"; 81 } 82 catch (Exception e) { 83 JSTEMPLATE = "// JavaScript script"; 84 } 85 } 86 87 /** 88 * Returns the global ScriptEditorForJavaScript instance. 89 */ getEditor()90 public static ScriptEditorForJavaScript getEditor() 91 { 92 if (theScriptEditorForJavaScript == null) 93 { 94 synchronized(ScriptEditorForJavaScript.class) 95 { 96 if (theScriptEditorForJavaScript == null) 97 { 98 theScriptEditorForJavaScript = 99 new ScriptEditorForJavaScript(); 100 } 101 } 102 } 103 return theScriptEditorForJavaScript; 104 } 105 106 /** 107 * Get the ScriptEditorForJavaScript instance for this URL 108 * 109 * @param url The URL of the script source file 110 * 111 * @return The ScriptEditorForJavaScript associated with 112 * the given URL if one exists, otherwise null. 113 */ getEditor(URL url)114 public static ScriptEditorForJavaScript getEditor(URL url) 115 { 116 synchronized (BEING_EDITED) { 117 return (ScriptEditorForJavaScript)BEING_EDITED.get(url); 118 } 119 } 120 121 /** 122 * Returns whether or not the script source being edited in this 123 * ScriptEditorForJavaScript has been modified 124 */ isModified()125 public boolean isModified() 126 { 127 return rhinoWindow.isModified( scriptURL ); 128 } 129 130 /** 131 * Returns the text being displayed in this ScriptEditorForJavaScript 132 * 133 * @return The text displayed in this ScriptEditorForJavaScript 134 */ getText()135 public String getText() 136 { 137 return rhinoWindow.getText( scriptURL ); 138 } 139 140 /** 141 * Returns the Rhino Debugger url of this ScriptEditorForJavaScript 142 * 143 * @return The url of this ScriptEditorForJavaScript 144 */ getURL()145 public String getURL() 146 { 147 return scriptURL.toString(); 148 } 149 150 /** 151 * Returns the template text for JavaScript scripts 152 * 153 * @return The template text for JavaScript scripts 154 */ getTemplate()155 public String getTemplate() 156 { 157 return JSTEMPLATE; 158 } 159 160 /** 161 * Returns the default extension for JavaScript scripts 162 * 163 * @return The default extension for JavaScript scripts 164 */ getExtension()165 public String getExtension() 166 { 167 return "js"; 168 } 169 170 /** 171 * Opens an editor window for the specified ScriptMetaData. 172 * If an editor window is already open for that data it will be 173 * moved to the front. 174 * 175 * @param context The context in which to execute the script 176 * @param entry The metadata describing the script 177 */ edit(final XScriptContext context, ScriptMetaData entry)178 public void edit(final XScriptContext context, ScriptMetaData entry) 179 { 180 try { 181 String sUrl = entry.getParcelLocation(); 182 if ( !sUrl.endsWith( "/" ) ) 183 { 184 sUrl += "/"; 185 } 186 sUrl += entry.getLanguageName(); 187 final URL url = entry.getSourceURL(); 188 SwingInvocation.invoke( 189 new Runnable() { 190 public void run() { 191 synchronized (BEING_EDITED) { 192 ScriptEditorForJavaScript editor = 193 (ScriptEditorForJavaScript) BEING_EDITED.get( 194 url); 195 if (editor == null) { 196 editor = new ScriptEditorForJavaScript( 197 context, url); 198 BEING_EDITED.put(url, editor); 199 } 200 } 201 assert rhinoWindow != null; 202 rhinoWindow.showScriptWindow(url); 203 rhinoWindow.toFront(); 204 } 205 }); 206 } 207 catch ( IOException e ) 208 { 209 LogUtils.DEBUG("Caught exception: " + e); 210 LogUtils.DEBUG(LogUtils.getTrace(e)); 211 } 212 } 213 214 // Ensures that new instances of this class can only be created using 215 // the factory methods ScriptEditorForJavaScript()216 private ScriptEditorForJavaScript() 217 { 218 } 219 ScriptEditorForJavaScript(XScriptContext context, URL url)220 private ScriptEditorForJavaScript(XScriptContext context, URL url) 221 { 222 initUI(); 223 Scriptable scope = getScope( context ); 224 this.rhinoWindow.openFile(url, scope, new closeHandler( url ) ); 225 226 227 this.scriptURL = url; 228 } 229 230 /** 231 * Executes the script edited by the editor 232 * 233 */ 234 execute()235 public Object execute() throws Exception 236 { 237 rhinoWindow.toFront(); 238 239 return this.rhinoWindow.runScriptWindow( scriptURL ); 240 } 241 242 /** 243 * Indicates the line where error occured 244 * 245 */ indicateErrorLine( int lineNum )246 public void indicateErrorLine( int lineNum ) 247 { 248 this.rhinoWindow.toFront(); 249 this.rhinoWindow.highlighLineInScriptWindow( scriptURL, lineNum ); 250 } 251 // This code is based on the main method of the Rhino Debugger Main class 252 // We pass in the XScriptContext in the global scope for script execution initUI()253 private void initUI() { 254 try { 255 synchronized ( ScriptEditorForJavaScript.class ) 256 { 257 if ( this.rhinoWindow != null ) 258 { 259 return; 260 } 261 262 final Main sdb = new Main("Rhino JavaScript Debugger"); 263 org.mozilla.javascript.tools.shell.ShellContextFactory contextFactory = 264 new org.mozilla.javascript.tools.shell.ShellContextFactory(); 265 sdb.attachTo(contextFactory); 266 contextFactory.setLanguageVersion(Context.VERSION_1_8); 267 contextFactory.setOptimizationLevel(9); 268 sdb.pack(); 269 sdb.setSize(640, 640); 270 sdb.setVisible(true); 271 sdb.setExitAction(new Runnable() { 272 public void run() { 273 sdb.clearAllBreakpoints(); 274 sdb.dispose(); 275 shutdown(); 276 } 277 }); 278 /* 279 Context.addContextListener(sdb); 280 sdb.setScopeProvider(new ScopeProvider() { 281 public Scriptable getScope() { 282 return org.mozilla.javascript.tools.shell.Main.getScope(); 283 } 284 }); 285 */ 286 sdb.addWindowListener( new WindowAdapter() { 287 public void windowClosing(WindowEvent e) { 288 shutdown(); 289 } 290 }); 291 this.rhinoWindow = sdb; 292 } 293 } catch (Exception exc) { 294 LogUtils.DEBUG( LogUtils.getTrace( exc ) ); 295 } 296 } 297 shutdown()298 private void shutdown() 299 { 300 // dereference Rhino Debugger window 301 this.rhinoWindow = null; 302 this.scriptURL = null; 303 // remove all scripts from BEING_EDITED 304 synchronized( BEING_EDITED ) 305 { 306 java.util.Iterator iter = BEING_EDITED.keySet().iterator(); 307 java.util.Vector keysToRemove = new java.util.Vector(); 308 while ( iter.hasNext() ) 309 { 310 311 URL key = (URL)iter.next(); 312 keysToRemove.add( key ); 313 } 314 for ( int i=0; i<keysToRemove.size(); i++ ) 315 { 316 BEING_EDITED.remove( keysToRemove.elementAt( i ) ); 317 } 318 keysToRemove = null; 319 } 320 321 } getScope(XScriptContext xsctxt )322 private Scriptable getScope(XScriptContext xsctxt ) 323 { 324 Context ctxt = Context.enter(); 325 ImporterTopLevel scope = new ImporterTopLevel(ctxt); 326 327 Scriptable jsCtxt = Context.toObject(xsctxt, scope); 328 scope.put("XSCRIPTCONTEXT", scope, jsCtxt); 329 330 Scriptable jsArgs = Context.toObject( 331 new Object[0], scope); 332 scope.put("ARGUMENTS", scope, jsArgs); 333 334 Context.exit(); 335 return scope; 336 } 337 338 class closeHandler implements Runnable 339 { 340 URL url; closeHandler( URL url )341 closeHandler( URL url ) 342 { 343 this.url = url; 344 } run()345 public void run() 346 { 347 synchronized( BEING_EDITED ) 348 { 349 Object o = BEING_EDITED.remove( this.url ); 350 } 351 } 352 } 353 } 354