diff options
Diffstat (limited to 'scripting/java/com/sun/star/script/framework/provider/javascript')
3 files changed, 771 insertions, 0 deletions
diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptEditorForJavaScript.java b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptEditorForJavaScript.java new file mode 100644 index 000000000000..b9849d1e4e1d --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptEditorForJavaScript.java @@ -0,0 +1,376 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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.log.LogUtils; + +import java.io.InputStream; +import java.io.IOException; +import java.net.URL; + +import java.util.Map; +import java.util.HashMap; + +import javax.swing.SwingUtilities; +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) + { + 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 metadata The metadata describing the script + * @param context The context in which to execute the script + * + */ + public void edit(final XScriptContext context, ScriptMetaData entry) + { + try { + String sUrl = entry.getParcelLocation(); + if ( !sUrl.endsWith( "/" ) ) + { + sUrl += "/"; + } + sUrl += entry.getLanguageName(); + URL url = entry.getSourceURL(); + + // check if there is already an editing session for this script + //if (BEING_EDITED.containsKey(url)) + if ( rhinoWindow != null ) + { + ScriptEditorForJavaScript editor = + (ScriptEditorForJavaScript) BEING_EDITED.get(url); + if ( editor == null ) + { + editor = new ScriptEditorForJavaScript( context, url ); + editor.edit( context, entry ); + } + else + { + rhinoWindow.showScriptWindow( url ); + } + } + else + { + ScriptEditorForJavaScript editor = + new ScriptEditorForJavaScript( context, 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; + synchronized( ScriptEditorForJavaScript.class ) + { + BEING_EDITED.put(url, this); + } + + } + + /** + * 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"); + swingInvoke(new Runnable() { + public void run() { + 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 static void swingInvoke(Runnable f) { + if (SwingUtilities.isEventDispatchThread()) { + f.run(); + return; + } + try { + SwingUtilities.invokeAndWait(f); + } 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 ); + } + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptProviderForJavaScript.java b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptProviderForJavaScript.java new file mode 100755 index 000000000000..9f04d9105374 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptProviderForJavaScript.java @@ -0,0 +1,358 @@ +/************************************************************************* +* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ +package com.sun.star.script.framework.provider.javascript; + +import com.sun.star.uno.XComponentContext; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.frame.XModel; +import com.sun.star.registry.XRegistryKey; +import com.sun.star.comp.loader.FactoryHelper; + +import com.sun.star.document.XScriptInvocationContext; +import com.sun.star.reflection.InvocationTargetException; + +import java.net.URL; + +import com.sun.star.script.provider.XScript; + +import com.sun.star.script.provider.ScriptExceptionRaisedException; +import com.sun.star.script.provider.ScriptFrameworkErrorException; +import com.sun.star.script.provider.ScriptFrameworkErrorType; + +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptContext; +import com.sun.star.script.framework.provider.ClassLoaderFactory; +import com.sun.star.script.framework.provider.ScriptProvider; +import com.sun.star.script.framework.provider.ScriptEditor; +import com.sun.star.script.framework.container.ScriptMetaData; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ImporterTopLevel; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.JavaScriptException; + +public class ScriptProviderForJavaScript +{ + public static class _ScriptProviderForJavaScript extends ScriptProvider + { + public _ScriptProviderForJavaScript(XComponentContext ctx) + { + super(ctx, "JavaScript"); + } + + public XScript getScript( /*IN*/String scriptURI ) + throws com.sun.star.uno.RuntimeException, + ScriptFrameworkErrorException + { + ScriptMetaData scriptData = null; + try + { + scriptData = getScriptData( scriptURI ); + ScriptImpl script = new ScriptImpl( m_xContext, scriptData, m_xModel, m_xInvocContext ); + return script; + } + catch ( com.sun.star.uno.RuntimeException re ) + { + throw new ScriptFrameworkErrorException( "Failed to create script object: " + re.getMessage(), + null, scriptData.getLanguageName(), language, ScriptFrameworkErrorType.UNKNOWN ); + } + } + + public boolean hasScriptEditor() + { + return true; + } + + public ScriptEditor getScriptEditor() + { + return ScriptEditorForJavaScript.getEditor(); + } + } + + /** + * Returns a factory for creating the service. + * This method is called by the <code>JavaLoader</code> + * <p> + * + * @param implName the name of the implementation for which a service is desired + * @param multiFactory the service manager to be used if needed + * @param regKey the registryKey + * @return returns a <code>XSingleServiceFactory</code> for creating + * the component + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey ) + { + XSingleServiceFactory xSingleServiceFactory = null; + + if ( implName.equals( ScriptProviderForJavaScript._ScriptProviderForJavaScript.class.getName() ) ) + { + xSingleServiceFactory = FactoryHelper.getServiceFactory( + ScriptProviderForJavaScript._ScriptProviderForJavaScript.class, + "com.sun.star.script.provider.ScriptProviderForJavaScript", + multiFactory, + regKey ); + } + + return xSingleServiceFactory; + } +} +class ScriptImpl implements XScript +{ + private ScriptMetaData metaData; + private XComponentContext m_xContext; + private XMultiComponentFactory m_xMultiComponentFactory; + private XModel m_xModel; + private XScriptInvocationContext m_xInvocContext; + + ScriptImpl( XComponentContext ctx, ScriptMetaData metaData, XModel xModel, XScriptInvocationContext xInvocContext ) throws com.sun.star.uno.RuntimeException + { + this.metaData = metaData; + this.m_xContext = ctx; + this.m_xModel = xModel; + this.m_xInvocContext = xInvocContext; + try + { + this.m_xMultiComponentFactory = m_xContext.getServiceManager(); + } + catch ( Exception e ) + { + LogUtils.DEBUG( LogUtils.getTrace( e ) ); + throw new com.sun.star.uno.RuntimeException( + "Error constructing ScriptImpl: [javascript]"); + } + LogUtils.DEBUG("ScriptImpl [javascript] script data = " + metaData ); + } + + /** + * The invoke method of the ScriptProviderForJavaScript runs the + * JavaScript script specified in the URI + * + * + * + * @param aParams All parameters; pure, out params are + * undefined in sequence, i.e., the value + * has to be ignored by the callee + * + * @param aOutParamIndex Out indices + * + * @param aOutParam Out parameters + * + * @returns The value returned from the function + * being invoked + * + * @throws ScriptFrameworkErrorException If there is no matching script name + * + * + * @throws InvocationTargetException If the running script throws + * an exception this information + * is captured and rethrown as + * ScriptErrorRaisedException or + * ScriptExceptionRaisedException + */ + + public Object invoke( + /*IN*/Object[] params, + /*OUT*/short[][] aOutParamIndex, + /*OUT*/Object[][] aOutParam ) + + throws ScriptFrameworkErrorException, InvocationTargetException + { + // Initialise the out paramters - not used at the moment + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + + + ClassLoader cl = null; + URL sourceUrl = null; + try { + cl = ClassLoaderFactory.getURLClassLoader( metaData ); + sourceUrl = metaData.getSourceURL(); + } + catch ( java.net.MalformedURLException mfu ) + { + throw new ScriptFrameworkErrorException( + mfu.getMessage(), null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.MALFORMED_URL ); + } + catch ( com.sun.star.script.framework.provider.NoSuitableClassLoaderException nsc ) + { + // Framework error + throw new ScriptFrameworkErrorException( + nsc.getMessage(), null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.UNKNOWN ); + } + Context ctxt = null; + + try + { + String editorURL = sourceUrl.toString(); + Object result = null; + String source = null; + ScriptEditorForJavaScript editor = + ScriptEditorForJavaScript.getEditor( + metaData.getSourceURL() ); + + if (editor != null) + { + editorURL = editor.getURL(); + result = editor.execute(); + if ( result != null && + result.getClass().getName().equals( "org.mozilla.javascript.Undefined" ) ) + { + // Always return a string + // TODO revisit + return Context.toString( result ); + } + + } + + if (editor != null && editor.isModified() == true) + { + LogUtils.DEBUG("GOT A MODIFIED SOURCE"); + source = editor.getText(); + } + else + { + metaData.loadSource(); + source = metaData.getSource(); + + } + + if ( source == null || source.length() == 0 ) { + throw new ScriptFrameworkErrorException( + "Failed to read source data for script", null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.UNKNOWN ); + } + + /* Set the context ClassLoader on the current thread to + be our custom ClassLoader. This is the suggested method + for setting up a ClassLoader to be used by the Rhino + interpreter + */ + if (cl != null) { + Thread.currentThread().setContextClassLoader(cl); + } + + // Initialize a Rhino Context object + ctxt = Context.enter(); + + /* The ImporterTopLevel ensures that importClass and + importPackage statements work in Javascript scripts + Make the XScriptContext available as a global variable + to the script + */ + ImporterTopLevel scope = new ImporterTopLevel(ctxt); + + Scriptable jsCtxt = Context.toObject( + ScriptContext.createContext( + m_xModel, m_xInvocContext, m_xContext, + m_xMultiComponentFactory), scope); + scope.put("XSCRIPTCONTEXT", scope, jsCtxt); + + Scriptable jsArgs = Context.toObject(params, scope); + scope.put("ARGUMENTS", scope, jsArgs); + + result = ctxt.evaluateString(scope, + source, "<stdin>", 1, null); + result = ctxt.toString(result); + return result; + } + catch (JavaScriptException jse) { + LogUtils.DEBUG( "Caught JavaScriptException exception for JavaScript type = " + jse.getClass() ); + String message = jse.getMessage(); + //int lineNo = jse.getLineNumber(); + Object wrap = jse.getValue(); + LogUtils.DEBUG( "\t message " + message ); + LogUtils.DEBUG( "\t wrapped type " + wrap.getClass() ); + LogUtils.DEBUG( "\t wrapped toString " + wrap.toString() ); + ScriptExceptionRaisedException se = new + ScriptExceptionRaisedException( message ); + se.lineNum = -1; + se.language = "JavaScript"; + se.scriptName = metaData.getLanguageName(); + se.exceptionType = wrap.getClass().getName(); + se.language = metaData.getLanguage(); + LogUtils.DEBUG( "ExceptionRaised exception " ); + LogUtils.DEBUG( "\t message " + se.getMessage() ); + LogUtils.DEBUG( "\t lineNum " + se.lineNum ); + LogUtils.DEBUG( "\t language " + se.language ); + LogUtils.DEBUG( "\t scriptName " + se.scriptName ); + raiseEditor( se.lineNum ); + throw new InvocationTargetException( "JavaScript uncaught exception" + metaData.getLanguageName(), null, se ); + } + catch (Exception ex) { + LogUtils.DEBUG("Caught Exception " + ex ); + LogUtils.DEBUG("rethrowing as ScriptFramework error" ); + throw new ScriptFrameworkErrorException( + ex.getMessage(), null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.UNKNOWN ); + } + finally { + if ( ctxt != null ) + { + Context.exit(); + } + } + } + + private void raiseEditor( int lineNum ) + { + ScriptEditorForJavaScript editor = null; + try + { + URL sourceUrl = metaData.getSourceURL(); + editor = ScriptEditorForJavaScript.getEditor( sourceUrl ); + if ( editor == null ) + { + editor = ScriptEditorForJavaScript.getEditor(); + editor.edit( + ScriptContext.createContext(m_xModel, m_xInvocContext, + m_xContext, m_xMultiComponentFactory), metaData ); + editor = ScriptEditorForJavaScript.getEditor( sourceUrl ); + } + if ( editor != null ) + { + System.out.println("** Have raised IDE for JavaScript, calling indicateErrorLine for line " + lineNum ); + editor.indicateErrorLine( lineNum ); + } + } + catch( Exception ignore ) + { + } + } +} + diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/template.js b/scripting/java/com/sun/star/script/framework/provider/javascript/template.js new file mode 100644 index 000000000000..d992791e647c --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/template.js @@ -0,0 +1,37 @@ +// Hello World in JavaScript +// Import standard OpenOffice.org API classes. For more information on +// these classes and the OpenOffice.org API, see the OpenOffice.org +// Developers Guide at: +// http://api.openoffice.org/ + +importClass(Packages.com.sun.star.uno.UnoRuntime); +importClass(Packages.com.sun.star.text.XTextDocument); +importClass(Packages.com.sun.star.text.XText); +importClass(Packages.com.sun.star.text.XTextRange); +importClass(Packages.com.sun.star.frame.XModel); + +// Import XScriptContext class. An instance of this class is available +// to all JavaScript scripts in the global variable "XSCRIPTCONTEXT". This +// variable can be used to access the document for which this script +// was invoked. +// +// Methods available are: +// +// XSCRIPTCONTEXT.getDocument() returns XModel +// XSCRIPTCONTEXT.getInvocationContext() returns XScriptInvocationContext or NULL +// XSCRIPTCONTEXT.getDesktop() returns XDesktop +// XSCRIPTCONTEXT.getComponentContext() returns XComponentContext +// +// For more information on using this class see the scripting +// developer guides at: +// +// http://api.openoffice.org/docs/DevelopersGuide/ScriptingFramework/ScriptingFramework.xhtml +// + +oDoc = UnoRuntime.queryInterface(XModel,XSCRIPTCONTEXT.getInvocationContext()); +if ( !oDoc ) + oDoc = XSCRIPTCONTEXT.getDocument(); +xTextDoc = UnoRuntime.queryInterface(XTextDocument,oDoc); +xText = xTextDoc.getText(); +xTextRange = xText.getEnd(); +xTextRange.setString( "Hello World (in JavaScript)" ); |