I think I need to change the list of commands to be stored with canonical names to reasonably do that (my plan was to use StringUtilities.getMatchingNames()
Well, what do you mean by "list of commands"?
Each FaxBot has a SortedListModel of Monster objects.
Each Monster has a name, and actualName, a command, and a category, all of which are taken straight from the XML config file.
Each FaxBot also has two Maps which go from canonicalized actualName to monster.name or monster.command.
The ASH command uses those to translate from a $monster to appropriate monster name/command.
Now, the CLI command lets you type a "command" and uses the FaxBot.hasCommand method to see if it is valid. If so, it passes the command (as entered by the user) to the FaxBot, with no knowledge of just what monster will be requested.
Seems to me there could be some refactoring in FaxBot:
Monster getMonsterByActualName( String name )
Monster getMonsterByCommand( String command )
They both return null if no monster matches or the Monster object, otherwise.
Instead of have the caller pass monster.name and monster.command to
FaxRequestFrame.requestFax( final String botName, final String monster, final String command, final boolean checkOnline ),
It would pass the looked up Monster object to
FaxRequestFrame.requestFax( final String botName, final Monster monster, final boolean checkOnline ),
which would extract monster.name and monster.command.
Given the above, the FaxBot object would have two data structures to aid Monster getMonsterByActualName( String name ) and Monster getMonsterByCommand( String command ). Instead of:
private final Map<String, String> monsterByActualName = new HashMap<String, String>();
private final Map<String, String> commandByActualName = new HashMap<String, String>();
it would build:
private final Map<String, Monster> monsterByActualName = new HashMap<String, Monster>();
private final Map<String, Monster> monsterByCommand = new HashMap<String, Monster>();
private String[] canonicalizedCommands
and the CLI command would call getMonsterByCommand( String command ) with the command substring, it would look it up using StringUtilities.getMatchingNames, as you suggest, and if there is a unique match, it would look up the full command in monsterByCommand and return that.
Huh. Well, I'll see if I can do the initial refactoring for you, at least.