/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package apple.applescript;

import java.io.*;
import java.nio.file.Files;
import java.util.*;
import java.util.Map.Entry;

import javax.script.*;

AppleScriptEngine implements JSR 223 for AppleScript on Mac OS X
/** * AppleScriptEngine implements JSR 223 for AppleScript on Mac OS X */
public class AppleScriptEngine implements ScriptEngine { private static native void initNative(); private static native long createContextFrom(final Object object); private static native Object createObjectFrom(final long context); private static native void disposeContext(final long context); private static native long evalScript(final String script, long contextptr); private static native long evalScriptFromURL(final String filename, long contextptr); static { System.loadLibrary("AppleScriptEngine"); initNative(); TRACE("<static-init>"); } static void checkSecurity() { final SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) securityManager.checkExec("/usr/bin/osascript"); } static void TRACE(final String str) { // System.out.println(AppleScriptEngine.class.getName() + "." + str); }
Accessor for the ScriptEngine's long name variable
Returns:the long name of the ScriptEngine
/** * Accessor for the ScriptEngine's long name variable * @return the long name of the ScriptEngine */
protected static String getEngine() { TRACE("getEngine()"); return AppleScriptEngineFactory.ENGINE_NAME; }
Accessor for the ScriptEngine's version
Returns:the version of the ScriptEngine
/** * Accessor for the ScriptEngine's version * @return the version of the ScriptEngine */
protected static String getEngineVersion() { TRACE("getEngineVersion()"); return AppleScriptEngineFactory.ENGINE_VERSION; }
Accessor for the ScriptEngine's short name
Returns:the short name of the ScriptEngine
/** * Accessor for the ScriptEngine's short name * @return the short name of the ScriptEngine */
protected static String getName() { TRACE("getName()"); return AppleScriptEngineFactory.ENGINE_SHORT_NAME; }
Accessor for the ScriptEngine's supported language name
Returns:the language the ScriptEngine supports
/** * Accessor for the ScriptEngine's supported language name * @return the language the ScriptEngine supports */
protected static String getLanguage() { TRACE("getLanguage()"); return AppleScriptEngineFactory.LANGUAGE; }
The no argument constructor sets up the object with default members, a factory for the engine and a fresh context.
See Also:
  • init.init()
/** * The no argument constructor sets up the object with default members, * a factory for the engine and a fresh context. * @see com.apple.applescript.AppleScriptEngine#init() */
public AppleScriptEngine() { TRACE("<ctor>()"); // set our parent factory to be a new factory factory = AppleScriptEngineFactory.getFactory(); // set up our noarg bindings setContext(new SimpleScriptContext()); put(ARGV, ""); init(); }
All AppleScriptEngines share the same ScriptEngineFactory
/** * All AppleScriptEngines share the same ScriptEngineFactory */
private final ScriptEngineFactory factory;
The local context for the AppleScriptEngine
/** * The local context for the AppleScriptEngine */
private ScriptContext context;
The constructor taking a factory as an argument sets the parent factory for this engine to be the passed factory, and sets up the engine with a fresh context
Params:
  • factory –
See Also:
  • init.init()
/** * The constructor taking a factory as an argument sets the parent factory for * this engine to be the passed factory, and sets up the engine with a fresh context * @param factory * @see com.apple.applescript.AppleScriptEngine#init() */
public AppleScriptEngine(final ScriptEngineFactory factory) { // inherit the factory passed to us this.factory = factory; // set up our noarg bindings setContext(new SimpleScriptContext()); put(ARGV, ""); init(); }
The initializer populates the local context with some useful predefined variables:
  • javax_script_language_version - the version of AppleScript that the AppleScriptEngine supports.
  • javax_script_language - "AppleScript" -- the language supported by the AppleScriptEngine.
  • javax_script_engine - "AppleScriptEngine" -- the name of the ScriptEngine.
  • javax_script_engine_version - the version of the AppleScriptEngine
  • javax_script_argv - "" -- AppleScript does not take arguments from the command line
  • javax_script_filename - "" -- the currently executing filename
  • javax_script_name - "AppleScriptEngine" -- the short name of the AppleScriptEngine
  • THREADING - null -- the AppleScriptEngine does not support concurrency, you will have to implement thread-safeness yourself.
/** * The initializer populates the local context with some useful predefined variables: * <ul><li><code>javax_script_language_version</code> - the version of AppleScript that the AppleScriptEngine supports.</li> * <li><code>javax_script_language</code> - "AppleScript" -- the language supported by the AppleScriptEngine.</li> * <li><code>javax_script_engine</code> - "AppleScriptEngine" -- the name of the ScriptEngine.</li> * <li><code>javax_script_engine_version</code> - the version of the AppleScriptEngine</li> * <li><code>javax_script_argv</code> - "" -- AppleScript does not take arguments from the command line</li> * <li><code>javax_script_filename</code> - "" -- the currently executing filename</li> * <li><code>javax_script_name</code> - "AppleScriptEngine" -- the short name of the AppleScriptEngine</li> * <li><code>THREADING</code> - null -- the AppleScriptEngine does not support concurrency, you will have to implement thread-safeness yourself.</li></ul> */
private void init() { TRACE("init()"); // set up our context /* TODO -- name of current executable? bad java documentation at: * https://docs.oracle.com/javase/6/docs/api/javax/script/ScriptEngine.html#FILENAME */ put(ScriptEngine.FILENAME, ""); put(ScriptEngine.ENGINE, getEngine()); put(ScriptEngine.ENGINE_VERSION, getEngineVersion()); put(ScriptEngine.NAME, getName()); put(ScriptEngine.LANGUAGE, getLanguage()); put(ScriptEngine.LANGUAGE_VERSION, getLanguageVersion()); // TODO -- for now, err on the side of caution and say that we are NOT thread-safe put("THREADING", null); }
Uses the AppleScriptEngine to get the local AppleScript version
Returns:the version of AppleScript running on the system
/** * Uses the AppleScriptEngine to get the local AppleScript version * @return the version of AppleScript running on the system */
protected String getLanguageVersion() { TRACE("AppleScriptEngine.getLanguageVersion()"); try { final Object result = eval("get the version of AppleScript"); if (result instanceof String) return (String)result; } catch (final ScriptException e) { e.printStackTrace(); } return "unknown"; }
Implementation required by ScriptEngine parent
Returns the factory parent of this AppleScriptEngine
/** * Implementation required by ScriptEngine parent<br /> * Returns the factory parent of this AppleScriptEngine */
public ScriptEngineFactory getFactory() { return factory; }
Implementation required by ScriptEngine parent
Return the engine's context
Returns:this ScriptEngine's context
/** * Implementation required by ScriptEngine parent<br /> * Return the engine's context * @return this ScriptEngine's context */
public ScriptContext getContext() { return context; }
Implementation required by ScriptEngine parent
Set a new context for the engine
Params:
  • context – the new context to install in the engine
/** * Implementation required by ScriptEngine parent<br /> * Set a new context for the engine * @param context the new context to install in the engine */
public void setContext(final ScriptContext context) { this.context = context; }
Implementation required by ScriptEngine parent
Create and return a new set of simple bindings.
Returns:a new and empty set of bindings
/** * Implementation required by ScriptEngine parent<br /> * Create and return a new set of simple bindings. * @return a new and empty set of bindings */
public Bindings createBindings() { return new SimpleBindings(); }
Implementation required by ScriptEngine parent
Return the engines bindings for the context indicated by the argument.
Params:
  • scope – contextual scope to return.
Returns:the bindings in the engine for the scope indicated by the parameter
/** * Implementation required by ScriptEngine parent<br /> * Return the engines bindings for the context indicated by the argument. * @param scope contextual scope to return. * @return the bindings in the engine for the scope indicated by the parameter */
public Bindings getBindings(final int scope) { return context.getBindings(scope); }
Implementation required by ScriptEngine parent
Sets the bindings for the indicated scope
Params:
  • bindings – a set of bindings to assign to the engine
  • scope – the scope that the passed bindings should be assigned to
/** * Implementation required by ScriptEngine parent<br /> * Sets the bindings for the indicated scope * @param bindings a set of bindings to assign to the engine * @param scope the scope that the passed bindings should be assigned to */
public void setBindings(final Bindings bindings, final int scope) { context.setBindings(bindings, scope); }
Implementation required by ScriptEngine parent
Insert a key and value into the engine's bindings (scope: engine)
Params:
  • key – the key of the pair
  • value – the value of the pair
/** * Implementation required by ScriptEngine parent<br /> * Insert a key and value into the engine's bindings (scope: engine) * @param key the key of the pair * @param value the value of the pair */
public void put(final String key, final Object value) { getBindings(ScriptContext.ENGINE_SCOPE).put(key, value); }
Implementation required by ScriptEngine parent
Get a value from the engine's bindings using a key (scope: engine)
Params:
  • key – the key of the pair
Returns:the value of the pair
/** * Implementation required by ScriptEngine parent<br /> * Get a value from the engine's bindings using a key (scope: engine) * @param key the key of the pair * @return the value of the pair */
public Object get(final String key) { return getBindings(ScriptContext.ENGINE_SCOPE).get(key); }
Implementation required by ScriptEngine parent
Passes the Reader argument, as well as the engine's context to a lower evaluation function.
Prefers FileReader or BufferedReader wrapping FileReader as argument.
Params:
  • reader – a Reader to AppleScript source or compiled AppleScript
See Also:
  • eval.eval(Reader, ScriptContext)
Returns:an Object corresponding to the return value of the script
/** * Implementation required by ScriptEngine parent<br /> * Passes the Reader argument, as well as the engine's context to a lower evaluation function.<br /> * Prefers FileReader or BufferedReader wrapping FileReader as argument. * @param reader a Reader to AppleScript source or compiled AppleScript * @return an Object corresponding to the return value of the script * @see com.apple.applescript.AppleScriptEngine#eval(Reader, ScriptContext) */
public Object eval(final Reader reader) throws ScriptException { return eval(reader, getContext()); }
Implementation required by ScriptEngine parent
Uses the passed bindings as the context for executing the passed script.
Params:
  • reader – a stream to AppleScript source or compiled AppleScript
  • bindings – a Bindings object representing the contexts to execute inside
See Also:
  • eval.eval(Reader, ScriptContext)
Returns:the return value of the script
/** * Implementation required by ScriptEngine parent<br /> * Uses the passed bindings as the context for executing the passed script. * @param reader a stream to AppleScript source or compiled AppleScript * @param bindings a Bindings object representing the contexts to execute inside * @return the return value of the script * @see com.apple.applescript.AppleScriptEngine#eval(Reader, ScriptContext) */
public Object eval(final Reader reader, final Bindings bindings) throws ScriptException { final Bindings tmp = getContext().getBindings(ScriptContext.ENGINE_SCOPE); getContext().setBindings(bindings, ScriptContext.ENGINE_SCOPE); final Object retval = eval(reader); getContext().setBindings(tmp, ScriptContext.ENGINE_SCOPE); return retval; }
Implementation required by ScriptEngine parent
This function can execute either AppleScript source or compiled AppleScript and functions by writing the contents of the Reader to a temporary file and then executing it with the engine's context.
Params:
  • reader –
  • scriptContext –
Returns:an Object corresponding to the return value of the script
/** * Implementation required by ScriptEngine parent<br /> * This function can execute either AppleScript source or compiled AppleScript and functions by writing the * contents of the Reader to a temporary file and then executing it with the engine's context. * @param reader * @param scriptContext * @return an Object corresponding to the return value of the script */
public Object eval(final Reader reader, final ScriptContext context) throws ScriptException { checkSecurity(); // write our passed reader to a temporary file File tmpfile; FileWriter tmpwrite; try { tmpfile = Files.createTempFile("AppleScriptEngine.", ".scpt").toFile(); tmpwrite = new FileWriter(tmpfile); // read in our input and write directly to tmpfile /* TODO -- this may or may not be avoidable for certain Readers, * if a filename can be grabbed, it would be faster to get that and * use the underlying file than writing a temp file. */ int data; while ((data = reader.read()) != -1) { tmpwrite.write(data); } tmpwrite.close(); // set up our context business final long contextptr = scriptContextToNSDictionary(context); try { final long retCtx = evalScriptFromURL("file://" + tmpfile.getCanonicalPath(), contextptr); Object retVal = (retCtx == 0) ? null : createObjectFrom(retCtx); disposeContext(retCtx); return retVal; } finally { disposeContext(contextptr); tmpfile.delete(); } } catch (final IOException e) { throw new ScriptException(e); } }
Implementation required by ScriptEngine parent
Evaluate an AppleScript script passed as a source string. Using the engine's built in context.
Params:
  • script – the string to execute.
See Also:
  • eval.eval(String, ScriptContext)
Returns:an Object representing the return value of the script
/** * Implementation required by ScriptEngine parent<br /> * Evaluate an AppleScript script passed as a source string. Using the engine's built in context. * @param script the string to execute. * @return an Object representing the return value of the script * @see com.apple.applescript.AppleScriptEngine#eval(String, ScriptContext) */
public Object eval(final String script) throws ScriptException { return eval(script, getContext()); }
Implementation required by ScriptEngine parent
Evaluate an AppleScript script passed as a source string with a custom ScriptContext.
Params:
  • script – the AppleScript source to compile and execute.
  • scriptContext – the context to execute the script under
See Also:
  • eval.eval(String, ScriptContext)
/** * Implementation required by ScriptEngine parent<br /> * Evaluate an AppleScript script passed as a source string with a custom ScriptContext. * @param script the AppleScript source to compile and execute. * @param scriptContext the context to execute the script under * @see com.apple.applescript.AppleScriptEngine#eval(String, ScriptContext) */
public Object eval(final String script, final Bindings bindings) throws ScriptException { final Bindings tmp = getContext().getBindings(ScriptContext.ENGINE_SCOPE); getContext().setBindings(bindings, ScriptContext.ENGINE_SCOPE); final Object retval = eval(script); getContext().setBindings(tmp, ScriptContext.ENGINE_SCOPE); return retval; }
Implementation required by ScriptEngine parent
Params:
  • script –
  • scriptContext –
/** * Implementation required by ScriptEngine parent * @param script * @param scriptContext */
public Object eval(final String script, final ScriptContext context) throws ScriptException { checkSecurity(); final long ctxPtr = scriptContextToNSDictionary(context); try { final long retCtx = evalScript(script, ctxPtr); Object retVal = (retCtx == 0) ? null : createObjectFrom(retCtx); disposeContext(retCtx); return retVal; } finally { disposeContext(ctxPtr); } }
Converts a ScriptContext into an NSDictionary
Params:
  • context – ScriptContext for the engine
Returns:a pointer to an NSDictionary
/** * Converts a ScriptContext into an NSDictionary * @param context ScriptContext for the engine * @return a pointer to an NSDictionary */
private long scriptContextToNSDictionary(final ScriptContext context) throws ScriptException { final Map<String, Object> contextAsMap = new HashMap<String, Object>(); for (final Entry<String, Object> e : context.getBindings(ScriptContext.ENGINE_SCOPE).entrySet()) { contextAsMap.put(e.getKey().replaceAll("\\.", "_"), e.getValue()); } return createContextFrom(contextAsMap); } }