Package nodebox.function

Source Code of nodebox.function.ClojureLibrary$ClojureFunction

package nodebox.function;

import clojure.java.api.Clojure;
import clojure.lang.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import nodebox.util.FileUtils;
import nodebox.util.LoadException;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;

final class ClojureLibrary extends FunctionLibrary {

    /**
     * Run the Clojure register-nodes function in the library.
     *
     * @param fileName The file name.
     * @return The new Clojure library.
     * @throws LoadException If the script could not be loaded.
     */
    public static ClojureLibrary loadScript(String fileName) throws LoadException {
        return loadScript(null, fileName);
    }

    /**
     * Run the Clojure register-nodes function in the library.
     *
     * @param baseFile The file to which the path of this library is relative to.
     * @param fileName The file name.
     * @return The new Clojure library.
     * @throws LoadException If the script could not be loaded.
     */
    public static ClojureLibrary loadScript(File baseFile, String fileName) throws LoadException {
        File file;
        if (baseFile != null) {
            file = new File(baseFile.getAbsoluteFile(), fileName);
        } else {
            file = new File(fileName);
        }
        return loadScript(file);
    }

    private static ClojureLibrary loadScript(File file) {
        Object returnValue;
        try {
            IFn loadFile = Clojure.var("clojure.core", "load-file");
            returnValue = loadFile.invoke(file.getCanonicalPath());
        } catch (IOException e) {
            throw new LoadException(file, e);
        }
        // We need a Var as the last statement, because we need to retrieve the current namespace.
        if (!(returnValue instanceof Var)) {
            throw new LoadException(file,
                    String.format("The last statement does not define a var, but %s.\n", returnValue));
        }
        Var nodesVar = (Var) returnValue;
        Namespace ns = nodesVar.ns;
        String namespace = ns.name.getName();

        ImmutableMap.Builder<String, Function> builder = ImmutableMap.builder();
        for (Object item : ns.getMappings()) {
            MapEntry entry = (MapEntry) item;
            if (entry.getValue() instanceof Var) {
                Var var = (Var) entry.getValue();
                if (var.ns.toString().equals(namespace)) {
                    String name = entry.getKey().toString();
                    if (var.deref() instanceof IFn) {
                        Function f = new ClojureFunction(name, var.fn());
                        builder.put(name, f);
                    }
                }
            }
        }
        return new ClojureLibrary(namespace, file, builder.build());
    }

    private final String namespace;
    private final File file;
    private ImmutableMap<String, Function> functionMap;

    private ClojureLibrary(String namespace, File file, ImmutableMap<String, Function> functionMap) {
        this.namespace = namespace;
        this.file = file;
        this.functionMap = functionMap;
    }

    @Override
    public String getLink(File baseFile) {
        File parentFile = baseFile != null ? baseFile.getParentFile() : null;
        return "clojure:" + FileUtils.getRelativeLink(file, parentFile);
    }

    public String getSimpleIdentifier() {
        return file.getName();
    }

    public String getNamespace() {
        return namespace;
    }

    public String getLanguage() {
        return "clojure";
    }

    public File getFile() {
        return file;
    }

    public Function getFunction(String name) {
        return functionMap.get(name);
    }

    public boolean hasFunction(String name) {
        return functionMap.containsKey(name);
    }

    /**
     * Reloads the clojure module.
     */
    @Override
    public void reload() {
        ClojureLibrary reloadedLibrary = loadScript(file);
        if (!reloadedLibrary.namespace.equals(namespace))
            throw new RuntimeException("The namespace of a function library should not be changed.");
        this.functionMap = reloadedLibrary.functionMap;
    }

    private static final class ClojureFunction implements Function {

        private final String name;
        private final IFn fn;
        private final ImmutableList<Argument> arguments;

        public ClojureFunction(String name, IFn fn) {
            this.name = name;
            this.fn = fn;
            this.arguments = introspect(fn);
        }

        public String getName() {
            return name;
        }

        public Object invoke(Object... args) throws Exception {
            return fn.applyTo(RT.arrayToList(args));
        }

        public ImmutableList<Argument> getArguments() {
            return arguments;
        }

        private static ImmutableList<Argument> introspect(IFn fn) {
            // Each function is a separate class.
            Class functionClass = fn.getClass();
            Method m = Functions.findMethod(functionClass, "invoke");
            return Functions.introspect(m);
        }

    }
}
TOP

Related Classes of nodebox.function.ClojureLibrary$ClojureFunction

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.