/* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 . */ 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 { private static class ScriptProviderForJavaScript_2 extends ScriptProvider { public ScriptProviderForJavaScript_2(XComponentContext ctx) { super(ctx, "JavaScript"); } @Override 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 ); } } @Override public boolean hasScriptEditor() { return true; } @Override public ScriptEditor getScriptEditor() { return ScriptEditorForJavaScript.getEditor(); } } /** * Returns a factory for creating the service. * This method is called by the JavaLoader *

* * @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 XSingleServiceFactory 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_2.class.getName() ) ) { xSingleServiceFactory = FactoryHelper.getServiceFactory( ScriptProviderForJavaScript.ScriptProviderForJavaScript_2.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 * * @return 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 parameters - 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 ); } 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()) { 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, "", 1, null); result = Context.toString(result); return result; } catch (JavaScriptException jse) { LogUtils.DEBUG( "Caught JavaScriptException exception for JavaScript type = " + jse.getClass() ); String message = jse.getMessage(); 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 ) { } } }