/*
* Copyright 2016-present Facebook, Inc.
*
* 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.facebook.buck.rage;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** Helper methods for handling input from the user. */
public class UserInput {
private static final Pattern RANGE_OR_NUMBER =
Pattern.compile("((?<rangelo>\\d+)\\s*\\-\\s*(?<rangehi>\\d+))|(?<num>\\d+)");
private static final Pattern NUMBER = Pattern.compile("(?<num>\\d+)");
private final PrintStream output;
private final BufferedReader reader;
public UserInput(PrintStream output, BufferedReader inputReader) {
this.output = output;
this.reader = inputReader;
}
public String ask(String question) throws IOException {
output.println();
output.println(question);
output.flush();
return reader.readLine();
}
public boolean confirm(String question) throws IOException {
ImmutableMap<String, Boolean> supportedResponses =
ImmutableMap.of(
"", true,
"y", true,
"n", false);
for (; ; ) {
output.println();
output.println(question + " (Y/n)?");
output.flush();
String line = reader.readLine();
// Stdin was closed.
if (line == null) {
return false;
}
String response = line.trim().toLowerCase();
if (supportedResponses.containsKey(response)) {
return supportedResponses.get(response);
} else {
output.println("Please answer 'y' or 'n'.");
}
}
}
public static Integer parseOne(String input) {
Matcher matcher = NUMBER.matcher(input);
Preconditions.checkArgument(matcher.matches(), "Malformed entry %s.", input);
return Integer.parseInt(input);
}
public static ImmutableSet<Integer> parseRange(String input) {
ImmutableSet.Builder<Integer> result = ImmutableSet.builder();
String[] elements = input.split("[, ]+");
for (String element : elements) {
Matcher matcher = RANGE_OR_NUMBER.matcher(element);
Preconditions.checkArgument(matcher.matches(), "Malformed entry %s.", element);
String numberString = matcher.group("num");
String rangeLowString = matcher.group("rangelo");
String rangeHighString = matcher.group("rangehi");
Preconditions.checkArgument(
numberString != null || rangeHighString != null, "Malformed entry %s.", element);
if (numberString != null) {
result.add(Integer.parseInt(numberString));
} else {
int rangeLow = Integer.parseInt(rangeLowString);
int rangeHigh = Integer.parseInt(rangeHighString);
for (int i = Math.min(rangeLow, rangeHigh); i <= Math.max(rangeLow, rangeHigh); ++i) {
result.add(i);
}
}
}
return result.build();
}
public <T> Optional<T> selectOne(
String prompt, List<T> entries, Function<T, String> entryFormatter) throws IOException {
Preconditions.checkArgument(entries.size() > 0);
output.println();
output.println(prompt);
output.println();
for (int i = 0; i < entries.size(); i++) {
output.printf("[%d]. %s.\n", i, entryFormatter.apply(entries.get(i)));
}
Integer index = 0;
try {
String response = ask("(input individual number, for example 1 or 2)");
if (response.trim().isEmpty()) {
index = 0;
} else {
index = parseOne(response);
}
Preconditions.checkArgument(
index >= 0 && index < entries.size(), "Index %s out of bounds.", index);
} catch (IllegalArgumentException e) {
output.printf("Illegal choice: %s\n", e.getMessage());
return Optional.empty();
}
return Optional.of(entries.get(index));
}
public <T> ImmutableSet<T> selectRange(
String prompt, List<T> entries, Function<T, String> entryFormatter) throws IOException {
Preconditions.checkArgument(entries.size() > 0);
output.println();
output.println(prompt);
output.println();
for (int i = 0; i < entries.size(); i++) {
output.printf("[%d]. %s.\n", i, entryFormatter.apply(entries.get(i)));
}
ImmutableSet<Integer> buildIndexes;
for (; ; ) {
try {
String response = ask("(input individual numbers or ranges, for example 1,2,3-9)");
if (response.trim().isEmpty()) {
buildIndexes = ImmutableSet.of(0);
} else {
buildIndexes = parseRange(response);
}
for (Integer index : buildIndexes) {
Preconditions.checkArgument(
index >= 0 && index < entries.size(), "Index %s out of bounds.", index);
}
break;
} catch (IllegalArgumentException e) {
output.printf("Illegal range %s.\n", e.getMessage());
}
}
ImmutableSet.Builder<T> result = ImmutableSet.builder();
for (Integer index : buildIndexes) {
result.add(entries.get(index));
}
return result.build();
}
}