blob: 24af1086574c9861626add0aaadd7a38e146c037 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 Fabio Zadrozny and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Fabio Zadrozny - initial API and implementation - http://eclip.se/8519
*******************************************************************************/
package org.eclipse.e4.core.macros.internal;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleScriptContext;
import org.eclipse.e4.core.macros.IMacroInstruction;
import org.eclipse.e4.core.macros.IMacroInstructionFactory;
import org.eclipse.e4.core.macros.IMacroPlaybackContext;
import org.eclipse.e4.core.macros.MacroPlaybackException;
/**
* Actually loads a macro from a JS file to be played back. Works with the
* contents saved from {@code ComposableMacro#toJSBytes()}.
* <p>
* Currently the saved macro is a JavaScript file to be played back again with a
* "runMacro" function which may have multiple "runMacroInstruction" calls to
* run a macro instruction which was previously persisted with
* {@link IMacroInstruction#toMap()}.
* </p>
*/
public class SavedJSMacro implements IMacro {
/**
* The file which contains the contents of the macro.
*/
private final File fFile;
/**
* Creates a macro which is backed by the contents of a javascript file.
*
* @param file
* the file with the contents of the macro.
*/
public SavedJSMacro(File file) {
fFile = file;
}
/**
* Static method to be called when playing back a macro to run a macro
* instruction.
*
* @param macroPlaybackContext
* the context for the macro playback.
* @param macroInstructionId
* the id of the macro instruction to be executed.
* @param macroInstructionParameters
* the parameters to create the macro instruction.
* @param macroInstructionIdToFactory
* a map pointing from the macro instruction id to the factory
* used to create the related macro instruction.
* @throws Exception
* if something happened when creating the macro instruction or
* actually executing it.
*/
@SuppressWarnings({ "rawtypes" })
public static void runMacroInstruction(IMacroPlaybackContext macroPlaybackContext, String macroInstructionId,
Object macroInstructionParameters, Map<String, IMacroInstructionFactory> macroInstructionIdToFactory)
throws Exception {
Map<String, String> stringMap = new HashMap<>();
Map m = (Map) macroInstructionParameters;
Set<Map.Entry> entrySet = m.entrySet();
for (Map.Entry entry : entrySet) {
Object key = entry.getKey();
Object value = entry.getValue();
stringMap.put(key.toString(), value.toString());
}
IMacroInstructionFactory macroFactory = macroInstructionIdToFactory.get(macroInstructionId);
if (macroFactory == null) {
throw new IllegalStateException(
"Unable to find IMacroInstructionFactory for macro instruction: " + macroInstructionId); //$NON-NLS-1$
}
IMacroInstruction macroInstruction = macroFactory.create(stringMap);
macroInstruction.execute(macroPlaybackContext);
}
@Override
public void playback(IMacroPlaybackContext macroPlaybackContext,
Map<String, IMacroInstructionFactory> macroInstructionIdToFactory) throws MacroPlaybackException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript"); //$NON-NLS-1$
if (engine == null) {
throw new MacroPlaybackException(Messages.SavedJSMacro_NoJavaScriptEngineFound);
}
SimpleScriptContext context = new SimpleScriptContext();
context.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
Bindings engineScope = context.getBindings(ScriptContext.ENGINE_SCOPE);
// Setup the default context.
engineScope.put("__macroPlaybackContext", macroPlaybackContext); //$NON-NLS-1$
engineScope.put("__macroInstructionIdToFactory", macroInstructionIdToFactory); //$NON-NLS-1$
try {
engine.eval("" + //$NON-NLS-1$
"__macro = Java.type('org.eclipse.e4.core.macros.internal.SavedJSMacro');\n" //$NON-NLS-1$
+ "function runMacroInstruction(macroMacroInstructionId, macroInstructionParameters){" //$NON-NLS-1$
+ " __macro.runMacroInstruction(__macroPlaybackContext, macroMacroInstructionId, macroInstructionParameters, __macroInstructionIdToFactory);" //$NON-NLS-1$
+ "}" //$NON-NLS-1$
+ "", context); //$NON-NLS-1$
// The contents to execute are actually built at:
// org.eclipse.e4.core.macros.internal.ComposableMacro.toJSBytes()
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(fFile), "UTF-8"))) { //$NON-NLS-1$
// Let any exception running it go through.
// It should define a runMacro() method which we can run later on.
engine.eval(reader, context);
// Actually run the macro now.
engine.eval("runMacro();", context); //$NON-NLS-1$
}
} catch (Exception e) {
throw new MacroPlaybackException(
MessageFormat.format(Messages.SavedJSMacro_MacrosEvalError, e.getMessage()), e);
}
}
}