Line data Source code
1 : // Copyright (C) 2009 The Android Open Source Project
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : package com.google.gerrit.pgm.init.api;
16 :
17 : import com.google.errorprone.annotations.FormatMethod;
18 : import com.google.errorprone.annotations.FormatString;
19 : import com.google.gerrit.common.Die;
20 : import com.google.gerrit.common.Nullable;
21 : import java.io.Console;
22 : import java.util.EnumSet;
23 : import java.util.Set;
24 :
25 : /** Console based interaction with the invoking user. */
26 15 : public abstract class ConsoleUI {
27 : /** Get a UI instance, assuming interactive mode. */
28 : public static ConsoleUI getInstance() {
29 0 : return getInstance(false);
30 : }
31 :
32 : /** Get a UI instance, possibly forcing batch mode. */
33 : public static ConsoleUI getInstance(boolean batchMode) {
34 15 : Console console = batchMode ? null : System.console();
35 15 : return console != null ? new Interactive(console) : new Batch();
36 : }
37 :
38 : /** Constructs an exception indicating the user aborted the operation. */
39 : protected static Die abort() {
40 0 : return new Die("aborted by user");
41 : }
42 :
43 : /** Returns true if this is a batch UI that has no user interaction. */
44 : public abstract boolean isBatch();
45 :
46 : /** Display a header message before a series of prompts. */
47 : public abstract void header(String fmt, Object... args);
48 :
49 : /** Display a message. */
50 : public abstract void message(String fmt, Object... args);
51 :
52 : /** Request the user to answer a yes/no question. */
53 : public abstract boolean yesno(Boolean def, String fmt, Object... args);
54 :
55 : /** Prints a message asking the user to let us know when its safe to continue. */
56 : public abstract void waitForUser();
57 :
58 : /** Prompt the user for a string, suggesting a default, and returning choice. */
59 : public abstract String readString(String def, String fmt, Object... args);
60 :
61 : /** Prompt the user to make a choice from an allowed list of values. */
62 : public abstract String readString(
63 : String def, Set<String> allowedValues, String fmt, Object... args);
64 :
65 : /** Prompt the user for an integer value, suggesting a default. */
66 : public int readInt(int def, String fmt, Object... args) {
67 : for (; ; ) {
68 15 : String p = readString(String.valueOf(def), fmt, args);
69 : try {
70 15 : return Integer.parseInt(p.trim(), 10);
71 0 : } catch (NumberFormatException e) {
72 0 : System.err.println("error: Invalid integer format: " + p.trim());
73 : }
74 0 : }
75 : }
76 :
77 : /** Prompt the user for a password, returning the string; null if blank. */
78 : public abstract String password(String fmt, Object... args);
79 :
80 : /** Display an error message on the system stderr. */
81 : @FormatMethod
82 : public void error(String format, Object... args) {
83 15 : System.err.println(String.format(format, args));
84 15 : System.err.flush();
85 15 : }
86 :
87 : /** Prompt the user to make a choice from an enumeration's values. */
88 : public abstract <T extends Enum<?>, A extends EnumSet<? extends T>> T readEnum(
89 : T def, A options, String fmt, Object... args);
90 :
91 : private static class Interactive extends ConsoleUI {
92 : private final Console console;
93 :
94 0 : Interactive(Console console) {
95 0 : this.console = console;
96 0 : }
97 :
98 : @Override
99 : public boolean isBatch() {
100 0 : return false;
101 : }
102 :
103 : @Override
104 : @FormatMethod
105 : public boolean yesno(Boolean def, String fmt, Object... args) {
106 0 : final String prompt = String.format(fmt, args);
107 : for (; ; ) {
108 0 : String y = "y";
109 0 : String n = "n";
110 0 : if (def != null) {
111 0 : if (def) {
112 0 : y = "Y";
113 : } else {
114 0 : n = "N";
115 : }
116 : }
117 :
118 0 : String yn = console.readLine("%-30s [%s/%s]? ", prompt, y, n);
119 0 : if (yn == null) {
120 0 : throw abort();
121 : }
122 0 : yn = yn.trim();
123 0 : if (def != null && yn.isEmpty()) {
124 0 : return def;
125 : }
126 0 : if (yn.equalsIgnoreCase("y") || yn.equalsIgnoreCase("yes")) {
127 0 : return true;
128 : }
129 0 : if (yn.equalsIgnoreCase("n") || yn.equalsIgnoreCase("no")) {
130 0 : return false;
131 : }
132 0 : }
133 : }
134 :
135 : @Override
136 : public void waitForUser() {
137 0 : if (console.readLine("Press enter to continue ") == null) {
138 0 : throw abort();
139 : }
140 0 : }
141 :
142 : @Override
143 : @FormatMethod
144 : public String readString(String def, @FormatString String fmt, Object... args) {
145 0 : final String prompt = String.format(fmt, args);
146 : String r;
147 0 : if (def != null) {
148 0 : r = console.readLine("%-30s [%s]: ", prompt, def);
149 : } else {
150 0 : r = console.readLine("%-30s : ", prompt);
151 : }
152 0 : if (r == null) {
153 0 : throw abort();
154 : }
155 0 : r = r.trim();
156 0 : if (r.isEmpty()) {
157 0 : return def;
158 : }
159 0 : return r;
160 : }
161 :
162 : @Override
163 : @FormatMethod
164 : public String readString(
165 : String def, Set<String> allowedValues, @FormatString String fmt, Object... args) {
166 : for (; ; ) {
167 0 : String r = readString(def, fmt, args);
168 0 : if (allowedValues.contains(r.toLowerCase())) {
169 0 : return r.toLowerCase();
170 : }
171 0 : if (!"?".equals(r)) {
172 0 : console.printf("error: '%s' is not a valid choice\n", r);
173 : }
174 0 : console.printf(" Supported options are:\n");
175 0 : for (String v : allowedValues) {
176 0 : console.printf(" %s\n", v.toLowerCase());
177 0 : }
178 0 : }
179 : }
180 :
181 : @Override
182 : @FormatMethod
183 : @Nullable
184 : public String password(String fmt, Object... args) {
185 0 : final String prompt = String.format(fmt, args);
186 : for (; ; ) {
187 0 : final char[] a1 = console.readPassword("%-30s : ", prompt);
188 0 : if (a1 == null) {
189 0 : throw abort();
190 : }
191 :
192 0 : final char[] a2 = console.readPassword("%30s : ", "confirm password");
193 0 : if (a2 == null) {
194 0 : throw abort();
195 : }
196 :
197 0 : final String s1 = new String(a1);
198 0 : final String s2 = new String(a2);
199 0 : if (!s1.equals(s2)) {
200 0 : console.printf("error: Passwords did not match; try again\n");
201 0 : continue;
202 : }
203 0 : return !s1.isEmpty() ? s1 : null;
204 : }
205 : }
206 :
207 : @Override
208 : @FormatMethod
209 : public <T extends Enum<?>, A extends EnumSet<? extends T>> T readEnum(
210 : T def, A options, String fmt, Object... args) {
211 0 : final String prompt = String.format(fmt, args);
212 : for (; ; ) {
213 0 : String r = console.readLine("%-30s [%s/?]: ", prompt, def.toString().toLowerCase());
214 0 : if (r == null) {
215 0 : throw abort();
216 : }
217 0 : r = r.trim();
218 0 : if (r.isEmpty()) {
219 0 : return def;
220 : }
221 0 : for (T e : options) {
222 0 : if (e.toString().equalsIgnoreCase(r)) {
223 0 : return e;
224 : }
225 0 : }
226 0 : if (!"?".equals(r)) {
227 0 : console.printf("error: '%s' is not a valid choice\n", r);
228 : }
229 0 : console.printf(" Supported options are:\n");
230 0 : for (T e : options) {
231 0 : console.printf(" %s\n", e.toString().toLowerCase());
232 0 : }
233 0 : }
234 : }
235 :
236 : @Override
237 : public void header(String fmt, Object... args) {
238 0 : fmt = fmt.replace("\n", "\n*** ");
239 0 : console.printf("\n*** " + fmt + "\n*** \n\n", args);
240 0 : }
241 :
242 : @Override
243 : public void message(String fmt, Object... args) {
244 0 : console.printf(fmt, args);
245 0 : }
246 : }
247 :
248 : private static class Batch extends ConsoleUI {
249 : @Override
250 : public boolean isBatch() {
251 15 : return true;
252 : }
253 :
254 : @Override
255 : public boolean yesno(Boolean def, String fmt, Object... args) {
256 15 : return def != null ? def : true;
257 : }
258 :
259 : @Override
260 : public String readString(String def, String fmt, Object... args) {
261 15 : return def;
262 : }
263 :
264 : @Override
265 : public String readString(String def, Set<String> allowedValues, String fmt, Object... args) {
266 15 : return def;
267 : }
268 :
269 : @Override
270 0 : public void waitForUser() {}
271 :
272 : @Override
273 : public String password(String fmt, Object... args) {
274 0 : return null;
275 : }
276 :
277 : @Override
278 : public <T extends Enum<?>, A extends EnumSet<? extends T>> T readEnum(
279 : T def, A options, String fmt, Object... args) {
280 15 : return def;
281 : }
282 :
283 : @Override
284 15 : public void header(String fmt, Object... args) {}
285 :
286 : @Override
287 15 : public void message(String fmt, Object... args) {}
288 : }
289 : }
|