/************************************************************************** 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()); } }