Java tutorial
/* * -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for * the specific language governing rights and limitations under the License. * * The Original Code is Rhino code, released May 6, 1998. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by the Initial Developer are Copyright (C) * 1997-1999 the Initial Developer. All Rights Reserved. * * Contributor(s): Patrick Beard Igor Bukanov Norris Boyd Rob Ginda Kurt * Westerfeld Matthias Radestock * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 or later (the "GPL"), in which case the * provisions of the GPL are applicable instead of those above. If you wish to * allow use of your version of this file only under the terms of the GPL and * not to allow others to use your version of this file under the MPL, indicate * your decision by deleting the provisions above and replacing them with the * notice and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under either * the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package org.moyrax.javascript.shell; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.Reader; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.net.URLConnection; import java.util.HashMap; import java.util.Map; import net.sourceforge.htmlunit.corejs.javascript.Context; import net.sourceforge.htmlunit.corejs.javascript.ContextAction; import net.sourceforge.htmlunit.corejs.javascript.ContextFactory; import net.sourceforge.htmlunit.corejs.javascript.Function; import net.sourceforge.htmlunit.corejs.javascript.ImporterTopLevel; import net.sourceforge.htmlunit.corejs.javascript.NativeArray; import net.sourceforge.htmlunit.corejs.javascript.Script; import net.sourceforge.htmlunit.corejs.javascript.ScriptRuntime; import net.sourceforge.htmlunit.corejs.javascript.Scriptable; import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject; import net.sourceforge.htmlunit.corejs.javascript.Synchronizer; import net.sourceforge.htmlunit.corejs.javascript.Undefined; import net.sourceforge.htmlunit.corejs.javascript.Wrapper; import net.sourceforge.htmlunit.corejs.javascript.serialize.ScriptableInputStream; import net.sourceforge.htmlunit.corejs.javascript.serialize.ScriptableOutputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.moyrax.javascript.annotation.GlobalFunction; import org.moyrax.javascript.tool.ToolErrorReporter; /** * This class provides for sharing functions across multiple threads. This is of * particular interest to server applications. * * @author Norris Boyd */ @SuppressWarnings("all") @org.moyrax.javascript.annotation.Script public class Global extends ImporterTopLevel { /** Default logger for this class. */ private static final Log log = LogFactory.getLog(Global.class); static final long serialVersionUID = 4029130780977538005L; /** * Keeps the whole list of scopes binded to this {@link ScriptableObject}. */ private static final Map<String, Scriptable> scopes = new HashMap<String, Scriptable>(); NativeArray history; private InputStream inStream; private PrintStream outStream; private PrintStream errStream; private boolean sealedStdLib = false; boolean initialized; private QuitAction quitAction; private Scriptable wrappedScope; public Global() { } public Global(final Scriptable aWrappedScope) { this.wrappedScope = aWrappedScope; } /** * Occurs when this {@link Scriptable} object is initialized. * * @param scope Scope that's being initialized. */ public static void init(final Scriptable scope) { if (!scopes.containsKey(scope.getClassName())) { scopes.put(scope.getClassName(), new Global(scope)); } else { log.debug("Initialization warning", new IllegalArgumentException("The scope is already registered: " + scope.getClassName())); } } /** * Set the action to call from quit(). */ public void initQuitAction(QuitAction quitAction) { if (quitAction == null) throw new IllegalArgumentException("quitAction is null"); if (this.quitAction != null) throw new IllegalArgumentException("The method is once-call."); this.quitAction = quitAction; } public void init(ContextFactory factory) { factory.call(new ContextAction() { public Object run(Context cx) { init(cx); return null; } }); } public void init(Context cx) { // Define some global functions particular to the shell. Note // that these functions are not part of ECMA. initStandardObjects(cx, sealedStdLib); String[] names = { "defineClass", "deserialize", "help", "load", "loadClass", "print", "quit", "readFile", "readUrl", "runCommand", "seal", "serialize", "spawn", "sync", "toint32", "version", }; defineFunctionProperties(names, Global.class, ScriptableObject.DONTENUM); // Set up "environment" in the global scope to provide access to the // System environment variables. Environment.defineClass(this); Environment environment = new Environment(this); defineProperty("environment", environment, ScriptableObject.DONTENUM); history = (NativeArray) cx.newArray(this, 0); defineProperty("history", history, ScriptableObject.DONTENUM); initialized = true; } /** * Print a help message. * * This method is defined as a JavaScript function. */ @GlobalFunction public static void help(Context cx, Scriptable thisObj, Object[] args, Function funObj) { PrintStream out = getInstance(funObj).getOut(); out.println(ToolErrorReporter.getMessage("msg.help")); } /** * Print the string values of its arguments. * * This method is defined as a JavaScript function. Note that its arguments * are of the "varargs" form, which allows it to handle an arbitrary number of * arguments supplied to the JavaScript function. * */ @GlobalFunction public static Object print(Context cx, Scriptable thisObj, Object[] args, Function funObj) { PrintStream out = getInstance(funObj).getOut(); for (int i = 0; i < args.length; i++) { if (i > 0) out.print(" "); // Convert the arbitrary JavaScript value into a string form. String s = Context.toString(args[i]); out.print(s); } out.println(); return Context.getUndefinedValue(); } /** * Call embedding-specific quit action passing its argument as int32 exit * code. * * This method is defined as a JavaScript function. */ @GlobalFunction public static void quit(Context cx, Scriptable thisObj, Object[] args, Function funObj) { Global global = getInstance(funObj); if (global.quitAction != null) { int exitCode = (args.length == 0 ? 0 : ScriptRuntime.toInt32(args[0])); global.quitAction.quit(cx, exitCode); } } /** * Get and set the language version. * * This method is defined as a JavaScript function. */ @GlobalFunction public static double version(Context cx, Scriptable thisObj, Object[] args, Function funObj) { double result = cx.getLanguageVersion(); if (args.length > 0) { double d = Context.toNumber(args[0]); cx.setLanguageVersion((int) d); } return result; } /** * Load and execute a set of JavaScript source files. * * This method is defined as a JavaScript function. * */ @GlobalFunction public static void load(Context cx, Scriptable thisObj, Object[] args, Function funObj) { for (int i = 0; i < args.length; i++) { Main.processFile(cx, thisObj, Context.toString(args[i])); } } /** * Load a Java class that defines a JavaScript object using the conventions * outlined in ScriptableObject.defineClass. * <p> * This method is defined as a JavaScript function. * * @exception IllegalAccessException if access is not available to a reflected * class member * @exception InstantiationException if unable to instantiate the named class * @exception InvocationTargetException if an exception is thrown during * execution of methods of the named class * @exception ClassDefinitionException if the format of the class causes this * exception in ScriptableObject.defineClass * @see org.mozilla.javascript.ScriptableObject#defineClass */ @GlobalFunction public static void defineClass(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws IllegalAccessException, InstantiationException, InvocationTargetException { Class clazz = getClass(args); ScriptableObject.defineClass(thisObj, clazz); } /** * Load and execute a script compiled to a class file. * <p> * This method is defined as a JavaScript function. When called as a * JavaScript function, a single argument is expected. This argument should be * the name of a class that implements the Script interface, as will any * script compiled by jsc. * * @exception IllegalAccessException if access is not available to the class * @exception InstantiationException if unable to instantiate the named class * @exception InvocationTargetException if an exception is thrown during * execution of methods of the named class * @see org.mozilla.javascript.ScriptableObject#defineClass */ @GlobalFunction public static void loadClass(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws IllegalAccessException, InstantiationException, InvocationTargetException { Class clazz = getClass(args); if (!Script.class.isAssignableFrom(clazz)) { throw reportRuntimeError("msg.must.implement.Script"); } Script script = (Script) clazz.newInstance(); script.exec(cx, thisObj); } private static Class getClass(Object[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException { if (args.length == 0) { throw reportRuntimeError("msg.expected.string.arg"); } Object arg0 = args[0]; if (arg0 instanceof Wrapper) { Object wrapped = ((Wrapper) arg0).unwrap(); if (wrapped instanceof Class) return (Class) wrapped; } String className = Context.toString(args[0]); try { return Class.forName(className); } catch (ClassNotFoundException cnfe) { throw reportRuntimeError("msg.class.not.found", className); } } @GlobalFunction public static void serialize(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws IOException { if (args.length < 2) { throw Context.reportRuntimeError( "Expected an object to serialize and a filename to write " + "the serialization to"); } Object obj = args[0]; String filename = Context.toString(args[1]); FileOutputStream fos = new FileOutputStream(filename); Scriptable scope = ScriptableObject.getTopLevelScope(thisObj); ScriptableOutputStream out = new ScriptableOutputStream(fos, scope); out.writeObject(obj); out.close(); } @GlobalFunction public static Object deserialize(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws IOException, ClassNotFoundException { if (args.length < 1) { throw Context.reportRuntimeError("Expected a filename to read the serialization from"); } String filename = Context.toString(args[0]); FileInputStream fis = new FileInputStream(filename); Scriptable scope = ScriptableObject.getTopLevelScope(thisObj); ObjectInputStream in = new ScriptableInputStream(fis, scope); Object deserialized = in.readObject(); in.close(); return Context.toObject(deserialized, scope); } /** * The spawn function runs a given function or script in a different thread. * * js> function g() { a = 7; } js> a = 3; 3 js> spawn(g) * Thread[Thread-1,5,main] js> a 3 */ @GlobalFunction public static Object spawn(Context cx, Scriptable thisObj, Object[] args, Function funObj) { Scriptable scope = funObj.getParentScope(); Runner runner; if (args.length != 0 && args[0] instanceof Function) { Object[] newArgs = null; if (args.length > 1 && args[1] instanceof Scriptable) { newArgs = cx.getElements((Scriptable) args[1]); } if (newArgs == null) { newArgs = ScriptRuntime.emptyArgs; } runner = new Runner(scope, (Function) args[0], newArgs); } else if (args.length != 0 && args[0] instanceof Script) { runner = new Runner(scope, (Script) args[0]); } else { throw reportRuntimeError("msg.spawn.args"); } runner.factory = cx.getFactory(); Thread thread = new Thread(runner); thread.start(); return thread; } /** * The sync function creates a synchronized function (in the sense of a Java * synchronized method) from an existing function. The new function * synchronizes on the <code>this</code> object of its invocation. js> var o = * { f : sync(function(x) { print("entry"); * Packages.java.lang.Thread.sleep(x*1000); print("exit"); })}; js> * spawn(function() {o.f(5);}); Thread[Thread-0,5,main] entry js> * spawn(function() {o.f(5);}); Thread[Thread-1,5,main] js> exit entry exit */ @GlobalFunction public static Object sync(Context cx, Scriptable thisObj, Object[] args, Function funObj) { if (args.length == 1 && args[0] instanceof Function) { return new Synchronizer((Function) args[0]); } else { throw reportRuntimeError("msg.sync.args"); } } /** * Execute the specified command with the given argument and options as a * separate process and return the exit status of the process. * <p> * Usage: * * <pre> * runCommand(command) * runCommand(command, arg1, ..., argN) * runCommand(command, arg1, ..., argN, options) * </pre> * * All except the last arguments to runCommand are converted to strings and * denote command name and its arguments. If the last argument is a JavaScript * object, it is an option object. Otherwise it is converted to string * denoting the last argument and options objects assumed to be empty. Te * following properties of the option object are processed: * <ul> * <li><tt>args</tt> - provides an array of additional command arguments * <li><tt>env</tt> - explicit environment object. All its enumeratable * properties define the corresponding environment variable names. * <li><tt>input</tt> - the process input. If it is not java.io.InputStream, * it is converted to string and sent to the process as its input. If not * specified, no input is provided to the process. * <li><tt>output</tt> - the process output instead of java.lang.System.out. * If it is not instance of java.io.OutputStream, the process output is read, * converted to a string, appended to the output property value converted to * string and put as the new value of the output property. * <li><tt>err</tt> - the process error output instead of * java.lang.System.err. If it is not instance of java.io.OutputStream, the * process error output is read, converted to a string, appended to the err * property value converted to string and put as the new value of the err * property. * </ul> */ @GlobalFunction public static Object runCommand(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws IOException { int L = args.length; if (L == 0 || (L == 1 && args[0] instanceof Scriptable)) { throw reportRuntimeError("msg.runCommand.bad.args"); } InputStream in = null; OutputStream out = null, err = null; ByteArrayOutputStream outBytes = null, errBytes = null; Object outObj = null, errObj = null; String[] environment = null; Scriptable params = null; Object[] addArgs = null; if (args[L - 1] instanceof Scriptable) { params = (Scriptable) args[L - 1]; --L; Object envObj = ScriptableObject.getProperty(params, "env"); if (envObj != Scriptable.NOT_FOUND) { if (envObj == null) { environment = new String[0]; } else { if (!(envObj instanceof Scriptable)) { throw reportRuntimeError("msg.runCommand.bad.env"); } Scriptable envHash = (Scriptable) envObj; Object[] ids = ScriptableObject.getPropertyIds(envHash); environment = new String[ids.length]; for (int i = 0; i != ids.length; ++i) { Object keyObj = ids[i], val; String key; if (keyObj instanceof String) { key = (String) keyObj; val = ScriptableObject.getProperty(envHash, key); } else { int ikey = ((Number) keyObj).intValue(); key = Integer.toString(ikey); val = ScriptableObject.getProperty(envHash, ikey); } if (val == ScriptableObject.NOT_FOUND) { val = Undefined.instance; } environment[i] = key + '=' + ScriptRuntime.toString(val); } } } Object inObj = ScriptableObject.getProperty(params, "input"); if (inObj != Scriptable.NOT_FOUND) { in = toInputStream(inObj); } outObj = ScriptableObject.getProperty(params, "output"); if (outObj != Scriptable.NOT_FOUND) { out = toOutputStream(outObj); if (out == null) { outBytes = new ByteArrayOutputStream(); out = outBytes; } } errObj = ScriptableObject.getProperty(params, "err"); if (errObj != Scriptable.NOT_FOUND) { err = toOutputStream(errObj); if (err == null) { errBytes = new ByteArrayOutputStream(); err = errBytes; } } Object addArgsObj = ScriptableObject.getProperty(params, "args"); if (addArgsObj != Scriptable.NOT_FOUND) { Scriptable s = Context.toObject(addArgsObj, getTopLevelScope(thisObj)); addArgs = cx.getElements(s); } } Global global = getInstance(funObj); if (out == null) { out = (global != null) ? global.getOut() : System.out; } if (err == null) { err = (global != null) ? global.getErr() : System.err; } // If no explicit input stream, do not send any input to process, // in particular, do not use System.in to avoid deadlocks // when waiting for user input to send to process which is already // terminated as it is not always possible to interrupt read method. String[] cmd = new String[(addArgs == null) ? L : L + addArgs.length]; for (int i = 0; i != L; ++i) { cmd[i] = ScriptRuntime.toString(args[i]); } if (addArgs != null) { for (int i = 0; i != addArgs.length; ++i) { cmd[L + i] = ScriptRuntime.toString(addArgs[i]); } } int exitCode = runProcess(cmd, environment, in, out, err); if (outBytes != null) { String s = ScriptRuntime.toString(outObj) + outBytes.toString(); ScriptableObject.putProperty(params, "output", s); } if (errBytes != null) { String s = ScriptRuntime.toString(errObj) + errBytes.toString(); ScriptableObject.putProperty(params, "err", s); } return new Integer(exitCode); } /** * The seal function seals all supplied arguments. */ @GlobalFunction public static void seal(Context cx, Scriptable thisObj, Object[] args, Function funObj) { for (int i = 0; i != args.length; ++i) { Object arg = args[i]; if (!(arg instanceof ScriptableObject) || arg == Undefined.instance) { if (!(arg instanceof Scriptable) || arg == Undefined.instance) { throw reportRuntimeError("msg.shell.seal.not.object"); } else { throw reportRuntimeError("msg.shell.seal.not.scriptable"); } } } for (int i = 0; i != args.length; ++i) { Object arg = args[i]; ((ScriptableObject) arg).sealObject(); } } /** * The readFile reads the given file context and convert it to a string using * the specified character coding or default character coding if explicit * coding argument is not given. * <p> * Usage: * * <pre> * readFile(filePath) * readFile(filePath, charCoding) * </pre> * * The first form converts file's context to string using the default * character coding. */ @GlobalFunction public static Object readFile(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws IOException { if (args.length == 0) { throw reportRuntimeError("msg.shell.readFile.bad.args"); } String path = ScriptRuntime.toString(args[0]); String charCoding = null; if (args.length >= 2) { charCoding = ScriptRuntime.toString(args[1]); } return readUrl(path, charCoding, true); } /** * The readUrl opens connection to the given URL, read all its data and * converts them to a string using the specified character coding or default * character coding if explicit coding argument is not given. * <p> * Usage: * * <pre> * readUrl(url) * readUrl(url, charCoding) * </pre> * * The first form converts file's context to string using the default * charCoding. */ @GlobalFunction public static Object readUrl(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws IOException { if (args.length == 0) { throw reportRuntimeError("msg.shell.readUrl.bad.args"); } String url = ScriptRuntime.toString(args[0]); String charCoding = null; if (args.length >= 2) { charCoding = ScriptRuntime.toString(args[1]); } return readUrl(url, charCoding, false); } /** * Convert the argumnet to int32 number. */ @GlobalFunction public static Object toint32(Context cx, Scriptable thisObj, Object[] args, Function funObj) { Object arg = (args.length != 0 ? args[0] : Undefined.instance); if (arg instanceof Integer) return arg; return ScriptRuntime.wrapInt(ScriptRuntime.toInt32(arg)); } public InputStream getIn() { return inStream == null ? System.in : inStream; } public void setIn(InputStream in) { inStream = in; } public PrintStream getOut() { return outStream == null ? System.out : outStream; } public void setOut(PrintStream out) { outStream = out; } public PrintStream getErr() { return errStream == null ? System.err : errStream; } public void setErr(PrintStream err) { errStream = err; } public void setSealedStdLib(boolean value) { sealedStdLib = value; } private static Global getInstance(Function function) { Scriptable scope = scopes.get(function.getParentScope().getClassName()); if (!(scope instanceof Global)) throw reportRuntimeError("msg.shell.bad.function.scope", String.valueOf(scope)); return (Global) scope; } /** * If any of in, out, err is null, the corresponding process stream will be * closed immediately, otherwise it will be closed as soon as all data will be * read from/written to process */ private static int runProcess(String[] cmd, String[] environment, InputStream in, OutputStream out, OutputStream err) throws IOException { Process p; if (environment == null) { p = Runtime.getRuntime().exec(cmd); } else { p = Runtime.getRuntime().exec(cmd, environment); } PipeThread inThread = null, errThread = null; try { InputStream errProcess = null; try { if (err != null) { errProcess = p.getErrorStream(); } else { p.getErrorStream().close(); } InputStream outProcess = null; try { if (out != null) { outProcess = p.getInputStream(); } else { p.getInputStream().close(); } OutputStream inProcess = null; try { if (in != null) { inProcess = p.getOutputStream(); } else { p.getOutputStream().close(); } if (out != null) { // Read process output on this thread if (err != null) { errThread = new PipeThread(true, errProcess, err); errThread.start(); } if (in != null) { inThread = new PipeThread(false, in, inProcess); inThread.start(); } pipe(true, outProcess, out); } else if (in != null) { // No output, read process input on this thread if (err != null) { errThread = new PipeThread(true, errProcess, err); errThread.start(); } pipe(false, in, inProcess); in.close(); } else if (err != null) { // No output or input, read process err // on this thread pipe(true, errProcess, err); errProcess.close(); errProcess = null; } // wait for process completion for (;;) { try { p.waitFor(); break; } catch (InterruptedException ex) { } } return p.exitValue(); } finally { // pipe will close stream as well, but for reliability // duplicate it in any case if (inProcess != null) { inProcess.close(); } } } finally { if (outProcess != null) { outProcess.close(); } } } finally { if (errProcess != null) { errProcess.close(); } } } finally { p.destroy(); if (inThread != null) { for (;;) { try { inThread.join(); break; } catch (InterruptedException ex) { } } } if (errThread != null) { for (;;) { try { errThread.join(); break; } catch (InterruptedException ex) { } } } } } static void pipe(boolean fromProcess, InputStream from, OutputStream to) throws IOException { try { final int SIZE = 4096; byte[] buffer = new byte[SIZE]; for (;;) { int n; if (!fromProcess) { n = from.read(buffer, 0, SIZE); } else { try { n = from.read(buffer, 0, SIZE); } catch (IOException ex) { // Ignore exception as it can be cause by closed pipe break; } } if (n < 0) { break; } if (fromProcess) { to.write(buffer, 0, n); to.flush(); } else { try { to.write(buffer, 0, n); to.flush(); } catch (IOException ex) { // Ignore exception as it can be cause by closed pipe break; } } } } finally { try { if (fromProcess) { from.close(); } else { to.close(); } } catch (IOException ex) { // Ignore errors on close. On Windows JVM may throw invalid // refrence exception if process terminates too fast. } } } private static InputStream toInputStream(Object value) throws IOException { InputStream is = null; String s = null; if (value instanceof Wrapper) { Object unwrapped = ((Wrapper) value).unwrap(); if (unwrapped instanceof InputStream) { is = (InputStream) unwrapped; } else if (unwrapped instanceof byte[]) { is = new ByteArrayInputStream((byte[]) unwrapped); } else if (unwrapped instanceof Reader) { s = readReader((Reader) unwrapped); } else if (unwrapped instanceof char[]) { s = new String((char[]) unwrapped); } } if (is == null) { if (s == null) { s = ScriptRuntime.toString(value); } is = new ByteArrayInputStream(s.getBytes()); } return is; } private static OutputStream toOutputStream(Object value) { OutputStream os = null; if (value instanceof Wrapper) { Object unwrapped = ((Wrapper) value).unwrap(); if (unwrapped instanceof OutputStream) { os = (OutputStream) unwrapped; } } return os; } private static String readUrl(String filePath, String charCoding, boolean urlIsFile) throws IOException { int chunkLength; InputStream is = null; try { if (!urlIsFile) { URL urlObj = new URL(filePath); URLConnection uc = urlObj.openConnection(); is = uc.getInputStream(); chunkLength = uc.getContentLength(); if (chunkLength <= 0) chunkLength = 1024; if (charCoding == null) { String type = uc.getContentType(); if (type != null) { charCoding = getCharCodingFromType(type); } } } else { File f = new File(filePath); long length = f.length(); chunkLength = (int) length; if (chunkLength != length) throw new IOException("Too big file size: " + length); if (chunkLength == 0) { return ""; } is = new FileInputStream(f); } Reader r; if (charCoding == null) { r = new InputStreamReader(is); } else { r = new InputStreamReader(is, charCoding); } return readReader(r, chunkLength); } finally { if (is != null) is.close(); } } private static String getCharCodingFromType(String type) { int i = type.indexOf(';'); if (i >= 0) { int end = type.length(); ++i; while (i != end && type.charAt(i) <= ' ') { ++i; } String charset = "charset"; if (charset.regionMatches(true, 0, type, i, charset.length())) { i += charset.length(); while (i != end && type.charAt(i) <= ' ') { ++i; } if (i != end && type.charAt(i) == '=') { ++i; while (i != end && type.charAt(i) <= ' ') { ++i; } if (i != end) { // i is at the start of non-empty // charCoding spec while (type.charAt(end - 1) <= ' ') { --end; } return type.substring(i, end); } } } } return null; } private static String readReader(Reader reader) throws IOException { return readReader(reader, 4096); } private static String readReader(Reader reader, int initialBufferSize) throws IOException { char[] buffer = new char[initialBufferSize]; int offset = 0; for (;;) { int n = reader.read(buffer, offset, buffer.length - offset); if (n < 0) { break; } offset += n; if (offset == buffer.length) { char[] tmp = new char[buffer.length * 2]; System.arraycopy(buffer, 0, tmp, 0, offset); buffer = tmp; } } return new String(buffer, 0, offset); } static RuntimeException reportRuntimeError(String msgId) { String message = ToolErrorReporter.getMessage(msgId); return Context.reportRuntimeError(message); } static RuntimeException reportRuntimeError(String msgId, String msgArg) { String message = ToolErrorReporter.getMessage(msgId, msgArg); return Context.reportRuntimeError(message); } } class Runner implements Runnable, ContextAction { Runner(Scriptable scope, Function func, Object[] args) { this.scope = scope; f = func; this.args = args; } Runner(Scriptable scope, Script script) { this.scope = scope; s = script; } public void run() { factory.call(this); } public Object run(Context cx) { if (f != null) return f.call(cx, scope, scope, args); else return s.exec(cx, scope); } ContextFactory factory; private Scriptable scope; private Function f; private Script s; private Object[] args; } class PipeThread extends Thread { PipeThread(boolean fromProcess, InputStream from, OutputStream to) { setDaemon(true); this.fromProcess = fromProcess; this.from = from; this.to = to; } public void run() { try { Global.pipe(fromProcess, from, to); } catch (IOException ex) { throw Context.throwAsScriptRuntimeEx(ex); } } private boolean fromProcess; private InputStream from; private OutputStream to; }