blob: 4f04407e04446985929b51dd7d64f08b158facc8 [file] [log] [blame]
/**************************************************************
*
* 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 occured
*
*/
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<keysToRemove.size(); i++ )
{
BEING_EDITED.remove( keysToRemove.elementAt( i ) );
}
keysToRemove = null;
}
}
private Scriptable getScope(XScriptContext xsctxt )
{
Context ctxt = Context.enter();
ImporterTopLevel scope = new ImporterTopLevel(ctxt);
Scriptable jsCtxt = Context.toObject(xsctxt, scope);
scope.put("XSCRIPTCONTEXT", scope, jsCtxt);
Scriptable jsArgs = Context.toObject(
new Object[0], scope);
scope.put("ARGUMENTS", scope, jsArgs);
Context.exit();
return scope;
}
class closeHandler implements Runnable
{
URL url;
closeHandler( URL url )
{
this.url = url;
}
public void run()
{
synchronized( BEING_EDITED )
{
Object o = BEING_EDITED.remove( this.url );
}
}
}
}