/*
 * Copyright (c) 2005, 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.io;

import java.util.*;
import java.nio.charset.Charset;
import jdk.internal.access.JavaIOAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.io.JdkConsoleImpl;
import jdk.internal.io.JdkConsoleProvider;
import jdk.internal.util.StaticProperty;
import sun.nio.cs.UTF_8;

/**
 * Methods to access the character-based console device, if any, associated
 * with the current Java virtual machine.
 *
 * <p> Whether a virtual machine has a console is dependent upon the
 * underlying platform and also upon the manner in which the virtual
 * machine is invoked.  If the virtual machine is started from an
 * interactive command line without redirecting the standard input and
 * output streams then its console will exist and will typically be
 * connected to the keyboard and display from which the virtual machine
 * was launched.  If the virtual machine is started automatically, for
 * example by a background job scheduler, then it may not
 * have a console.
 * <p>
 * If this virtual machine has a console then it is represented by a
 * unique instance of this class which can be obtained by invoking the
 * {@link java.lang.System#console()} method.  If no console device is
 * available then an invocation of that method will return {@code null}.
 * <p>
 * Read and write operations are synchronized to guarantee the atomic
 * completion of critical operations; therefore invoking methods
 * {@link #readLine()}, {@link #readPassword()}, {@link #format format()},
 * {@link #printf printf()} as well as the read, format and write operations
 * on the objects returned by {@link #reader()} and {@link #writer()} may
 * block in multithreaded scenarios.
 * <p>
 * Read and write operations use the {@code Charset}s specified by
 * {@link System##stdin.encoding stdin.encoding} and {@link
 * System##stdout.encoding stdout.encoding}, respectively. The
 * {@code Charset} used for write operations can also be retrieved using
 * the {@link #charset()} method. Since {@code Console} is intended for
 * interactive use on a terminal, these charsets are typically the same.
 * <p>
 * Operations that format strings are locale sensitive, using either the
 * specified {@code Locale}, or the
 * {@link Locale##default_locale default format Locale} to produce localized
 * formatted strings.
 * <p>
 * Invoking {@code close()} on the objects returned by the {@link #reader()}
 * and the {@link #writer()} will not close the underlying stream of those
 * objects.
 * <p>
 * The console-read methods return {@code null} when the end of the
 * console input stream is reached, for example by typing control-D on
 * Unix or control-Z on Windows.  Subsequent read operations will succeed
 * if additional characters are later entered on the console's input
 * device.
 * <p>
 * Unless otherwise specified, passing a {@code null} argument to any method
 * in this class will cause a {@link NullPointerException} to be thrown.
 * <p>
 * <b>Security note:</b>
 * If an application needs to read a password or other secure data, it should
 * use {@link #readPassword()} or {@link #readPassword(String, Object...)} and
 * manually zero the returned character array after processing to minimize the
 * lifetime of sensitive data in memory.
 *
 * {@snippet lang=java :
 * Console cons;
 * char[] passwd;
 * if ((cons = System.console()) != null &&
 *     (passwd = cons.readPassword("[%s]", "Password:")) != null) {
 *     ...
 *     java.util.Arrays.fill(passwd, ' ');
 * }
 * }
 *
 * @author  Xueming Shen
 * @since   1.6
 */
public sealed class Console implements Flushable permits ProxyingConsole {
    /**
     * Package private no-arg constructor.
     */
    Console() {}

    /**
     * Retrieves the unique {@link java.io.PrintWriter PrintWriter} object
     * associated with this console.
     *
     * @return  The printwriter associated with this console
     */
    public PrintWriter writer() {
        throw newUnsupportedOperationException();
    }

    /**
     * Retrieves the unique {@link java.io.Reader Reader} object associated
     * with this console.
     * <p>
     * This method is intended to be used by sophisticated applications, for
     * example, a {@link java.util.Scanner} object which utilizes the rich
     * parsing/scanning functionality provided by the {@code Scanner}:
     * {@snippet lang=java :
     *     Console con = System.console();
     *     if (con != null) {
     *         Scanner sc = new Scanner(con.reader());
     *         code: // @replace substring="code:" replacement="..."
     *     }
     * }
     * <p>
     * For simple applications requiring only line-oriented reading, use
     * {@link #readLine}.
     * <p>
     * The bulk read operations {@link java.io.Reader#read(char[]) read(char[])},
     * {@link java.io.Reader#read(char[], int, int) read(char[], int, int)} and
     * {@link java.io.Reader#read(java.nio.CharBuffer) read(java.nio.CharBuffer)}
     * on the returned object will not read in characters beyond the line
     * bound for each invocation, even if the destination buffer has space for
     * more characters. The {@code Reader}'s {@code read} methods may block if a
     * line bound has not been entered or reached on the console's input device.
     * A line bound is considered to be any one of a line feed ({@code '\n'}),
     * a carriage return ({@code '\r'}), a carriage return followed immediately
     * by a linefeed, or an end of stream.
     *
     * @return  The reader associated with this console
     */
    public Reader reader() {
        throw newUnsupportedOperationException();
    }

    /**
     * Writes a formatted string to this console's output stream using
     * the specified format string and arguments with the
     * {@link Locale##default_locale default format locale}.
     *
     * @param  format
     *         A format string as described in {@link
     *         Formatter##syntax Format string syntax}.
     *
     * @param  args
     *         Arguments referenced by the format specifiers in the format
     *         string.  If there are more arguments than format specifiers, the
     *         extra arguments are ignored.  The number of arguments is
     *         variable and may be zero.  The maximum number of arguments is
     *         limited by the maximum dimension of a Java array as defined by
     *         <cite>The Java Virtual Machine Specification</cite>.
     *         The behavior on a
     *         {@code null} argument depends on the {@link
     *         Formatter##syntax conversion}.
     *
     * @throws  IllegalFormatException
     *          If a format string contains an illegal syntax, a format
     *          specifier that is incompatible with the given arguments,
     *          insufficient arguments given the format string, or other
     *          illegal conditions.  For specification of all possible
     *          formatting errors, see the {@link
     *          Formatter##detail Details} section
     *          of the formatter class specification.
     *
     * @return  This console
     */
    public Console format(String format, Object ... args) {
        throw newUnsupportedOperationException();
    }

    /**
     * Writes a formatted string to this console's output stream using
     * the specified format string and arguments with the specified
     * {@code locale}.
     *
     * @param  locale The {@linkplain Locale locale} to apply during
     *         formatting.  If {@code locale} is {@code null} then no localization
     *         is applied.
     *
     * @param  format
     *         A format string as described in {@link
     *         Formatter##syntax Format string syntax}.
     *
     * @param  args
     *         Arguments referenced by the format specifiers in the format
     *         string.  If there are more arguments than format specifiers, the
     *         extra arguments are ignored.  The number of arguments is
     *         variable and may be zero.  The maximum number of arguments is
     *         limited by the maximum dimension of a Java array as defined by
     *         <cite>The Java Virtual Machine Specification</cite>.
     *         The behavior on a
     *         {@code null} argument depends on the {@link
     *         Formatter##syntax conversion}.
     *
     * @throws  IllegalFormatException
     *          If a format string contains an illegal syntax, a format
     *          specifier that is incompatible with the given arguments,
     *          insufficient arguments given the format string, or other
     *          illegal conditions.  For specification of all possible
     *          formatting errors, see the {@link
     *          Formatter##detail Details} section
     *          of the formatter class specification.
     *
     * @return  This console
     * @since   23
     */
    public Console format(Locale locale, String format, Object ... args) {
        throw newUnsupportedOperationException();
    }

    /**
     * A convenience method to write a formatted string to this console's
     * output stream using the specified format string and arguments with
     * the {@link Locale##default_locale default format locale}.
     *
     * @implSpec This is the same as calling {@code format(format, args)}.
     *
     * @param  format
     *         A format string as described in {@link
     *         Formatter##syntax Format string syntax}.
     *
     * @param  args
     *         Arguments referenced by the format specifiers in the format
     *         string.  If there are more arguments than format specifiers, the
     *         extra arguments are ignored.  The number of arguments is
     *         variable and may be zero.  The maximum number of arguments is
     *         limited by the maximum dimension of a Java array as defined by
     *         <cite>The Java Virtual Machine Specification</cite>.
     *         The behavior on a
     *         {@code null} argument depends on the {@link
     *         Formatter##syntax conversion}.
     *
     * @throws  IllegalFormatException
     *          If a format string contains an illegal syntax, a format
     *          specifier that is incompatible with the given arguments,
     *          insufficient arguments given the format string, or other
     *          illegal conditions.  For specification of all possible
     *          formatting errors, see the {@link
     *          Formatter##detail Details} section of the
     *          formatter class specification.
     *
     * @return  This console
     */
    public Console printf(String format, Object ... args) {
        throw newUnsupportedOperationException();
    }

    /**
     * A convenience method to write a formatted string to this console's
     * output stream using the specified format string and arguments with
     * the specified {@code locale}.
     *
     * @implSpec This is the same as calling
     *         {@code format(locale, format, args)}.
     *
     * @param  locale The {@linkplain Locale locale} to apply during
     *         formatting.  If {@code locale} is {@code null} then no localization
     *         is applied.
     *
     * @param  format
     *         A format string as described in {@link
     *         Formatter##syntax Format string syntax}.
     *
     * @param  args
     *         Arguments referenced by the format specifiers in the format
     *         string.  If there are more arguments than format specifiers, the
     *         extra arguments are ignored.  The number of arguments is
     *         variable and may be zero.  The maximum number of arguments is
     *         limited by the maximum dimension of a Java array as defined by
     *         <cite>The Java Virtual Machine Specification</cite>.
     *         The behavior on a
     *         {@code null} argument depends on the {@link
     *         Formatter##syntax conversion}.
     *
     * @throws  IllegalFormatException
     *          If a format string contains an illegal syntax, a format
     *          specifier that is incompatible with the given arguments,
     *          insufficient arguments given the format string, or other
     *          illegal conditions.  For specification of all possible
     *          formatting errors, see the {@link
     *          Formatter##detail Details} section of the
     *          formatter class specification.
     *
     * @return  This console
     * @since   23
     */
    public Console printf(Locale locale, String format, Object ... args) {
        throw newUnsupportedOperationException();
    }

    /**
     * Provides a formatted prompt using the
     * {@link Locale##default_locale default format locale}, then reads a
     * single line of text from the console.
     *
     * @param  format
     *         A format string as described in {@link
     *         Formatter##syntax Format string syntax}.
     *
     * @param  args
     *         Arguments referenced by the format specifiers in the format
     *         string.  If there are more arguments than format specifiers, the
     *         extra arguments are ignored.  The number of arguments is
     *         variable and may be zero.  The maximum number of arguments is
     *         limited by the maximum dimension of a Java array as defined by
     *         <cite>The Java Virtual Machine Specification</cite>.
     *         The behavior on a
     *         {@code null} argument depends on the {@link
     *         Formatter##syntax conversion}.
     *
     * @throws  IllegalFormatException
     *          If a format string contains an illegal syntax, a format
     *          specifier that is incompatible with the given arguments,
     *          insufficient arguments given the format string, or other
     *          illegal conditions.  For specification of all possible
     *          formatting errors, see the {@link
     *          Formatter##detail Details} section of the
     *          formatter class specification.
     *
     * @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.
     */
    public String readLine(String format, Object ... args) {
        throw newUnsupportedOperationException();
    }

    /**
     * Provides a formatted prompt using the specified {@code locale}, then
     * reads a single line of text from the console.
     *
     * @param  locale The {@linkplain Locale locale} to apply during
     *         formatting.  If {@code locale} is {@code null} then no localization
     *         is applied.
     *
     * @param  format
     *         A format string as described in {@link
     *         Formatter##syntax Format string syntax}.
     *
     * @param  args
     *         Arguments referenced by the format specifiers in the format
     *         string.  If there are more arguments than format specifiers, the
     *         extra arguments are ignored.  The number of arguments is
     *         variable and may be zero.  The maximum number of arguments is
     *         limited by the maximum dimension of a Java array as defined by
     *         <cite>The Java Virtual Machine Specification</cite>.
     *         The behavior on a
     *         {@code null} argument depends on the {@link
     *         Formatter##syntax conversion}.
     *
     * @throws  IllegalFormatException
     *          If a format string contains an illegal syntax, a format
     *          specifier that is incompatible with the given arguments,
     *          insufficient arguments given the format string, or other
     *          illegal conditions.  For specification of all possible
     *          formatting errors, see the {@link
     *          Formatter##detail Details} section of the
     *          formatter class specification.
     *
     * @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.
     * @since   23
     */
    public String readLine(Locale locale, String format, Object ... args) {
        throw newUnsupportedOperationException();
    }

    /**
     * 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.
     */
    public String readLine() {
        throw newUnsupportedOperationException();
    }

    /**
     * Provides a formatted prompt using the
     * {@link Locale##default_locale default format locale}, then reads a
     * password or passphrase from the console with echoing disabled.
     *
     * @param  format
     *         A format string as described in {@link
     *         Formatter##syntax Format string syntax}
     *         for the prompt text.
     *
     * @param  args
     *         Arguments referenced by the format specifiers in the format
     *         string.  If there are more arguments than format specifiers, the
     *         extra arguments are ignored.  The number of arguments is
     *         variable and may be zero.  The maximum number of arguments is
     *         limited by the maximum dimension of a Java array as defined by
     *         <cite>The Java Virtual Machine Specification</cite>.
     *         The behavior on a
     *         {@code null} argument depends on the {@link
     *         Formatter##syntax conversion}.
     *
     * @throws  IllegalFormatException
     *          If a format string contains an illegal syntax, a format
     *          specifier that is incompatible with the given arguments,
     *          insufficient arguments given the format string, or other
     *          illegal conditions.  For specification of all possible
     *          formatting errors, see the {@link
     *          Formatter##detail Details} section of the
     *          formatter class specification.
     *
     * @throws IOError
     *         If an I/O error occurs.
     *
     * @return  A character array containing the password or passphrase read
     *          from the console, not including any line-termination characters,
     *          or {@code null} if an end of stream has been reached.
     */
    public char[] readPassword(String format, Object ... args) {
        throw newUnsupportedOperationException();
    }

    /**
     * Provides a formatted prompt using the specified {@code locale}, then
     * reads a password or passphrase from the console with echoing disabled.
     *
     * @param  locale The {@linkplain Locale locale} to apply during
     *         formatting.  If {@code locale} is {@code null} then no localization
     *         is applied.
     *
     * @param  format
     *         A format string as described in {@link
     *         Formatter##syntax Format string syntax}
     *         for the prompt text.
     *
     * @param  args
     *         Arguments referenced by the format specifiers in the format
     *         string.  If there are more arguments than format specifiers, the
     *         extra arguments are ignored.  The number of arguments is
     *         variable and may be zero.  The maximum number of arguments is
     *         limited by the maximum dimension of a Java array as defined by
     *         <cite>The Java Virtual Machine Specification</cite>.
     *         The behavior on a
     *         {@code null} argument depends on the {@link
     *         Formatter##syntax conversion}.
     *
     * @throws  IllegalFormatException
     *          If a format string contains an illegal syntax, a format
     *          specifier that is incompatible with the given arguments,
     *          insufficient arguments given the format string, or other
     *          illegal conditions.  For specification of all possible
     *          formatting errors, see the {@link
     *          Formatter##detail Details} section of the
     *          formatter class specification.
     *
     * @throws IOError
     *         If an I/O error occurs.
     *
     * @return  A character array containing the password or passphrase read
     *          from the console, not including any line-termination characters,
     *          or {@code null} if an end of stream has been reached.
     * @since   23
     */
    public char[] readPassword(Locale locale, String format, Object ... args) {
        throw newUnsupportedOperationException();
    }

    /**
     * Reads a password or passphrase from the console with echoing disabled.
     *
     * @throws IOError
     *         If an I/O error occurs.
     *
     * @return  A character array containing the password or passphrase read
     *          from the console, not including any line-termination characters,
     *          or {@code null} if an end of stream has been reached.
     */
    public char[] readPassword() {
        throw newUnsupportedOperationException();
    }

    /**
     * Flushes the console and forces any buffered output to be written
     * immediately.
     */
    public void flush() {
        throw newUnsupportedOperationException();
    }

    /**
     * {@return the {@link java.nio.charset.Charset Charset} object used for
     * the write operations on this {@code Console}}
     * <p>
     * The returned charset is used for encoding the data that is sent to
     * the output (e.g., display), specified by the host environment or user.
     * It defaults to the one based on {@link System##stdout.encoding stdout.encoding},
     * and may not necessarily be the same as the default charset returned from
     * {@link java.nio.charset.Charset#defaultCharset() Charset.defaultCharset()}.
     *
     * @since 17
     */
    public Charset charset() {
        throw newUnsupportedOperationException();
    }

    /**
     * {@return {@code true} if the {@code Console} instance is a terminal}
     * <p>
     * This method returns {@code true} if the console device, associated with the current
     * Java virtual machine, is a terminal, typically an interactive command line
     * connected to a keyboard and display.
     *
     * @implNote The default implementation returns the value equivalent to calling
     * {@code isatty(stdin/stdout)} on POSIX platforms, or whether standard in/out file
     * descriptors are character devices or not on Windows.
     *
     * @since 22
     */
    public boolean isTerminal() {
        return istty;
    }

    private static UnsupportedOperationException newUnsupportedOperationException() {
        return new UnsupportedOperationException(
                "Console class itself does not provide implementation");
    }

    private static final boolean istty = istty();
    private static final Charset STDIN_CHARSET =
        Charset.forName(StaticProperty.stdinEncoding(), UTF_8.INSTANCE);
    private static final Charset STDOUT_CHARSET =
        Charset.forName(StaticProperty.stdoutEncoding(), UTF_8.INSTANCE);
    private static final Console cons = instantiateConsole();
    static {
        // Set up JavaIOAccess in SharedSecrets
        SharedSecrets.setJavaIOAccess(new JavaIOAccess() {
            public Console console() {
                return cons;
            }
        });
    }

    private static Console instantiateConsole() {
        Console c = null;

        try {
            /*
             * The JdkConsole provider used for Console instantiation can be specified
             * with the system property "jdk.console", whose value designates the module
             * name of the implementation, and which defaults to the value of
             * {@code JdkConsoleProvider.DEFAULT_PROVIDER_MODULE_NAME}. If multiple
             * provider implementations exist in that module, the first one found is used.
             * If no providers are available, or instantiation failed, java.base built-in
             * Console implementation is used.
             */
            var consModName = System.getProperty("jdk.console",
                    JdkConsoleProvider.DEFAULT_PROVIDER_MODULE_NAME);

            for (var jcp : ServiceLoader.load(ModuleLayer.boot(), JdkConsoleProvider.class)) {
                if (consModName.equals(jcp.getClass().getModule().getName())) {
                    var jc = jcp.console(istty, STDIN_CHARSET, STDOUT_CHARSET);
                    if (jc != null) {
                        c = new ProxyingConsole(jc);
                    }
                    break;
                }
            }
        } catch (ServiceConfigurationError _) {
        }

        // If not found, default to built-in Console
        if (istty && c == null) {
            c = new ProxyingConsole(new JdkConsoleImpl(STDIN_CHARSET, STDOUT_CHARSET));
        }

        return c;
    }

    private static native boolean istty();
}
