Skip to content

Commit c3776db

Browse files
committedNov 14, 2024
8342936: Enhance java.io.IO with parameter-less println() and readln()
Reviewed-by: asotona, jpai, naoto
1 parent b54bd82 commit c3776db

File tree

14 files changed

+252
-10
lines changed

14 files changed

+252
-10
lines changed
 

‎src/java.base/share/classes/java/io/Console.java

+31
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,19 @@ public Console println(Object obj) {
172172
throw newUnsupportedOperationException();
173173
}
174174

175+
/**
176+
* Terminates the current line in this console's output stream using
177+
* {@link System#lineSeparator()} and then flushes the console.
178+
*
179+
* @return This console
180+
*
181+
* @since 24
182+
*/
183+
@PreviewFeature(feature = PreviewFeature.Feature.IMPLICIT_CLASSES)
184+
public Console println() {
185+
return println("");
186+
}
187+
175188
/**
176189
* Writes a string representation of the specified object to this console's
177190
* output stream and then flushes the console.
@@ -214,6 +227,24 @@ public String readln(String prompt) {
214227
throw newUnsupportedOperationException();
215228
}
216229

230+
/**
231+
* Reads a single line of text from this console.
232+
*
233+
* @throws IOError
234+
* If an I/O error occurs.
235+
*
236+
* @return A string containing the line read from the console, not
237+
* including any line-termination characters, or {@code null}
238+
* if an end of stream has been reached without having read
239+
* any characters.
240+
*
241+
* @since 24
242+
*/
243+
@PreviewFeature(feature = PreviewFeature.Feature.IMPLICIT_CLASSES)
244+
public String readln() {
245+
throw newUnsupportedOperationException();
246+
}
247+
217248
/**
218249
* Writes a formatted string to this console's output stream using
219250
* the specified format string and arguments with the

‎src/java.base/share/classes/java/io/IO.java

+33
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ public static void println(Object obj) {
6363
con().println(obj);
6464
}
6565

66+
/**
67+
* Terminates the current line on the system console and then flushes
68+
* that console.
69+
*
70+
* <p> The effect is as if {@link Console#println() println()}
71+
* had been called on {@code System.console()}.
72+
*
73+
* @throws IOError if {@code System.console()} returns {@code null},
74+
* or if an I/O error occurs
75+
* @since 24
76+
*/
77+
public static void println() {
78+
con().println();
79+
}
80+
6681
/**
6782
* Writes a string representation of the specified object to the system
6883
* console and then flushes that console.
@@ -99,6 +114,24 @@ public static String readln(String prompt) {
99114
return con().readln(prompt);
100115
}
101116

117+
/**
118+
* Reads a single line of text from the system console.
119+
*
120+
* <p> The effect is as if {@link Console#readln() readln()}
121+
* had been called on {@code System.console()}.
122+
*
123+
* @return a string containing the line read from the system console, not
124+
* including any line-termination characters. Returns {@code null} if an
125+
* end of stream has been reached without having read any characters.
126+
*
127+
* @throws IOError if {@code System.console()} returns {@code null},
128+
* or if an I/O error occurs
129+
* @since 24
130+
*/
131+
public static String readln() {
132+
return con().readln();
133+
}
134+
102135
private static Console con() {
103136
var con = System.console();
104137
if (con != null) {

‎src/java.base/share/classes/java/io/ProxyingConsole.java

+12
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@ public String readln(String prompt) {
117117
}
118118
}
119119

120+
/**
121+
* {@inheritDoc}
122+
*
123+
* @throws IOError {@inheritDoc}
124+
*/
125+
@Override
126+
public String readln() {
127+
synchronized (readLock) {
128+
return delegate.readln();
129+
}
130+
}
131+
120132
/**
121133
* {@inheritDoc}
122134
*/

‎src/java.base/share/classes/jdk/internal/io/JdkConsole.java

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public interface JdkConsole {
4141
JdkConsole println(Object obj);
4242
JdkConsole print(Object obj);
4343
String readln(String prompt);
44+
String readln();
4445
JdkConsole format(Locale locale, String format, Object ... args);
4546
String readLine(Locale locale, String format, Object ... args);
4647
String readLine();

‎src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java

+15
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@ public String readln(String prompt) {
9090
return line;
9191
}
9292

93+
@Override
94+
public String readln() {
95+
String line = null;
96+
synchronized(readLock) {
97+
try {
98+
char[] ca = readline(false);
99+
if (ca != null)
100+
line = new String(ca);
101+
} catch (IOException x) {
102+
throw new IOError(x);
103+
}
104+
}
105+
return line;
106+
}
107+
93108
@Override
94109
public JdkConsole format(Locale locale, String format, Object ... args) {
95110
formatter.format(locale, format, args).flush();

‎src/jdk.internal.le/share/classes/jdk/internal/org/jline/JdkConsoleProviderImpl.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ public String readln(String prompt) {
9898
return getDelegate(true).readln(prompt);
9999
}
100100

101+
@Override
102+
public String readln() {
103+
return getDelegate(true).readln();
104+
}
105+
101106
@Override
102107
public JdkConsole format(Locale locale, String format, Object... args) {
103108
JdkConsole delegate = getDelegate(false);
@@ -224,6 +229,11 @@ public String readln(String prompt) {
224229
}
225230
}
226231

232+
@Override
233+
public String readln() {
234+
return readLine();
235+
}
236+
227237
@Override
228238
public JdkConsole format(Locale locale, String format, Object ... args) {
229239
writer().format(locale, format, args).flush();
@@ -242,7 +252,12 @@ public String readLine(Locale locale, String format, Object ... args) {
242252

243253
@Override
244254
public String readLine() {
245-
return readLine(Locale.getDefault(Locale.Category.FORMAT), "");
255+
try {
256+
initJLineIfNeeded();
257+
return jline.readLine();
258+
} catch (EndOfFileException eofe) {
259+
return null;
260+
}
246261
}
247262

248263
@Override

‎src/jdk.jshell/share/classes/jdk/internal/jshell/tool/IOContext.java

+4
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ public String readUserLine(String prompt) throws IOException {
6767
throw new UserInterruptException("");
6868
}
6969

70+
public String readUserLine() throws IOException {
71+
throw new UserInterruptException("");
72+
}
73+
7074
public Writer userOutput() {
7175
throw new UnsupportedOperationException();
7276
}

‎src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java

+9
Original file line numberDiff line numberDiff line change
@@ -4112,6 +4112,15 @@ public String readLine(String prompt) {
41124112
}
41134113
}
41144114

4115+
@Override
4116+
public String readLine() throws IOError {
4117+
try {
4118+
return input.readUserLine();
4119+
} catch (IOException ex) {
4120+
throw new IOError(ex);
4121+
}
4122+
}
4123+
41154124
@Override
41164125
public char[] readPassword(String prompt) {
41174126
try {

‎src/jdk.jshell/share/classes/jdk/jshell/JShellConsole.java

+16
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.io.PrintWriter;
2929
import java.io.Reader;
3030
import java.nio.charset.Charset;
31+
import jdk.internal.javac.PreviewFeature;
3132

3233
/**
3334
* An interface providing functionality for {@link java.io.Console} in the user's snippet.
@@ -75,6 +76,21 @@ public interface JShellConsole {
7576
*/
7677
public String readLine(String prompt) throws IOError;
7778

79+
/**
80+
* Reads a single line of text from the console.
81+
*
82+
* @throws IOError
83+
* If an I/O error occurs.
84+
*
85+
* @return A string containing the line read from the console, not
86+
* including any line-termination characters, or {@code null}
87+
* if an end of stream has been reached.
88+
* @see java.io.Console#readLine()
89+
* @since 24
90+
*/
91+
@PreviewFeature(feature=PreviewFeature.Feature.IMPLICIT_CLASSES)
92+
public String readLine() throws IOError;
93+
7894
/**
7995
* Provides a prompt, then reads a password or passphrase from
8096
* the console with echoing disabled.

‎src/jdk.jshell/share/classes/jdk/jshell/execution/impl/ConsoleImpl.java

+26-1
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,16 @@ public String readln(String prompt) {
233233
}
234234
}
235235

236+
/**
237+
* {@inheritDoc}
238+
*
239+
* @throws IOError {@inheritDoc}
240+
*/
241+
@Override
242+
public String readln() {
243+
return readLine();
244+
}
245+
236246
/**
237247
* {@inheritDoc}
238248
*/
@@ -269,7 +279,15 @@ public String readLine(Locale locale, String format, Object... args) {
269279
*/
270280
@Override
271281
public String readLine() {
272-
return readLine(Locale.getDefault(Locale.Category.FORMAT), "");
282+
try {
283+
return sendAndReceive(() -> {
284+
remoteInput.write(Task.READ_LINE_NO_PROMPT.ordinal());
285+
char[] line = readChars();
286+
return new String(line);
287+
});
288+
} catch (IOException ex) {
289+
throw new IOError(ex);
290+
}
273291
}
274292

275293
/**
@@ -404,6 +422,12 @@ public synchronized void write(int b) throws IOException {
404422
bp = 0;
405423
}
406424
}
425+
case READ_LINE_NO_PROMPT -> {
426+
String line = console.readLine();
427+
char[] chars = line.toCharArray();
428+
sendChars(sinkOutput, chars, 0, chars.length);
429+
bp = 0;
430+
}
407431
case READ_PASSWORD -> {
408432
char[] data = readCharsOrNull(1);
409433
if (data != null) {
@@ -478,6 +502,7 @@ private enum Task {
478502
FLUSH_OUTPUT,
479503
READ_CHARS,
480504
READ_LINE,
505+
READ_LINE_NO_PROMPT,
481506
READ_PASSWORD,
482507
FLUSH_CONSOLE,
483508
CHARSET,

‎test/jdk/java/io/IO/IO.java

+37-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
* questions.
2222
*/
2323

24+
import java.io.Writer;
2425
import java.lang.reflect.Method;
2526
import java.nio.file.Files;
2627
import java.nio.file.Path;
@@ -33,6 +34,7 @@
3334
import org.junit.jupiter.api.Assumptions;
3435
import org.junit.jupiter.api.BeforeAll;
3536
import org.junit.jupiter.api.Nested;
37+
import org.junit.jupiter.api.Test;
3638
import org.junit.jupiter.api.condition.EnabledOnOs;
3739
import org.junit.jupiter.api.condition.OS;
3840
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
@@ -48,7 +50,7 @@
4850

4951
/*
5052
* @test
51-
* @bug 8305457
53+
* @bug 8305457 8342936
5254
* @summary java.io.IO tests
5355
* @library /test/lib
5456
* @run junit IO
@@ -131,22 +133,26 @@ public void inputTestInteractive(String console, String prompt) throws Exception
131133
var testSrc = System.getProperty("test.src", ".");
132134
var command = new ArrayList<String>();
133135
command.add(expect.toString());
134-
command.add(Path.of(testSrc, "input.exp").toAbsolutePath().toString());
136+
String expectInputName = PROMPT_NONE.equals(prompt) ? "input-no-prompt"
137+
: "input";
138+
command.add(Path.of(testSrc, expectInputName + ".exp").toAbsolutePath().toString());
135139
command.add(System.getProperty("test.jdk") + "/bin/java");
136140
command.add("--enable-preview");
137141
if (console != null)
138142
command.add("-Djdk.console=" + console);
139143
command.add(Path.of(testSrc, "Input.java").toAbsolutePath().toString());
140-
command.add(prompt == null ? "0" : "1");
144+
command.add(prompt == null ? "0" : PROMPT_NONE.equals(prompt) ? "2" : "1");
141145
command.add(String.valueOf(prompt));
142146
OutputAnalyzer output = ProcessTools.executeProcess(command.toArray(new String[]{}));
143147
output.reportDiagnosticSummary();
144148
assertEquals(0, output.getExitValue());
145149
}
146150

151+
private static final String PROMPT_NONE = "prompt-none";
152+
147153
public static Stream<Arguments> args() {
148154
// cross product: consoles x prompts
149-
return Stream.of(null, "gibberish").flatMap(console -> Stream.of(null, "?", "%s")
155+
return Stream.of(null, "gibberish").flatMap(console -> Stream.of(null, "?", "%s", PROMPT_NONE)
150156
.map(prompt -> new String[]{console, prompt}).map(Arguments::of));
151157
}
152158
}
@@ -172,6 +178,33 @@ public void printTest(String mode) throws Exception {
172178
out.substring(out.length() / 2));
173179
}
174180

181+
@Test //JDK-8342936
182+
public void printlnNoParamsTest() throws Exception {
183+
var file = Path.of("PrintlnNoParams.java");
184+
try (Writer w = Files.newBufferedWriter(file)) {
185+
w.write("""
186+
void main() {
187+
print("1 ");
188+
print("2 ");
189+
print("3 ");
190+
println();
191+
System.console().print("1 ");
192+
System.console().print("2 ");
193+
System.console().print("3 ");
194+
System.console().println();
195+
}
196+
""");
197+
}
198+
var pb = ProcessTools.createTestJavaProcessBuilder("--enable-preview", file.toString());
199+
OutputAnalyzer output = ProcessTools.executeProcess(pb);
200+
assertEquals(0, output.getExitValue());
201+
assertTrue(output.getStderr().isEmpty());
202+
output.reportDiagnosticSummary();
203+
String out = output.getStdout();
204+
String nl = System.getProperty("line.separator");
205+
assertEquals("1 2 3 " + nl + "1 2 3 " + nl, out);
206+
}
207+
175208

176209
@ParameterizedTest
177210
@ValueSource(strings = {"println", "print", "input"})

‎test/jdk/java/io/IO/Input.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
public class Input {
2929

3030
public static void main(String[] args) throws IOException {
31-
if (args[0].equals("0"))
32-
System.out.print(readln(null));
33-
else
34-
System.out.print(readln(args[1]));
31+
switch (args[0]) {
32+
case "0" -> System.out.print(readln(null));
33+
case "1" -> System.out.print(readln(args[1]));
34+
case "2" -> System.out.print(readln());
35+
default -> throw new AssertionError("Unknown command: " + args[0]);
36+
}
3537
}
3638
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#
2+
# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
#
5+
# This code is free software; you can redistribute it and/or modify it
6+
# under the terms of the GNU General Public License version 2 only, as
7+
# published by the Free Software Foundation.
8+
#
9+
# This code is distributed in the hope that it will be useful, but WITHOUT
10+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
# version 2 for more details (a copy is included in the LICENSE file that
13+
# accompanied this code).
14+
#
15+
# You should have received a copy of the GNU General Public License version
16+
# 2 along with this work; if not, write to the Free Software Foundation,
17+
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
#
19+
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
# or visit www.oracle.com if you need additional information or have any
21+
# questions.
22+
#
23+
24+
set prompt [lindex $argv $argc-1]
25+
set stty_init "rows 24 cols 80"
26+
set timeout -1
27+
28+
spawn {*}$argv
29+
send "hello\r"
30+
expect eof

‎test/langtools/jdk/jshell/ConsoleTest.java

+16
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* @test
2626
* @bug 8298425
2727
* @summary Verify behavior of System.console()
28+
* @enablePreview
2829
* @build KullaTesting TestingInputStream
2930
* @run testng ConsoleTest
3031
*/
@@ -67,6 +68,13 @@ public String readLine(String prompt) throws IOError {
6768
}
6869
};
6970
assertEval("System.console().readLine(\"expected\")", "\"AB\"");
71+
console = new ThrowingJShellConsole() {
72+
@Override
73+
public String readLine() throws IOError {
74+
return "AB";
75+
}
76+
};
77+
assertEval("System.console().readLine()", "\"AB\"");
7078
console = new ThrowingJShellConsole() {
7179
@Override
7280
public char[] readPassword(String prompt) throws IOError {
@@ -210,6 +218,10 @@ public String readLine(String prompt) throws IOError {
210218
return console.readLine(prompt);
211219
}
212220
@Override
221+
public String readLine() throws IOError {
222+
return console.readLine();
223+
}
224+
@Override
213225
public char[] readPassword(String prompt) throws IOError {
214226
return console.readPassword(prompt);
215227
}
@@ -240,6 +252,10 @@ public String readLine(String prompt) throws IOError {
240252
throw new IllegalStateException("Not expected!");
241253
}
242254
@Override
255+
public String readLine() throws IOError {
256+
throw new IllegalStateException("Not expected!");
257+
}
258+
@Override
243259
public char[] readPassword(String prompt) throws IOError {
244260
throw new IllegalStateException("Not expected!");
245261
}

0 commit comments

Comments
 (0)
Please sign in to comment.