com.chiorichan.factory.ScriptingFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.chiorichan.factory.ScriptingFactory.java

Source

/**
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2016 Chiori Greene a.k.a. Chiori-chan <me@chiorichan.com>
 * All Right Reserved.
 */
package com.chiorichan.factory;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

import java.io.File;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.io.Charsets;
import org.apache.commons.lang3.Validate;

import com.chiorichan.AppConfig;
import com.chiorichan.AppLoader;
import com.chiorichan.event.EventBus;
import com.chiorichan.event.EventException;
import com.chiorichan.event.Listener;
import com.chiorichan.factory.event.PostEvalEvent;
import com.chiorichan.factory.event.PostImageProcessor;
import com.chiorichan.factory.event.PostJSMinProcessor;
import com.chiorichan.factory.event.PreCoffeeProcessor;
import com.chiorichan.factory.event.PreEvalEvent;
import com.chiorichan.factory.event.PreLessProcessor;
import com.chiorichan.factory.groovy.GroovyRegistry;
import com.chiorichan.factory.parsers.PreIncludesParserWrapper;
import com.chiorichan.factory.parsers.PreLinksParserWrapper;
import com.chiorichan.lang.ReportingLevel;
import com.chiorichan.lang.ScriptingException;
import com.chiorichan.logger.LogSource;
import com.chiorichan.services.ObjectContext;
import com.chiorichan.util.FileFunc;
import com.chiorichan.util.SecureFunc;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class ScriptingFactory implements LogSource {
    private static final List<ScriptingRegistry> scripting = Lists.newCopyOnWriteArrayList();
    static {
        new GroovyRegistry();

        /**
         * Register Pre-Processors
         */
        register(new PreLinksParserWrapper());
        register(new PreIncludesParserWrapper());
        if (AppConfig.get().getBoolean("advanced.processors.coffeeProcessorEnabled", true))
            register(new PreCoffeeProcessor());
        if (AppConfig.get().getBoolean("advanced.processors.lessProcessorEnabled", true))
            register(new PreLessProcessor());
        // register( new SassPreProcessor() );

        /**
         * Register Post-Processors
         */
        if (AppConfig.get().getBoolean("advanced.processors.minifierJSProcessorEnabled", true))
            register(new PostJSMinProcessor());
        if (AppConfig.get().getBoolean("advanced.processors.imageProcessorEnabled", true))
            register(new PostImageProcessor());
    }

    // For Web Use
    public static ScriptingFactory create(BindingProvider provider) {
        return new ScriptingFactory(provider.getBinding());
    }

    // For General Use
    public static ScriptingFactory create(Map<String, Object> rawBinding) {
        return new ScriptingFactory(new ScriptBinding(rawBinding));
    }

    // For General Use
    public static ScriptingFactory create(ScriptBinding binding) {
        return new ScriptingFactory(binding);
    }

    public static void register(Listener listener) {
        EventBus.instance().registerEvents(listener, new ObjectContext(AppLoader.instances().get(0)));
    }

    /**
     * Registers the provided ScriptingProcessing with the EvalFactory
     *
     * @param registry
     *             The {@link ScriptingRegistry} instance to handle provided types
     */
    public static void register(ScriptingRegistry registry) {
        if (!scripting.contains(registry))
            scripting.add(registry);
    }

    private final Map<ScriptingEngine, List<String>> engines = Maps.newLinkedHashMap();

    private final ScriptBinding binding;

    private final List<ByteBuf> bufferStack = Lists.newLinkedList();

    private Charset charset = Charsets.toCharset(AppConfig.get().getString("server.defaultEncoding", "UTF-8"));

    private final ByteBuf output = Unpooled.buffer();

    private final StackFactory stackFactory = new StackFactory();

    private ScriptingFactory(ScriptBinding binding) {
        Validate.notNull(binding, "The EvalBinding can't be null");
        this.binding = binding;
    }

    public ScriptBinding binding() {
        return binding;
    }

    public Charset charset() {
        return charset;
    }

    private void compileEngines(ScriptingContext context) {
        for (ScriptingRegistry registry : scripting)
            for (ScriptingEngine engine : registry.makeEngines(context))
                if (!contains(engine)) {
                    engine.setBinding(binding);
                    engine.setOutput(output, charset);
                    engines.put(engine, engine.getTypes());
                }
    }

    private boolean contains(ScriptingEngine engine2) {
        for (ScriptingEngine engine1 : engines.keySet())
            if (engine1.getClass() == engine2.getClass())
                return true;
        return false;
    }

    public ScriptingResult eval(ScriptingContext context) {
        ScriptingResult result = context.result();

        context.factory(this);
        context.charset(charset);
        context.baseSource(new String(context.readBytes(), charset));
        binding.setVariable("__FILE__", context.filename() == null ? "<no file>" : context.filename());

        if (result.hasNonIgnorableExceptions())
            return result;

        try {
            String name = "EvalScript" + SecureFunc.rand(8) + ".chi";
            if (!context.isVirtual()) {
                String rel = FileFunc.relPath(context.file().getParentFile(), context.site().directory())
                        .replace('\\', '.').replace('/', '.');
                context.cache(
                        new File(context.cache(), rel.contains(".") ? rel.substring(0, rel.indexOf(".")) : rel));
                context.scriptPackage(rel.contains(".") ? rel.substring(rel.indexOf(".") + 1) : "");
                name = context.file().getName();
            }

            context.scriptName(name);
            stackFactory.stack(name, context);

            PreEvalEvent preEvent = new PreEvalEvent(context);
            try {
                EventBus.instance().callEventWithException(preEvent);
            } catch (Exception e) {
                if (context.result().handleException(e.getCause() == null ? e : e.getCause(), context))
                    return result;
            }

            if (preEvent.isCancelled())
                if (context.result().handleException(new ScriptingException(ReportingLevel.E_ERROR,
                        "Evaluation was cancelled by an internal event"), context))
                    return result;

            if (engines.size() == 0)
                compileEngines(context);

            if (engines.size() > 0)
                for (Entry<ScriptingEngine, List<String>> entry : engines.entrySet())
                    if (entry.getValue() == null || entry.getValue().isEmpty()
                            || entry.getValue().contains(context.shell().toLowerCase()))
                        try {
                            bufferStack.add(output.copy());
                            output.clear();
                            String hash = context.bufferHash();
                            entry.getKey().eval(context);
                            if (context.bufferHash().equals(hash))
                                context.resetAndWrite(output);
                            else
                                context.write(output);
                            output.clear();
                            output.writeBytes(bufferStack.remove(bufferStack.size() - 1));
                            break;
                        } catch (Throwable cause) {
                            if (context.result().handleException(cause, context))
                                return result;
                        }

            PostEvalEvent postEvent = new PostEvalEvent(context);
            try {
                EventBus.instance().callEventWithException(postEvent);
            } catch (EventException e) {
                if (context.result().handleException(e.getCause() == null ? e : e.getCause(), context))
                    return result;
            }
        } finally {
            stackFactory.unstack();
        }

        return result.success(true);
    }

    public Charset getCharset() {
        return charset;
    }

    public String getFileName() {
        List<ScriptTraceElement> scriptTrace = getScriptTrace();

        if (scriptTrace.size() < 1)
            return "<unknown>";

        String fileName = scriptTrace.get(scriptTrace.size() - 1).context().filename();

        if (fileName == null || fileName.isEmpty())
            return "<unknown>";

        return fileName;
    }

    /**
     * Attempts to find the current line number for the current groovy script.
     *
     * @return The current line number. Returns -1 if no there was a problem getting the current line number.
     */
    public int getLineNumber() {
        List<ScriptTraceElement> scriptTrace = getScriptTrace();

        if (scriptTrace.size() < 1)
            return -1;

        return scriptTrace.get(scriptTrace.size() - 1).getLineNumber();
    }

    @Override
    public String getLoggerId() {
        return "ScriptFactory";
    }

    public ByteBuf getOutputStream() {
        return output;
    }

    public List<ScriptTraceElement> getScriptTrace() {
        return stackFactory.examineStackTrace(Thread.currentThread().getStackTrace());
    }

    /**
     * Gives externals subroutines access to the current output stream via print()
     *
     * @param text
     *             The text to output
     */
    public void print(String text) {
        output.writeBytes(text.getBytes(charset));
    }

    /**
     * Gives externals subroutines access to the current output stream via println()
     *
     * @param text
     *             The text to output
     */
    public void println(String text) {
        output.writeBytes((text + "\n").getBytes(charset));
    }

    public void setEncoding(Charset charset) {
        this.charset = charset;
    }

    public void setVariable(String key, Object val) {
        binding.setVariable(key, val);
    }

    public StackFactory stack() {
        return stackFactory;
    }
}