Package org.mule.tools.rhinodo.rhino

Source Code of org.mule.tools.rhinodo.rhino.NodeRequire$TryExtensionsResult

/**
* Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
*
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/

package org.mule.tools.rhinodo.rhino;

import org.apache.commons.io.FilenameUtils;
import org.mozilla.javascript.*;
import org.mozilla.javascript.commonjs.module.ModuleScope;
import org.mozilla.javascript.commonjs.module.ModuleScriptProvider;
import org.mozilla.javascript.commonjs.module.Require;
import org.mule.tools.rhinodo.api.NativeModule;
import org.mule.tools.rhinodo.impl.ExitCallbackExecutor;
import org.mule.tools.rhinodo.impl.NodeModuleImplBuilder;
import org.mule.tools.rhinodo.node.child_process.ChildProcessNativeModule;
import org.mule.tools.rhinodo.node.fs.FsNativeModule;
import org.mule.tools.rhinodo.node.process.ProcessNativeModule;
import org.mule.tools.rhinodo.node.timer.ClearInterval;
import org.mule.tools.rhinodo.node.timer.ClearTimeout;
import org.mule.tools.rhinodo.node.timer.SetInterval;
import org.mule.tools.rhinodo.node.timer.SetTimeout;
import org.mule.tools.rhinodo.node.vm.VmNativeModule;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import java.io.File;
import java.io.FilenameFilter;
import java.net.URI;
import java.util.*;

public class NodeRequire extends Require {
    private HashMap<String, Scriptable> nativeModuleMap;

    private Script preExec;
    private Script postExec;
    private final ExitCallbackExecutor exitCallbackExecutor;

    private static void executeOptionalScript(Script script, Context cx,
                                              Scriptable executionScope)
    {
        if(script != null) {
            script.exec(cx, executionScope);
        }
    }

    private BaseFunction compile = new BaseFunction() {
        @Override
        public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {

            final ScriptableObject moduleObject = (ScriptableObject)cx.newObject(
                    thisObj);

            defineReadOnlyProperty(moduleObject, "id", args[0]);

            String path = Context.toString(args[1]);
            File file = new File(path);
            URI uri = file.toURI();
            URI base;

            if (file.exists() && file.isFile()) {
                base = file.getParentFile().toURI();
            } else {
                base = file.toURI();
            }

            final Scriptable executionScope = new ModuleScope(thisObj, uri, base);

            defineReadOnlyProperty(moduleObject, "uri", uri.toString());

            Scriptable exports = cx.newObject(scope);
            executionScope.put("exports", executionScope, exports);
            executionScope.put("module", executionScope, moduleObject);
            moduleObject.put("exports", moduleObject, exports);
            install(executionScope);
            executeOptionalScript(preExec, cx, executionScope);
            cx.compileString(Context.toString(args[0]), path, 0, null).exec(cx, executionScope);
            executeOptionalScript(postExec, cx, executionScope);
            return ScriptRuntime.toObject(scope,
                    ScriptableObject.getProperty(moduleObject, "exports"));
        }
    };

    private static void defineReadOnlyProperty(ScriptableObject obj,
                                               String name, Object value) {
        ScriptableObject.putProperty(obj, name, value);
        obj.setAttributes(name, ScriptableObject.READONLY |
                ScriptableObject.PERMANENT);
    }

    public NodeRequire(final Queue<Function> asyncCallbacksQueue, final Scriptable env, final Context cx,
                       final Scriptable globalScope,
                       final ModuleScriptProvider moduleScriptProvider, final Script preExec, final Script postExec,
                       boolean sandboxed, final ExitCallbackExecutor exitCallbackExecutor) {
        super(cx,globalScope,moduleScriptProvider,preExec,postExec,sandboxed);
        this.exitCallbackExecutor = exitCallbackExecutor;
        loadNativeModules(env, cx, globalScope, asyncCallbacksQueue);

        ScriptableObject.putProperty(this, "cache", new NativeObject());

        //Adding extensions property
        NativeObject extensions = new NativeObject();
        BaseFunction notImplemented = new BaseFunction() {
            @Override
            public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
                if (ScriptableObject.getProperty(env, "RHINODO_IGNORE_NOT_IMPLEMENTED_EXTENSIONS") != null) {
                    return cx.newObject(scope);
                }
                throw new NotImplementedException();
            }
        };
        ScriptableObject.putProperty(extensions, ".js", new BaseFunction() {
            @Override
            public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
                return NodeRequire.this.callSuperWrapped(cx, scope, thisObj, new Object[]{args[1]});
            }
        });
        ScriptableObject.putProperty(extensions, ".json", notImplemented);
        ScriptableObject.putProperty(extensions, ".node", notImplemented);

        ScriptableObject.putProperty(this, "extensions", extensions);
        Scriptable process = nativeModuleMap.get("process");
        ScriptableObject.putProperty(globalScope, "process", process);

        ScriptableObject.putProperty(globalScope, "clearTimeout", new ClearTimeout());
        ScriptableObject.putProperty(globalScope, "setTimeout", new SetTimeout(asyncCallbacksQueue));
        ScriptableObject.putProperty(globalScope, "clearInterval", new ClearInterval());
        ScriptableObject.putProperty(globalScope, "setInterval", new SetInterval(asyncCallbacksQueue));

        this.preExec = preExec;
        this.postExec = postExec;
    }

    private void loadNativeModules(Scriptable env, Context cx, Scriptable globalScope,
                                   Queue<Function> asyncCallbacksQueue) {
        NativeModule[] nativeModules = {new FsNativeModule(asyncCallbacksQueue),
                new ProcessNativeModule(env, asyncCallbacksQueue, exitCallbackExecutor),
                new ChildProcessNativeModule(asyncCallbacksQueue),
                new VmNativeModule(asyncCallbacksQueue)};
        nativeModuleMap = new HashMap<String, Scriptable>();

        for (NativeModule nativeModule : nativeModules) {
            nativeModuleMap.put(nativeModule.getId(), nativeModule.getModule(cx, globalScope));
        }

    }

    public Object callSuperWrapped(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        try {
            ScriptableObject.putProperty(thisObj, "_compile", compile);
            return super.call(cx, scope, thisObj, args);
        } catch (JavaScriptException e) {
            if ( thisObj instanceof ModuleScope) {
                String id = (String)Context.jsToJava(args[0], String.class);
                ModuleScope moduleScope = (ModuleScope) thisObj;
                URI base = moduleScope.getBase();
                URI current = moduleScope.getUri();
                URI uri = current.resolve(id  + ".js/");

                if (!id.startsWith("./") && !id.startsWith("../") && base != null &&
                        new File(uri.getPath()).exists()) {
                    // try to convert to a relative URI rooted on base
                    return super.call(cx, scope, thisObj, new Object[]{uri.getPath()});
                }
            }
            throw e;
        }
    }

    public static class TryExtensionsResult {
        private final String extensionAsString;
        private final Function callback;
        private final File file;

        public TryExtensionsResult(String extensionAsString, Function value, File file) {
            this.extensionAsString = extensionAsString;
            this.callback = value;
            this.file = file;
        }

        public String getExtensionAsString() {
            return extensionAsString;
        }

        public Function getCallback() {
            return callback;
        }

        public File getFile() {
            return file;
        }
    }

    public TryExtensionsResult tryExtensions(String id, ModuleScope thisObj) {
        File cwdFile = getBasePathForModule(thisObj);
        NativeObject extensions = ScriptableObject.getTypedProperty(this, "extensions", NativeObject.class);
        Object[] propertyIds = ScriptableObject.getPropertyIds(extensions);

        if (!(id.startsWith("./") || id.startsWith("../") || id.startsWith("/"))) {
            return null;
        }

        for (Object extension : propertyIds) {
            String extensionAsString = (String) extension;
            File file;
            if ( cwdFile != null ) {
                file = new File(FilenameUtils.concat(cwdFile.getAbsolutePath(), id + extensionAsString));

                /* Case of ./ only where we need to add index.js */
                if (!file.exists()) {
                    file = new File(FilenameUtils.concat(cwdFile.getAbsolutePath(), id + "index" + extensionAsString));
                }
            } else {
                /* Case of /a/b/c.js */
                file = new File(id + extensionAsString);
            }

            if ( file.exists() ) {
                Function value = ScriptableObject.getTypedProperty(extensions, extensionAsString,
                        Function.class);
                return new TryExtensionsResult(extensionAsString, value, file);
            }
        }

        return null;
    }


    @Override
    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        String id = Context.toString(args[0]);

        Scriptable moduleToLoad;
        if ((moduleToLoad = nativeModuleMap.get(id)) != null ) {
            return moduleToLoad;
        }

        Scriptable extensions = ScriptableObject.getTypedProperty(this, "extensions", Scriptable.class);

        File file = new File(id);
        File packageJson = new File(id, "package.json");
        /* Case when a path like a/b/c is required and a file a/b/c.js exists */
        TryExtensionsResult extensionsResult;

        /* Absolute path */
        if (id.startsWith("/") && (extensionsResult = tryExtensions(id, null)) != null) {
            return extensionsResult.getCallback().call(cx, scope, thisObj, new Object[]{thisObj,
                    extensionsResult.getFile().getAbsolutePath()});
        /* Relative Path */
        } else if ( thisObj instanceof ModuleScope &&
                (extensionsResult = tryExtensions(id, (ModuleScope)thisObj)) != null ) {
            return extensionsResult.getCallback().call(cx, scope, thisObj,
                    new Object[]{thisObj, extensionsResult.getFile().getAbsolutePath()});
        } else if file.exists() && !file.isDirectory() &&
                ScriptableObject.hasProperty(extensions, "." + FilenameUtils.getExtension(id) ) ) {
            return ScriptableObject.getTypedProperty(extensions,  "." + FilenameUtils.getExtension( id),
                    Function.class ).call(cx, scope, thisObj, new Object[]{thisObj, id});
        /* Case when a path like a/b/c and a file named a/b/c/package.json exists */
        } else if ( file.isDirectory() && packageJson.exists() && packageJson.isFile() ) {
            Map<String,String> map = NodeModuleImplBuilder.getPackageJSONMap(packageJson);

            String main;
            if ( map != null && (main = map.get("main")) != null ) {
                /* Fetch entry point */
                File mainFile = new File(FilenameUtils.concat(file.getPath(), main));
                return callSuperWrapped(cx, scope, thisObj, new Object[]{mainFile.getAbsolutePath()});
            }
        } else if (thisObj instanceof ModuleScope) {
            File cwdFile = getBasePathForModule((ModuleScope) thisObj);
            cwdFile = getModuleRootDirectory(cwdFile);

            if (cwdFile == null) {
                return callSuperWrapped(cx, scope, thisObj, args);
            }

            File modulePath = new File(new File(cwdFile, "node_modules"), id);
            File modulePackageJSON = new File(modulePath, "package.json");
            if ( modulePackageJSON.exists()) {
                Map<String,String> map = NodeModuleImplBuilder.getPackageJSONMap(modulePackageJSON);

                String main = map.get("main");
                if (main == null) {
                    main = "index.js";
                }
                /* Fetch entry point */
                File mainFile = new File(FilenameUtils.concat(modulePath.getPath(),
                        main));
                if ( !mainFile.exists() ) {
                    mainFile = new File(FilenameUtils.concat(modulePath.getPath(),
                            main + ".js"));
                }
                return callSuperWrapped(cx, scope, thisObj,
                        new Object[]{mainFile.getAbsolutePath()});
            }

            cwdFile = getModuleRootDirectory(cwdFile.getParentFile());

            if (cwdFile == null) {
                return callSuperWrapped(cx, scope, thisObj, args);
            }

            modulePath = new File(new File(cwdFile, "node_modules"), id);
            modulePackageJSON = new File(modulePath, "package.json");
            if ( modulePackageJSON.exists()) {
                Map<String,String> map = NodeModuleImplBuilder.getPackageJSONMap(modulePackageJSON);

                /* Fetch entry point */
                File mainFile = new File(FilenameUtils.concat(modulePath.getPath(),
                        map.get("main")));
                if ( !mainFile.exists() ) {
                    mainFile = new File(FilenameUtils.concat(modulePath.getPath(),
                            map.get("main")+ ".js"));
                }
                return callSuperWrapped(cx, scope, thisObj,
                        new Object[]{mainFile.getAbsolutePath()});
            }
        }

        return callSuperWrapped(cx, scope, thisObj, args);
    }

    private File getModuleRootDirectory(File cwdFile) {
        String[] list;

        FilenameFilter filenameFilter = new FilenameFilter() {
            @Override
            public boolean accept(File file, String s) {
                return s.equals("package.json");
            }
        };
        list = cwdFile.list(filenameFilter);

        while (list.length != 1 ) {
            cwdFile = cwdFile.getParentFile();
            if ( cwdFile == null ) {
                return null;
            }
            list = cwdFile.list(filenameFilter);
        }

        return cwdFile;
    }

    private File getBasePathForModule(ModuleScope thisObj) {
        ModuleScope moduleScope = thisObj;
        if (moduleScope == null) {
            return null;
        }
        URI base = moduleScope.getUri();
        File cwdFile = new File(base.getPath());
        if ( !cwdFile.isDirectory() ) {
            cwdFile = cwdFile.getParentFile();
        }
        return cwdFile;
    }
}
TOP

Related Classes of org.mule.tools.rhinodo.rhino.NodeRequire$TryExtensionsResult

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.