001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.api.utils.command;
021
022import com.google.common.base.Joiner;
023import com.google.common.base.Preconditions;
024import com.google.common.collect.ImmutableList;
025import com.google.common.collect.ImmutableMap;
026import com.google.common.collect.Lists;
027import com.google.common.collect.Maps;
028import org.apache.commons.lang.StringUtils;
029import org.sonar.api.utils.System2;
030
031import java.io.File;
032import java.util.Collections;
033import java.util.List;
034import java.util.Map;
035
036/**
037 * @since 2.7
038 */
039public class Command {
040  private final String executable;
041  private final List<String> arguments = Lists.newArrayList();
042  private final Map<String, String> env;
043  private File directory;
044  private boolean newShell = false;
045  private final System2 system;
046
047  Command(String executable, System2 system) {
048    Preconditions.checkArgument(!StringUtils.isBlank(executable), "Command executable can not be blank");
049    this.executable = executable;
050    this.env = Maps.newHashMap(system.envVariables());
051    this.system = system;
052  }
053
054  /**
055   * Create a command line without any arguments
056   *
057   * @param executable
058   */
059  public static Command create(String executable) {
060    return new Command(executable, System2.INSTANCE);
061  }
062
063  public String getExecutable() {
064    return executable;
065  }
066
067  public List<String> getArguments() {
068    return ImmutableList.copyOf(arguments);
069  }
070
071  public Command addArgument(String arg) {
072    arguments.add(arg);
073    return this;
074  }
075
076  public Command addArguments(List<String> args) {
077    arguments.addAll(args);
078    return this;
079  }
080
081  public Command addArguments(String[] args) {
082    Collections.addAll(arguments, args);
083    return this;
084  }
085
086  public File getDirectory() {
087    return directory;
088  }
089
090  /**
091   * Sets working directory.
092   */
093  public Command setDirectory(File d) {
094    this.directory = d;
095    return this;
096  }
097
098  /**
099   * @see org.sonar.api.utils.command.Command#getEnvironmentVariables()
100   * @since 3.2
101   */
102  public Command setEnvironmentVariable(String name, String value) {
103    this.env.put(name, value);
104    return this;
105  }
106
107  /**
108   * Environment variables that are propagated during command execution.
109   * The initial value is a copy of the environment of the current process.
110   *
111   * @return a non-null and immutable map of variables
112   * @since 3.2
113   */
114  public Map<String, String> getEnvironmentVariables() {
115    return ImmutableMap.copyOf(env);
116  }
117
118  /**
119   * <code>true</code> if a new shell should be used to execute the command.
120   * The default behavior is to not use a new shell.
121   *
122   * @since 3.3
123   */
124  public boolean isNewShell() {
125    return newShell;
126  }
127
128  /**
129   * Set to <code>true</code> if a new shell should be used to execute the command.
130   * This is useful when the executed command is a script with no execution rights (+x on unix).
131   *
132   * On windows, the command will be executed with <code>cmd /C executable</code>.
133   * On other platforms, the command will be executed with <code>sh executable</code>.
134   *
135   * @since 3.3
136   */
137  public Command setNewShell(boolean b) {
138    this.newShell = b;
139    return this;
140  }
141
142  List<String> toStrings() {
143    ImmutableList.Builder<String> command = ImmutableList.builder();
144    if (newShell) {
145      if (system.isOsWindows()) {
146        command.add("cmd", "/C", "call");
147      } else {
148        command.add("sh");
149      }
150    }
151    command.add(executable);
152    command.addAll(arguments);
153    return command.build();
154  }
155
156  public String toCommandLine() {
157    return Joiner.on(" ").join(toStrings());
158  }
159
160  @Override
161  public String toString() {
162    return toCommandLine();
163  }
164}