package com.mobilesorcery.sdk.core;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* A fluent class for building command lines.
*
* @author fmattias
*/
public class CommandLineBuilder
{
/**
* Path to the executable file starting the command line.
*/
private final String m_executable;
/**
* The set of flags, if a flag does not have a parameter its
* key is null.
*/
private final HashMap<String, String> m_params = new LinkedHashMap<String, String>();
/**
* The trailing parameters of the command line, e.g. files to the cp command.
*/
private final ArrayList<String> m_endParams = new ArrayList<String>();
/**
* The last flag that was added.
*/
private String m_lastFlag = null;
private final HashSet<String> hiddenFlags = new HashSet<String>();
/**
* Creates a class that builds a command line.
*
* @param executable Path to the executable.
*/
public CommandLineBuilder(String executable)
{
this(executable, false);
}
public CommandLineBuilder(String executable, boolean lenient)
{
if(!lenient && ! (new File( executable )).exists( ) )
{
throw new IllegalArgumentException( "Executable does not exist." );
}
m_executable = executable;
}
/**
* Adds a flag to the command line.
*
* @param flag A flag to the command line.
* @return itself.
*/
public CommandLineBuilder flag(String flag)
{
if( flag == null || flag.length( ) == 0 )
{
throw new IllegalArgumentException( "Flag is null or empty." );
}
m_lastFlag = flag;
m_params.put( flag, null );
return this;
}
public CommandLineBuilder flag(String flag, boolean hidden) {
hiddenFlags.add(flag);
return flag(flag);
}
/**
* Adds a parameter to the command line, either a flag or one of the
* trailing parameters.
*
* @param parameter either a flag parameter or an end parameter.
* @return itself.
*/
public CommandLineBuilder with(String parameter)
{
if( parameter == null )
{
return this;
}
if( m_lastFlag != null )
{
m_params.put( m_lastFlag, parameter );
}
else
{
throw new UnsupportedOperationException( "Trying to add parameter without flag." );
}
m_lastFlag = null;
return this;
}
/**
* Takes the absolute path of the given file or
* directory and adds it to the command line.
*
* @param file A representation of a path in the file system.
* @return itself.
*/
public CommandLineBuilder with(File file)
{
with( file.getAbsolutePath( ) );
return this;
}
/**
* Adds a trailing parameter.
*
* @return itself.
*/
public CommandLineBuilder param(String parameter)
{
if( parameter == null )
{
throw new IllegalArgumentException( "Cannot add trailing parameter." );
}
m_endParams.add( parameter );
return this;
}
/**
* Adds a trailing parameter.
*
* @return itself.
*/
public CommandLineBuilder param(File param)
{
return param( param.getAbsolutePath( ) );
}
/**
* Returns the command line as a string array, where each flag and
* parameter is a separate array element. The array begins with
* the path to the executable. No quotation of strings is performed.
*
* @return The command line in the form of an array.
*/
public String[] asArray()
{
ArrayList<String> cmdLine = new ArrayList<String>( );
cmdLine.add( m_executable );
/* Add flags and their parameters */
for( String flag : m_params.keySet( ) )
{
cmdLine.add( flag );
String value = m_params.get( flag );
if( value != null )
{
cmdLine.add( value );
}
}
/* Add trailing arguments */
cmdLine.addAll( m_endParams );
return cmdLine.toArray( new String[ cmdLine.size( ) ] );
}
/**
* Converts the command line into a single string. Arguments
* containing spaces are automatically quoted.
*
* @return the command line as a string.
*/
@Override
public String toString()
{
StringBuffer cmdLine = new StringBuffer( quote( m_executable ) );
addFlags( m_params, cmdLine );
addTrailingArgs( m_endParams, cmdLine );
return cmdLine.toString( );
}
/**
* Converts the command line into a single string. Arguments containing
* spaces are quoted. Flags in the given list of hidden flags are
* hidden with "*HIDDEN*".
*
* @param hiddenFlags A list of flags which should be hidden from the
* command line.
* @return the command line as a string.
*/
public String toHiddenString()
{
/* Copy internal map and hide specified parameters. */
Map<String, String> paramsHidden = new LinkedHashMap<String, String>(m_params);
for(String hiddenFlag : hiddenFlags) {
if( paramsHidden.containsKey( hiddenFlag ) ) {
paramsHidden.put(hiddenFlag, "***HIDDEN***");
}
}
// Throwaway cmdline
StringBuffer cmdLine = new StringBuffer(quote( m_executable ));
addFlags(paramsHidden, cmdLine);
addTrailingArgs(m_endParams, cmdLine );
return cmdLine.toString( );
}
/**
* Flattens a map of flag pairs elements into to a string, and quotes
* the parameter if it contains spaces or tabs.
*
* @param flags A map of flag pairs that will be appended in the
* command line.
* @param cmdLine The command flag pairs will be appended here.
*/
private void addFlags(Map<String, String> flags, StringBuffer cmdLine)
{
for( String flag : flags.keySet( ) )
{
cmdLine.append( " " + flag );
String value = flags.get( flag );
if( value != null )
{
cmdLine.append( " " + quote( value ) );
}
}
}
/**
* Flattens a list of parameters into a string, and quotes a parameter
* if it contains spaces or tabs.
*
* @param args A list of arguments that will be appended to the command
* line.
* @param cmdLine The command parameters will be appended here.
*/
private void addTrailingArgs(List<String> args, StringBuffer cmdLine)
{
for( String parameter : args )
{
cmdLine.append( " " + quote( parameter ) );
}
}
/**
* Surrounds the input string with quotation marks
* if the input string contains whitespace.
*
* @param input Input string to be quoted.
* @return A quoted input string.
*/
private String quote(String input)
{
if( input == null )
{
return null;
}
if( input.contains( " " ) || input.contains( "\t" ) )
{
return "\"" + input + "\"";
}
else
{
return input;
}
}
}