package org.graalvm.component.installer;
import java.io.Console;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import org.graalvm.component.installer.model.ComponentRegistry;
import java.io.PrintStream;
import java.net.URL;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.function.Supplier;
public class Environment implements Feedback, CommandInput, Config {
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(
"org.graalvm.component.installer.Bundle");
private final String commandName;
private final LinkedList<String> parameters;
private final Map<String, String> options;
private final boolean verbose;
private final ResourceBundle bundle;
private int parameterPos;
private InputStream in = System.in;
private PrintStream err = System.err;
private PrintStream out = System.out;
private ComponentRegistry localRegistry;
private boolean stacktraces;
private ComponentIterable fileIterable;
private Map<URL, Path> fileMap = new HashMap<>();
private boolean allOutputToErr;
private boolean autoYesEnabled;
private boolean nonInteractive;
private Path graalHome;
private FileOperations fileOperations;
private CatalogFactory catalogFactory;
private ComponentCatalog componentCatalog;
Environment(String commandName, List<String> parameters, Map<String, String> options) {
this(commandName, (String) null, parameters, options);
}
Environment(String commandName, InstallerCommand cmdInstance, List<String> parameters, Map<String, String> options) {
this(commandName, makeBundle(cmdInstance), parameters, options);
}
public void setIn(InputStream input) {
this.in = input;
}
private static String makeBundle(InstallerCommand cmdInstance) {
if (cmdInstance == null) {
return null;
}
String s = cmdInstance.getClass().getName();
s = s.substring(0, s.lastIndexOf('.'));
return s;
}
public Environment(String commandName, String bundlePackage, List<String> parameters, Map<String, String> options) {
this.commandName = commandName;
this.parameters = new LinkedList<>(parameters);
this.options = options;
this.verbose = options.containsKey(Commands.OPTION_VERBOSE);
this.stacktraces = options.containsKey(Commands.OPTION_DEBUG);
if (bundlePackage != null) {
bundle = ResourceBundle.getBundle(bundlePackage + ".Bundle");
} else {
bundle = BUNDLE;
}
this.fileIterable = new FileIterable(this, this);
}
@Override
public Environment enableStacktraces() {
this.stacktraces = true;
return this;
}
@Override
public boolean isAutoYesEnabled() {
return autoYesEnabled;
}
@Override
public void setAutoYesEnabled(boolean autoYesEnabled) {
this.autoYesEnabled = autoYesEnabled;
}
@Override
public boolean isNonInteractive() {
return nonInteractive;
}
@Override
public void setNonInteractive(boolean nonInteractive) {
this.nonInteractive = nonInteractive;
}
public boolean isAllOutputToErr() {
return allOutputToErr;
}
@Override
public void setAllOutputToErr(boolean allOutputToErr) {
this.allOutputToErr = allOutputToErr;
if (allOutputToErr) {
out = err;
} else {
out = System.out;
}
}
@Override
public void setFileIterable(ComponentIterable fileIterable) {
this.fileIterable = fileIterable;
}
@Override
public void setCatalogFactory(CatalogFactory catalogFactory) {
this.catalogFactory = catalogFactory;
}
@Override
public ComponentCatalog getRegistry() {
if (componentCatalog == null) {
componentCatalog = catalogFactory.createComponentCatalog(this);
}
return componentCatalog;
}
@Override
public ComponentRegistry getLocalRegistry() {
return localRegistry;
}
public void setLocalRegistry(ComponentRegistry r) {
this.localRegistry = r;
}
public void setGraalHome(Path f) {
this.graalHome = f.normalize();
}
public void setErr(PrintStream err) {
this.err = err;
}
public void setOut(PrintStream out) {
this.out = out;
}
@Override
public void error(String bundleKey, Throwable error, Object... args) {
print(false, bundle, err, bundleKey, args);
if (stacktraces && error != null) {
error.printStackTrace(err);
}
}
@Override
public RuntimeException failure(String bundleKey, Throwable error, Object... args) {
return new FailedOperationException(createMessage(bundle, bundleKey, args), error);
}
@Override
public void message(String bundleKey, Object... args) {
print(false, bundle, err, bundleKey, args);
}
@Override
public boolean verbosePart(String bundleKey, Object... args) {
if (bundleKey != null) {
print(true, false, bundle, out, bundleKey, args);
}
return verbose;
}
@Override
public void output(String bundleKey, Object... args) {
print(false, bundle, out, bundleKey, args);
}
@Override
public void outputPart(String bundleKey, Object... args) {
print(false, false, bundle, out, bundleKey, args);
}
@Override
public boolean verboseOutput(String bundleKey, Object... args) {
if (bundleKey != null) {
print(true, bundle, out, bundleKey, args);
}
return verbose;
}
@Override
public String l10n(String bundleKey, Object... args) {
return createMessage(bundle, bundleKey, args);
}
@Override
public boolean verbatimOut(String msg, boolean beVerbose) {
print(beVerbose, true, null, out, msg);
return beVerbose;
}
@Override
public boolean verbatimPart(String msg, boolean beVerbose) {
print(beVerbose, false, null, out, msg);
return beVerbose;
}
@Override
public boolean verbatimPart(String msg, boolean error, boolean beVerbose) {
print(beVerbose, false, null, error ? err : out, msg);
return beVerbose;
}
@Override
public boolean backspace(int chars, boolean beVerbose) {
if (beVerbose && !verbose) {
return false;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < chars; i++) {
sb.append('\b');
}
verbatimPart(sb.toString(), beVerbose);
return verbose;
}
@Override
public <T> Feedback withBundle(Class<T> clazz) {
String s = clazz.getName();
s = s.substring(0, s.lastIndexOf('.'));
ResourceBundle localBundle = ResourceBundle.getBundle(s + ".Bundle");
return new Feedback() {
@Override
public void message(String bundleKey, Object... params) {
print(false, localBundle, err, bundleKey, params);
}
@Override
public void output(String bundleKey, Object... params) {
print(false, localBundle, out, bundleKey, params);
}
@Override
public boolean verbosePart(String bundleKey, Object... params) {
if (bundleKey != null) {
print(true, false, localBundle, out, bundleKey, params);
}
return verbose;
}
@Override
public boolean verboseOutput(String bundleKey, Object... params) {
if (bundleKey != null) {
print(true, localBundle, out, bundleKey, params);
}
return verbose;
}
@Override
public boolean verbatimOut(String msg, boolean verboseOutput) {
print(verboseOutput, null, out, msg);
return verboseOutput;
}
@Override
public void error(String key, Throwable t, Object... params) {
print(false, localBundle, err, key, params);
if (stacktraces && t != null) {
t.printStackTrace(err);
}
}
@Override
public String l10n(String key, Object... params) {
return createMessage(localBundle, key, params);
}
@Override
public RuntimeException failure(String key, Throwable t, Object... params) {
return new FailedOperationException(createMessage(localBundle, key, params), t);
}
@Override
public <X> Feedback withBundle(Class<X> anotherClazz) {
return Environment.this.withBundle(anotherClazz);
}
@Override
public void outputPart(String bundleKey, Object... params) {
print(false, false, localBundle, out, bundleKey, params);
}
@Override
public boolean verbatimPart(String msg, boolean verboseOutput) {
print(verboseOutput, false, null, out, msg);
return verbose;
}
@Override
public boolean verbatimPart(String msg, boolean error, boolean verboseOutput) {
print(verboseOutput, false, null, error ? err : out, msg);
return verbose;
}
@Override
public boolean backspace(int chars, boolean beVerbose) {
return Environment.this.backspace(chars, beVerbose);
}
@Override
public String acceptLine(boolean autoYes) {
return Environment.this.acceptLine(autoYes);
}
@Override
public char[] acceptPassword() {
return Environment.this.acceptPassword();
}
@Override
public void addLocalFileCache(URL location, Path local) {
Environment.this.addLocalFileCache(location, local);
}
@Override
public Path getLocalCache(URL location) {
return Environment.this.getLocalCache(location);
}
@Override
public boolean isNonInteractive() {
return Environment.this.isNonInteractive();
}
};
}
@SuppressWarnings({"unchecked"})
private static String createMessage(ResourceBundle bundle, String bundleKey, Object... args) {
if (bundle == null) {
return bundleKey;
}
if (args == null) {
return bundle.getString(bundleKey);
}
for (int i = 0; i < args.length; i++) {
Object o = args[i];
if (o instanceof Supplier) {
Object v = ((Supplier<Object>) o).get();
if (v == null || v instanceof String) {
args[i] = v;
}
}
}
if (bundleKey == null) {
return String.valueOf(args[0]);
}
return MessageFormat.format(
bundle.getString(bundleKey),
args);
}
private void print(boolean beVerbose, ResourceBundle msgBundle, PrintStream stm, String bundleKey, Object... args) {
if (beVerbose && !this.verbose) {
return;
}
print(beVerbose, true, msgBundle, stm, bundleKey, args);
}
private void print(boolean beVerbose, boolean addNewline, ResourceBundle msgBundle, PrintStream stm, String bundleKey, Object... args) {
if (beVerbose && !this.verbose) {
return;
}
if (addNewline) {
stm.println(createMessage(msgBundle, bundleKey, args));
stm.flush();
} else {
stm.print(createMessage(msgBundle, bundleKey, args));
stm.flush();
}
}
@Override
public Path getGraalHomePath() {
return graalHome;
}
@Override
public String nextParameter() {
if (parameterPos >= parameters.size()) {
return null;
}
return parameters.get(parameterPos++);
}
@Override
public String peekParameter() {
if (parameterPos >= parameters.size()) {
return null;
}
return parameters.get(parameterPos);
}
@Override
public String requiredParameter() {
if (!hasParameter()) {
throw new FailedOperationException(
MessageFormat.format(BUNDLE.getString("ERROR_MissingParameter"), commandName));
}
return nextParameter();
}
@Override
public boolean hasParameter() {
return parameters.size() > parameterPos;
}
@Override
public ComponentIterable existingFiles() {
return fileIterable;
}
@Override
public String optValue(String optName) {
return options.get(optName);
}
public char acceptCharacter() {
try {
int input = in.read();
if (input == -1) {
throw new UserAbortException();
}
return (char) input;
} catch (EOFException ex) {
throw new UserAbortException(ex);
} catch (IOException ex) {
throw withBundle(Environment.class).failure("ERROR_UserInput", ex, ex.getMessage());
}
}
@Override
public String acceptLine(boolean autoYes) {
if (autoYes && isAutoYesEnabled()) {
return AUTO_YES;
}
if (isNonInteractive()) {
throw new NonInteractiveException(withBundle(Environment.class).l10n("ERROR_NoninteractiveInput"));
}
StringBuilder sb = new StringBuilder();
char c;
while ((c = acceptCharacter()) != '\n') {
if (c == 0x08) {
sb.delete(sb.length() - 1, sb.length());
} else {
sb.append(c);
}
}
if (sb.length() > 0 && sb.charAt(sb.length() - 1) == '\r') {
sb.delete(sb.length() - 1, sb.length());
}
return sb.toString();
}
@Override
public char[] acceptPassword() {
if (isNonInteractive()) {
throw new NonInteractiveException(withBundle(Environment.class).l10n("ERROR_NoninteractiveInput"));
}
Console console = System.console();
if (console != null) {
console.flush();
return console.readPassword();
} else {
return acceptLine(false).toCharArray();
}
}
@Override
public void addLocalFileCache(URL location, Path local) {
fileMap.put(location, local);
}
@Override
public Path getLocalCache(URL location) {
return fileMap.get(location);
}
@Override
public FileOperations getFileOperations() {
return fileOperations;
}
public void setFileOperations(FileOperations fileOperations) {
this.fileOperations = fileOperations;
}
public boolean close() throws IOException {
if (out != null) {
out.flush();
}
if (err != null) {
err.flush();
}
if (fileOperations != null) {
return fileOperations.flush();
} else {
return false;
}
}
@Override
public CatalogFactory getCatalogFactory() {
return catalogFactory;
}
public void resetParameters() {
parameterPos = 0;
}
@Override
public String getParameter(String key, boolean cmdLine) {
if (cmdLine) {
return System.getProperty(key);
} else {
return System.getenv(key.toUpperCase(Locale.ENGLISH));
}
}
@Override
public Map<String, String> parameters(boolean cmdLine) {
if (cmdLine) {
Map<String, String> res = new HashMap<>();
for (String s : System.getProperties().stringPropertyNames()) {
res.put(s, System.getProperty(s));
}
return res;
} else {
return System.getenv();
}
}
}