| // 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.errorprone.annotations.FormatMethod; | |
| import com.google.errorprone.annotations.FormatString; | |
| import com.google.gerrit.common.Die; | |
| import com.google.gerrit.common.Nullable; | |
| import java.io.Console; | |
| import java.util.EnumSet; | |
| import java.util.Locale; | |
| 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"); | |
| } | |
| /** Returns 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. */ | |
| @FormatMethod | |
| 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 | |
| @FormatMethod | |
| 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 | |
| @FormatMethod | |
| public String readString(String def, @FormatString 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 | |
| @FormatMethod | |
| public String readString( | |
| String def, Set<String> allowedValues, @FormatString String fmt, Object... args) { | |
| for (; ; ) { | |
| String r = readString(def, fmt, args); | |
| if (allowedValues.contains(r.toLowerCase(Locale.US))) { | |
| return r.toLowerCase(Locale.US); | |
| } | |
| 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(Locale.US)); | |
| } | |
| } | |
| } | |
| @Override | |
| @FormatMethod | |
| @Nullable | |
| 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 | |
| @FormatMethod | |
| 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(Locale.US)); | |
| 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(Locale.US)); | |
| } | |
| } | |
| } | |
| @Override | |
| public void header(String fmt, Object... args) { | |
| fmt = fmt.replace("\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) {} | |
| } | |
| } |