package sk.stuba.fiit.perconik.environment.java;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.Joiner;
import static java.util.Arrays.asList;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
public abstract class JavaProcessBuilder {
final Path java;
final String target;
final List<String> options;
final List<String> arguments;
JavaProcessBuilder(final Path java, final String target) {
this.java = checkNotNull(java);
this.target = checkNotNull(target);
this.options = newArrayList();
this.arguments = newArrayList();
}
public static final class Defaults {
public static final Path java = Paths.get("java");
private Defaults() {}
}
public static JavaProcessBuilder forClass(final String name) {
return new ClassTarget(Defaults.java, checkNotNullOrEmpty(name));
}
public static JavaProcessBuilder forClass(final String name, final Path java) {
return new ClassTarget(java, checkNotNullOrEmpty(name));
}
public static JavaProcessBuilder forJar(final Path jar) {
return new JarTarget(Defaults.java, jar.toString());
}
public static JavaProcessBuilder forJar(final Path jar, final Path java) {
return new JarTarget(java, jar.toString());
}
private static final class ClassTarget extends JavaProcessBuilder {
ClassTarget(final Path java, final String target) {
super(java, target);
}
@Override
void addTarget(final List<String> command) {
command.add(this.target);
}
}
private static final class JarTarget extends JavaProcessBuilder {
JarTarget(final Path java, final String target) {
super(java, target);
}
@Override
void addTarget(final List<String> command) {
command.add("-jar");
command.add(this.target);
}
}
abstract void addTarget(List<String> command);
void addOption(final String value) {
this.options.add("-" + checkNotNullOrEmpty(value));
}
void addOptionWithArgument(final String name, final Object argument) {
this.options.add("-" + checkNotNullOrEmpty(name));
String value = argument.toString();
if (!value.isEmpty()) {
this.options.add(value);
}
}
void addNonStandardOption(final String value) {
this.options.add("-X" + checkNotNullOrEmpty(value));
}
void addSystemProperty(final String name, final Object value) {
this.options.add("-D" + checkNotNullOrEmpty(name) + "=" + value.toString());
}
void addArgument(final Object value) {
this.arguments.add(checkNotNullOrEmpty(value.toString()));
}
public JavaProcessBuilder option(final String value) {
this.addOption(value);
return this;
}
public JavaProcessBuilder option(final String name, final Object argument) {
this.addOptionWithArgument(name, argument);
return this;
}
public JavaProcessBuilder options(final String ... values) {
return this.options(asList(values));
}
public JavaProcessBuilder options(final Iterable<String> values) {
for (String value: values) {
this.addOption(value);
}
return this;
}
public JavaProcessBuilder nonStandardOption(final String value) {
this.addNonStandardOption(value);
return this;
}
public JavaProcessBuilder nonStandardOptions(final String ... values) {
return this.nonStandardOptions(Arrays.asList(values));
}
public JavaProcessBuilder nonStandardOptions(final Iterable<String> values) {
for (String value: values) {
this.addNonStandardOption(value);
}
return this;
}
public JavaProcessBuilder systemProperty(final String name, final Object value) {
this.addSystemProperty(name, value);
return this;
}
public JavaProcessBuilder systemProperty(final Entry<String, ?> property) {
return this.systemProperty(property.getKey(), property.getValue());
}
public JavaProcessBuilder systemProperties(final Map<String, ?> properties) {
return this.systemProperties(properties.entrySet());
}
public <E extends Entry<String, ?>> JavaProcessBuilder systemProperties(final Iterable<E> properties) {
for (Entry<String, ?> entry: properties) {
this.addSystemProperty(entry.getKey(), entry.getValue());
}
return this;
}
public JavaProcessBuilder argument(final Object value) {
this.addArgument(value);
return this;
}
public JavaProcessBuilder arguments(final Object ... values) {
return this.arguments(Arrays.asList(values));
}
public JavaProcessBuilder arguments(final Iterable<?> values) {
for (Object value: values) {
this.addArgument(value);
}
return this;
}
private static String checkNotNullOrEmpty(final String value) {
checkArgument(!isNullOrEmpty(value));
return value;
}
public Path java() {
return this.java;
}
public String target() {
return this.target;
}
public List<String> options() {
return newArrayList(this.options);
}
public List<String> arguments() {
return newArrayList(this.arguments);
}
public List<String> toCommand() {
int size = 4 + this.options.size() + this.arguments.size();
List<String> command = newArrayListWithCapacity(size);
command.add(this.java.toString());
command.addAll(this.options);
this.addTarget(command);
command.addAll(this.arguments);
return command;
}
public ProcessBuilder toProcessBuilder() {
return new ProcessBuilder(this.toCommand());
}
@Override
public String toString() {
return Joiner.on(' ').join(this.toCommand());
}
public Process start() throws IOException {
return this.toProcessBuilder().start();
}
}