com.chiorichan.factory.groovy.EmbeddedGroovyEngine.java Source code

Java tutorial

Introduction

Here is the source code for com.chiorichan.factory.groovy.EmbeddedGroovyEngine.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.groovy;

import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;

import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.chiorichan.factory.ScriptBinding;
import com.chiorichan.factory.ScriptingContext;
import com.chiorichan.factory.ScriptingEngine;
import com.chiorichan.lang.ReportingLevel;
import com.chiorichan.lang.ScriptingException;

/**
 * SeaShell for handling GSP files.
 * ie. Embedded Groovy Files
 *
 * sample:
 * <p>
 * This is plain html<% print ", with a twist of groovy. Today's date: " + date("") %>.
 * </p>
 */
public class EmbeddedGroovyEngine implements ScriptingEngine {
    private static final String MARKER_END = "%>";
    private static final String MARKER_START = "<%";

    private Binding binding = new Binding();
    private GroovyRegistry registry;

    public EmbeddedGroovyEngine(GroovyRegistry registry) {
        this.registry = registry;
    }

    public String escapeFragment(String fragment) {
        String brackets = "\"\"\"";

        fragment = fragment.replace("\\$", "$");
        fragment = fragment.replace("$", "\\$");

        if (fragment.endsWith("\""))
            brackets = "'''";

        return "print " + brackets + fragment + brackets + "; ";
    }

    @Override
    public boolean eval(ScriptingContext context) throws Exception {
        String source = context.readString();

        int fullFileIndex = 0;
        String[] dontStartWith = new String[] { "println", "print", "echo", "def", "import", "if", "for", "do", "}",
                "else", "//", "/*", "\n", "\r" };

        StringBuilder output = new StringBuilder();

        while (fullFileIndex < source.length()) {
            int startIndex = source.indexOf(MARKER_START, fullFileIndex);
            if (-1 != startIndex) {
                // Append all the simple text until the marker

                String fragment = escapeFragment(source.substring(fullFileIndex, startIndex));
                if (!fragment.isEmpty())
                    output.append(fragment);

                int endIndex = source.indexOf(MARKER_END, Math.max(startIndex, fullFileIndex));

                if (endIndex == -1)
                    throw new ScriptingException(ReportingLevel.E_PARSE,
                            "Marker `<%` was not closed after line "
                                    + (StringUtils.countMatches(output.toString(), "\n") + 1)
                                    + ", please check your source file and try again.");

                // Re gets the fragment?
                fragment = source.substring(startIndex + MARKER_START.length(), endIndex);

                boolean appendPrint = true;

                for (String s : dontStartWith)
                    if (fragment.trim().startsWith(s) || fragment.startsWith(s) || fragment.trim().isEmpty())
                        appendPrint = false;

                // void will disable the print depend
                if (fragment.trim().startsWith("void")) {
                    appendPrint = false;
                    fragment = fragment.replace(" *void ?", "");
                }

                if (appendPrint)
                    fragment = "print " + fragment;

                if (!fragment.isEmpty())
                    output.append(fragment + "; ");

                // Position index after end marker
                fullFileIndex = endIndex + MARKER_END.length();
            } else {
                String fragment = escapeFragment(source.substring(fullFileIndex));

                if (!fragment.isEmpty())
                    output.append(fragment);

                // Position index after the end of the file
                fullFileIndex = source.length() + 1;
            }
        }

        context.baseSource(output.toString());
        try {
            Script script = GroovyRegistry.getCachedScript(context, binding);
            if (script == null) {
                GroovyShell shell = registry.getNewShell(context, binding);
                script = registry.makeScript(shell, output.toString(), context);
            }
            context.result().object(script.run());
        } catch (Throwable t) {
            // Clear the input source code and replace it with the exception stack trace
            // context.resetAndWrite( ExceptionUtils.getStackTrace( t ) );
            context.reset();
            throw t;
        }
        return true;
    }

    @Override
    public List<String> getTypes() {
        return Arrays.asList("embedded", "gsp", "jsp", "chi");
    }

    @Override
    public void setBinding(ScriptBinding binding) {
        // Groovy Binding will keep the original EvalBinding map updated automatically. YAY!
        this.binding = new Binding(binding.getVariables());
    }

    @Override
    public void setOutput(ByteBuf buffer, Charset charset) {
        try {
            binding.setProperty("out", new PrintStream(new ByteBufOutputStream(buffer), true, charset.name()));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}