blob: 07f8b249ec2a6c05182af2939769987872888d05 [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 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 params 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();
ctxt.setLanguageVersion(Context.VERSION_1_8);
ctxt.setOptimizationLevel(9);
/* 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.lineNumber();
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 = lineNo;
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 )
{
}
}
}