org.omegat.gui.scripting.ScriptRunner.java Source code

Java tutorial

Introduction

Here is the source code for org.omegat.gui.scripting.ScriptRunner.java

Source

/**************************************************************************
 OmegaT - Computer Assisted Translation (CAT) tool
      with fuzzy matching, translation memory, keyword search,
      glossaries, and translation leveraging into updated projects.
    
 Copyright (C) 2015 Aaron Madlon-Kay
           Home page: http://www.omegat.org/
           Support center: http://groups.yahoo.com/group/OmegaT/
    
 This file is part of OmegaT.
    
 OmegaT is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
    
 OmegaT is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
    
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 **************************************************************************/

package org.omegat.gui.scripting;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.script.Bindings;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.swing.SwingUtilities;

import org.apache.commons.io.FilenameUtils;
import org.omegat.core.Core;
import org.omegat.util.Log;
import org.omegat.util.OStrings;
import org.omegat.util.StringUtil;

public class ScriptRunner {

    /**
     * Scripts that want to run on the Event Dispatch Thread should define a
     * top-level function with this name and NOT evaluate it.
     */
    public static final String SCRIPT_GUI_FUNCTION_NAME = "gui";

    public static final String DEFAULT_SCRIPT = "groovy";
    public static final String VAR_CONSOLE = "console";
    public static final String VAR_MAINWINDOW = "mainWindow";
    public static final String VAR_GLOSSARY = "glossary";
    public static final String VAR_CORE = "Core";
    public static final String VAR_EDITOR = "editor";
    public static final String VAR_PROJECT = "project";
    public static final String VAR_RESOURCES = "res";

    public static final ScriptEngineManager MANAGER = new ScriptEngineManager(ScriptRunner.class.getClassLoader());

    /**
     * Execute as read from the file associated with the supplied
     * {@link ScriptItem}. This is a convenience method for
     * {@link #executeScript(String, ScriptItem, Map)}.
     * 
     * @param item
     * @param additionalBindings
     * @return
     * @throws ScriptException
     * @throws IOException
     * @throws Exception
     */
    public static String executeScript(ScriptItem item, Map<String, Object> additionalBindings)
            throws IOException, ScriptException {
        return executeScript(null, item, additionalBindings);
    }

    /**
     * Execute a script either in string form or, if <code>script</code> is
     * null, as read from the file associated with the supplied
     * {@link ScriptItem}. The engine is resolved via the filename extension
     * associated with <code>item</code> (defaults to {@link #DEFAULT_SCRIPT}).
     * <p>
     * This is a convenience method for
     * {@link #executeScript(String, ScriptEngine, Map)}.
     * 
     * @param script
     *            The script in string form. Can be null.
     * @param item
     *            The associated {@link ScriptItem}. Must not be null. If
     *            <code>script</code> is null, the script content will be read
     *            from the associated file. The script engine will be resolved
     *            from the filename. The resource bundle associated with the
     *            <code>item</code> will be included in the bindings as
     *            {@link #VAR_RESOURCES}.
     * @param additionalBindings
     *            A map of bindings that will be included along with other
     *            bindings
     * @return
     * @throws IOException
     * @throws ScriptException
     */
    public static String executeScript(String script, ScriptItem item, Map<String, Object> additionalBindings)
            throws IOException, ScriptException {
        Map<String, Object> bindings = new HashMap<String, Object>();
        if (additionalBindings != null) {
            bindings.putAll(additionalBindings);
        }
        bindings.put(VAR_RESOURCES, item.getResourceBundle());
        String extension = DEFAULT_SCRIPT;
        if (item.getFile() != null) {
            extension = FilenameUtils.getExtension(item.getFile().getName());
        }
        ScriptEngine engine = MANAGER.getEngineByExtension(extension);
        if (engine == null) {
            engine = MANAGER.getEngineByName(DEFAULT_SCRIPT);
        }
        if (StringUtil.isEmpty(script)) {
            script = item.getText();
        }

        StringBuilder result = new StringBuilder();
        Object eval = executeScript(script, engine, bindings);
        if (eval != null) {
            result.append(OStrings.getString("SCW_SCRIPT_RESULT")).append('\n');
            result.append(eval.toString()).append('\n');
        }
        return result.toString();
    }

    /**
     * Execute a script with a given engine and bindings.
     * 
     * @param script
     *            The script in string form
     * @param engine
     *            The engine
     * @param additionalBindings
     *            A map of bindings that will be included along with other
     *            bindings
     * @return The evaluation result
     * @throws ScriptException
     */
    public static Object executeScript(String script, ScriptEngine engine, Map<String, Object> additionalBindings)
            throws ScriptException {
        // logResult(StaticUtils.format(OStrings.getString("SCW_SELECTED_LANGUAGE"),
        // engine.getFactory().getEngineName()));
        Bindings bindings = engine.createBindings();
        bindings.put(VAR_PROJECT, Core.getProject());
        bindings.put(VAR_EDITOR, Core.getEditor());
        bindings.put(VAR_GLOSSARY, Core.getGlossary());
        bindings.put(VAR_MAINWINDOW, Core.getMainWindow());
        bindings.put(VAR_CORE, Core.class);

        if (additionalBindings != null) {
            bindings.putAll(additionalBindings);
        }
        engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
        Object result = engine.eval(script);
        if (engine instanceof Invocable) {
            invokeGuiScript((Invocable) engine);
        }
        return result;
    }

    private static void invokeGuiScript(Invocable engine) throws ScriptException {
        Runnable invoke = () -> {
            try {
                engine.invokeFunction(SCRIPT_GUI_FUNCTION_NAME);
            } catch (NoSuchMethodException e) {
                // No GUI invocation defined
            } catch (ScriptException e) {
                throw new RuntimeException(e);
            }
        };
        if (SwingUtilities.isEventDispatchThread()) {
            invoke.run();
        } else {
            try {
                SwingUtilities.invokeAndWait(invoke);
            } catch (InvocationTargetException e) {
                // The original cause is double-wrapped at this point
                if (e.getCause().getCause() instanceof ScriptException) {
                    throw (ScriptException) e.getCause().getCause();
                } else {
                    Log.log(e);
                }
            } catch (InterruptedException e) {
                Log.log(e);
            }
        }
    }

    public static List<String> getAvailableScriptExtensions() {
        return MANAGER.getEngineFactories().stream().flatMap(factory -> factory.getExtensions().stream())
                .collect(Collectors.toList());
    }
}