blob: 444f64fe7a38480ee11cf20ef8eed1d6dfe3ddd4 [file] [log] [blame]
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.pgm.init.api;
import com.google.gerrit.common.Die;
import java.io.Console;
import java.util.EnumSet;
import java.util.Set;
/** Console based interaction with the invoking user. */
public abstract class ConsoleUI {
/** Get a UI instance, assuming interactive mode. */
public static ConsoleUI getInstance() {
return getInstance(false);
}
/** Get a UI instance, possibly forcing batch mode. */
public static ConsoleUI getInstance(boolean batchMode) {
Console console = batchMode ? null : System.console();
return console != null ? new Interactive(console) : new Batch();
}
/** Constructs an exception indicating the user aborted the operation. */
protected static Die abort() {
return new Die("aborted by user");
}
/** @return true if this is a batch UI that has no user interaction. */
public abstract boolean isBatch();
/** Display a header message before a series of prompts. */
public abstract void header(String fmt, Object... args);
/** Display a message. */
public abstract void message(String fmt, Object... args);
/** Request the user to answer a yes/no question. */
public abstract boolean yesno(Boolean def, String fmt, Object... args);
/** Prints a message asking the user to let us know when its safe to continue. */
public abstract void waitForUser();
/** Prompt the user for a string, suggesting a default, and returning choice. */
public abstract String readString(String def, String fmt, Object... args);
/** Prompt the user to make a choice from an allowed list of values. */
public abstract String readString(
String def, Set<String> allowedValues, String fmt, Object... args);
/** Prompt the user for an integer value, suggesting a default. */
public int readInt(int def, String fmt, Object... args) {
for (; ; ) {
String p = readString(String.valueOf(def), fmt, args);
try {
return Integer.parseInt(p.trim(), 10);
} catch (NumberFormatException e) {
System.err.println("error: Invalid integer format: " + p.trim());
}
}
}
/** Prompt the user for a password, returning the string; null if blank. */
public abstract String password(String fmt, Object... args);
/** Display an error message on the system stderr. */
public void error(String format, Object... args) {
System.err.println(String.format(format, args));
System.err.flush();
}
/** Prompt the user to make a choice from an enumeration's values. */
public abstract <T extends Enum<?>, A extends EnumSet<? extends T>> T readEnum(
T def, A options, String fmt, Object... args);
private static class Interactive extends ConsoleUI {
private final Console console;
Interactive(Console console) {
this.console = console;
}
@Override
public boolean isBatch() {
return false;
}
@Override
public boolean yesno(Boolean def, String fmt, Object... args) {
final String prompt = String.format(fmt, args);
for (; ; ) {
String y = "y";
String n = "n";
if (def != null) {
if (def) {
y = "Y";
} else {
n = "N";
}
}
String yn = console.readLine("%-30s [%s/%s]? ", prompt, y, n);
if (yn == null) {
throw abort();
}
yn = yn.trim();
if (def != null && yn.isEmpty()) {
return def;
}
if (yn.equalsIgnoreCase("y") || yn.equalsIgnoreCase("yes")) {
return true;
}
if (yn.equalsIgnoreCase("n") || yn.equalsIgnoreCase("no")) {
return false;
}
}
}
@Override
public void waitForUser() {
if (console.readLine("Press enter to continue ") == null) {
throw abort();
}
}
@Override
public String readString(String def, String fmt, Object... args) {
final String prompt = String.format(fmt, args);
String r;
if (def != null) {
r = console.readLine("%-30s [%s]: ", prompt, def);
} else {
r = console.readLine("%-30s : ", prompt);
}
if (r == null) {
throw abort();
}
r = r.trim();
if (r.isEmpty()) {
return def;
}
return r;
}
@Override
public String readString(String def, Set<String> allowedValues, String fmt, Object... args) {
for (; ; ) {
String r = readString(def, fmt, args);
if (allowedValues.contains(r.toLowerCase())) {
return r.toLowerCase();
}
if (!"?".equals(r)) {
console.printf("error: '%s' is not a valid choice\n", r);
}
console.printf(" Supported options are:\n");
for (String v : allowedValues) {
console.printf(" %s\n", v.toLowerCase());
}
}
}
@Override
public String password(String fmt, Object... args) {
final String prompt = String.format(fmt, args);
for (; ; ) {
final char[] a1 = console.readPassword("%-30s : ", prompt);
if (a1 == null) {
throw abort();
}
final char[] a2 = console.readPassword("%30s : ", "confirm password");
if (a2 == null) {
throw abort();
}
final String s1 = new String(a1);
final String s2 = new String(a2);
if (!s1.equals(s2)) {
console.printf("error: Passwords did not match; try again\n");
continue;
}
return !s1.isEmpty() ? s1 : null;
}
}
@Override
public <T extends Enum<?>, A extends EnumSet<? extends T>> T readEnum(
T def, A options, String fmt, Object... args) {
final String prompt = String.format(fmt, args);
for (; ; ) {
String r = console.readLine("%-30s [%s/?]: ", prompt, def.toString().toLowerCase());
if (r == null) {
throw abort();
}
r = r.trim();
if (r.isEmpty()) {
return def;
}
for (T e : options) {
if (e.toString().equalsIgnoreCase(r)) {
return e;
}
}
if (!"?".equals(r)) {
console.printf("error: '%s' is not a valid choice\n", r);
}
console.printf(" Supported options are:\n");
for (T e : options) {
console.printf(" %s\n", e.toString().toLowerCase());
}
}
}
@Override
public void header(String fmt, Object... args) {
fmt = fmt.replaceAll("\n", "\n*** ");
console.printf("\n*** " + fmt + "\n*** \n\n", args);
}
@Override
public void message(String fmt, Object... args) {
console.printf(fmt, args);
}
}
private static class Batch extends ConsoleUI {
@Override
public boolean isBatch() {
return true;
}
@Override
public boolean yesno(Boolean def, String fmt, Object... args) {
return def != null ? def : true;
}
@Override
public String readString(String def, String fmt, Object... args) {
return def;
}
@Override
public String readString(String def, Set<String> allowedValues, String fmt, Object... args) {
return def;
}
@Override
public void waitForUser() {}
@Override
public String password(String fmt, Object... args) {
return null;
}
@Override
public <T extends Enum<?>, A extends EnumSet<? extends T>> T readEnum(
T def, A options, String fmt, Object... args) {
return def;
}
@Override
public void header(String fmt, Object... args) {}
@Override
public void message(String fmt, Object... args) {}
}
}