4632N/A/*
4632N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
4632N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4632N/A *
4632N/A * This code is free software; you can redistribute it and/or modify it
4632N/A * under the terms of the GNU General Public License version 2 only, as
4632N/A * published by the Free Software Foundation. Oracle designates this
4632N/A * particular file as subject to the "Classpath" exception as provided
4632N/A * by Oracle in the LICENSE file that accompanied this code.
4632N/A *
4632N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4632N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4632N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4632N/A * version 2 for more details (a copy is included in the LICENSE file that
4632N/A * accompanied this code).
4632N/A *
4632N/A * You should have received a copy of the GNU General Public License version
4632N/A * 2 along with this work; if not, write to the Free Software Foundation,
4632N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4632N/A *
4632N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4632N/A * or visit www.oracle.com if you need additional information or have any
4632N/A * questions.
4632N/A */
4632N/A
4632N/Apackage apple.applescript;
4632N/A
4632N/Aimport java.io.*;
5089N/Aimport java.nio.file.Files;
4632N/Aimport java.util.*;
4632N/Aimport java.util.Map.Entry;
4632N/A
4632N/Aimport javax.script.*;
4632N/A
4632N/A/**
4632N/A * AppleScriptEngine implements JSR 223 for AppleScript on Mac OS X
4632N/A */
4632N/Apublic class AppleScriptEngine implements ScriptEngine {
4632N/A private static native void initNative();
4632N/A
4632N/A private static native long createContextFrom(final Object object);
4632N/A private static native Object createObjectFrom(final long context);
4632N/A private static native void disposeContext(final long context);
4632N/A
4632N/A private static native long evalScript(final String script, long contextptr);
4632N/A private static native long evalScriptFromURL(final String filename, long contextptr);
4632N/A
4632N/A static {
4632N/A System.loadLibrary("AppleScriptEngine");
4632N/A initNative();
4632N/A TRACE("<static-init>");
4632N/A }
4632N/A
4632N/A static void checkSecurity() {
4632N/A final SecurityManager securityManager = System.getSecurityManager();
4632N/A if (securityManager != null) securityManager.checkExec("/usr/bin/osascript");
4632N/A }
4632N/A
4632N/A static void TRACE(final String str) {
4632N/A// System.out.println(AppleScriptEngine.class.getName() + "." + str);
4632N/A }
4632N/A
4632N/A /**
4632N/A * Accessor for the ScriptEngine's long name variable
4632N/A * @return the long name of the ScriptEngine
4632N/A */
4632N/A protected static String getEngine() {
4632N/A TRACE("getEngine()");
4632N/A return AppleScriptEngineFactory.ENGINE_NAME;
4632N/A }
4632N/A
4632N/A /**
4632N/A * Accessor for the ScriptEngine's version
4632N/A * @return the version of the ScriptEngine
4632N/A */
4632N/A protected static String getEngineVersion() {
4632N/A TRACE("getEngineVersion()");
4632N/A return AppleScriptEngineFactory.ENGINE_VERSION;
4632N/A }
4632N/A
4632N/A /**
4632N/A * Accessor for the ScriptEngine's short name
4632N/A * @return the short name of the ScriptEngine
4632N/A */
4632N/A protected static String getName() {
4632N/A TRACE("getName()");
4632N/A return AppleScriptEngineFactory.ENGINE_SHORT_NAME;
4632N/A }
4632N/A
4632N/A /**
4632N/A * Accessor for the ScriptEngine's supported language name
4632N/A * @return the language the ScriptEngine supports
4632N/A */
4632N/A protected static String getLanguage() {
4632N/A TRACE("getLanguage()");
4632N/A return AppleScriptEngineFactory.LANGUAGE;
4632N/A }
4632N/A
4632N/A /**
4632N/A * The no argument constructor sets up the object with default members,
4632N/A * a factory for the engine and a fresh context.
4632N/A * @see com.apple.applescript.AppleScriptEngine#init()
4632N/A */
4632N/A public AppleScriptEngine() {
4632N/A TRACE("<ctor>()");
4632N/A // set our parent factory to be a new factory
4632N/A factory = AppleScriptEngineFactory.getFactory();
4632N/A
4632N/A // set up our noarg bindings
4632N/A setContext(new SimpleScriptContext());
4632N/A put(ARGV, "");
4632N/A
4632N/A init();
4632N/A }
4632N/A
4632N/A /**
4632N/A * All AppleScriptEngines share the same ScriptEngineFactory
4632N/A */
4632N/A private final ScriptEngineFactory factory;
4632N/A
4632N/A /**
4632N/A * The local context for the AppleScriptEngine
4632N/A */
4632N/A private ScriptContext context;
4632N/A
4632N/A /**
4632N/A * The constructor taking a factory as an argument sets the parent factory for
4632N/A * this engine to be the passed factory, and sets up the engine with a fresh context
4632N/A * @param factory
4632N/A * @see com.apple.applescript.AppleScriptEngine#init()
4632N/A */
4632N/A public AppleScriptEngine(final ScriptEngineFactory factory) {
4632N/A // inherit the factory passed to us
4632N/A this.factory = factory;
4632N/A
4632N/A // set up our noarg bindings
4632N/A setContext(new SimpleScriptContext());
4632N/A put(ARGV, "");
4632N/A
4632N/A init();
4632N/A }
4632N/A
4632N/A /**
4632N/A * The initializer populates the local context with some useful predefined variables:
4632N/A * <ul><li><code>javax_script_language_version</code> - the version of AppleScript that the AppleScriptEngine supports.</li>
4632N/A * <li><code>javax_script_language</code> - "AppleScript" -- the language supported by the AppleScriptEngine.</li>
4632N/A * <li><code>javax_script_engine</code> - "AppleScriptEngine" -- the name of the ScriptEngine.</li>
4632N/A * <li><code>javax_script_engine_version</code> - the version of the AppleScriptEngine</li>
4632N/A * <li><code>javax_script_argv</code> - "" -- AppleScript does not take arguments from the command line</li>
4632N/A * <li><code>javax_script_filename</code> - "" -- the currently executing filename</li>
4632N/A * <li><code>javax_script_name</code> - "AppleScriptEngine" -- the short name of the AppleScriptEngine</li>
4632N/A * <li><code>THREADING</code> - null -- the AppleScriptEngine does not support concurrency, you will have to implement thread-safeness yourself.</li></ul>
4632N/A */
4632N/A private void init() {
4632N/A TRACE("init()");
4632N/A // set up our context
4632N/A/* TODO -- name of current executable? bad java documentation at:
4632N/A * http://java.sun.com/javase/6/docs/api/javax/script/ScriptEngine.html#FILENAME */
4632N/A put(ScriptEngine.FILENAME, "");
4632N/A put(ScriptEngine.ENGINE, getEngine());
4632N/A put(ScriptEngine.ENGINE_VERSION, getEngineVersion());
4632N/A put(ScriptEngine.NAME, getName());
4632N/A put(ScriptEngine.LANGUAGE, getLanguage());
4632N/A put(ScriptEngine.LANGUAGE_VERSION, getLanguageVersion());
4632N/A
4632N/A // TODO -- for now, err on the side of caution and say that we are NOT thread-safe
4632N/A put("THREADING", null);
4632N/A }
4632N/A
4632N/A /**
4632N/A * Uses the AppleScriptEngine to get the local AppleScript version
4632N/A * @return the version of AppleScript running on the system
4632N/A */
4632N/A protected String getLanguageVersion() {
4632N/A TRACE("AppleScriptEngine.getLanguageVersion()");
4632N/A try {
4632N/A final Object result = eval("get the version of AppleScript");
4632N/A if (result instanceof String) return (String)result;
4632N/A } catch (final ScriptException e) { e.printStackTrace(); }
4632N/A return "unknown";
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Returns the factory parent of this AppleScriptEngine
4632N/A */
4632N/A public ScriptEngineFactory getFactory() {
4632N/A return factory;
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Return the engine's context
4632N/A * @return this ScriptEngine's context
4632N/A */
4632N/A public ScriptContext getContext() {
4632N/A return context;
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Set a new context for the engine
4632N/A * @param context the new context to install in the engine
4632N/A */
4632N/A public void setContext(final ScriptContext context) {
4632N/A this.context = context;
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Create and return a new set of simple bindings.
4632N/A * @return a new and empty set of bindings
4632N/A */
4632N/A public Bindings createBindings() {
4632N/A return new SimpleBindings();
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Return the engines bindings for the context indicated by the argument.
4632N/A * @param scope contextual scope to return.
4632N/A * @return the bindings in the engine for the scope indicated by the parameter
4632N/A */
4632N/A public Bindings getBindings(final int scope) {
4632N/A return context.getBindings(scope);
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Sets the bindings for the indicated scope
4632N/A * @param bindings a set of bindings to assign to the engine
4632N/A * @param scope the scope that the passed bindings should be assigned to
4632N/A */
4632N/A public void setBindings(final Bindings bindings, final int scope) {
4632N/A context.setBindings(bindings, scope);
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Insert a key and value into the engine's bindings (scope: engine)
4632N/A * @param key the key of the pair
4632N/A * @param value the value of the pair
4632N/A */
4632N/A public void put(final String key, final Object value) {
4632N/A getBindings(ScriptContext.ENGINE_SCOPE).put(key, value);
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Get a value from the engine's bindings using a key (scope: engine)
4632N/A * @param key the key of the pair
4632N/A * @return the value of the pair
4632N/A */
4632N/A public Object get(final String key) {
4632N/A return getBindings(ScriptContext.ENGINE_SCOPE).get(key);
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Passes the Reader argument, as well as the engine's context to a lower evaluation function.<br />
4632N/A * Prefers FileReader or BufferedReader wrapping FileReader as argument.
4632N/A * @param reader a Reader to AppleScript source or compiled AppleScript
4632N/A * @return an Object corresponding to the return value of the script
4632N/A * @see com.apple.applescript.AppleScriptEngine#eval(Reader, ScriptContext)
4632N/A */
4632N/A public Object eval(final Reader reader) throws ScriptException {
4632N/A return eval(reader, getContext());
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Uses the passed bindings as the context for executing the passed script.
4632N/A * @param reader a stream to AppleScript source or compiled AppleScript
4632N/A * @param bindings a Bindings object representing the contexts to execute inside
4632N/A * @return the return value of the script
4632N/A * @see com.apple.applescript.AppleScriptEngine#eval(Reader, ScriptContext)
4632N/A */
4632N/A public Object eval(final Reader reader, final Bindings bindings) throws ScriptException {
4632N/A final Bindings tmp = getContext().getBindings(ScriptContext.ENGINE_SCOPE);
4632N/A getContext().setBindings(bindings, ScriptContext.ENGINE_SCOPE);
4632N/A final Object retval = eval(reader);
4632N/A getContext().setBindings(tmp, ScriptContext.ENGINE_SCOPE);
4632N/A return retval;
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * This function can execute either AppleScript source or compiled AppleScript and functions by writing the
4632N/A * contents of the Reader to a temporary file and then executing it with the engine's context.
4632N/A * @param reader
4632N/A * @param scriptContext
4632N/A * @return an Object corresponding to the return value of the script
4632N/A */
4632N/A public Object eval(final Reader reader, final ScriptContext context) throws ScriptException {
4632N/A checkSecurity();
4632N/A
4632N/A // write our passed reader to a temporary file
4632N/A File tmpfile;
4632N/A FileWriter tmpwrite;
4632N/A try {
5089N/A tmpfile = Files.createTempFile("AppleScriptEngine.", ".scpt").toFile();
4632N/A tmpwrite = new FileWriter(tmpfile);
4632N/A
4632N/A // read in our input and write directly to tmpfile
4632N/A /* TODO -- this may or may not be avoidable for certain Readers,
4632N/A * if a filename can be grabbed, it would be faster to get that and
4632N/A * use the underlying file than writing a temp file.
4632N/A */
4632N/A int data;
4632N/A while ((data = reader.read()) != -1) {
4632N/A tmpwrite.write(data);
4632N/A }
4632N/A tmpwrite.close();
4632N/A
4632N/A // set up our context business
4632N/A final long contextptr = scriptContextToNSDictionary(context);
4632N/A try {
4632N/A final long retCtx = evalScriptFromURL("file://" + tmpfile.getCanonicalPath(), contextptr);
4632N/A Object retVal = (retCtx == 0) ? null : createObjectFrom(retCtx);
4632N/A disposeContext(retCtx);
4632N/A return retVal;
4632N/A } finally {
4632N/A disposeContext(contextptr);
4632N/A tmpfile.delete();
4632N/A }
4632N/A } catch (final IOException e) {
4632N/A throw new ScriptException(e);
4632N/A }
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Evaluate an AppleScript script passed as a source string. Using the engine's built in context.
4632N/A * @param script the string to execute.
4632N/A * @return an Object representing the return value of the script
4632N/A * @see com.apple.applescript.AppleScriptEngine#eval(String, ScriptContext)
4632N/A */
4632N/A public Object eval(final String script) throws ScriptException {
4632N/A return eval(script, getContext());
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent<br />
4632N/A * Evaluate an AppleScript script passed as a source string with a custom ScriptContext.
4632N/A * @param script the AppleScript source to compile and execute.
4632N/A * @param scriptContext the context to execute the script under
4632N/A * @see com.apple.applescript.AppleScriptEngine#eval(String, ScriptContext)
4632N/A */
4632N/A public Object eval(final String script, final Bindings bindings) throws ScriptException {
4632N/A final Bindings tmp = getContext().getBindings(ScriptContext.ENGINE_SCOPE);
4632N/A getContext().setBindings(bindings, ScriptContext.ENGINE_SCOPE);
4632N/A
4632N/A final Object retval = eval(script);
4632N/A getContext().setBindings(tmp, ScriptContext.ENGINE_SCOPE);
4632N/A
4632N/A return retval;
4632N/A }
4632N/A
4632N/A /**
4632N/A * Implementation required by ScriptEngine parent
4632N/A * @param script
4632N/A * @param scriptContext
4632N/A */
4632N/A public Object eval(final String script, final ScriptContext context) throws ScriptException {
4632N/A checkSecurity();
4632N/A final long ctxPtr = scriptContextToNSDictionary(context);
4632N/A try {
4632N/A final long retCtx = evalScript(script, ctxPtr);
4632N/A Object retVal = (retCtx == 0) ? null : createObjectFrom(retCtx);
4632N/A disposeContext(retCtx);
4632N/A return retVal;
4632N/A } finally {
4632N/A disposeContext(ctxPtr);
4632N/A }
4632N/A }
4632N/A
4632N/A /**
4632N/A * Converts a ScriptContext into an NSDictionary
4632N/A * @param context ScriptContext for the engine
4632N/A * @return a pointer to an NSDictionary
4632N/A */
4632N/A private long scriptContextToNSDictionary(final ScriptContext context) throws ScriptException {
4632N/A final Map<String, Object> contextAsMap = new HashMap<String, Object>();
4632N/A for (final Entry<String, Object> e : context.getBindings(ScriptContext.ENGINE_SCOPE).entrySet()) {
4632N/A contextAsMap.put(e.getKey().replaceAll("\\.", "_"), e.getValue());
4632N/A }
4632N/A return createContextFrom(contextAsMap);
4632N/A }
4632N/A}