/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ package com.sun.star.script.framework.provider.javascript; import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ImporterTopLevel; import org.mozilla.javascript.tools.debugger.Main; import org.mozilla.javascript.tools.debugger.ScopeProvider; import com.sun.star.script.provider.XScriptContext; import com.sun.star.script.framework.container.ScriptMetaData; import com.sun.star.script.framework.provider.ScriptEditor; import com.sun.star.script.framework.provider.SwingInvocation; import com.sun.star.script.framework.log.LogUtils; import java.io.InputStream; import java.io.IOException; import java.net.URL; import java.util.Map; import java.util.HashMap; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; public class ScriptEditorForJavaScript implements ScriptEditor { // global ScriptEditorForJavaScript instance private static ScriptEditorForJavaScript theScriptEditorForJavaScript; // template for JavaScript scripts private static String JSTEMPLATE; static private Main rhinoWindow; private URL scriptURL; // global list of ScriptEditors, key is URL of file being edited private static Map BEING_EDITED = new HashMap(); static { try { URL url = ScriptEditorForJavaScript.class.getResource("template.js"); InputStream in = url.openStream(); StringBuffer buf = new StringBuffer(); byte[] b = new byte[1024]; int len = 0; while ((len = in.read(b)) != -1) { buf.append(new String(b, 0, len)); } in.close(); JSTEMPLATE = buf.toString(); } catch (IOException ioe) { JSTEMPLATE = "// JavaScript script"; } catch (Exception e) { JSTEMPLATE = "// JavaScript script"; } } /** * Returns the global ScriptEditorForJavaScript instance. */ public static ScriptEditorForJavaScript getEditor() { if (theScriptEditorForJavaScript == null) { synchronized(ScriptEditorForJavaScript.class) { if (theScriptEditorForJavaScript == null) { theScriptEditorForJavaScript = new ScriptEditorForJavaScript(); } } } return theScriptEditorForJavaScript; } /** * Get the ScriptEditorForJavaScript instance for this URL * * @param url The URL of the script source file * * @return The ScriptEditorForJavaScript associated with * the given URL if one exists, otherwise null. */ public static ScriptEditorForJavaScript getEditor(URL url) { synchronized (BEING_EDITED) { return (ScriptEditorForJavaScript)BEING_EDITED.get(url); } } /** * Returns whether or not the script source being edited in this * ScriptEditorForJavaScript has been modified */ public boolean isModified() { return rhinoWindow.isModified( scriptURL ); } /** * Returns the text being displayed in this ScriptEditorForJavaScript * * @return The text displayed in this ScriptEditorForJavaScript */ public String getText() { return rhinoWindow.getText( scriptURL ); } /** * Returns the Rhino Debugger url of this ScriptEditorForJavaScript * * @return The url of this ScriptEditorForJavaScript */ public String getURL() { return scriptURL.toString(); } /** * Returns the template text for JavaScript scripts * * @return The template text for JavaScript scripts */ public String getTemplate() { return JSTEMPLATE; } /** * Returns the default extension for JavaScript scripts * * @return The default extension for JavaScript scripts */ public String getExtension() { return "js"; } /** * Opens an editor window for the specified ScriptMetaData. * If an editor window is already open for that data it will be * moved to the front. * * @param context The context in which to execute the script * @param entry The metadata describing the script */ public void edit(final XScriptContext context, ScriptMetaData entry) { try { String sUrl = entry.getParcelLocation(); if ( !sUrl.endsWith( "/" ) ) { sUrl += "/"; } sUrl += entry.getLanguageName(); final URL url = entry.getSourceURL(); SwingInvocation.invoke( new Runnable() { public void run() { synchronized (BEING_EDITED) { ScriptEditorForJavaScript editor = (ScriptEditorForJavaScript) BEING_EDITED.get( url); if (editor == null) { editor = new ScriptEditorForJavaScript( context, url); BEING_EDITED.put(url, editor); } } assert rhinoWindow != null; rhinoWindow.showScriptWindow(url); rhinoWindow.toFront(); } }); } catch ( IOException e ) { LogUtils.DEBUG("Caught exception: " + e); LogUtils.DEBUG(LogUtils.getTrace(e)); } } // Ensures that new instances of this class can only be created using // the factory methods private ScriptEditorForJavaScript() { } private ScriptEditorForJavaScript(XScriptContext context, URL url) { initUI(); Scriptable scope = getScope( context ); this.rhinoWindow.openFile(url, scope, new closeHandler( url ) ); this.scriptURL = url; } /** * Executes the script edited by the editor * */ public Object execute() throws Exception { rhinoWindow.toFront(); return this.rhinoWindow.runScriptWindow( scriptURL ); } /** * Indicates the line where error occurred * */ public void indicateErrorLine( int lineNum ) { this.rhinoWindow.toFront(); this.rhinoWindow.highlighLineInScriptWindow( scriptURL, lineNum ); } // This code is based on the main method of the Rhino Debugger Main class // We pass in the XScriptContext in the global scope for script execution private void initUI() { try { synchronized ( ScriptEditorForJavaScript.class ) { if ( this.rhinoWindow != null ) { return; } final Main sdb = new Main("Rhino JavaScript Debugger"); org.mozilla.javascript.tools.shell.ShellContextFactory contextFactory = new org.mozilla.javascript.tools.shell.ShellContextFactory(); sdb.attachTo(contextFactory); contextFactory.setLanguageVersion(Context.VERSION_1_8); contextFactory.setOptimizationLevel(9); sdb.pack(); sdb.setSize(640, 640); sdb.setVisible(true); sdb.setExitAction(new Runnable() { public void run() { sdb.clearAllBreakpoints(); sdb.dispose(); shutdown(); } }); /* Context.addContextListener(sdb); sdb.setScopeProvider(new ScopeProvider() { public Scriptable getScope() { return org.mozilla.javascript.tools.shell.Main.getScope(); } }); */ sdb.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { shutdown(); } }); this.rhinoWindow = sdb; } } catch (Exception exc) { LogUtils.DEBUG( LogUtils.getTrace( exc ) ); } } private void shutdown() { // dereference Rhino Debugger window this.rhinoWindow = null; this.scriptURL = null; // remove all scripts from BEING_EDITED synchronized( BEING_EDITED ) { java.util.Iterator iter = BEING_EDITED.keySet().iterator(); java.util.Vector keysToRemove = new java.util.Vector(); while ( iter.hasNext() ) { URL key = (URL)iter.next(); keysToRemove.add( key ); } for ( int i=0; i