Looking for a better way to define and use constants In a custom Ant task, I have the following constants:

private static final String _REVISION = ".revision";
private static final String _CODEBASE_INFO = ".codebase";
private static final String _DIFF = ".diff";
private static final String _TYPE_GIT = "git";
private static final String _TYPE_SVN = "svn";
private static final String[] _COMMAND_REV_GIT =
    {"git", "rev-parse", "HEAD"};
private static final String[] _COMMAND_REV_SVN =
    {"svn", "info"};
private static final String[][] _COMMAND_CODEBASEINFO_GIT = {
    {"git", "branch", "-v"},
    {"git", "remote", "-v"},
    {"git", "log", "--max-count=1"},
    {"git", "status"}};
private static final String[][] _COMMAND_CODEBASEINFO_SVN = {
    _COMMAND_REV_SVN,
    {"svn", "status"}};
private static final String[][] _COMMAND_DIFF_GIT = {
    {"git", "diff", "HEAD"},
    {"git", "diff", "HEAD", "--no-prefix"}};
private static final String[][] _COMMAND_DIFF_SVN = {
    {"svn", "diff"}};

private static final Map&lt;String, Object&gt; _COMMANDS;

static {
    Map&lt;String, Object&gt; map = new HashMap&lt;&gt;();
    map.put(_TYPE_GIT + _REVISION, _COMMAND_REV_GIT);
    map.put(_TYPE_SVN + _REVISION, _COMMAND_REV_SVN);
    map.put(_TYPE_GIT + _DIFF, _COMMAND_DIFF_GIT);
    map.put(_TYPE_SVN + _DIFF, _COMMAND_DIFF_SVN);
    map.put(_TYPE_GIT + _CODEBASE_INFO, _COMMAND_CODEBASEINFO_GIT);
    map.put(_TYPE_SVN + _CODEBASE_INFO, _COMMAND_CODEBASEINFO_SVN);
    _COMMANDS = Collections.unmodifiableMap(map);
}


What the task should do is to run commands according to a given version control type. For example if I give it git then it will run commands in _COMMAND_REV_GIT, _COMMAND_CODEBASEINFO_GIT and _COMMAND_DIFF_GIT. 

The reason I separate them into three constants is that the outputs of them have to be handled in different ways.

The reason I use a map is that I don't want to use a lot of "if" or "switch-case" in my code. In the map there are keys like git.revision, git.codebase, etc. If git is given when running this task, _COMMAND_REV_GIT will be fetched from the map using the key git.revision in the method handling running the revision command:

String[] command = _COMMANDS.get(_givenType + _REVISION);


But these constants looks really complicated, and I think the types of version control should be Enum rather than String, so is the suffixes indicating the types of the commands. So how should I modify these codes to make it clear, safe and elegant?
You should create an ScmCommands interface like this:

public interface ScmCommands {
    public String[] getRevisionCommand();

    public List&lt;String[]&gt; getDiffCommand();

    public List&lt;String[]&gt; getCodebaseInfoCommand();
}


and two implementations: SvnCommands, GitCommands.

It would improve type safety a lot. (With a Map&lt;String, Object&gt; you usually have to cast the values which is error-prone). Furthermore, it eliminates the magic constants. It's easy to call Map.get() with a mistyped key. With the interface the compiler warns you if the method name is wrong.



Edit: I've changed the return types. Sample implementations are the following:

public class GitCommands implements ScmCommands {

    private static final String GIT_COMMAND = "git";

    public GitCommands() {
    }

    @Override
    public String[] getRevisionCommand() {
        return new String[] { GIT_COMMAND, "rev-parse", "HEAD" };
    }

    @Override
    public List&lt;String[]&gt; getDiffCommand() {
        final List&lt;String[]&gt; result = new ArrayList&lt;String[]&gt;();
        result.add(new String[] { GIT_COMMAND, "diff", "HEAD" });
        result.add(new String[] { GIT_COMMAND, "diff", "HEAD", "--no-prefix" });
        return result;
    }

    @Override
    public List&lt;String[]&gt; getCodebaseInfoCommand() {
        final List&lt;String[]&gt; result = new ArrayList&lt;String[]&gt;();
        result.add(new String[] { GIT_COMMAND, "branch", "-v" });
        result.add(new String[] { GIT_COMMAND, "remote", "-v" });
        result.add(new String[] { GIT_COMMAND, "log", "--max-count=1" });
        result.add(new String[] { GIT_COMMAND, "status" });
        return result;
    }
}




public class SvnCommands implements ScmCommands {

    private static final String SVN_COMMAND = "svn";

    public SvnCommands() {
    }

    @Override
    public String[] getRevisionCommand() {
        return new String[] { SVN_COMMAND, "info" };
    }

    @Override
    public List&lt;String[]&gt; getDiffCommand() {
        final List&lt;String[]&gt; result = new ArrayList&lt;String[]&gt;();
        result.add(new String[] {SVN_COMMAND, "diff"});
        return result;
    }

    @Override
    public List&lt;String[]&gt; getCodebaseInfoCommand() {
        final List&lt;String[]&gt; result = new ArrayList&lt;String[]&gt;();
        result.add(new String[] { SVN_COMMAND, "status" });
        return result;
    }

}


It fits well to ProcessBuilder but I'd consider extracting out all SCM related methods to an interface (for example Scm), and move the code to SCM-specific classes which implement the Scm interface, like GitScm, SubversionScm etc. Calling command line executables doesn't smell good, maybe it would be better to use native Subversion and Git Java libraries.