/* * Scriptographer * * This file is part of Scriptographer, a Scripting Plugin for Adobe Illustrator * http://scriptographer.org/ * * Copyright (c) 2002-2010, Juerg Lehni * http://scratchdisk.com/ * * All rights reserved. See LICENSE file for details. * * File created on Feb 19, 2007. */ package com.scratchdisk.script; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import com.scratchdisk.util.ClassUtils; /** * @author lehni * */ public abstract class ScriptEngine { private static HashMap<String, ScriptEngine> enginesByName = new HashMap<String, ScriptEngine>(); private static HashMap<String, ScriptEngine> enginesByExtension = new HashMap<String, ScriptEngine>(); private static boolean loaded = false; private HashMap<File, Script> scriptCache = new HashMap<File, Script>(); public ScriptEngine(String name, String extension) { addName(name); addExtension(extension); } public void addExtension(String extension) { enginesByExtension.put(extension, this); } public void addName(String name) { enginesByName.put(name, this); } public static void loadEngines() { // Do not call loadEngines immediately, as we want the scripting engines // to be instantiated in the same thread as from where they are used... // TODO: Move to a multi threaded scenario, where RhinoEngine creates // Contexts for reach script call... String[] lines = ClassUtils.getServiceInformation(ScriptEngine.class); if (lines != null) { for (int i = 0; i < lines.length; i++) { try { Class.forName(lines[i]).newInstance(); } catch (Exception e) { e.printStackTrace(); } } loaded = true; } ArgumentConverter.loadConverters(); } public static ScriptEngine getEngineByName(String name) { if (!loaded) loadEngines(); return enginesByName.get(name); } public static ScriptEngine getEngineByExtension(String extension) { if (!loaded) loadEngines(); return enginesByExtension.get(extension); } public static ScriptEngine getEngineByFile(File file) { String name = file.getName(); int pos = name.lastIndexOf('.'); return pos != -1 ? getEngineByExtension(name.substring(pos + 1)) : null; } public abstract <T> T toJava(Object object, Class<T> type); public abstract ArgumentReader getArgumentReader(Object object); public abstract boolean observe(Map object, Object key, PropertyObserver observer); @SuppressWarnings("unchecked") public static <T> T convertToJava(Object object, Class<T> type) { if (type.isInstance(object)) return (T) object; for (ScriptEngine engine : enginesByName.values()) { T res = engine.toJava(object, type); if (type.isInstance(res)) return res; } return null; } public static ArgumentReader convertToArgumentReader(Object object) { for (ScriptEngine engine : enginesByName.values()) { ArgumentReader reader = engine.getArgumentReader(object); if (reader != null) return reader; } return null; } public static boolean observeChanges(Map object, Object key, PropertyObserver observer) { for (ScriptEngine engine : enginesByName.values()) { if (engine.observe(object, key, observer)) return true; } return false; } public abstract Scope createScope(); public abstract Scope getScope(Object object); public abstract Scope getGlobalScope(); protected abstract Script compileScript(File file) throws ScriptException, IOException; /** * Compiles the specified file. * Caching for the compiled scripts is used for speed increase. * * @param file * @throws IOException * @throws ScriptException */ public Script compile(File file) throws ScriptException, IOException { Script script = scriptCache.get(file); if (script == null || script.hasChanged()) { script = compileScript(file); scriptCache.put(file, script); } return script; } public abstract Script compile(String code, String name); public Object evaluate(String code, String name, Scope scope) throws ScriptException { Script script = compile(code, name); return script.execute(scope); } /** * Returns a shortened version of the path to the script file. The script * engine can provide its own mechanism, e.g. allowing multiple script * roots, through this one simple method. Returning null means the path * should not be shown to the user. */ public String[] getScriptPath(File file) { ArrayList<String> parts = new ArrayList<String>(); while (file != null) { parts.add(0, file.getName()); file = file.getParentFile(); } return parts.toArray(new String[parts.size()]); } }