/*
* Copyright (C) 2013 Andrey Chaschev.
*
* 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 bear.plugins.sh;
import bear.console.AbstractConsoleCommand;
import bear.console.ConsoleCallback;
import bear.core.SessionContext;
import bear.session.Result;
import bear.task.BearException;
import bear.vcs.CommandLineOperator;
import bear.vcs.CommandLineResult;
import com.google.common.base.Joiner;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static bear.vcs.CommandLineResult.error;
/**
* @author Andrey Chaschev chaschev@gmail.com
*/
public abstract class CommandLine<T extends CommandLineResult<?>, SCRIPT extends Script> extends AbstractConsoleCommand<T> {
public String cd = ".";
public List strings = new ArrayList(4);
protected ResultParser<T> parser;
protected ResultValidator validator = new ResultValidator() {
@Override
public void validate(String script, String output) {
CommandBuilder.defaultValidation(script, output);
}
};
/**
* Null when used outside of a script.
*/
@Nullable
//todo change to Script
protected SCRIPT script;
protected SystemSession sys;
protected CommandLine(SystemSession sys) {
this.sys = sys;
}
protected CommandLine(SCRIPT script) {
this.script = script;
this.sys = script.sys;
}
static CommandLineResult<?> parseWithParser(ResultParser<? extends CommandLineResult<?>> parser, ResultValidator validator, String commandOutput, SessionContext $, String script) {
CommandLineResult<?> obj = null;
if(validator != null){
try {
validator.validate(script, commandOutput);
} catch (Exception e) {
return parser == null ? error(e) : parser.error(e);
}
}
if (parser != null) {
obj = parser.parse(script,commandOutput);
obj.output = commandOutput;
}else{
obj = new CommandLineResult(script, commandOutput, Result.OK);
}
return obj;
}
public CommandLine<T, SCRIPT> a(String... strings) {
for (String s : strings) {
if(!StringUtils.isBlank(s)){
this.strings.add(s);
}
}
return this;
}
public CommandLine<T, SCRIPT> a(List<String> strings) {
for (String s : strings) {
if(!StringUtils.isBlank(s)){
this.strings.add(s);
}
}
return this;
}
public CommandLine<T, SCRIPT> addSplit(String s) {
Collections.addAll(strings, s.split("\\s+"));
return this;
}
public CommandLine<T, SCRIPT> p(Map<String, String> params) {
for (Map.Entry<String, String> e : params.entrySet()) {
strings.add(" --" + e.getKey() + "=" + e.getValue() + " ");
}
return this;
}
public T parseResult(SessionContext $, String text) {
return (T) parseWithParser(parser, validator, text, $, asText(false));
}
public CommandLine<T, SCRIPT> setParser(ResultParser<T> parser) {
this.parser = parser;
return this;
}
public CommandLine<T, SCRIPT> setValidator(ResultValidator validator) {
this.validator = validator;
return this;
}
public CommandLine<T, SCRIPT> cd(String cd) {
this.cd = cd;
return this;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("run \"");
Joiner.on("\" \"").appendTo(sb, strings);
sb.append("\" in dir '").append(cd).append('\'');
sb.append(", timeout: ").append(timeoutMs).append("ms");
return sb.toString();
}
public CommandLine<T, SCRIPT> semicolon() {
strings.add(new CommandLineOperator(";"));
return this;
}
public CommandLine<T, SCRIPT> redirectFrom(String path) {
strings.add(new CommandLineOperator("<" + path));
return this;
}
public CommandLine<T, SCRIPT> redirectTo(String path) {
strings.add(new CommandLineOperator(">" + path));
return this;
}
public CommandLine<T, SCRIPT> addRaw(String format, String... args) {
return addRaw(format, false, args);
}
public CommandLine<T, SCRIPT> addRaw(String format, boolean force, String... args) {
if(format.contains("rm ") && !force){
throw new BearException("rm in raw mode is forbidden. Use rmLine(...) or rm(...) to avoid deleting system libs.");
}
strings.add(new CommandLineOperator(String.format(format, args)));
return this;
}
public CommandLine<T, SCRIPT> addRaw(String s) {
strings.add(new CommandLineOperator(s));
return this;
}
public CommandLine<T, SCRIPT> stty() {
strings.add(new CommandLineOperator("stty -echo;"));
return this;
}
public CommandLine<T, SCRIPT> sudo() {
strings.add(new CommandLineOperator("stty -echo; sudo "));
return this;
}
public CommandLine<T, SCRIPT> timeoutMs(long timeoutMs) {
this.timeoutMs = timeoutMs;
return this;
}
public CommandLine<T, SCRIPT> timeoutSec(long timeoutSec) {
return timeoutMs(1000 * timeoutSec);
}
public CommandLine<T, SCRIPT> timeoutMin(long timeoutMin) {
return timeoutSec(60 * timeoutMin);
}
public CommandLine<T, SCRIPT> bash() {
strings.add(new CommandLineOperator("bash -c"));
return this;
}
public CommandLine<T, SCRIPT> setVar(String k, String v) {
strings.add(new CommandLineOperator("export " + k + "=" + v + "; "));
return this;
}
public CommandLine pipe() {
return addRaw(" | ");
}
public SCRIPT build() {
if(script == null){
throw new IllegalStateException("you need to create a script first");
}
return script;
}
public void setScript(@Nullable SCRIPT script) {
this.script = script;
}
@Override
public String asText(boolean forExecution) {
StringBuilder sb = new StringBuilder(128);
CommandLine line = this;
if (line.cd != null && !".".equals(line.cd)) {
sb.append("cd ").append(line.cd).append(" && ");
}
// if (forExecution && sys.isSudo()) {
// sb.append("stty -echo && sudo ");
// }
List strings = line.strings;
for (Object o : strings) {
if (o instanceof CommandLineOperator) {
String s = o.toString();
if(forExecution || !s.contains("export ")){
sb.append(o);
}
} else {
if(forExecution) {
sb.append('"');
if(o instanceof String){
// will make: rm "/foo/ba"*"" -> rm "/foo/ba"*
sb.append(extractWildcards((String)o));
}else{
sb.append(o);
}
sb.append('"');
}else{
sb.append(o);
}
}
sb.append(" ");
}
return sb.toString();
}
@Override
public CommandLine<T, SCRIPT> setCallback(ConsoleCallback callback) {
super.setCallback(callback);
return this;
}
private String extractWildcards(String s) {
s = s.replace("*", "\"*\"");
return s;
}
public boolean isDefaultDir() {
return cd == null || ".".equals(cd);
}
public CommandLine<T, SCRIPT> timeoutForInstallation() {
return timeoutMs(sys.$(sys.getBear().installationTimeoutMs));
}
public CommandLine<T, SCRIPT> timeoutForBuild() {
return timeoutMs(sys.$(sys.getBear().buildTimeoutMs));
}
public CommandLine<T, SCRIPT> timeoutShort() {
return timeoutMs(sys.$(sys.getBear().shortTimeoutMs));
}
public CommandLine sudo(boolean sudo){
if(sudo) return sudo();
return this;
}
public CommandLine sudoOrStty(boolean sudo){
if(sudo) return sudo();
return stty();
}
public CommandLine sshCallback(SessionContext $) {
setCallback($.sshCallback());
return this;
}
}