1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package java.io;
17
18import java.util.Formatter;
19import libcore.io.ErrnoException;
20import libcore.io.Libcore;
21import static libcore.io.OsConstants.*;
22
23/**
24 * Provides access to the console, if available. The system-wide instance can
25 * be accessed via {@link java.lang.System#console}.
26 * @since 1.6
27 */
28public final class Console implements Flushable {
29    private static final Object CONSOLE_LOCK = new Object();
30
31    private static final Console console = makeConsole();
32
33    private final ConsoleReader reader;
34    private final PrintWriter writer;
35
36    /**
37     * Secret accessor for {@code System.console}.
38     * @hide
39     */
40    public static Console getConsole() {
41        return console;
42    }
43
44    private static Console makeConsole() {
45        // We don't care about stderr, because this class only uses stdin and stdout.
46        if (!Libcore.os.isatty(FileDescriptor.in) || !Libcore.os.isatty(FileDescriptor.out)) {
47            return null;
48        }
49        try {
50            return new Console(System.in, System.out);
51        } catch (IOException ex) {
52            throw new AssertionError(ex);
53        }
54    }
55
56    private Console(InputStream in, OutputStream out) throws IOException {
57        this.reader = new ConsoleReader(in);
58        this.writer = new ConsoleWriter(out);
59    }
60
61    public void flush() {
62        writer.flush();
63    }
64
65    /**
66     * Writes a formatted string to the console using
67     * the specified format string and arguments.
68     *
69     * @param format the format string (see {@link java.util.Formatter#format})
70     * @param args
71     *            the list of arguments passed to the formatter. If there are
72     *            more arguments than required by {@code format},
73     *            additional arguments are ignored.
74     * @return the console instance.
75     */
76    public Console format(String format, Object... args) {
77        Formatter f = new Formatter(writer);
78        f.format(format, args);
79        f.flush();
80        return this;
81    }
82
83    /**
84     * Equivalent to {@code format(format, args)}.
85     */
86    public Console printf(String format, Object... args) {
87        return format(format, args);
88    }
89
90    /**
91     * Returns the {@link Reader} associated with this console.
92     */
93    public Reader reader() {
94        return reader;
95    }
96
97    /**
98     * Reads a line from the console.
99     *
100     * @return the line, or null at EOF.
101     */
102    public String readLine() {
103        try {
104            return reader.readLine();
105        } catch (IOException e) {
106            throw new IOError(e);
107        }
108    }
109
110    /**
111     * Reads a line from this console, using the specified prompt.
112     * The prompt is given as a format string and optional arguments.
113     * Note that this can be a source of errors: if it is possible that your
114     * prompt contains {@code %} characters, you must use the format string {@code "%s"}
115     * and pass the actual prompt as a parameter.
116     *
117     * @param format the format string (see {@link java.util.Formatter#format})
118     * @param args
119     *            the list of arguments passed to the formatter. If there are
120     *            more arguments than required by {@code format},
121     *            additional arguments are ignored.
122     * @return the line, or null at EOF.
123     */
124    public String readLine(String format, Object... args) {
125        synchronized (CONSOLE_LOCK) {
126            format(format, args);
127            return readLine();
128        }
129    }
130
131    /**
132     * Reads a password from the console. The password will not be echoed to the display.
133     *
134     * @return a character array containing the password, or null at EOF.
135     */
136    public char[] readPassword() {
137        synchronized (CONSOLE_LOCK) {
138            int previousState = setEcho(false, 0);
139            try {
140                String password = readLine();
141                writer.println(); // We won't have echoed the user's newline.
142                return (password == null) ? null : password.toCharArray();
143            } finally {
144                setEcho(true, previousState);
145            }
146        }
147    }
148
149    private static int setEcho(boolean on, int previousState) {
150        try {
151            return setEchoImpl(on, previousState);
152        } catch (IOException ex) {
153            throw new IOError(ex);
154        }
155    }
156    private static native int setEchoImpl(boolean on, int previousState) throws IOException;
157
158    /**
159     * Reads a password from the console. The password will not be echoed to the display.
160     * A formatted prompt is also displayed.
161     *
162     * @param format the format string (see {@link java.util.Formatter#format})
163     * @param args
164     *            the list of arguments passed to the formatter. If there are
165     *            more arguments than required by {@code format},
166     *            additional arguments are ignored.
167     * @return a character array containing the password, or null at EOF.
168     */
169    public char[] readPassword(String format, Object... args) {
170        synchronized (CONSOLE_LOCK) {
171            format(format, args);
172            return readPassword();
173        }
174    }
175
176    /**
177     * Returns the {@link Writer} associated with this console.
178     */
179    public PrintWriter writer() {
180        return writer;
181    }
182
183    private static class ConsoleReader extends BufferedReader {
184        public ConsoleReader(InputStream in) throws IOException {
185            super(new InputStreamReader(in, System.getProperty("file.encoding")), 256);
186            lock = CONSOLE_LOCK;
187        }
188
189        @Override
190        public void close() {
191            // Console.reader cannot be closed.
192        }
193    }
194
195    private static class ConsoleWriter extends PrintWriter {
196        public ConsoleWriter(OutputStream out) {
197            super(out, true);
198            lock = CONSOLE_LOCK;
199        }
200
201        @Override
202        public void close() {
203            // Console.writer cannot be closed.
204            flush();
205        }
206    }
207}
208