com.simplyian.voxelscript.script.JSLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.simplyian.voxelscript.script.JSLoader.java

Source

/*
 * This file is part of VoxelScript.
 *
 * Copyright (c) 2012-2013, THEDevTeam <http://thedevteam.org/>
 *
 * Licensed 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.simplyian.voxelscript.script;

import java.io.File;
import java.io.FileInputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.EcmaError;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;

import com.simplyian.voxelscript.VoxelScriptPlugin;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.mozilla.javascript.*;

public class JSLoader {
    private final VoxelScriptPlugin plugin;

    // Quick and dirty way to check if something is already being loaded
    private Set<String> loadingPackages = new HashSet<String>();

    private Set<String> loadingScripts = new HashSet<String>();

    private Context cx;

    private Scriptable scope;

    public JSLoader(VoxelScriptPlugin plugin) {
        this.plugin = plugin;
    }

    /**
     * Initializes the script loader, preparing it for loading scripts.
     */
    public void begin() {
        cx = Context.enter();
        scope = cx.initStandardObjects();
        plugin.getModuleManager().setupModuleFunction(scope);
        plugin.getPackageManager().setupPackageFunction(scope);
    }

    /**
     * Ends the loading process.
     */
    public void end() {
        Context.exit();
    }

    /**
     * Loads a package provided its directory.
     *
     * @param name
     * @param dir
     * @return
     * @throws IOException
     */
    public Package loadPackage(String name, File dir) throws IOException {
        Scriptable packageScope = cx.newObject(scope);
        packageScope.setPrototype(scope);
        packageScope.setParentScope(scope);
        packageScope.put("include", scope, new IncludeFunction(plugin, dir, this, packageScope));
        packageScope.put("resource", scope, new ResourceFunction(plugin, dir));

        File mainFile = null;
        PackageDescription desc = null;

        File packageJson = new File(dir.getPath(), "package.json");
        if (!packageJson.exists()) {
            plugin.getLogger().log(Level.WARNING, "A package.json was not found for the package '" + name + "'.");
            mainFile = new File(dir.getPath(), "index.js");
            if (!mainFile.exists()) {
                plugin.getLogger().log(Level.WARNING,
                        "An index.js was not found for the package '" + name + "'. This package was not loaded.");
                return null;
            }
            desc = PackageDescription.load(name, null);
        } else {
            // TODO load package.json
        }

        String script = FileUtils.readFileToString(mainFile);
        if (loadingPackages.contains(name.toLowerCase())) {
            plugin.getLogger().log(Level.WARNING,
                    "Circular dependency detected for something wanting package '" + name + "'. Stopping...");
            return null;
        }

        loadingPackages.add(name.toLowerCase());
        Scriptable exports = runScript(name, script, packageScope);
        loadingPackages.remove(name.toLowerCase());
        if (exports == null) {
            return null;
        }
        return new Package(desc, exports);
    }

    /**
     * Loads the given script.
     *
     * @param cx
     * @param scope
     * @param name
     * @param file
     * @return
     */
    public Script loadScript(String name, File file) throws IOException {
        String script = FileUtils.readFileToString(file);
        if (loadingScripts.contains(name.toLowerCase())) {
            plugin.getLogger().log(Level.WARNING,
                    "Circular dependency detected for something wanting script '" + name + "'. Stopping...");
            return null;
        }

        loadingScripts.add(name.toLowerCase());
        Scriptable exports = runScript(name, script);
        loadingScripts.remove(name.toLowerCase());
        if (exports == null) {
            return null;
        }
        return new Script(name, exports);
    }

    /**
     * Runs a script under the default scope.
     *
     * @param name
     * @param script
     * @return
     */
    Scriptable runScript(String name, String script) {
        return runScript(name, script, scope);
    }

    /**
     * Runs a script.
     *
     * @param name
     * @param script
     * @param importFunction The import function.
     * @return The exports of the script.
     */
    Scriptable runScript(String name, String script, Scriptable parentScope) {
        try {
            Scriptable scriptScope = cx.newObject(parentScope);
            scriptScope.setPrototype(parentScope);
            scriptScope.setParentScope(parentScope);

            // Execute the JS
            cx.evaluateString(scriptScope, "var exports = {};", name, 1, null);
            cx.evaluateString(scriptScope, script, name, 1, null);

            // Get the exports
            Object exportsObj = ScriptableObject.getProperty(scriptScope, "exports");
            Scriptable exports = null;
            if (exportsObj instanceof Scriptable) {
                exports = (Scriptable) exportsObj;
            }

            // Create the script
            return exports;
        } catch (EcmaError ex) {
            plugin.getLogger().log(Level.SEVERE,
                    "JavaScript error: could not finish running the script '" + name + "'.", ex);
            return null;
        } catch (Exception ex) {
            plugin.getLogger().log(Level.SEVERE, "An error occured while executing the script '" + name + "'.", ex);
            return null;
        }
    }
}