org.gradle.api.internal.tasks.userinput.DefaultUserInputHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2017 the original author or authors.
*
* 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 org.gradle.api.internal.tasks.userinput;
import com.google.common.base.CharMatcher;
import com.google.common.collect.Lists;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.gradle.api.Transformer;
import org.gradle.internal.logging.events.OutputEventListener;
import org.gradle.internal.logging.events.PromptOutputEvent;
import org.gradle.internal.logging.events.UserInputRequestEvent;
import org.gradle.internal.logging.events.UserInputResumeEvent;
import org.gradle.internal.time.Clock;
import org.gradle.util.internal.TextUtil;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class DefaultUserInputHandler implements UserInputHandler {
private static final List YES_NO_CHOICES = Lists.newArrayList("yes", "no");
private final OutputEventListener outputEventBroadcaster;
private final Clock clock;
private final UserInputReader userInputReader;
private final AtomicBoolean hasAsked = new AtomicBoolean();
private final AtomicBoolean interrupted = new AtomicBoolean();
public DefaultUserInputHandler(OutputEventListener outputEventBroadcaster, Clock clock, UserInputReader userInputReader) {
this.outputEventBroadcaster = outputEventBroadcaster;
this.clock = clock;
this.userInputReader = userInputReader;
}
@Override
public Boolean askYesNoQuestion(String question) {
StringBuilder builder = new StringBuilder();
builder.append(question);
builder.append(" [");
builder.append(StringUtils.join(YES_NO_CHOICES, ", "));
builder.append("] ");
return prompt(builder.toString(), new BooleanParser());
}
@Override
public boolean askYesNoQuestion(String question, final boolean defaultValue) {
StringBuilder builder = new StringBuilder();
builder.append(question);
builder.append(" (default: ");
builder.append(defaultValue ? "yes" : "no");
builder.append(") [");
builder.append(StringUtils.join(YES_NO_CHOICES, ", "));
builder.append("] ");
return prompt(builder.toString(), defaultValue, new BooleanParser());
}
@Override
public T selectOption(String question, final Collection options, final T defaultOption) {
final List values = new ArrayList(options);
StringBuilder builder = new StringBuilder();
builder.append(question);
builder.append(":");
builder.append(TextUtil.getPlatformLineSeparator());
for (int i = 0; i < options.size(); i++) {
T option = values.get(i);
builder.append(" ");
builder.append(i + 1);
builder.append(": ");
builder.append(option);
builder.append(TextUtil.getPlatformLineSeparator());
}
builder.append("Enter selection (default: ");
builder.append(defaultOption);
builder.append(") [1..");
builder.append(options.size());
builder.append("] ");
return prompt(builder.toString(), defaultOption, new Transformer() {
@Override
public T transform(String sanitizedInput) {
if (sanitizedInput.matches("\\d+")) {
int value = Integer.parseInt(sanitizedInput);
if (value > 0 && value <= values.size()) {
return values.get(value - 1);
}
}
sendPrompt("Please enter a value between 1 and " + options.size() + ": ");
return null;
}
});
}
@Override
public String askQuestion(String question, final String defaultValue) {
StringBuilder builder = new StringBuilder();
builder.append(question);
builder.append(" (default: ");
builder.append(defaultValue);
builder.append("): ");
return prompt(builder.toString(), defaultValue, new Transformer() {
@Override
public String transform(String sanitizedValue) {
return sanitizedValue;
}
});
}
@Override
public boolean interrupted() {
return interrupted.get();
}
private T prompt(String prompt, final T defaultValue, final Transformer parser) {
T result = prompt(prompt, new Transformer() {
@Override
public T transform(String sanitizedInput) {
if (sanitizedInput.isEmpty()) {
return defaultValue;
}
return parser.transform(sanitizedInput);
}
});
if (result == null) {
return defaultValue;
}
return result;
}
@Nullable
private T prompt(String prompt, Transformer parser) {
if (interrupted.get()) {
return null;
}
outputEventBroadcaster.onOutput(new UserInputRequestEvent());
try {
// Add a line before the first question that has been asked of the user
// This makes the assumption that all questions happen together, which is ok for now
// It would be better to allow this handler to ask the output renderers to show a blank line before the prompt, if not already present
if (hasAsked.compareAndSet(false, true)) {
sendPrompt(TextUtil.getPlatformLineSeparator());
}
sendPrompt(prompt);
while (true) {
String input = userInputReader.readInput();
if (input == null) {
interrupted.set(true);
return null;
}
String sanitizedInput = sanitizeInput(input);
T result = parser.transform(sanitizedInput);
if (result != null) {
return result;
}
}
} finally {
// Send a end-of-line. This is a workaround to convince the console that the cursor is at the start of the line to avoid indenting the next line of text that is displayed
// It would be better for the console to listen for stuff read from stdin that would also be echoed to the output and update its state based on this
sendPrompt(TextUtil.getPlatformLineSeparator());
outputEventBroadcaster.onOutput(new UserInputResumeEvent());
}
}
private void sendPrompt(String prompt) {
outputEventBroadcaster.onOutput(new PromptOutputEvent(clock.getCurrentTime(), prompt));
}
private String sanitizeInput(String input) {
return CharMatcher.javaIsoControl().removeFrom(StringUtils.trim(input));
}
private class BooleanParser implements Transformer {
@Override
public Boolean transform(String value) {
if (YES_NO_CHOICES.contains(value)) {
return BooleanUtils.toBoolean(value);
}
sendPrompt("Please enter 'yes' or 'no': ");
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy