Files
JetBrainsRuntime/src/java.base/share/classes/java/lang/IO.java
Stuart Marks c7df72ff0f 8358809: Improve link to stdin.encoding from java.lang.IO
Reviewed-by: naoto
Backport-of: d024f58e61
2025-06-07 00:56:45 +00:00

197 lines
7.8 KiB
Java

/*
* Copyright (c) 2024, 2025, 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 java.lang;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOError;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* A collection of static methods that provide convenient access to {@link System#in}
* and {@link System#out} for line-oriented input and output.
* <p>
* The {@link #readln()} and {@link #readln(String)} methods decode bytes read from
* {@code System.in} into characters. The charset used for decoding is specified by the
* {@link System##stdin.encoding stdin.encoding} property. If this property is not present,
* or if the charset it names cannot be loaded, then UTF-8 is used instead. Decoding
* always replaces malformed and unmappable byte sequences with the charset's default
* replacement string.
* <p>
* Charset decoding is set up upon the first call to one of the {@code readln} methods.
* Decoding may buffer additional bytes beyond those that have been decoded to characters
* returned to the application. After the first call to one of the {@code readln} methods,
* any subsequent use of {@code System.in} results in unspecified behavior.
*
* @apiNote
* The expected use case is that certain applications will use only the {@code readln}
* methods to read from the standard input, and they will not mix these calls with
* other techniques for reading from {@code System.in}.
*
* @since 25
*/
public final class IO {
/*
* We are deliberately not including printf, at least not initially, for
* the following reasons. First, it introduces a rather cryptic and arcane
* formatting language that isn't really suited to beginners. Second, it
* is inherently localizable, which drags in a whole bunch of issues about
* what locale should be used for formatting, the possible inclusion of
* an overload with an explicit Locale parameter, and so forth. These issues
* are best avoided for the time being. Third, when string templates come
* along, they might offer a better alternative to printf-style formatting,
* so it's best not be saddled with this unnecessarily.
*/
/*
* Notes on flushing. We want flushing to occur after every call to println
* and print, so that the user can see output immediately. This could be
* important if the user calls print() to issue a prompt before calling
* readln() instead of the readln(prompt) overload. It's also important to
* flush after print() in case the user is relying on print() to emit output
* as sort of a progress indicator.
*
* We rely on System.out to have autoflush enabled, which flushes after every
* println() call, so we needn't flush again. We flush unconditionally after
* calls to print(). Since System.out is doing a lot of flushing anyway, there
* isn't much point trying to make this conditional, for example, only if
* stdout is connected to a terminal.
*/
private IO() {
throw new Error("no instances");
}
/**
* Writes a string representation of the specified object and then writes
* a line separator to the standard output.
*
* <p> The effect is as if {@link java.io.PrintStream#println(Object) println(obj)}
* had been called on {@code System.out}.
*
* @param obj the object to print, may be {@code null}
*/
public static void println(Object obj) {
System.out.println(obj);
}
/**
* Writes a line separator to the standard output.
*
* <p> The effect is as if {@link java.io.PrintStream#println() println()}
* had been called on {@code System.out}.
*/
public static void println() {
System.out.println();
}
/**
* Writes a string representation of the specified object to the
* standard output.
*
* <p> The effect is as if {@link java.io.PrintStream#print(Object) print(obj)}
* had been called on {@code System.out}.
*
* @param obj the object to print, may be {@code null}
*/
public static void print(Object obj) {
var out = System.out;
out.print(obj);
out.flush();
}
/**
* Reads a single line of text from the standard input.
* <p>
* One line is read from the decoded input as if by
* {@link java.io.BufferedReader#readLine() BufferedReader.readLine()}
* and then the result is returned.
* <p>
* If necessary, this method first sets up charset decoding, as described in
* above in the class specification.
*
* @return a string containing the line read from the standard input, not
* including any line separator characters. Returns {@code null} if an
* end of stream has been reached without having read any characters.
*
* @throws IOError if an I/O error occurs
*/
public static String readln() {
try {
return reader().readLine();
} catch (IOException ioe) {
throw new IOError(ioe);
}
}
/**
* Writes a prompt and then reads a line of input.
* <p>
* Writes a prompt as if by calling {@link #print print}, and then reads a single
* line of text as if by calling {@link #readln() readln}.
* <p>
* If necessary, this method first sets up charset decoding, as described in
* above in the class specification.
*
* @param prompt the prompt string, may be {@code null}
*
* @return a string containing the line read from the standard input, not
* including any line separator characters. Returns {@code null} if an
* end of stream has been reached without having read any characters.
*
* @throws IOError if an I/O error occurs
*/
public static String readln(String prompt) {
print(prompt);
return readln();
}
/**
* The BufferedReader used by readln(). Initialized under a class lock by
* the reader() method. All access should be through the reader() method.
*/
private static BufferedReader br;
/**
* On the first call, creates an InputStreamReader to decode characters from
* System.in, wraps it in a BufferedReader, and returns the BufferedReader.
* These objects are cached and returned by subsequent calls.
*
* @return the internal BufferedReader instance
*/
static synchronized BufferedReader reader() {
if (br == null) {
String enc = System.getProperty("stdin.encoding", "");
Charset cs = Charset.forName(enc, StandardCharsets.UTF_8);
br = new BufferedReader(new InputStreamReader(System.in, cs));
}
return br;
}
}