8342936: Enhance java.io.IO with parameter-less println() and readln()
Reviewed-by: asotona, jpai, naoto
This commit is contained in:
parent
b54bd824b5
commit
c3776db498
@ -172,6 +172,19 @@ public sealed class Console implements Flushable permits ProxyingConsole {
|
||||
throw newUnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the current line in this console's output stream using
|
||||
* {@link System#lineSeparator()} and then flushes the console.
|
||||
*
|
||||
* @return This console
|
||||
*
|
||||
* @since 24
|
||||
*/
|
||||
@PreviewFeature(feature = PreviewFeature.Feature.IMPLICIT_CLASSES)
|
||||
public Console println() {
|
||||
return println("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string representation of the specified object to this console's
|
||||
* output stream and then flushes the console.
|
||||
@ -214,6 +227,24 @@ public sealed class Console implements Flushable permits ProxyingConsole {
|
||||
throw newUnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single line of text from this console.
|
||||
*
|
||||
* @throws IOError
|
||||
* If an I/O error occurs.
|
||||
*
|
||||
* @return A string containing the line read from the console, not
|
||||
* including any line-termination characters, or {@code null}
|
||||
* if an end of stream has been reached without having read
|
||||
* any characters.
|
||||
*
|
||||
* @since 24
|
||||
*/
|
||||
@PreviewFeature(feature = PreviewFeature.Feature.IMPLICIT_CLASSES)
|
||||
public String readln() {
|
||||
throw newUnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a formatted string to this console's output stream using
|
||||
* the specified format string and arguments with the
|
||||
|
@ -63,6 +63,21 @@ public final class IO {
|
||||
con().println(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the current line on the system console and then flushes
|
||||
* that console.
|
||||
*
|
||||
* <p> The effect is as if {@link Console#println() println()}
|
||||
* had been called on {@code System.console()}.
|
||||
*
|
||||
* @throws IOError if {@code System.console()} returns {@code null},
|
||||
* or if an I/O error occurs
|
||||
* @since 24
|
||||
*/
|
||||
public static void println() {
|
||||
con().println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string representation of the specified object to the system
|
||||
* console and then flushes that console.
|
||||
@ -99,6 +114,24 @@ public final class IO {
|
||||
return con().readln(prompt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single line of text from the system console.
|
||||
*
|
||||
* <p> The effect is as if {@link Console#readln() readln()}
|
||||
* had been called on {@code System.console()}.
|
||||
*
|
||||
* @return a string containing the line read from the system console, not
|
||||
* including any line-termination characters. Returns {@code null} if an
|
||||
* end of stream has been reached without having read any characters.
|
||||
*
|
||||
* @throws IOError if {@code System.console()} returns {@code null},
|
||||
* or if an I/O error occurs
|
||||
* @since 24
|
||||
*/
|
||||
public static String readln() {
|
||||
return con().readln();
|
||||
}
|
||||
|
||||
private static Console con() {
|
||||
var con = System.console();
|
||||
if (con != null) {
|
||||
|
@ -117,6 +117,18 @@ final class ProxyingConsole extends Console {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws IOError {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String readln() {
|
||||
synchronized (readLock) {
|
||||
return delegate.readln();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -41,6 +41,7 @@ public interface JdkConsole {
|
||||
JdkConsole println(Object obj);
|
||||
JdkConsole print(Object obj);
|
||||
String readln(String prompt);
|
||||
String readln();
|
||||
JdkConsole format(Locale locale, String format, Object ... args);
|
||||
String readLine(Locale locale, String format, Object ... args);
|
||||
String readLine();
|
||||
|
@ -90,6 +90,21 @@ public final class JdkConsoleImpl implements JdkConsole {
|
||||
return line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readln() {
|
||||
String line = null;
|
||||
synchronized(readLock) {
|
||||
try {
|
||||
char[] ca = readline(false);
|
||||
if (ca != null)
|
||||
line = new String(ca);
|
||||
} catch (IOException x) {
|
||||
throw new IOError(x);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdkConsole format(Locale locale, String format, Object ... args) {
|
||||
formatter.format(locale, format, args).flush();
|
||||
|
@ -98,6 +98,11 @@ public class JdkConsoleProviderImpl implements JdkConsoleProvider {
|
||||
return getDelegate(true).readln(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readln() {
|
||||
return getDelegate(true).readln();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdkConsole format(Locale locale, String format, Object... args) {
|
||||
JdkConsole delegate = getDelegate(false);
|
||||
@ -224,6 +229,11 @@ public class JdkConsoleProviderImpl implements JdkConsoleProvider {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readln() {
|
||||
return readLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdkConsole format(Locale locale, String format, Object ... args) {
|
||||
writer().format(locale, format, args).flush();
|
||||
@ -242,7 +252,12 @@ public class JdkConsoleProviderImpl implements JdkConsoleProvider {
|
||||
|
||||
@Override
|
||||
public String readLine() {
|
||||
return readLine(Locale.getDefault(Locale.Category.FORMAT), "");
|
||||
try {
|
||||
initJLineIfNeeded();
|
||||
return jline.readLine();
|
||||
} catch (EndOfFileException eofe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -67,6 +67,10 @@ abstract class IOContext implements AutoCloseable {
|
||||
throw new UserInterruptException("");
|
||||
}
|
||||
|
||||
public String readUserLine() throws IOException {
|
||||
throw new UserInterruptException("");
|
||||
}
|
||||
|
||||
public Writer userOutput() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
@ -4112,6 +4112,15 @@ public class JShellTool implements MessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readLine() throws IOError {
|
||||
try {
|
||||
return input.readUserLine();
|
||||
} catch (IOException ex) {
|
||||
throw new IOError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] readPassword(String prompt) {
|
||||
try {
|
||||
|
@ -28,6 +28,7 @@ import java.io.IOError;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.Charset;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
|
||||
/**
|
||||
* An interface providing functionality for {@link java.io.Console} in the user's snippet.
|
||||
@ -75,6 +76,21 @@ public interface JShellConsole {
|
||||
*/
|
||||
public String readLine(String prompt) throws IOError;
|
||||
|
||||
/**
|
||||
* Reads a single line of text from the console.
|
||||
*
|
||||
* @throws IOError
|
||||
* If an I/O error occurs.
|
||||
*
|
||||
* @return A string containing the line read from the console, not
|
||||
* including any line-termination characters, or {@code null}
|
||||
* if an end of stream has been reached.
|
||||
* @see java.io.Console#readLine()
|
||||
* @since 24
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.IMPLICIT_CLASSES)
|
||||
public String readLine() throws IOError;
|
||||
|
||||
/**
|
||||
* Provides a prompt, then reads a password or passphrase from
|
||||
* the console with echoing disabled.
|
||||
|
@ -233,6 +233,16 @@ public class ConsoleImpl {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws IOError {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String readln() {
|
||||
return readLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ -269,7 +279,15 @@ public class ConsoleImpl {
|
||||
*/
|
||||
@Override
|
||||
public String readLine() {
|
||||
return readLine(Locale.getDefault(Locale.Category.FORMAT), "");
|
||||
try {
|
||||
return sendAndReceive(() -> {
|
||||
remoteInput.write(Task.READ_LINE_NO_PROMPT.ordinal());
|
||||
char[] line = readChars();
|
||||
return new String(line);
|
||||
});
|
||||
} catch (IOException ex) {
|
||||
throw new IOError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -404,6 +422,12 @@ public class ConsoleImpl {
|
||||
bp = 0;
|
||||
}
|
||||
}
|
||||
case READ_LINE_NO_PROMPT -> {
|
||||
String line = console.readLine();
|
||||
char[] chars = line.toCharArray();
|
||||
sendChars(sinkOutput, chars, 0, chars.length);
|
||||
bp = 0;
|
||||
}
|
||||
case READ_PASSWORD -> {
|
||||
char[] data = readCharsOrNull(1);
|
||||
if (data != null) {
|
||||
@ -478,6 +502,7 @@ public class ConsoleImpl {
|
||||
FLUSH_OUTPUT,
|
||||
READ_CHARS,
|
||||
READ_LINE,
|
||||
READ_LINE_NO_PROMPT,
|
||||
READ_PASSWORD,
|
||||
FLUSH_CONSOLE,
|
||||
CHARSET,
|
||||
|
@ -21,6 +21,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -33,6 +34,7 @@ import jdk.test.lib.process.ProcessTools;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledOnOs;
|
||||
import org.junit.jupiter.api.condition.OS;
|
||||
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
|
||||
@ -48,7 +50,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8305457
|
||||
* @bug 8305457 8342936
|
||||
* @summary java.io.IO tests
|
||||
* @library /test/lib
|
||||
* @run junit IO
|
||||
@ -131,22 +133,26 @@ public class IO {
|
||||
var testSrc = System.getProperty("test.src", ".");
|
||||
var command = new ArrayList<String>();
|
||||
command.add(expect.toString());
|
||||
command.add(Path.of(testSrc, "input.exp").toAbsolutePath().toString());
|
||||
String expectInputName = PROMPT_NONE.equals(prompt) ? "input-no-prompt"
|
||||
: "input";
|
||||
command.add(Path.of(testSrc, expectInputName + ".exp").toAbsolutePath().toString());
|
||||
command.add(System.getProperty("test.jdk") + "/bin/java");
|
||||
command.add("--enable-preview");
|
||||
if (console != null)
|
||||
command.add("-Djdk.console=" + console);
|
||||
command.add(Path.of(testSrc, "Input.java").toAbsolutePath().toString());
|
||||
command.add(prompt == null ? "0" : "1");
|
||||
command.add(prompt == null ? "0" : PROMPT_NONE.equals(prompt) ? "2" : "1");
|
||||
command.add(String.valueOf(prompt));
|
||||
OutputAnalyzer output = ProcessTools.executeProcess(command.toArray(new String[]{}));
|
||||
output.reportDiagnosticSummary();
|
||||
assertEquals(0, output.getExitValue());
|
||||
}
|
||||
|
||||
private static final String PROMPT_NONE = "prompt-none";
|
||||
|
||||
public static Stream<Arguments> args() {
|
||||
// cross product: consoles x prompts
|
||||
return Stream.of(null, "gibberish").flatMap(console -> Stream.of(null, "?", "%s")
|
||||
return Stream.of(null, "gibberish").flatMap(console -> Stream.of(null, "?", "%s", PROMPT_NONE)
|
||||
.map(prompt -> new String[]{console, prompt}).map(Arguments::of));
|
||||
}
|
||||
}
|
||||
@ -172,6 +178,33 @@ public class IO {
|
||||
out.substring(out.length() / 2));
|
||||
}
|
||||
|
||||
@Test //JDK-8342936
|
||||
public void printlnNoParamsTest() throws Exception {
|
||||
var file = Path.of("PrintlnNoParams.java");
|
||||
try (Writer w = Files.newBufferedWriter(file)) {
|
||||
w.write("""
|
||||
void main() {
|
||||
print("1 ");
|
||||
print("2 ");
|
||||
print("3 ");
|
||||
println();
|
||||
System.console().print("1 ");
|
||||
System.console().print("2 ");
|
||||
System.console().print("3 ");
|
||||
System.console().println();
|
||||
}
|
||||
""");
|
||||
}
|
||||
var pb = ProcessTools.createTestJavaProcessBuilder("--enable-preview", file.toString());
|
||||
OutputAnalyzer output = ProcessTools.executeProcess(pb);
|
||||
assertEquals(0, output.getExitValue());
|
||||
assertTrue(output.getStderr().isEmpty());
|
||||
output.reportDiagnosticSummary();
|
||||
String out = output.getStdout();
|
||||
String nl = System.getProperty("line.separator");
|
||||
assertEquals("1 2 3 " + nl + "1 2 3 " + nl, out);
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"println", "print", "input"})
|
||||
|
@ -28,9 +28,11 @@ import static java.io.IO.readln;
|
||||
public class Input {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args[0].equals("0"))
|
||||
System.out.print(readln(null));
|
||||
else
|
||||
System.out.print(readln(args[1]));
|
||||
switch (args[0]) {
|
||||
case "0" -> System.out.print(readln(null));
|
||||
case "1" -> System.out.print(readln(args[1]));
|
||||
case "2" -> System.out.print(readln());
|
||||
default -> throw new AssertionError("Unknown command: " + args[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
test/jdk/java/io/IO/input-no-prompt.exp
Normal file
30
test/jdk/java/io/IO/input-no-prompt.exp
Normal file
@ -0,0 +1,30 @@
|
||||
#
|
||||
# Copyright (c) 2024, 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.
|
||||
#
|
||||
|
||||
set prompt [lindex $argv $argc-1]
|
||||
set stty_init "rows 24 cols 80"
|
||||
set timeout -1
|
||||
|
||||
spawn {*}$argv
|
||||
send "hello\r"
|
||||
expect eof
|
@ -25,6 +25,7 @@
|
||||
* @test
|
||||
* @bug 8298425
|
||||
* @summary Verify behavior of System.console()
|
||||
* @enablePreview
|
||||
* @build KullaTesting TestingInputStream
|
||||
* @run testng ConsoleTest
|
||||
*/
|
||||
@ -67,6 +68,13 @@ public class ConsoleTest extends KullaTesting {
|
||||
}
|
||||
};
|
||||
assertEval("System.console().readLine(\"expected\")", "\"AB\"");
|
||||
console = new ThrowingJShellConsole() {
|
||||
@Override
|
||||
public String readLine() throws IOError {
|
||||
return "AB";
|
||||
}
|
||||
};
|
||||
assertEval("System.console().readLine()", "\"AB\"");
|
||||
console = new ThrowingJShellConsole() {
|
||||
@Override
|
||||
public char[] readPassword(String prompt) throws IOError {
|
||||
@ -210,6 +218,10 @@ public class ConsoleTest extends KullaTesting {
|
||||
return console.readLine(prompt);
|
||||
}
|
||||
@Override
|
||||
public String readLine() throws IOError {
|
||||
return console.readLine();
|
||||
}
|
||||
@Override
|
||||
public char[] readPassword(String prompt) throws IOError {
|
||||
return console.readPassword(prompt);
|
||||
}
|
||||
@ -240,6 +252,10 @@ public class ConsoleTest extends KullaTesting {
|
||||
throw new IllegalStateException("Not expected!");
|
||||
}
|
||||
@Override
|
||||
public String readLine() throws IOError {
|
||||
throw new IllegalStateException("Not expected!");
|
||||
}
|
||||
@Override
|
||||
public char[] readPassword(String prompt) throws IOError {
|
||||
throw new IllegalStateException("Not expected!");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user