/*******************************************************************************
* Copyright (c) 2005, 2006 Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Bjorn Freeman-Benson - initial implementation
* Ward Cunningham - initial implementation
*******************************************************************************/
package org.eclipse.eclipsemonkey.lang.javascript;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.InvalidRegistryObjectException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.eclipsemonkey.DOMDescriptor;
import org.eclipse.eclipsemonkey.EclipseMonkeyPlugin;
import org.eclipse.eclipsemonkey.IMonkeyScriptRunner;
import org.eclipse.eclipsemonkey.RunMonkeyException;
import org.eclipse.eclipsemonkey.ScriptMetadata;
import org.eclipse.eclipsemonkey.StoredScript;
import org.eclipse.eclipsemonkey.dom.IMonkeyDOMFactory;
import org.eclipse.eclipsemonkey.dom.Utilities;
import org.eclipse.eclipsemonkey.lang.javascript.doms.IJavaScriptDOMFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.activities.IActivityManager;
import org.eclipse.ui.activities.IIdentifier;
import org.eclipse.ui.console.MessageConsoleStream;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeJavaObject;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.WrappedException;
/**
*
* @author Ingo Muschenetz
*
*/
public class JavaScriptRunner implements IMonkeyScriptRunner
{
IPath path;
IWorkbenchWindow window;
StoredScript storedScript;
ClassLoader old_classloader;
JavaScriptClassLoader classloader;
private static final IActivityManager activityManager = PlatformUI.getWorkbench().getActivitySupport().getActivityManager();
/**
*
* @param path
* @param window
*/
public JavaScriptRunner(IPath path, IWorkbenchWindow window)
{
this.path = path;
if(window == null)
{
this.window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
}
else
{
this.window = window;
}
}
/**
* @see org.eclipse.eclipsemonkey.IMonkeyScriptRunner#getStoredScript()
*/
public StoredScript getStoredScript()
{
return storedScript;
}
/**
* @see org.eclipse.eclipsemonkey.IMonkeyScriptRunner#run(java.lang.String, java.lang.Object[])
*/
public Object run(String entryName, Object[] functionArgs) throws RunMonkeyException
{
Object result = null;
try
{
Context cx = Context.enter();
Scriptable sharedScope = null;
String fileName = this.path.toPortableString();
Map<String, StoredScript> scriptStore = EclipseMonkeyPlugin.getDefault().getScriptStore();
storedScript = (scriptStore.get(fileName));
if(storedScript == null)
{
MessageBox box = new MessageBox(PlatformUI.getWorkbench().getDisplay().getActiveShell(), SWT.OK | SWT.ICON_ERROR);
box.setText(Messages.JavaScriptRunner_TTL_Error_executing_script);
box.setMessage(MessageFormat.format(Messages.JavaScriptRunner_MSG_Error_executing_script, fileName));
box.open();
return null;
}
if (!storedScript.metadata.ensure_doms_are_loaded(window))
{
return null;
}
String sharedScopeName = storedScript.metadata.getScopeName();
if (sharedScopeName != null)
{
Map<String, Object> scopeStore = EclipseMonkeyPlugin.getDefault().getScopeStore();
sharedScope = (Scriptable) scopeStore.get(sharedScopeName);
if (sharedScope == null)
{
sharedScope = new JavaScriptGlobal(cx);
scopeStore.put(sharedScopeName, sharedScope);
}
}
defineDynamicVariables(path);
ClassLoader oldLoader = null;
//defineClassLoader(cx);
try
{
Scriptable compiledScope = (Scriptable) storedScript.extra.get("compiledScope"); //$NON-NLS-1$
boolean needs_compiling = compiledScope == null;
if (needs_compiling)
{
compiledScope = new JavaScriptGlobal(cx);
storedScript.extra.put("compiledScope", compiledScope); //$NON-NLS-1$
if (sharedScope != null)
{
compiledScope.setParentScope(sharedScope);
}
}
defineStandardGlobalVariables(compiledScope);
defineExtensionGlobalVariables(compiledScope, storedScript.metadata);
if (needs_compiling)
{
String contents = Utilities.getFileContents(path);
Script compiledScript = cx.compileString(contents, fileName, 1, null);
// place path in script's global
compiledScope.put(JavaScriptGlobal.LOCATION_PROPERTY, compiledScope, this.path);
// place script's class loader in script's global
JavaScriptClassLoader cl = new JavaScriptClassLoader();
compiledScope.put(JavaScriptGlobal.CLASS_LOADER_PROPERTY, compiledScope, cl);
compiledScript.exec(cx, compiledScope);
storedScript.extra.put("compiledScript", compiledScript); //$NON-NLS-1$
EclipseMonkeyPlugin.getDefault().notifyScriptsChanged();
}
Object fObj = compiledScope.get(entryName, compiledScope);
if (!(fObj instanceof Function))
{
throw new EvaluatorException(MessageFormat.format(Messages.JavaScriptRunner_ERR_MSG_function_not_defined, entryName), fileName, 0, "", 0); //$NON-NLS-2$
}
else
{
Object scriptsLoader = compiledScope.get(JavaScriptGlobal.CLASS_LOADER_PROPERTY, compiledScope);
if (scriptsLoader != null && scriptsLoader instanceof ClassLoader)
{
cx.setApplicationClassLoader((ClassLoader) scriptsLoader);
oldLoader = cx.getApplicationClassLoader();
}
Function f = (Function) fObj;
result = f.call(cx, compiledScope, compiledScope, functionArgs);
}
}
finally
{
if (oldLoader != null)
{
cx.setApplicationClassLoader(oldLoader);
}
//undefineClassLoader(cx);
undefineDynamicVariables(path);
}
}
catch (WrappedException x)
{
error(x, x.getWrappedException().toString());
}
catch (EvaluatorException x)
{
error(x, x.lineSource() + "\n" + x.details()); //$NON-NLS-1$
}
catch (RhinoException x)
{
error(x, x.details());
}
catch (IOException x)
{
error(x, this.path.toString(), x.toString());
}
catch (CoreException x)
{
error(x, this.path.toString(), x.toString());
}
finally
{
Context.exit();
}
if (result instanceof NativeJavaObject)
{
result = ((NativeJavaObject) result).unwrap();
}
return result;
}
private void defineStandardGlobalVariables(Scriptable scope)
{
Object wrappedWindow = Context.javaToJS(window, scope);
ScriptableObject.putProperty(scope, "window", wrappedWindow); //$NON-NLS-1$
}
private static final boolean isActivityEnabled(IConfigurationElement element){
String extensionId = element.getAttribute("id"); //$NON-NLS-1$
String extensionPluginId = element.getNamespaceIdentifier();
String extensionString = null;
if (extensionPluginId != null && extensionId != null
&& extensionPluginId.length() > 0
&& extensionId.length() > 0) {
extensionString = extensionPluginId + "/" + extensionId; //$NON-NLS-1$
}
if ( extensionString != null) {
final IIdentifier id = activityManager.getIdentifier(extensionString);
if ( id != null ){
return id.isEnabled();
}
}
return true;
}
private void defineExtensionGlobalVariables(Scriptable scope,
ScriptMetadata metadata) throws IOException
{
IExtensionRegistry registry = Platform.getExtensionRegistry();
IExtensionPoint point = registry
.getExtensionPoint("org.eclipse.eclipsemonkey.dom"); //$NON-NLS-1$
if (point != null) {
IExtension[] extensions = point.getExtensions();
for (int i = 0; i < extensions.length; i++) {
IExtension extension = extensions[i];
IConfigurationElement[] configurations = extension
.getConfigurationElements();
for (int j = 0; j < configurations.length; j++) {
IConfigurationElement element = configurations[j];
if ( !isActivityEnabled(element)){
/* Do nothing because activity is disabled */
continue;
}
try {
IExtension declaring = element.getDeclaringExtension();
// String declaring_plugin_id = declaring
// .getDeclaringPluginDescriptor()
// .getUniqueIdentifier();
String declaring_plugin_id = declaring.getNamespaceIdentifier();
if (metadata.containsDOM_by_plugin(declaring_plugin_id)) {
String variableName = element
.getAttribute("variableName"); //$NON-NLS-1$
Object object = element
.createExecutableExtension("class"); //$NON-NLS-1$
IMonkeyDOMFactory factory = (IMonkeyDOMFactory) object;
Object rootObject = factory.getDOMroot();
// ClassLoader rootLoader = rootObject.getClass()
// .getClassLoader();
// classloader.add(rootLoader);
Object wrappedRoot = Context.javaToJS(rootObject,
scope);
ScriptableObject.putProperty(scope, variableName,
wrappedRoot);
}
} catch (InvalidRegistryObjectException x) {
// ignore bad extensions
} catch (CoreException x) {
// ignore bad extensions
}
}
}
}
point = registry.getExtensionPoint("org.eclipse.eclipsemonkey.lang.javascript.javascript_dom"); //$NON-NLS-1$
if (point != null) {
IExtension[] extensions = point.getExtensions();
for (int i = 0; i < extensions.length; i++) {
IExtension extension = extensions[i];
IConfigurationElement[] configurations = extension
.getConfigurationElements();
for (int j = 0; j < configurations.length; j++) {
IConfigurationElement element = configurations[j];
try {
if ( !isActivityEnabled(element)){
/* Do nothing because activity is disabled */
continue;
}
IExtension declaring = element.getDeclaringExtension();
// String declaring_plugin_id = declaring
// .getDeclaringPluginDescriptor()
// .getUniqueIdentifier();
String declaring_plugin_id = declaring.getNamespaceIdentifier();
if (metadata.containsDOM_by_plugin(declaring_plugin_id)) {
String variableName = element
.getAttribute("variableName"); //$NON-NLS-1$
String basedOnDOM = element
.getAttribute("basedOn"); //$NON-NLS-1$
if(basedOnDOM != null && basedOnDOM.trim().length() > 0)
{
Pattern p = Pattern.compile("\\s*(\\p{Graph}+)\\/((\\p{Alnum}|\\.)+)", //$NON-NLS-1$
Pattern.DOTALL);
Matcher m = p.matcher(basedOnDOM);
while (m.find()) {
metadata.getDOMs().add(
new DOMDescriptor(m.group(1), m.group(2)));
}
if(metadata.ensure_doms_are_loaded(window) == false)
{
throw new IOException("Cannot load the required DOM extension:\n\n" + basedOnDOM + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
Object object = element
.createExecutableExtension("class"); //$NON-NLS-1$
IJavaScriptDOMFactory factory = (IJavaScriptDOMFactory) object;
Object rootObject = factory.getDOMroot(scope);
// ClassLoader rootLoader = rootObject.getClass().getClassLoader();
// classloader.add(rootLoader);
Object wrappedRoot = Context.javaToJS(rootObject,
scope);
ScriptableObject.putProperty(scope, variableName,
wrappedRoot);
}
} catch (InvalidRegistryObjectException x) {
// ignore bad extensions
} catch (CoreException x) {
// ignore bad extensions
}
}
}
}
}
private void error(RhinoException x, String string)
throws RunMonkeyException {
RunMonkeyException e = new RunMonkeyException(x.getClass().getName(), x.sourceName(),
new Integer(x.lineNumber()), string);
MessageConsoleStream cs = JavaScriptGlobal.getConsoleStream();
cs.println(e.toString());
throw e;
}
private void defineDynamicVariables(IPath path) {
Utilities.state().begin(path);
Utilities.state().set(Utilities.SCRIPT_NAME,
path.toPortableString());
}
private void undefineDynamicVariables(IPath path) {
Utilities.state().end(path);
}
private void error(Exception x, String fileName, String string)
throws RunMonkeyException {
RunMonkeyException e = new RunMonkeyException(x.getClass().getName(), fileName, null,
string);
MessageConsoleStream cs = JavaScriptGlobal.getConsoleStream();
cs.println(e.toString());
throw e;
}
// private void defineClassLoader(Context cx) {
// old_classloader = cx.getApplicationClassLoader();
// classloader = new JavaScriptClassLoader();
// cx.setApplicationClassLoader(classloader);
// }
//
// private void undefineClassLoader(Context cx) {
// cx.setApplicationClassLoader(old_classloader);
// }
}