com.aptana.ide.scripting.Global.java Source code

Java tutorial

Introduction

Here is the source code for com.aptana.ide.scripting.Global.java

Source

    /**
     * This file Copyright (c) 2005-2008 Aptana, Inc. This program is
     * dual-licensed under both the Aptana Public License and the GNU General
     * Public license. You may elect to use one or the other of these licenses.
     * 
     * This program is distributed in the hope that it will be useful, but
     * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
     * NONINFRINGEMENT. Redistribution, except as permitted by whichever of
     * the GPL or APL you select, is prohibited.
     *
     * 1. For the GPL license (GPL), you can redistribute and/or modify this
     * program under the terms of the GNU General Public License,
     * Version 3, as published by the Free Software Foundation.  You should
     * have received a copy of the GNU General Public License, Version 3 along
     * with this program; if not, write to the Free Software Foundation, Inc., 51
     * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     * 
     * Aptana provides a special exception to allow redistribution of this file
     * with certain other free and open source software ("FOSS") code and certain additional terms
     * pursuant to Section 7 of the GPL. You may view the exception and these
     * terms on the web at http://www.aptana.com/legal/gpl/.
     * 
     * 2. For the Aptana Public License (APL), this program and the
     * accompanying materials are made available under the terms of the APL
     * v1.0 which accompanies this distribution, and is available at
     * http://www.aptana.com/legal/apl/.
     * 
     * You may view the GPL, Aptana's exception and additional terms, and the
     * APL in the file titled license.html at the root of the corresponding
     * plugin containing this source file.
     * 
     * Any modifications to this file must keep this entire header intact.
     */
    package com.aptana.ide.scripting;

    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.StringWriter;
    import java.lang.reflect.InvocationTargetException;
    import java.util.HashMap;
    import java.util.Set;

    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.ParserConfigurationException;

    import org.eclipse.core.runtime.Platform;
    import org.eclipse.jface.dialogs.InputDialog;
    import org.eclipse.jface.dialogs.MessageDialog;
    import org.eclipse.jface.preference.IPreferenceStore;
    import org.eclipse.jface.window.Window;
    import org.eclipse.swt.layout.GridData;
    import org.eclipse.swt.widgets.Display;
    import org.eclipse.swt.widgets.Shell;
    import org.eclipse.ui.IWorkbench;
    import org.eclipse.ui.PlatformUI;
    import org.eclipse.ui.console.MessageConsoleStream;
    import org.mozilla.javascript.Context;
    import org.mozilla.javascript.Function;
    import org.mozilla.javascript.Script;
    import org.mozilla.javascript.Scriptable;
    import org.mozilla.javascript.ScriptableObject;
    import org.osgi.framework.Bundle;
    import org.w3c.dom.Document;
    import org.xml.sax.SAXException;

    import com.aptana.ide.core.IdeLog;
    import com.aptana.ide.core.StringUtils;
    import com.aptana.ide.editor.css.CSSPlugin;
    import com.aptana.ide.editor.html.HTMLPlugin;
    import com.aptana.ide.editor.js.JSPlugin;
    import com.aptana.ide.editor.xml.XMLPlugin;
    import com.aptana.ide.editors.UnifiedEditorsPlugin;
    import com.aptana.ide.editors.preferences.IPreferenceConstants;
    import com.aptana.ide.editors.views.actions.ActionsViewEventTypes;
    import com.aptana.ide.editors.views.profiles.ProfilesViewEventTypes;
    import com.aptana.ide.scripting.editors.Editors;
    import com.aptana.ide.scripting.editors.Region;
    import com.aptana.ide.scripting.io.File;
    import com.aptana.ide.scripting.io.PrintStream;
    import com.aptana.ide.scripting.io.WebRequest;
    import com.aptana.ide.scripting.menus.Menus;
    import com.aptana.ide.scripting.parsing.CSSTokenTypes;
    import com.aptana.ide.scripting.parsing.HTMLTokenTypes;
    import com.aptana.ide.scripting.parsing.JSTokenTypes;
    import com.aptana.ide.scripting.parsing.ScriptDocTokenTypes;
    import com.aptana.ide.scripting.parsing.TokenCategories;
    import com.aptana.ide.scripting.parsing.XMLTokenTypes;
    import com.aptana.ide.scripting.views.Views;

    /**
     * @author Kevin Lindsey
     */
    public class Global extends ScriptableObject {
        /*
         * Fields
         */
        private static final long serialVersionUID = 1996593475808707552L;
        private static int scriptIndex = 0;

        /**
         * The property name used to store a libraries unique id
         */
        public static final String idPropertyName = "__sid__"; //$NON-NLS-1$

        private Menus _menus;
        private Editors _editors;
        private Views _views;
        private PrintStream _err;
        // private PrintStream _out;
        private HashMap _scriptInfo;
        private HashMap _idXref;

        private HashMap _runningSetTimeouts;
        private int _setTimeoutIndex;

        /*
         * Properties
         */

        /**
         * getClassName
         * 
         * @return String
         */
        public String getClassName() {
            return "Global"; //$NON-NLS-1$
        }

        /*
         * Constructors
         */

        /**
         * GlobalBase
         * 
         * @param cx
         */
        public Global(Context cx) {
            this._scriptInfo = new HashMap();
            this._idXref = new HashMap();
            this._runningSetTimeouts = new HashMap();

            // create local instances
            this._menus = new Menus(this);
            this._editors = new Editors(this);
            this._views = new Views(this);

            // wrap output streams
            // CHECKSTYLE:OFF
            this._err = new PrintStream(this, System.err);
            // CHECKSTYLE:ON
            // this._out = new PrintStream(this, System.out);

            // create unsealed standard objects
            cx.initStandardObjects(this, false);

            // make list of functions
            String[] names = { "alert", //$NON-NLS-1$
                    "clearTimeout", //$NON-NLS-1$
                    "confirm", //$NON-NLS-1$
                    "execute", //$NON-NLS-1$
                    "getPreference", //$NON-NLS-1$
                    "getProperty", //$NON-NLS-1$
                    "include", //$NON-NLS-1$
                    "loadBundle", //$NON-NLS-1$
                    "loadLibrary", //$NON-NLS-1$
                    "parseXML", //$NON-NLS-1$
                    "prompt", //$NON-NLS-1$
                    "reloadLibrary", //$NON-NLS-1$
                    "runOnUIThread", //$NON-NLS-1$
                    "setPreference", //$NON-NLS-1$
                    "setTimeout", //$NON-NLS-1$
                    "share" //$NON-NLS-1$
            };

            // define functions
            this.defineFunctionProperties(names, Global.class, READONLY | PERMANENT);

            // define properties
            this.defineProperty("global", this, READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("editors", this._editors, READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("window", this, READONLY | PERMANENT); //$NON-NLS-1$
            // this.defineProperty("document", this, READONLY | PERMANENT);
            this.defineProperty("menus", this._menus, READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("views", this._views, READONLY | PERMANENT); //$NON-NLS-1$

            MessageConsoleStream stream = ScriptingEngine.getInstance().getConsoleStream();

            // this.defineProperty("err", stream, READONLY | PERMANENT);
            this.defineProperty("err", _err, READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("out", stream, READONLY | PERMANENT); //$NON-NLS-1$

            this.defineProperty("TokenCategories", new TokenCategories(this), READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("CSSTokenTypes", new CSSTokenTypes(this), READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("HTMLTokenTypes", new HTMLTokenTypes(this), READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("JSTokenTypes", new JSTokenTypes(this), READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("ScriptDocTokenTypes", new ScriptDocTokenTypes(this), READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("XMLTokenTypes", new XMLTokenTypes(this), READONLY | PERMANENT); //$NON-NLS-1$

            this.defineProperty("IPreferenceConstants", new IPreferenceConstants() {
//$NON-NLS-0$
            }, READONLY | PERMANENT);
            this.defineProperty("ProfilesViewEventTypes", new ProfilesViewEventTypes(), READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("ActionsViewEventTypes", new ActionsViewEventTypes(), READONLY | PERMANENT); //$NON-NLS-1$

            this.defineProperty("SWT", new org.eclipse.swt.SWT(), READONLY | PERMANENT); //$NON-NLS-1$
            this.defineProperty("GridData", new GridData(), READONLY | PERMANENT); //$NON-NLS-1$

            try {
                ScriptableObject.defineClass(this, File.class);
                ScriptableObject.defineClass(this, WebRequest.class);
                ScriptableObject.defineClass(this, Region.class);
                ScriptableObject.defineClass(this, com.aptana.ide.scripting.swt.Shell.class);
                ScriptableObject.defineClass(this, com.aptana.ide.scripting.swt.Button.class);
                ScriptableObject.defineClass(this, com.aptana.ide.scripting.swt.Text.class);
                ScriptableObject.defineClass(this, com.aptana.ide.scripting.swt.Label.class);
            } catch (IllegalAccessException e) {
                IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e);
            } catch (InstantiationException e) {
                IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e);
            } catch (InvocationTargetException e) {
                IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e);
            }
        }

        /*
         * Methods
         */

        /**
         * Execute a command-line in the system shell
         * 
         * @param cx
         * @param thisObj
         * @param args
         *            The string command to execute
         * @param funObj
         * @return Returns an object array where the first object is the return code, the second object is the text from
         *         stdout, and the third object is the text from stderr
         */
        public static Scriptable execute(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
            Scriptable result = null;

            if (args.length > 0) {
                String command = args[0].toString();
                String input = StringUtils.EMPTY;

                if (args.length > 1) {
                    input = args[1].toString();
                }

                Process p = null;
                int retCode = 0;
                String stdout = StringUtils.EMPTY;
                String stderr = StringUtils.EMPTY;

                try {
                    p = Runtime.getRuntime().exec(command);
                    p.getOutputStream().write(input.getBytes());
                    p.getOutputStream().flush();
                    p.getOutputStream().close();
                    retCode = p.waitFor();
                    // retCode = p.exitValue();
                    stdout = getText(p.getInputStream());
                    stderr = getText(p.getErrorStream());

                } catch (IOException e) {
                    if (p != null) {
                        retCode = p.exitValue();
                    }
                } catch (InterruptedException e) {
                    IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e);
                }

                // create Object
                Scriptable scope = ScriptableObject.getTopLevelScope(thisObj);

                result = cx.newObject(scope, "Object"); //$NON-NLS-1$

                // set property values
                result.put("code", result, new Integer(retCode)); //$NON-NLS-1$
                result.put("stdout", result, stdout); //$NON-NLS-1$
                result.put("stderr", result, stderr); //$NON-NLS-1$
            }

            // return result
            return result;
        }

        /**
         * export
         * 
         * @param obj
         */
        public void share(ScriptableObject obj) {
            if (obj != null) {
                Object[] ids = obj.getIds();

                for (int i = 0; i < ids.length; i++) {
                    Object id = ids[i];

                    if (id instanceof String) {
                        String propertyName = (String) id;

                        this.put(propertyName, this, obj.get(propertyName, obj));
                    }
                }
            }
        }

        /**
         * runOnUIThread
         * 
         * @param cx
         * @param thisObj
         * @param args
         * @param funObj
         */
        public static void runOnUIThread(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
            if (args.length > 0 && args[0] instanceof Function) {
                Function f = (Function) args[0];

                // get display
                final IWorkbench workbench = PlatformUI.getWorkbench();
                Display display = workbench.getDisplay();

                // execute callback in the correct thread
                display.syncExec(new ScriptThread(f.getParentScope(), f, new Object[0]));
            }
        }

        /**
         * runOnUIThreadAsync
         * 
         * @param cx
         * @param thisObj
         * @param args
         * @param funObj
         */
        public static void runOnUIThreadAsync(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
            if (args.length > 0 && args[0] instanceof Function) {
                Function f = (Function) args[0];

                // get display
                final IWorkbench workbench = PlatformUI.getWorkbench();
                Display display = workbench.getDisplay();

                // execute callback in the correct thread
                display.asyncExec(new ScriptThread(f.getParentScope(), f, new Object[0]));
            }
        }

        /**
         * parseXML
         * 
         * @param xml
         * @return Document
         */
        public Document parseXML(String xml) {
            Document result = null;

            if (xml != null && xml.length() > 0) {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder;
                try {
                    builder = factory.newDocumentBuilder();
                    // parse XML to create our document element
                    InputStream in = new ByteArrayInputStream(xml.getBytes());

                    result = builder.parse(in);
                } catch (ParserConfigurationException e) {
                    IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e);
                } catch (SAXException e) {
                    IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e);
                } catch (IOException e) {
                    IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e);
                }
            }

            return result;
        }

        /**
         * Get a string array of all script ids that have been registered
         * 
         * @return String[]
         */
        public String[] getScriptIds() {
            Set keySet = _scriptInfo.keySet();

            return (String[]) keySet.toArray(new String[keySet.size()]);
        }

        /**
         * Get the script info object for the specified script id
         * 
         * @param id
         *            The script id to lookup
         * @return The ScriptInfo for the specified id
         */
        public ScriptInfo getScriptInfo(String id) {
            ScriptInfo result = null;

            if (this._scriptInfo.containsKey(id)) {
                result = (ScriptInfo) this._scriptInfo.get(id);
            }

            return result;
        }

        /**
         * Determine if we have ScriptInfo for the given id
         * 
         * @param id
         *            The id to lookup
         * @return Returns true if we have a ScriptInfo entry for the given id
         */
        public boolean hasScriptInfo(String id) {
            return this._scriptInfo.containsKey(id);
        }

        /**
         * Get all text from the given input stream
         * 
         * @param stream
         *            The input stream
         * @return Returns all text from the input stream
         * @throws IOException
         */
        protected static String getText(InputStream stream) throws IOException {
            // create output buffer
            StringWriter sw = new StringWriter();

            // read contents into a string buffer
            try {
                // get buffered reader
                InputStreamReader isr = new InputStreamReader(stream);
                BufferedReader reader = new BufferedReader(isr);

                // create temporary buffer
                char[] buf = new char[1024];

                // fill buffer
                int numRead = reader.read(buf);

                // keep reading until the end of the stream
                while (numRead != -1) {
                    // output temp buffer to output buffer
                    sw.write(buf, 0, numRead);

                    // fill buffer
                    numRead = reader.read(buf);
                }
            } finally {
                if (stream != null) {
                    stream.close();
                }
            }

            // return string buffer's content
            return sw.toString();
        }

        /**
         * Perform any steps required to reset the global scope when reloading the scripting engine
         */
        public void shutdown() {
            this._scriptInfo.clear();
        }

        /**
         * Popup an alert box with the specified message
         * 
         * @param message
         *            The message to display in an alert dialog
         */
        public void alert(final String message) {
            final Display currentDisplay = Display.getCurrent();

            if (currentDisplay != null) {
                currentDisplay.syncExec(new Runnable() {
                    public void run() {
                        Shell shell = currentDisplay.getActiveShell();

                        if (shell != null) {
                            MessageDialog.openWarning(shell, "Aptana Studio", message); //$NON-NLS-1$
                        }
                    }
                });
            }
        }

        /**
         * Popup an confirm box with the specified message
         * 
         * @param message
         *            The message to display at the confirmation prompt
         * @return boolean
         */
        public boolean confirm(final String message) {
            /**
             * inner class for result
             */
            class Answer {
                public boolean result = false;
            }

            // create instance of inner class
            final Answer a = new Answer();

            // get reply from user
            final Display currentDisplay = Display.getCurrent();

            if (currentDisplay != null) {
                currentDisplay.syncExec(new Runnable() {
                    public void run() {
                        Shell shell = currentDisplay.getActiveShell();

                        if (shell != null) {
                            a.result = MessageDialog.openConfirm(shell, "Aptana Studio", message); //$NON-NLS-1$
                        }
                    }
                });
            }

            return a.result;
        }

        /**
         * Popup an prompt box with the specified message
         * 
         * @param cx
         * @param thisObj
         * @param args
         *            The message to display in the prompt dialog
         * @param funObj
         * @return boolean
         */
        public static Object prompt(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
            final Display currentDisplay = Display.getCurrent();
            String messageArg = Messages.Global_Prompt;
            String defaultValueArg = StringUtils.EMPTY;

            if (args.length > 0) {
                messageArg = Context.toString(args[0]);
            }
            if (args.length > 1) {
                defaultValueArg = Context.toString(args[1]);
            }

            /**
             * Answer
             */
            class Answer {
                public Object result = StringUtils.EMPTY;
            }

            final String message = messageArg;
            final String defaultValue = defaultValueArg;
            final Answer a = new Answer();

            if (currentDisplay != null) {
                currentDisplay.syncExec(new Runnable() {

                    public void run() {
                        Shell shell = currentDisplay.getActiveShell();

                        if (shell != null) {
                            InputDialog dialog = new InputDialog(null, "Aptana Prompt", message, defaultValue, null); //$NON-NLS-1$
                            int dialogResult = dialog.open();
                            if (dialogResult == Window.OK) {
                                a.result = dialog.getValue();
                            } else {
                                a.result = Context.getUndefinedValue();
                            }
                        }
                    }
                });
            }

            return a.result;
        }

        /**
         * Get a list of the full paths of all files in the Active Library view
         * 
         * @param property
         *            The system property name to retrieve
         * @return Returns a string array of full paths
         */
        public String getProperty(String property) {
            String result = null;

            if (property != null && property.length() > 0) {
                result = System.getProperty(property, "undefined"); //$NON-NLS-1$
            }

            return result;
        }

        /**
         * Get preference
         * 
         * @param cx
         * @param thisObj
         * @param args
         * @param funObj
         * @return Returns a string array of full paths
         */
        public static String getPreference(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
            String result = StringUtils.EMPTY;

            if (args.length > 0) {
                String name = Context.toString(args[0]);
                int pluginIndex = Editors.UNIFIED_EDITOR;
                IPreferenceStore prefStore;

                if (args.length > 1) {
                    pluginIndex = (int) Context.toNumber(args[1]);
                }

                switch (pluginIndex) {
                case Editors.CSS_EDITOR:
                    prefStore = CSSPlugin.getDefault().getPreferenceStore();
                    break;

                case Editors.HTML_EDITOR:
                    prefStore = HTMLPlugin.getDefault().getPreferenceStore();
                    break;

                case Editors.JAVASCRIPT_EDITOR:
                    prefStore = JSPlugin.getDefault().getPreferenceStore();
                    break;

                case Editors.XML_EDITOR:
                    prefStore = XMLPlugin.getDefault().getPreferenceStore();
                    break;

                case Editors.UNIFIED_EDITOR:
                default:
                    prefStore = UnifiedEditorsPlugin.getDefault().getPreferenceStore();
                    break;
                }

                result = prefStore.getString(name);
            }

            return result;
        }

        /**
         * Set a preference
         * 
         * @param name
         *            The name of the preference to set
         * @param value
         *            The new value for the specified preference
         */
        public void setPreference(String name, String value) {
            if (name != null && name.length() > 0 && value != null && value.length() > 0) {
                IPreferenceStore prefStore = UnifiedEditorsPlugin.getDefault().getPreferenceStore();

                prefStore.setValue(name, value);

                UnifiedEditorsPlugin.getDefault().savePluginPreferences();
            }
        }

        /**
         * Load a script into a currently executing library
         * 
         * @param cx
         * @param thisObj
         * @param args
         * @param funObj
         */
        public static void include(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
            if (args.length > 0) {
                String id = Context.toString(thisObj.get(idPropertyName, thisObj.getParentScope()));
                Global global = (Global) funObj.getParentScope();
                ScriptInfo info = global.getScriptInfo(id);
                String libraryName = Context.toString(args[0]);
                java.io.File library = new java.io.File(libraryName);

                if (library.exists() == false) {
                    java.io.File parent = info.getFile().getParentFile();
                    String name = parent.getAbsolutePath() + java.io.File.separator + libraryName;

                    library = new java.io.File(name);
                }

                if (library.exists()) {
                    try {
                        // grab the script's source
                        String source = FileUtilities.getStreamText(new FileInputStream(library));

                        // get script where this is executing
                        Scriptable scope = info.getScope();

                        // compile the script
                        Script script = cx.compileString(source, library.getAbsolutePath(), 1, null);

                        // exec
                        script.exec(cx, scope);
                    } catch (FileNotFoundException e) {
                        IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e);
                    }
                } else {
                    String message = StringUtils.format(Messages.Global_Cannot_Locate_File,
                            new Object[] { info.getFile().getAbsolutePath(), libraryName });

                    IdeLog.logError(ScriptingPlugin.getDefault(), message);
                }
            } else {
                IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Filename_Not_Defined);
            }
        }

        /**
         * loadBundle
         * 
         * @param cx
         * @param thisObj
         * @param args
         * @param funObj
         */
        public static void loadBundle(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
            if (args.length > 0) {
                String id = Context.toString(thisObj.get(idPropertyName, thisObj.getParentScope()));
                Global global = (Global) funObj.getParentScope();
                ScriptInfo info = global.getScriptInfo(id);
                String bundleName = Context.toString(args[0]);
                Bundle bundle = Platform.getBundle(bundleName);

                if (bundle == null) {
                    throw new RuntimeException(Messages.Global_Bundle_Not_Found + bundleName);
                }

                ScriptClassLoader classLoader = info.getClassLoader();

                classLoader.addBundle(bundle);
            }
        }

        /**
         * Load a library into the script engine. Libraries maintain their own scope. As a result, each script's global will
         * not collide with other library globals. However, each library's global is linked with the ScriptEngine's global.
         * This reduces duplicate definition of built-in objects like Object, Function, Array, etc. Libraries can access the
         * ScriptEngine global via the "global" property. Libraries can share data using "global"
         * 
         * @param cx
         *            The script's Context
         * @param thisObj
         *            The object or scope where this function was invoked
         * @param args
         *            The library path to load
         * @param funObj
         * @return Returns the script's id or undefined if the script failed to load
         */
        public static Object loadLibrary(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
            Object result = null;

            if (args.length > 0) {
                Global instance = ScriptingEngine.getInstance().getGlobal();
                String libraryPath = Context.toString(args[0]);
                String onLoadFunction = (args.length > 1) ? Context.toString(args[1]) : "onload"; //$NON-NLS-1$

                try {
                    // grab the script's source
                    String source = FileUtilities.getStreamText(new FileInputStream(libraryPath));

                    // compile the script
                    Script script = cx.compileString(source, libraryPath, 1, null);

                    // build script's local global scope
                    Scriptable scope = cx.newObject(instance);
                    scope.setPrototype(instance);
                    scope.setParentScope(null);

                    // store script state info
                    String id = instance.storeScriptState(libraryPath, script, scope);

                    // create local global to script's location
                    scope.put("location", scope, libraryPath); //$NON-NLS-1$

                    // execute script to load its definitions into its scope
                    Context.toBoolean(script.exec(cx, scope));

                    // grab the init function used to perform any needed load-time initialization
                    Object fObj = scope.get(onLoadFunction, scope);

                    // call the init function
                    if (fObj instanceof Function) {
                        Function f = (Function) fObj;
                        ScriptInfo info = instance.getScriptInfo(id);
                        ClassLoader oldClassLoader = cx.getApplicationClassLoader();

                        cx.setApplicationClassLoader(info.getClassLoader());

                        try {
                            f.call(cx, scope, scope, new Object[0]);
                        } catch (Exception e) {
                            cx.setApplicationClassLoader(oldClassLoader);
                        }
                    } else {
                        IdeLog.logError(ScriptingPlugin.getDefault(),
                                onLoadFunction + Messages.Global_Function_Does_Not_Exist + libraryPath);
                    }

                    // set result
                    result = scope;
                } catch (FileNotFoundException e) {
                    IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e);
                }
            }

            if (result == null) {
                result = Context.getUndefinedValue();
            }

            return result;
        }

        /**
         * reloadLibrary
         * 
         * @param filename
         */
        public void reloadLibrary(String filename) {
            if (filename != null && filename.length() > 0) {
                String key = this.getXrefId(filename);
                ScriptInfo info = this.getScriptInfo(key);
                Scriptable scope = info.getScope();
                Object onunload = scope.get("onunload", scope); //$NON-NLS-1$

                if (onunload instanceof Function) {
                    Function unloadFunction = (Function) onunload;

                    Context cx = Context.enter();

                    try {
                        unloadFunction.call(cx, scope, scope, new Object[0]);

                        this.removeScriptInfo(key);
                        this.removeXrefId(filename);

                        Global.loadLibrary(cx, this, new Object[] { filename, "onload" }, null); //$NON-NLS-1$
                    } catch (Exception e1) {
                        IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e1);
                    }

                    Context.exit();
                }
            }
        }

        /**
         * clearTimeout
         * 
         * @param timeoutId
         */
        public void clearTimeout(int timeoutId) {
            synchronized (this._runningSetTimeouts) {
                Integer id = new Integer(timeoutId);

                if (this._runningSetTimeouts.containsKey(id)) {
                    this._runningSetTimeouts.remove(id);
                }
            }
        }

        /**
         * setTimeout
         * 
         * @param f
         * @param timeout
         * @return int
         */
        public int setTimeout(final Function f, final int timeout) {
            final Global self = this;
            final int timeoutIndex = self._setTimeoutIndex++;

            Thread thread = new Thread(new Runnable() {
                public void run() {
                    boolean active = true;

                    try {
                        // sleep
                        Thread.sleep(timeout);

                        // check if this was canceled while sleeping
                        synchronized (self._runningSetTimeouts) {
                            active = self._runningSetTimeouts.containsKey(new Integer(timeoutIndex));
                        }

                        // call the associated function if this is still active
                        if (active) {
                            // get display
                            final IWorkbench workbench = PlatformUI.getWorkbench();

                            if (workbench == null) {
                                return;
                            }

                            Display display = workbench.getDisplay();

                            if (display == null || display.isDisposed()) {
                                return;
                            }

                            // execute callback in the correct thread
                            display.asyncExec(new Runnable() {
                                public void run() {
                                    Scriptable scope = f.getParentScope();
                                    Context cx = Context.enter();

                                    f.call(cx, scope, scope, new Object[0]);

                                    Context.exit();

                                    self.clearTimeout(timeoutIndex);
                                }
                            });
                        }
                    } catch (InterruptedException e) {
                        IdeLog.logError(ScriptingPlugin.getDefault(), Messages.Global_Error, e);
                    }
                }
            }, "Aptana: Script setTimeout"); //$NON-NLS-1$

            // save reference to this thread to indicate that it should run. Note that clearTimeout will
            // remove this entry
            // and if that is down before the thread wakes, then the action in the thread will not
            // execute
            this._runningSetTimeouts.put(new Integer(timeoutIndex), thread);

            thread.setDaemon(true);

            // start the thread
            thread.start();

            // return the timeout index in case the thread wants to abort this timeout
            return timeoutIndex;
        }

        /**
         * removeScriptInfo
         * 
         * @param id
         */
        public void removeScriptInfo(String id) {
            if (this._scriptInfo.containsKey(id)) {
                this._scriptInfo.remove(id);
            }
        }

        /**
         * storeScriptState
         * 
         * @param uri
         * @param script
         * @param scope
         * @return String
         */
        public synchronized String storeScriptState(String uri, Script script, Scriptable scope) {
            // inject the script id
            // NOTE: we do this before executing the script so it can have access to this value
            String id = "_" + (++scriptIndex) + "_"; //$NON-NLS-1$ //$NON-NLS-2$
            ScriptableObject.defineProperty(scope, idPropertyName, id, READONLY | DONTENUM);

            // save reference to script info
            // NOTE: we save script info before executing so we can call "include" in the script's
            // global scope. Doing
            // this after "exec" will cause "include" to get a null reference when trying to lookup this
            // script
            ScriptInfo info = new ScriptInfo(script, id, new java.io.File(uri), scope);
            this._scriptInfo.put(id, info);
            this._idXref.put(uri, id);

            return id;
        }

        /**
         * getXrefId
         * 
         * @param path
         * @return String
         */
        public String getXrefId(String path) {
            String result = null;

            if (this._idXref.containsKey(path)) {
                result = (String) this._idXref.get(path);
            }

            return result;
        }

        /**
         * removeXrefId
         * 
         * @param path
         */
        public void removeXrefId(String path) {
            if (this._idXref.containsKey(path)) {
                this._idXref.remove(path);
            }
        }

        /**
         * Get a reference to the editors instance
         * 
         * @return Editors
         */
        public Editors getEditors() {
            return this._editors;
        }

        /**
         * Get a reference to the menus instance
         * 
         * @return Menus
         */
        public Menus getMenus() {
            return this._menus;
        }

        /**
         * Get a reference to the views instance
         * 
         * @return Views
         */
        public Views getViews() {
            return this._views;
        }
    }