8170162: jshell tool: no mechanism to programmatically launch
8170044: jshell tool: jshell missing from javax.tools.ToolProvider
Reviewed-by: jjg
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java Tue Dec 20 06:06:01 2016 -0800
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java Tue Dec 20 13:42:13 2016 -0800
@@ -46,7 +46,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
-import java.util.prefs.BackingStoreException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -97,7 +96,7 @@
List<String> persistenHistory = Stream.of(repl.prefs.keys())
.filter(key -> key.startsWith(HISTORY_LINE_PREFIX))
.sorted()
- .map(key -> repl.prefs.get(key, null))
+ .map(key -> repl.prefs.get(key))
.collect(Collectors.toList());
in.setHistory(history = new EditingHistory(in, persistenHistory) {
@Override protected boolean isComplete(CharSequence input) {
@@ -215,23 +214,21 @@
@Override
public void close() throws IOException {
//save history:
- try {
- for (String key : repl.prefs.keys()) {
- if (key.startsWith(HISTORY_LINE_PREFIX))
- repl.prefs.remove(key);
+ for (String key : repl.prefs.keys()) {
+ if (key.startsWith(HISTORY_LINE_PREFIX)) {
+ repl.prefs.remove(key);
}
- Collection<? extends String> savedHistory = history.save();
- if (!savedHistory.isEmpty()) {
- int len = (int) Math.ceil(Math.log10(savedHistory.size()+1));
- String format = HISTORY_LINE_PREFIX + "%0" + len + "d";
- int index = 0;
- for (String historyLine : savedHistory) {
- repl.prefs.put(String.format(format, index++), historyLine);
- }
+ }
+ Collection<? extends String> savedHistory = history.save();
+ if (!savedHistory.isEmpty()) {
+ int len = (int) Math.ceil(Math.log10(savedHistory.size()+1));
+ String format = HISTORY_LINE_PREFIX + "%0" + len + "d";
+ int index = 0;
+ for (String historyLine : savedHistory) {
+ repl.prefs.put(String.format(format, index++), historyLine);
}
- } catch (BackingStoreException ex) {
- throw new IllegalStateException(ex);
}
+ repl.prefs.flush();
in.shutdown();
try {
in.getTerminal().restore();
@@ -417,6 +414,7 @@
}
}
+ @Override
public void beforeUserCode() {
synchronized (this) {
inputBytes = null;
@@ -424,6 +422,7 @@
input.setState(State.BUFFER);
}
+ @Override
public void afterUserCode() {
input.setState(State.WAIT);
}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java Tue Dec 20 06:06:01 2016 -0800
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java Tue Dec 20 13:42:13 2016 -0800
@@ -136,26 +136,13 @@
final InputStream userin;
final PrintStream userout;
final PrintStream usererr;
- final Preferences prefs;
+ final PersistentStorage prefs;
final Map<String, String> envvars;
final Locale locale;
final Feedback feedback = new Feedback();
/**
- * Simple constructor for the tool used by main.
- * @param in command line input
- * @param out command line output, feedback including errors, user System.out
- * @param err start-up errors and debugging info, user System.err
- */
- public JShellTool(InputStream in, PrintStream out, PrintStream err) {
- this(in, out, err, out, null, out, err,
- Preferences.userRoot().node("tool/JShell"),
- System.getenv(),
- Locale.getDefault());
- }
-
- /**
* The complete constructor for the tool (used by test harnesses).
* @param cmdin command line input -- snippets and commands
* @param cmdout command line output, feedback including errors
@@ -164,14 +151,14 @@
* @param userin code execution input, or null to use IOContext
* @param userout code execution output -- System.out.printf("hi")
* @param usererr code execution error stream -- System.err.printf("Oops")
- * @param prefs preferences to use
+ * @param prefs persistence implementation to use
* @param envvars environment variable mapping to use
* @param locale locale to use
*/
- public JShellTool(InputStream cmdin, PrintStream cmdout, PrintStream cmderr,
+ JShellTool(InputStream cmdin, PrintStream cmdout, PrintStream cmderr,
PrintStream console,
InputStream userin, PrintStream userout, PrintStream usererr,
- Preferences prefs, Map<String, String> envvars, Locale locale) {
+ PersistentStorage prefs, Map<String, String> envvars, Locale locale) {
this.cmdin = cmdin;
this.cmdout = cmdout;
this.cmderr = cmderr;
@@ -478,16 +465,6 @@
}
}
- /**
- * Normal start entry point
- * @param args
- * @throws Exception
- */
- public static void main(String[] args) throws Exception {
- new JShellTool(System.in, System.out, System.err)
- .start(args);
- }
-
public void start(String[] args) throws Exception {
List<String> loadList = processCommandArgs(args);
if (loadList == null) {
@@ -502,7 +479,7 @@
private void start(IOContext in, List<String> loadList) {
// If startup hasn't been set by command line, set from retained/default
if (startup == null) {
- startup = prefs.get(STARTUP_KEY, null);
+ startup = prefs.get(STARTUP_KEY);
if (startup == null) {
startup = DEFAULT_STARTUP;
}
@@ -513,7 +490,7 @@
resetState(); // Initialize
// Read replay history from last jshell session into previous history
- String prevReplay = prefs.get(REPLAY_RESTORE_KEY, null);
+ String prevReplay = prefs.get(REPLAY_RESTORE_KEY);
if (prevReplay != null) {
replayableHistoryPrevious = Arrays.asList(prevReplay.split(RECORD_SEPARATOR));
}
@@ -788,7 +765,7 @@
// These predefined modes are read-only
feedback.markModesReadOnly();
// Restore user defined modes retained on previous run with /set mode -retain
- String encoded = prefs.get(MODE_KEY, null);
+ String encoded = prefs.get(MODE_KEY);
if (encoded != null && !encoded.isEmpty()) {
if (!feedback.restoreEncodedModes(initmh, encoded)) {
// Catastrophic corruption -- remove the retained modes
@@ -802,7 +779,7 @@
}
commandLineFeedbackMode = null;
} else {
- String fb = prefs.get(FEEDBACK_KEY, null);
+ String fb = prefs.get(FEEDBACK_KEY);
if (fb != null) {
// Restore the feedback mode to use that was retained
// on a previous run with /set feedback -retain
@@ -1485,9 +1462,9 @@
}
// returns null if not stored in preferences
- static EditorSetting fromPrefs(Preferences prefs) {
+ static EditorSetting fromPrefs(PersistentStorage prefs) {
// Read retained editor setting (if any)
- String editorString = prefs.get(EDITOR_KEY, "");
+ String editorString = prefs.get(EDITOR_KEY);
if (editorString == null || editorString.isEmpty()) {
return null;
} else if (editorString.equals(BUILT_IN_REP)) {
@@ -1504,11 +1481,11 @@
}
}
- static void removePrefs(Preferences prefs) {
+ static void removePrefs(PersistentStorage prefs) {
prefs.remove(EDITOR_KEY);
}
- void toPrefs(Preferences prefs) {
+ void toPrefs(PersistentStorage prefs) {
prefs.put(EDITOR_KEY, (this == BUILT_IN_EDITOR)
? BUILT_IN_REP
: (wait ? WAIT_PREFIX : NORMAL_PREFIX) + String.join(RECORD_SEPARATOR, cmd));
@@ -1676,7 +1653,7 @@
}
void showSetStart() {
- String retained = prefs.get(STARTUP_KEY, null);
+ String retained = prefs.get(STARTUP_KEY);
if (retained != null) {
showSetStart(true, retained);
}
@@ -1774,6 +1751,7 @@
replayableHistory.subList(first + 1, replayableHistory.size()));
prefs.put(REPLAY_RESTORE_KEY, hist);
}
+ prefs.flush();
fluffmsg("jshell.msg.goodbye");
return true;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellToolBuilder.java Tue Dec 20 13:42:13 2016 -0800
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2016, 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 jdk.internal.jshell.tool;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+import jdk.jshell.tool.JavaShellToolBuilder;
+
+/**
+ * Builder for programmatically building the jshell tool.
+ */
+public class JShellToolBuilder implements JavaShellToolBuilder {
+
+ private static final String PREFERENCES_NODE = "tool/JShell";
+ private InputStream cmdIn = System.in;
+ private InputStream userIn = null;
+ private PrintStream cmdOut = System.out;
+ private PrintStream console = System.out;
+ private PrintStream userOut = System.out;
+ private PrintStream cmdErr = System.err;
+ private PrintStream userErr = System.err;
+ private PersistentStorage prefs = null;
+ private Map<String, String> vars = null;
+ private Locale locale = Locale.getDefault();
+ private boolean capturePrompt = false;
+
+ /**
+ * Set the input channels.
+ * Default, if not set, {@code in(System.in, null)}.
+ *
+ * @param cmdIn source of command input
+ * @param userIn source of input for running user code, or {@code null} to
+ * be extracted from cmdIn
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ @Override
+ public JavaShellToolBuilder in(InputStream cmdIn, InputStream userIn) {
+ this.cmdIn = cmdIn;
+ this.userIn = userIn;
+ return this;
+ }
+
+ /**
+ * Set the output channels. Same as {@code out(output, output, output)}.
+ * Default, if not set, {@code out(System.out)}.
+ *
+ * @param output destination of command feedback, console interaction, and
+ * user code output
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ @Override
+ public JavaShellToolBuilder out(PrintStream output) {
+ this.cmdOut = output;
+ this.console = output;
+ this.userOut = output;
+ return this;
+ }
+
+ /**
+ * Set the output channels.
+ * Default, if not set, {@code out(System.out, System.out, System.out)}.
+ *
+ * @param cmdOut destination of command feedback including error messages
+ * for users
+ * @param console destination of console interaction
+ * @param userOut destination of user code output. For example, user snippet
+ * {@code System.out.println("Hello")} when executed {@code Hello} goes to
+ * userOut.
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ @Override
+ public JavaShellToolBuilder out(PrintStream cmdOut, PrintStream console, PrintStream userOut) {
+ this.cmdOut = cmdOut;
+ this.console = console;
+ this.userOut = userOut;
+ return this;
+ }
+
+ /**
+ * Set the error channels. Same as {@code err(error, error)}.
+ * Default, if not set, {@code err(System.err)}.
+ *
+ * @param error destination of tool errors, and
+ * user code errors
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ @Override
+ public JavaShellToolBuilder err(PrintStream error) {
+ this.cmdErr = error;
+ this.userErr = error;
+ return this;
+ }
+
+ /**
+ * Set the error channels.
+ * Default, if not set, {@code err(System.err, System.err, System.err)}.
+ *
+ * @param cmdErr destination of tool start-up and fatal errors
+ * @param userErr destination of user code error output.
+ * For example, user snippet {@code System.err.println("Oops")}
+ * when executed {@code Oops} goes to userErr.
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ @Override
+ public JavaShellToolBuilder err(PrintStream cmdErr, PrintStream userErr) {
+ this.cmdErr = cmdErr;
+ this.userErr = userErr;
+ return this;
+ }
+
+ /**
+ * Set the storage mechanism for persistent information which includes
+ * input history and retained settings. Default if not set is the
+ * tool's standard persistence mechanism.
+ *
+ * @param prefs an instance of {@link java.util.prefs.Preferences} that
+ * is used to retrieve and store persistent information
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ @Override
+ public JavaShellToolBuilder persistence(Preferences prefs) {
+ this.prefs = new PreferencesStorage(prefs);
+ return this;
+ }
+
+ /**
+ * Set the storage mechanism for persistent information which includes
+ * input history and retained settings. Default if not set is the
+ * tool's standard persistence mechanism.
+ *
+ * @param prefsMap an instance of {@link java.util.Map} that
+ * is used to retrieve and store persistent information
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ @Override
+ public JavaShellToolBuilder persistence(Map<String, String> prefsMap) {
+ this.prefs = new MapStorage(prefsMap);
+ return this;
+ }
+
+ /**
+ * Set the source for environment variables.
+ * Default, if not set, {@code env(System.getenv())}.
+ *
+ * @param vars the Map of environment variable names to values
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ @Override
+ public JavaShellToolBuilder env(Map<String, String> vars) {
+ this.vars = vars;
+ return this;
+ }
+
+ /**
+ * Set the locale.
+ * Default, if not set, {@code locale(Locale.getDefault())}.
+ *
+ * @param locale the locale
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ @Override
+ public JavaShellToolBuilder locale(Locale locale) {
+ this.locale = locale;
+ return this;
+ }
+
+ /**
+ * Set if the special command capturing prompt override should be used.
+ * Default, if not set, {@code promptCapture(false)}.
+ *
+ * @param capture if {@code true}, basic prompt is the {@code ENQ}
+ * character and continuation prompt is the {@code ACK} character.
+ * If false, prompts are as set with set-up or user {@code /set} commands.
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ @Override
+ public JavaShellToolBuilder promptCapture(boolean capture) {
+ this.capturePrompt = capture;
+ return this;
+ }
+
+ /**
+ * Create a tool instance for testing. Not in JavaShellToolBuilder.
+ *
+ * @return the tool instance
+ */
+ public JShellTool rawTool() {
+ if (prefs == null) {
+ prefs = new PreferencesStorage(Preferences.userRoot().node(PREFERENCES_NODE));
+ }
+ if (vars == null) {
+ vars = System.getenv();
+ }
+ JShellTool sh = new JShellTool(cmdIn, cmdOut, cmdErr, console, userIn,
+ userOut, userErr, prefs, vars, locale);
+ sh.testPrompt = capturePrompt;
+ return sh;
+ }
+
+ /**
+ * Run an instance of the Java shell tool as configured by the other methods
+ * in this interface. This call is not destructive, more than one call of
+ * this method may be made from a configured builder.
+ *
+ * @param arguments the command-line arguments (including options), if any
+ * @throws Exception an unexpected fatal exception
+ */
+ @Override
+ public void run(String... arguments) throws Exception {
+ rawTool().start(arguments);
+ }
+
+ /**
+ * Persistence stored in Preferences.
+ */
+ private static class PreferencesStorage implements PersistentStorage {
+
+ final Preferences p;
+
+ PreferencesStorage(Preferences p) {
+ this.p = p;
+ }
+
+ @Override
+ public void clear() {
+ try {
+ p.clear();
+ } catch (BackingStoreException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ @Override
+ public String[] keys() {
+ try {
+ return p.keys();
+ } catch (BackingStoreException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ @Override
+ public String get(String key) {
+ return p.get(key, null);
+ }
+
+ @Override
+ public void put(String key, String value) {
+ p.put(key, value);
+ }
+
+ @Override
+ public void remove(String key) {
+ p.remove(key);
+ }
+
+ @Override
+ public void flush() {
+ try {
+ p.flush();
+ } catch (BackingStoreException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+ }
+
+ /**
+ * Persistence stored in a Map.
+ */
+ private static class MapStorage implements PersistentStorage {
+
+ final Map<String, String> map;
+
+ MapStorage(Map<String, String> map) {
+ this.map = map;
+ }
+
+ @Override
+ public void clear() {
+
+ try {
+ map.clear();
+ } catch (UnsupportedOperationException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ @Override
+ public String[] keys() {
+ Set<String> ks = map.keySet();
+ return ks.toArray(new String[ks.size()]);
+ }
+
+ @Override
+ public String get(String key) {
+ Objects.requireNonNull(key);
+ return map.get(key);
+ }
+
+ @Override
+ public void put(String key, String value) {
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(value);
+ map.put(key, value);
+ }
+
+ @Override
+ public void remove(String key) {
+ Objects.requireNonNull(key);
+ map.remove(key);
+ }
+
+ @Override
+ public void flush() {
+ // no-op always up-to-date
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellToolProvider.java Tue Dec 20 13:42:13 2016 -0800
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2016, 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 jdk.internal.jshell.tool;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+import javax.lang.model.SourceVersion;
+import javax.tools.Tool;
+import jdk.jshell.tool.JavaShellToolBuilder;
+
+/**
+ * Provider for launching the jshell tool.
+ */
+public class JShellToolProvider implements Tool {
+
+ /**
+ * Returns the name of this Java shell tool provider.
+ *
+ * @return the name of this tool provider
+ */
+ @Override
+ public String name() {
+ return "jshell";
+ }
+
+ /**
+ * Run the jshell tool. The streams {@code out} and {@code err} are
+ * converted to {@code PrintStream} if they are not already.
+ * Any {@code Exception} is caught, printed and results in a non-zero return.
+ *
+ * @param in command line input (snippets and commands), and execution
+ * "standard" input; use System.in if null
+ * @param out command line output, feedback including errors, and execution
+ * "standard" output; use System.out if null
+ * @param err start-up errors and execution "standard" error; use System.err
+ * if null
+ * @param arguments arguments to pass to the tool
+ * @return 0 for success; nonzero otherwise
+ * @throws NullPointerException if the array of arguments contains
+ * any {@code null} elements.
+ */
+ @Override
+ public int run(InputStream in, OutputStream out, OutputStream err, String... arguments) {
+ InputStream xin =
+ (in == null)
+ ? System.in
+ : in;
+ PrintStream xout =
+ (out == null)
+ ? System.out
+ : (out instanceof PrintStream)
+ ? (PrintStream) out
+ : new PrintStream(out);
+ PrintStream xerr =
+ (err == null)
+ ? System.err
+ : (err instanceof PrintStream)
+ ? (PrintStream) err
+ : new PrintStream(err);
+ try {
+ JavaShellToolBuilder
+ .builder()
+ .in(xin, null)
+ .out(xout)
+ .err(xerr)
+ .run(arguments);
+ return 0;
+ } catch (Throwable ex) {
+ xerr.println(ex.getMessage());
+ return 1;
+ }
+ }
+
+ /**
+ * Returns the source versions of the jshell tool.
+ * @return a set of supported source versions
+ */
+ @Override
+ public Set<SourceVersion> getSourceVersions() {
+ return Collections.unmodifiableSet(
+ EnumSet.range(SourceVersion.RELEASE_9, SourceVersion.latest()));
+ }
+
+ /**
+ * Launch the tool.
+ * @param arguments the command-line arguments (including options), if any
+ * @throws Exception an unexpected fatal exception
+ */
+ public static void main(String[] arguments) throws Exception {
+ JavaShellToolBuilder
+ .builder()
+ .run(arguments);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/PersistentStorage.java Tue Dec 20 13:42:13 2016 -0800
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2016, 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 jdk.internal.jshell.tool;
+
+/**
+ * The required functionality jshell uses for persistent storage. Implementable
+ * by both Preferences API and Map.
+ */
+interface PersistentStorage {
+
+ /**
+ * Removes all of the preferences (key-value associations) in
+ * preferences.
+ *
+ * @throws IllegalStateException if this operation cannot be completed
+ * because of the state of the system.
+ */
+ void clear();
+
+ /**
+ * Returns all of the keys that have an associated value in
+ * preferences.
+ *
+ * @return an array of the keys that have an associated value in this
+ * preference node.
+ * @throws IllegalStateException if this operation cannot be completed
+ * because of the state of the system.
+ */
+ String[] keys();
+
+ /**
+ * Returns the value associated with the specified key in preferences.
+ *
+ * @param key key whose associated value is to be returned.
+ * @return the value associated with {@code key}, or {@code null} if no
+ * value is associated with {@code key}.
+ * @throws IllegalStateException if this operation cannot be completed
+ * because of the state of the system.
+ * @throws NullPointerException if {@code key} is {@code null}.
+ */
+ String get(String key);
+
+ /**
+ * Associates the specified value with the specified key in this
+ * preference node.
+ *
+ * @param key key with which the specified value is to be associated.
+ * @param value value to be associated with the specified key.
+ * @throws NullPointerException if key or value is {@code null}.
+ * @throws IllegalArgumentException if key or value are too long.
+ * @throws IllegalStateException if this operation cannot be completed
+ * because of the state of the system.
+ */
+ void put(String key, String value);
+
+ /**
+ * Removes the value associated with the specified key in preferences,
+ * if any.
+ *
+ * @param key key whose mapping is to be removed from the preference
+ * node.
+ * @throws NullPointerException if {@code key} is {@code null}.
+ * @throws IllegalStateException if this operation cannot be completed
+ * because of the state of the system.
+ */
+ void remove(String key);
+
+ /**
+ * Forces any changes in the contents of this preferences to be stored.
+ * Once this method returns successfully, it is safe to assume that all
+ * changes have become as permanent as they are going to be.
+ * <p>
+ * Implementations are free to flush changes into the persistent store
+ * at any time. They do not need to wait for this method to be called.
+ *
+ * @throws IllegalStateException if this operation cannot be completed
+ * because of the state of the system.
+ */
+ void flush();
+
+}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/overview.html Tue Dec 20 06:06:01 2016 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
-<html>
-<head>
-<!--
-
-Copyright (c) 2016, 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.
--->
-
-</head>
-<body bgcolor="white">
-
-This document is the API specification for JShell -- support for
-Java™ Programming Language 'snippet' evaluating tools, such as
-Read-Eval-Print Loops (REPLs).
-
-</body>
-</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/tool/JavaShellToolBuilder.java Tue Dec 20 13:42:13 2016 -0800
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2016, 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 jdk.jshell.tool;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Locale;
+import java.util.Map;
+import java.util.prefs.Preferences;
+import jdk.internal.jshell.tool.JShellToolBuilder;
+
+/**
+ * Interface to configure and run a Java shell tool instance. An instance of the
+ * builder is created with the static {@link #builder} method. This builder can,
+ * optionally, be configured with the configuration methods. All configuration
+ * methods return the builder instance for use in chained initialization. All
+ * configuration methods have sensible defaults which will be used if they are
+ * not called.. After zero or more calls to configuration methods, the tool is
+ * launched with a call to {@link #run(java.lang.String...) }.
+ */
+public interface JavaShellToolBuilder {
+
+ /**
+ * Create a builder for launching the JDK jshell tool.
+ *
+ * @return a builder which can be used to configure and launch the jshell
+ * tool
+ */
+ static JavaShellToolBuilder builder() {
+ return new JShellToolBuilder();
+ }
+
+ /**
+ * Set the input channels.
+ *
+ * @implSpec If this method is not called, the behavior should be
+ * equivalent to calling {@code in(System.in, null)}.
+ *
+ * @param cmdIn source of command input
+ * @param userIn source of input for running user code, or {@code null} to
+ * extract user input from cmdIn
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ JavaShellToolBuilder in(InputStream cmdIn, InputStream userIn);
+
+ /**
+ * Set the output channels. Same as {@code out(output, output, output)}.
+ *
+ * @implSpec If neither {@code out} method is called, the behavior should be
+ * equivalent to calling {@code out(System.out)}.
+ *
+ * @param output destination of command feedback, console interaction, and
+ * user code output
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ JavaShellToolBuilder out(PrintStream output);
+
+ /**
+ * Set the output channels.
+ *
+ * @implSpec If neither {@code out} method is called, the behavior should be
+ * equivalent to calling {@code out(System.out, System.out, System.out)}.
+ *
+ * @param cmdOut destination of command feedback including error messages
+ * for users
+ * @param console destination of console interaction
+ * @param userOut destination of user code output. For example, user snippet
+ * {@code System.out.println("Hello")} when executed {@code Hello} goes to
+ * userOut.
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ JavaShellToolBuilder out(PrintStream cmdOut, PrintStream console, PrintStream userOut);
+
+ /**
+ * Set the error channels. Same as {@code err(error, error)}.
+ *
+ * @implSpec If neither {@code err} method is called, the behavior should be
+ * equivalent to calling {@code err(System.err)}.
+ *
+ * @param error destination of tool errors, and
+ * user code errors
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ JavaShellToolBuilder err(PrintStream error);
+
+ /**
+ * Set the error channels.
+ *
+ * @implSpec If neither {@code err} method is called, the behavior should be
+ * equivalent to calling {@code err(System.err, System.err, System.err)}.
+ *
+ * @param cmdErr destination of tool start-up and fatal errors
+ * @param userErr destination of user code error output.
+ * For example, user snippet {@code System.err.println("Oops")}
+ * when executed {@code Oops} goes to userErr.
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ JavaShellToolBuilder err(PrintStream cmdErr, PrintStream userErr);
+
+ /**
+ * Set the storage mechanism for persistent information which includes
+ * input history and retained settings.
+ *
+ * @implSpec If neither {@code persistence} method is called, the behavior
+ * should be to use the tool's standard persistence mechanism.
+ *
+ * @param prefs an instance of {@link java.util.prefs.Preferences} that
+ * is used to retrieve and store persistent information
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ JavaShellToolBuilder persistence(Preferences prefs);
+
+ /**
+ * Set the storage mechanism for persistent information which includes
+ * input history and retained settings.
+ *
+ * @implSpec If neither {@code persistence} method is called, the behavior
+ * should be to use the tool's standard persistence mechanism.
+ *
+ * @param prefsMap an instance of {@link java.util.Map} that
+ * is used to retrieve and store persistent information
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ JavaShellToolBuilder persistence(Map<String,String> prefsMap);
+
+ /**
+ * Set the source for environment variables.
+ *
+ * @implSpec If this method is not called, the behavior should be
+ * equivalent to calling {@code env(System.getenv())}.
+ *
+ * @param vars the Map of environment variable names to values
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ JavaShellToolBuilder env(Map<String,String> vars);
+
+ /**
+ * Set the locale.
+ *
+ * @implSpec If this method is not called, the behavior should be
+ * equivalent to calling {@code locale(Locale.getDefault())}.
+ *
+ * @param locale the locale
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ JavaShellToolBuilder locale(Locale locale);
+
+ /**
+ * Set to enable a command capturing prompt override.
+ *
+ * @implSpec If this method is not called, the behavior should be
+ * equivalent to calling {@code promptCapture(false)}.
+ *
+ * @param capture if {@code true}, basic prompt is the {@code ENQ}
+ * character and continuation prompt is the {@code ACK} character.
+ * If false, prompts are as set with set-up or user {@code /set} commands.
+ * @return the {@code JavaShellToolBuilder} instance
+ */
+ JavaShellToolBuilder promptCapture(boolean capture);
+
+ /**
+ * Run an instance of the Java shell tool as configured by the other methods
+ * in this interface. This call is not destructive, more than one call of
+ * this method may be made from a configured builder.
+ *
+ * @param arguments the command-line arguments (including options), if any
+ * @throws Exception an unexpected fatal exception
+ */
+ void run(String... arguments) throws Exception;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/tool/package-info.java Tue Dec 20 13:42:13 2016 -0800
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+/**
+ * Provides a mechanism to launch an instance of a Java™ shell tool.
+ * Allows configuration of the tool before launching. A builder is used
+ * to configure and launch the tool.
+ * <p>
+ * At the simplest, a builder is retrieved, and the builder is used to run the
+ * tool:
+ * <pre>
+ * {@code
+ * JavaShellToolBuilder
+ * .builder()
+ * .run();
+ * }
+ * </pre>
+ * The builder can be configured and the run can have arguments:
+ * <pre>
+ * {@code
+ * JavaShellToolBuilder
+ * .builder()
+ * .out(myCommandPrintStream, myOutputPrintStream)
+ * .locale(Locale.CANADA)
+ * .run("--feedback", "silent", "MyStart");
+ * }
+ * </pre>
+ */
+
+
+package jdk.jshell.tool;
+
--- a/langtools/src/jdk.jshell/share/classes/module-info.java Tue Dec 20 06:06:01 2016 -0800
+++ b/langtools/src/jdk.jshell/share/classes/module-info.java Tue Dec 20 13:42:13 2016 -0800
@@ -24,14 +24,38 @@
*/
/**
- * This document is the API specification for JShell -- support for
+ * This module provides support for
* Java™ Programming Language 'snippet' evaluating tools, such as
* Read-Eval-Print Loops (REPLs).
+ * Separate packages support building tools, configuring the execution of tools,
+ * and programmatically launching the existing Java™ shell tool.
+ * <p>
+ * The {@link jdk.jshell} is the package for creating 'snippet' evaluating tools.
+ * Generally, this is only package that would be needed for creating tools.
+ * </p>
+ * <p>
+ * The {@link jdk.jshell.spi} package specifies a Service Provider Interface (SPI)
+ * for defining execution engine implementations for tools based on the
+ * {@link jdk.jshell} API. The {@link jdk.jshell.execution} package provides
+ * standard implementations of {@link jdk.jshell.spi} interfaces and supporting code. It
+ * also serves as a library of functionality for defining new execution engine
+ * implementations.
+ * </p>
+ * <p>
+ * The {@link jdk.jshell.tool} supports programmatically launching the
+ * "jshell tool".
+ * </p>
+ * <p>
+ * The {@link jdk.jshell.execution} package contains implementations of the
+ * interfaces in {@link jdk.jshell.spi}. Otherwise, the four packages are
+ * independent, operate at different levels, and do not share functionality or
+ * definitions.
+ * </p>
*/
module jdk.jshell {
requires transitive java.compiler;
requires transitive jdk.jdi;
- requires java.prefs;
+ requires transitive java.prefs;
requires jdk.compiler;
requires jdk.internal.le;
requires jdk.internal.ed;
@@ -40,6 +64,9 @@
exports jdk.jshell;
exports jdk.jshell.spi;
exports jdk.jshell.execution;
+ exports jdk.jshell.tool;
uses jdk.internal.editor.spi.BuildInEditorProvider;
+
+ provides javax.tools.Tool with jdk.internal.jshell.tool.JShellToolProvider;
}
--- a/langtools/test/jdk/jshell/CommandCompletionTest.java Tue Dec 20 06:06:01 2016 -0800
+++ b/langtools/test/jdk/jshell/CommandCompletionTest.java Tue Dec 20 13:42:13 2016 -0800
@@ -43,22 +43,73 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.testng.annotations.Test;
+import jdk.internal.jshell.tool.JShellTool;
+import jdk.internal.jshell.tool.JShellToolBuilder;
+import jdk.jshell.SourceCodeAnalysis.Suggestion;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
-@Test
public class CommandCompletionTest extends ReplToolTesting {
- public void testCommand() {
- assertCompletion("/deb|", false);
- assertCompletion("/re|", false, "/reload ", "/reset ");
- assertCompletion("/h|", false, "/help ", "/history ");
+
+ private JShellTool repl;
+
+ @Override
+ protected void testRawRun(Locale locale, String[] args) {
+ repl = ((JShellToolBuilder) builder(locale))
+ .rawTool();
+ try {
+ repl.start(args);
+ } catch (Exception ex) {
+ fail("Repl tool died with exception", ex);
+ }
+ }
+
+ public void assertCompletion(boolean after, String code, boolean isSmart, String... expected) {
+ if (!after) {
+ setCommandInput("\n");
+ } else {
+ assertCompletion(code, isSmart, expected);
+ }
}
+ public void assertCompletion(String code, boolean isSmart, String... expected) {
+ List<String> completions = computeCompletions(code, isSmart);
+ assertEquals(completions, Arrays.asList(expected), "Command: " + code + ", output: " +
+ completions.toString());
+ }
+
+ private List<String> computeCompletions(String code, boolean isSmart) {
+ int cursor = code.indexOf('|');
+ code = code.replace("|", "");
+ assertTrue(cursor > -1, "'|' not found: " + code);
+ List<Suggestion> completions =
+ repl.commandCompletionSuggestions(code, cursor, new int[] {-1}); //XXX: ignoring anchor for now
+ return completions.stream()
+ .filter(s -> isSmart == s.matchesType())
+ .map(s -> s.continuation())
+ .distinct()
+ .collect(Collectors.toList());
+ }
+
+ @Test
+ public void testCommand() {
+ testNoStartUp(
+ a -> assertCompletion(a, "/deb|", false),
+ a -> assertCompletion(a, "/re|", false, "/reload ", "/reset "),
+ a -> assertCompletion(a, "/h|", false, "/help ", "/history ")
+ );
+ }
+
+ @Test
public void testList() {
test(false, new String[] {"--no-startup"},
a -> assertCompletion(a, "/l|", false, "/list "),
@@ -72,6 +123,7 @@
);
}
+ @Test
public void testDrop() {
test(false, new String[] {"--no-startup"},
a -> assertCompletion(a, "/d|", false, "/drop "),
@@ -83,6 +135,7 @@
);
}
+ @Test
public void testEdit() {
test(false, new String[]{"--no-startup"},
a -> assertCompletion(a, "/e|", false, "/edit ", "/exit "),
@@ -101,31 +154,38 @@
);
}
+ @Test
public void testHelp() {
- assertCompletion("/help |", false,
+ testNoStartUp(
+ a -> assertCompletion(a, "/help |", false,
"/! ", "/-<n> ", "/<id> ", "/? ", "/classpath ", "/drop ",
"/edit ", "/exit ", "/help ", "/history ", "/imports ",
"/list ", "/methods ", "/open ", "/reload ", "/reset ",
- "/save ", "/set ", "/types ", "/vars ", "intro ", "shortcuts ");
- assertCompletion("/? |", false,
+ "/save ", "/set ", "/types ", "/vars ", "intro ", "shortcuts "),
+ a -> assertCompletion(a, "/? |", false,
"/! ", "/-<n> ", "/<id> ", "/? ", "/classpath ", "/drop ",
"/edit ", "/exit ", "/help ", "/history ", "/imports ",
"/list ", "/methods ", "/open ", "/reload ", "/reset ",
- "/save ", "/set ", "/types ", "/vars ", "intro ", "shortcuts ");
- assertCompletion("/help /s|", false,
- "/save ", "/set ");
- assertCompletion("/help /set |", false,
- "editor", "feedback", "format", "mode", "prompt", "start", "truncation");
- assertCompletion("/help /edit |", false);
+ "/save ", "/set ", "/types ", "/vars ", "intro ", "shortcuts "),
+ a -> assertCompletion(a, "/help /s|", false,
+ "/save ", "/set "),
+ a -> assertCompletion(a, "/help /set |", false,
+ "editor", "feedback", "format", "mode", "prompt", "start", "truncation"),
+ a -> assertCompletion(a, "/help /edit |", false)
+ );
}
+ @Test
public void testReload() {
- assertCompletion("/reload |", false, "-quiet ", "-restore ");
- assertCompletion("/reload -restore |", false, "-quiet");
- assertCompletion("/reload -quiet |", false, "-restore");
- assertCompletion("/reload -restore -quiet |", false);
+ testNoStartUp(
+ a -> assertCompletion(a, "/reload |", false, "-quiet ", "-restore "),
+ a -> assertCompletion(a, "/reload -restore |", false, "-quiet"),
+ a -> assertCompletion(a, "/reload -quiet |", false, "-restore"),
+ a -> assertCompletion(a, "/reload -restore -quiet |", false)
+ );
}
+ @Test
public void testVarsMethodsTypes() {
test(false, new String[]{"--no-startup"},
a -> assertCompletion(a, "/v|", false, "/vars "),
@@ -141,36 +201,53 @@
);
}
+ @Test
public void testOpen() throws IOException {
Compiler compiler = new Compiler();
- assertCompletion("/o|", false, "/open ");
+ testNoStartUp(
+ a -> assertCompletion(a, "/o|", false, "/open ")
+ );
List<String> p1 = listFiles(Paths.get(""));
getRootDirectories().forEach(s -> p1.add(s.toString()));
Collections.sort(p1);
- assertCompletion("/open |", false, p1.toArray(new String[p1.size()]));
+ testNoStartUp(
+ a -> assertCompletion(a, "/open |", false, p1.toArray(new String[p1.size()]))
+ );
Path classDir = compiler.getClassDir();
List<String> p2 = listFiles(classDir);
- assertCompletion("/open " + classDir + "/|", false, p2.toArray(new String[p2.size()]));
+ testNoStartUp(
+ a -> assertCompletion(a, "/open " + classDir + "/|", false, p2.toArray(new String[p2.size()]))
+ );
}
+ @Test
public void testSave() throws IOException {
Compiler compiler = new Compiler();
- assertCompletion("/s|", false, "/save ", "/set ");
+ testNoStartUp(
+ a -> assertCompletion(a, "/s|", false, "/save ", "/set ")
+ );
List<String> p1 = listFiles(Paths.get(""));
Collections.addAll(p1, "-all ", "-history ", "-start ");
getRootDirectories().forEach(s -> p1.add(s.toString()));
Collections.sort(p1);
- assertCompletion("/save |", false, p1.toArray(new String[p1.size()]));
+ testNoStartUp(
+ a -> assertCompletion(a, "/save |", false, p1.toArray(new String[p1.size()]))
+ );
Path classDir = compiler.getClassDir();
List<String> p2 = listFiles(classDir);
- assertCompletion("/save " + classDir + "/|",
- false, p2.toArray(new String[p2.size()]));
- assertCompletion("/save -all " + classDir + "/|",
- false, p2.toArray(new String[p2.size()]));
+ testNoStartUp(
+ a -> assertCompletion(a, "/save " + classDir + "/|",
+ false, p2.toArray(new String[p2.size()])),
+ a -> assertCompletion(a, "/save -all " + classDir + "/|",
+ false, p2.toArray(new String[p2.size()]))
+ );
}
+ @Test
public void testClassPath() throws IOException {
- assertCompletion("/classp|", false, "/classpath ");
+ testNoStartUp(
+ a -> assertCompletion(a, "/classp|", false, "/classpath ")
+ );
Compiler compiler = new Compiler();
Path outDir = compiler.getPath("testClasspathCompletion");
Files.createDirectories(outDir);
@@ -182,9 +259,12 @@
compiler.jar(outDir, jarName, "pkg/A.class");
compiler.getPath(outDir).resolve(jarName);
List<String> paths = listFiles(outDir, CLASSPATH_FILTER);
- assertCompletion("/classpath " + outDir + "/|", false, paths.toArray(new String[paths.size()]));
+ testNoStartUp(
+ a -> assertCompletion(a, "/classpath " + outDir + "/|", false, paths.toArray(new String[paths.size()]))
+ );
}
+ @Test
public void testUserHome() throws IOException {
List<String> completions;
Path home = Paths.get(System.getProperty("user.home"));
@@ -194,9 +274,12 @@
.sorted()
.collect(Collectors.toList());
}
- assertCompletion("/classpath ~/|", false, completions.toArray(new String[completions.size()]));
+ testNoStartUp(
+ a -> assertCompletion(a, "/classpath ~/|", false, completions.toArray(new String[completions.size()]))
+ );
}
+ @Test
public void testSet() throws IOException {
List<String> p1 = listFiles(Paths.get(""));
getRootDirectories().forEach(s -> p1.add(s.toString()));
--- a/langtools/test/jdk/jshell/HistoryTest.java Tue Dec 20 06:06:01 2016 -0800
+++ b/langtools/test/jdk/jshell/HistoryTest.java Tue Dec 20 13:42:13 2016 -0800
@@ -32,13 +32,29 @@
*/
import java.lang.reflect.Field;
+import java.util.Locale;
import jdk.internal.jline.extra.EditingHistory;
import org.testng.annotations.Test;
+import jdk.internal.jshell.tool.JShellTool;
+import jdk.internal.jshell.tool.JShellToolBuilder;
import static org.testng.Assert.*;
-@Test
public class HistoryTest extends ReplToolTesting {
+ private JShellTool repl;
+
+ @Override
+ protected void testRawRun(Locale locale, String[] args) {
+ repl = ((JShellToolBuilder) builder(locale))
+ .rawTool();
+ try {
+ repl.start(args);
+ } catch (Exception ex) {
+ fail("Repl tool died with exception", ex);
+ }
+ }
+
+ @Test
public void testHistory() {
test(
a -> {if (!a) setCommandInput("void test() {\n");},
@@ -76,6 +92,7 @@
});
}
+ @Test
public void test8166744() {
test(
a -> {if (!a) setCommandInput("class C {\n");},
--- a/langtools/test/jdk/jshell/ReplToolTesting.java Tue Dec 20 06:06:01 2016 -0800
+++ b/langtools/test/jdk/jshell/ReplToolTesting.java Tue Dec 20 13:42:13 2016 -0800
@@ -25,7 +25,6 @@
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -36,17 +35,15 @@
import java.util.function.Predicate;
import java.util.prefs.AbstractPreferences;
import java.util.prefs.BackingStoreException;
-import java.util.prefs.Preferences;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import jdk.internal.jshell.tool.JShellTool;
-import jdk.jshell.SourceCodeAnalysis.Suggestion;
import org.testng.annotations.BeforeMethod;
+import jdk.jshell.tool.JavaShellToolBuilder;
import static java.util.stream.Collectors.toList;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
@@ -92,11 +89,9 @@
private Map<String, ClassInfo> classes;
private Map<String, ImportInfo> imports;
private boolean isDefaultStartUp = true;
- private Preferences prefs;
+ private Map<String, String> prefsMap;
private Map<String, String> envvars;
- public JShellTool repl = null;
-
public interface ReplTest {
void run(boolean after);
}
@@ -202,6 +197,10 @@
test(Locale.ROOT, isDefaultStartUp, args, DEFAULT_STARTUP_MESSAGE, tests);
}
+ public void testNoStartUp(ReplTest... tests) {
+ test(Locale.ROOT, false, new String[] {"--no-startup"}, DEFAULT_STARTUP_MESSAGE, tests);
+ }
+
public void test(Locale locale, boolean isDefaultStartUp, String[] args, String startUpMessage, ReplTest... tests) {
this.isDefaultStartUp = isDefaultStartUp;
initSnippets();
@@ -232,7 +231,7 @@
@BeforeMethod
public void setUp() {
- prefs = new MemoryPreferences();
+ prefsMap = new HashMap<>();
envvars = new HashMap<>();
}
@@ -240,7 +239,25 @@
envvars.put(name, value);
}
- public void testRaw(Locale locale, String[] args, ReplTest... tests) {
+ protected JavaShellToolBuilder builder(Locale locale) {
+ return JavaShellToolBuilder
+ .builder()
+ .in(cmdin, userin)
+ .out(new PrintStream(cmdout), new PrintStream(console), new PrintStream(userout))
+ .err(new PrintStream(cmderr), new PrintStream(usererr))
+ .persistence(prefsMap)
+ .env(envvars)
+ .locale(locale)
+ .promptCapture(true);
+ }
+
+ private void testRaw(Locale locale, String[] args, ReplTest... tests) {
+ testRawInit(tests);
+ testRawRun(locale, args);
+ testRawCheck(locale);
+ }
+
+ private void testRawInit(ReplTest... tests) {
cmdin = new WaitingTestingInputStream();
cmdout = new ByteArrayOutputStream();
cmderr = new ByteArrayOutputStream();
@@ -248,23 +265,18 @@
userin = new TestingInputStream();
userout = new ByteArrayOutputStream();
usererr = new ByteArrayOutputStream();
- repl = new JShellTool(
- cmdin,
- new PrintStream(cmdout),
- new PrintStream(cmderr),
- new PrintStream(console),
- userin,
- new PrintStream(userout),
- new PrintStream(usererr),
- prefs,
- envvars,
- locale);
- repl.testPrompt = true;
+ }
+
+ protected void testRawRun(Locale locale, String[] args) {
try {
- repl.start(args);
+ builder(locale)
+ .run(args);
} catch (Exception ex) {
fail("Repl tool died with exception", ex);
}
+ }
+
+ private void testRawCheck(Locale locale) {
// perform internal consistency checks on state, if desired
String cos = getCommandOutput();
String ceos = getCommandErrorOutput();
@@ -272,9 +284,9 @@
String ueos = getUserErrorOutput();
assertTrue((cos.isEmpty() || cos.startsWith("| Goodbye") || !locale.equals(Locale.ROOT)),
"Expected a goodbye, but got: " + cos);
- assertTrue(ceos.isEmpty(), "Expected empty error output, got: " + ceos);
- assertTrue(uos.isEmpty(), "Expected empty output, got: " + uos);
- assertTrue(ueos.isEmpty(), "Expected empty error output, got: " + ueos);
+ assertTrue(ceos.isEmpty(), "Expected empty command error output, got: " + ceos);
+ assertTrue(uos.isEmpty(), "Expected empty user output, got: " + uos);
+ assertTrue(ueos.isEmpty(), "Expected empty user error output, got: " + ueos);
}
public void assertReset(boolean after, String cmd) {
@@ -454,36 +466,6 @@
}
}
- public void assertCompletion(boolean after, String code, boolean isSmart, String... expected) {
- if (!after) {
- setCommandInput("\n");
- } else {
- assertCompletion(code, isSmart, expected);
- }
- }
-
- public void assertCompletion(String code, boolean isSmart, String... expected) {
- List<String> completions = computeCompletions(code, isSmart);
- assertEquals(completions, Arrays.asList(expected), "Command: " + code + ", output: " +
- completions.toString());
- }
-
- private List<String> computeCompletions(String code, boolean isSmart) {
- JShellTool js = this.repl != null ? this.repl
- : new JShellTool(null, null, null, null, null, null, null,
- prefs, envvars, Locale.ROOT);
- int cursor = code.indexOf('|');
- code = code.replace("|", "");
- assertTrue(cursor > -1, "'|' not found: " + code);
- List<Suggestion> completions =
- js.commandCompletionSuggestions(code, cursor, new int[] {-1}); //XXX: ignoring anchor for now
- return completions.stream()
- .filter(s -> isSmart == s.matchesType())
- .map(s -> s.continuation())
- .distinct()
- .collect(Collectors.toList());
- }
-
public Consumer<String> assertStartsWith(String prefix) {
return (output) -> assertTrue(output.startsWith(prefix), "Output: \'" + output + "' does not start with: " + prefix);
}
--- a/langtools/test/jdk/jshell/StartOptionTest.java Tue Dec 20 06:06:01 2016 -0800
+++ b/langtools/test/jdk/jshell/StartOptionTest.java Tue Dec 20 13:42:13 2016 -0800
@@ -22,7 +22,7 @@
*/
/*
- * @test 8151754 8080883 8160089 8166581
+ * @test 8151754 8080883 8160089 8170162 8166581
* @summary Testing start-up options.
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
@@ -33,19 +33,21 @@
* @run testng StartOptionTest
*/
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Locale;
+import java.util.ServiceLoader;
import java.util.function.Consumer;
-import jdk.internal.jshell.tool.JShellTool;
+import javax.tools.Tool;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
-
+import jdk.jshell.tool.JavaShellToolBuilder;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
@@ -59,21 +61,26 @@
private ByteArrayOutputStream userout;
private ByteArrayOutputStream usererr;
- private JShellTool getShellTool() {
- return new JShellTool(
- new TestingInputStream(),
- new PrintStream(cmdout),
- new PrintStream(cmderr),
- new PrintStream(console),
- null,
- new PrintStream(userout),
- new PrintStream(usererr),
- new ReplToolTesting.MemoryPreferences(),
- new HashMap<>(),
- Locale.ROOT);
+ private JavaShellToolBuilder builder() {
+ return JavaShellToolBuilder
+ .builder()
+ .out(new PrintStream(cmdout), new PrintStream(console), new PrintStream(userout))
+ .err(new PrintStream(cmderr), new PrintStream(usererr))
+ .persistence(new HashMap<>())
+ .env(new HashMap<>())
+ .locale(Locale.ROOT);
}
- private void check(ByteArrayOutputStream str, Consumer<String> checkOut, String label) {
+ private void runShell(String... args) {
+ try {
+ builder()
+ .run(args);
+ } catch (Exception ex) {
+ fail("Repl tool died with exception", ex);
+ }
+ }
+
+ protected void check(ByteArrayOutputStream str, Consumer<String> checkOut, String label) {
byte[] bytes = str.toByteArray();
str.reset();
String out = new String(bytes, StandardCharsets.UTF_8);
@@ -84,18 +91,28 @@
}
}
- private void start(Consumer<String> checkOutput, Consumer<String> checkError, String... args) throws Exception {
- JShellTool tool = getShellTool();
- tool.start(args);
- check(cmdout, checkOutput, "cmdout");
+ protected void start(Consumer<String> checkCmdOutput,
+ Consumer<String> checkUserOutput, Consumer<String> checkError,
+ String... args) throws Exception {
+ runShell(args);
+ check(cmdout, checkCmdOutput, "cmdout");
check(cmderr, checkError, "cmderr");
check(console, null, "console");
- check(userout, null, "userout");
+ check(userout, checkUserOutput, "userout");
check(usererr, null, "usererr");
}
- private void start(String expectedOutput, String expectedError, String... args) throws Exception {
- start(s -> assertEquals(s.trim(), expectedOutput, "cmdout: "), s -> assertEquals(s.trim(), expectedError, "cmderr: "), args);
+ protected void start(String expectedCmdOutput, String expectedError, String... args) throws Exception {
+ startWithUserOutput(expectedCmdOutput, "", expectedError, args);
+ }
+
+ private void startWithUserOutput(String expectedCmdOutput, String expectedUserOutput,
+ String expectedError, String... args) throws Exception {
+ start(
+ s -> assertEquals(s.trim(), expectedCmdOutput, "cmdout: "),
+ s -> assertEquals(s.trim(), expectedUserOutput, "userout: "),
+ s -> assertEquals(s.trim(), expectedError, "cmderr: "),
+ args);
}
@BeforeMethod
@@ -107,21 +124,31 @@
usererr = new ByteArrayOutputStream();
}
- @Test
+ protected String writeToFile(String stuff) throws Exception {
+ Compiler compiler = new Compiler();
+ Path p = compiler.getPath("doit.repl");
+ compiler.writeToFile(p, stuff);
+ return p.toString();
+ }
+
+ public void testCommandFile() throws Exception {
+ String fn = writeToFile("String str = \"Hello \"\n/list\nSystem.out.println(str + str)\n/exit\n");
+ startWithUserOutput("1 : String str = \"Hello \";", "Hello Hello", "", "--no-startup", fn, "-s");
+ }
+
public void testUsage() throws Exception {
for (String opt : new String[]{"-h", "--help"}) {
start(s -> {
assertTrue(s.split("\n").length >= 7, "Not enough usage lines: " + s);
assertTrue(s.startsWith("Usage: jshell <options>"), "Unexpect usage start: " + s);
- }, null, opt);
+ }, null, null, opt);
}
}
- @Test
public void testUnknown() throws Exception {
- start(s -> { },
+ start(null, null,
s -> assertEquals(s.trim(), "Unknown option: u"), "-unknown");
- start(s -> { },
+ start(null, null,
s -> assertEquals(s.trim(), "Unknown option: unknown"), "--unknown");
}
@@ -138,7 +165,7 @@
public void testStartupFailedOption() throws Exception {
try {
- start("", "", "-R-hoge-foo-bar");
+ builder().run("-R-hoge-foo-bar");
} catch (IllegalStateException ex) {
String s = ex.getMessage();
assertTrue(s.startsWith("Launching JShell execution engine threw: Failed remote"), s);
@@ -151,7 +178,6 @@
start("", "File 'UNKNOWN' for '--startup' is not found.", "--startup", "UNKNOWN");
}
- @Test
public void testClasspath() throws Exception {
for (String cp : new String[] {"--class-path"}) {
start("", "Only one --class-path option may be used.", cp, ".", "--class-path", ".");
@@ -159,7 +185,6 @@
}
}
- @Test
public void testFeedbackOptionConflict() throws Exception {
start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.",
"--feedback", "concise", "--feedback", "verbose");
@@ -173,15 +198,13 @@
start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-q", "-s");
}
- @Test
public void testNegFeedbackOption() throws Exception {
start("", "Argument to feedback missing.", "--feedback");
start("", "Does not match any current feedback mode: blorp -- --feedback blorp", "--feedback", "blorp");
}
- @Test
public void testVersion() throws Exception {
- start(s -> assertTrue(s.startsWith("jshell"), "unexpected version: " + s), null, "--version");
+ start(s -> assertTrue(s.startsWith("jshell"), "unexpected version: " + s), null, null, "--version");
}
@AfterMethod
--- a/langtools/test/jdk/jshell/ToolBasicTest.java Tue Dec 20 06:06:01 2016 -0800
+++ b/langtools/test/jdk/jshell/ToolBasicTest.java Tue Dec 20 13:42:13 2016 -0800
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8143037 8142447 8144095 8140265 8144906 8146138 8147887 8147886 8148316 8148317 8143955 8157953 8080347 8154714 8166649 8167643
+ * @bug 8143037 8142447 8144095 8140265 8144906 8146138 8147887 8147886 8148316 8148317 8143955 8157953 8080347 8154714 8166649 8167643 8170162
* @summary Tests for Basic tests for REPL tool
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
@@ -49,8 +49,6 @@
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.prefs.BackingStoreException;
-import java.util.prefs.Preferences;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -305,22 +303,18 @@
}
public void testStartupFileOption() {
- try {
- Compiler compiler = new Compiler();
- Path startup = compiler.getPath("StartupFileOption/startup.txt");
- compiler.writeToFile(startup, "class A { public String toString() { return \"A\"; } }");
- test(new String[]{"--startup", startup.toString()},
- (a) -> evaluateExpression(a, "A", "new A()", "A")
- );
- test(new String[]{"--no-startup"},
- (a) -> assertCommandCheckOutput(a, "printf(\"\")", assertStartsWith("| Error:\n| cannot find symbol"))
- );
- test(
- (a) -> assertCommand(a, "printf(\"A\")", "", "", null, "A", "")
- );
- } finally {
- removeStartup();
- }
+ Compiler compiler = new Compiler();
+ Path startup = compiler.getPath("StartupFileOption/startup.txt");
+ compiler.writeToFile(startup, "class A { public String toString() { return \"A\"; } }");
+ test(new String[]{"--startup", startup.toString()},
+ (a) -> evaluateExpression(a, "A", "new A()", "A")
+ );
+ test(new String[]{"--no-startup"},
+ (a) -> assertCommandCheckOutput(a, "printf(\"\")", assertStartsWith("| Error:\n| cannot find symbol"))
+ );
+ test(
+ (a) -> assertCommand(a, "printf(\"A\")", "", "", null, "A", "")
+ );
}
public void testLoadingFromArgs() {
@@ -436,45 +430,34 @@
assertEquals(Files.readAllLines(path), output);
}
- public void testStartRetain() throws BackingStoreException {
- try {
- Compiler compiler = new Compiler();
- Path startUpFile = compiler.getPath("startUp.txt");
- test(
- (a) -> assertVariable(a, "int", "a"),
- (a) -> assertVariable(a, "double", "b", "10", "10.0"),
- (a) -> assertMethod(a, "void f() {}", "()V", "f"),
- (a) -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"),
- (a) -> assertCommand(a, "/save " + startUpFile.toString(), null),
- (a) -> assertCommand(a, "/set start -retain " + startUpFile.toString(), null)
- );
- Path unknown = compiler.getPath("UNKNOWN");
- test(
- (a) -> assertCommandOutputStartsWith(a, "/set start -retain " + unknown.toString(),
- "| File '" + unknown + "' for '/set start' is not found.")
- );
- test(false, new String[0],
- (a) -> {
- loadVariable(a, "int", "a");
- loadVariable(a, "double", "b", "10.0", "10.0");
- loadMethod(a, "void f() {}", "()void", "f");
- loadImport(a, "import java.util.stream.*;", "", "java.util.stream.*");
- assertCommandCheckOutput(a, "/types", assertClasses());
- },
- (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
- (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
- (a) -> assertCommandCheckOutput(a, "/imports", assertImports())
- );
- } finally {
- removeStartup();
- }
- }
-
- private void removeStartup() {
- Preferences preferences = Preferences.userRoot().node("tool/JShell");
- if (preferences != null) {
- preferences.remove("STARTUP");
- }
+ public void testStartRetain() {
+ Compiler compiler = new Compiler();
+ Path startUpFile = compiler.getPath("startUp.txt");
+ test(
+ (a) -> assertVariable(a, "int", "a"),
+ (a) -> assertVariable(a, "double", "b", "10", "10.0"),
+ (a) -> assertMethod(a, "void f() {}", "()V", "f"),
+ (a) -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"),
+ (a) -> assertCommand(a, "/save " + startUpFile.toString(), null),
+ (a) -> assertCommand(a, "/set start -retain " + startUpFile.toString(), null)
+ );
+ Path unknown = compiler.getPath("UNKNOWN");
+ test(
+ (a) -> assertCommandOutputStartsWith(a, "/set start -retain " + unknown.toString(),
+ "| File '" + unknown + "' for '/set start' is not found.")
+ );
+ test(false, new String[0],
+ (a) -> {
+ loadVariable(a, "int", "a");
+ loadVariable(a, "double", "b", "10.0", "10.0");
+ loadMethod(a, "void f() {}", "()void", "f");
+ loadImport(a, "import java.util.stream.*;", "", "java.util.stream.*");
+ assertCommandCheckOutput(a, "/types", assertClasses());
+ },
+ (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
+ (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
+ (a) -> assertCommandCheckOutput(a, "/imports", assertImports())
+ );
}
public void testStartSave() throws IOException {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ToolProviderTest.java Tue Dec 20 13:42:13 2016 -0800
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ServiceLoader;
+import java.util.function.Consumer;
+import javax.tools.Tool;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import static org.testng.Assert.fail;
+
+/*
+ * @test
+ * @bug 8170044
+ * @summary Test ServiceLoader launching of jshell tool
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.jdeps/com.sun.tools.javap
+ * jdk.jshell/jdk.internal.jshell.tool
+ * @library /tools/lib
+ * @build Compiler toolbox.ToolBox
+ * @run testng ToolProviderTest
+ */
+@Test
+public class ToolProviderTest extends StartOptionTest {
+
+ private ByteArrayOutputStream cmdout;
+ private ByteArrayOutputStream cmderr;
+
+ @BeforeMethod
+ @Override
+ public void setUp() {
+ cmdout = new ByteArrayOutputStream();
+ cmderr = new ByteArrayOutputStream();
+ }
+
+ @Override
+ protected void start(Consumer<String> checkCmdOutput,
+ Consumer<String> checkUserOutput, Consumer<String> checkError,
+ String... args) throws Exception {
+ if (runShellServiceLoader(args) != 0) {
+ fail("Repl tool failed");
+ }
+ check(cmdout, checkCmdOutput, "cmdout");
+ check(cmderr, checkError, "cmderr");
+ }
+
+ private int runShellServiceLoader(String... args) {
+ ServiceLoader<Tool> sl = ServiceLoader.load(Tool.class);
+ for (Tool provider : sl) {
+ if (provider.name().equals("jshell")) {
+ return provider.run(new ByteArrayInputStream(new byte[0]), cmdout, cmderr, args);
+ }
+ }
+ throw new AssertionError("Repl tool not found by ServiceLoader: " + sl);
+ }
+
+ @Override
+ public void testCommandFile() throws Exception {
+ String fn = writeToFile("String str = \"Hello \"\n/list\nSystem.out.println(str + str)\n/exit\n");
+ start("1 : String str = \"Hello \";" + "\n" + "Hello Hello", "", "--no-startup", fn, "-s");
+ }
+
+ @Override
+ public void testStartupFailedOption() throws Exception {
+ if (runShellServiceLoader("-R-hoge-foo-bar") == 0) {
+ fail("Expected tool failure");
+ } else {
+ check(cmderr, s -> s.startsWith("Launching JShell execution engine threw: Failed remote"), "cmderr");
+ }
+ }
+}