Entry point for keyboard accelerators or anything else that wants to execute
a command. A new request object is created and a check performed, if the
passed in arguments are VALID/INVALID or INCOMPLETE. If they are INCOMPLETE
the ArgumentProvider on the sender is called or otherwise the default
ArgumentProvider to get the still required arguments.
If they are valid (or valid after the ArgumentProvider is done), the command
is executed.

@param command   Either a command, or the name of one
@param env       Current environment to execute the command in
@param sender    String that should be the same as the senderObject stored on
the environment in env[sender]
@param args      Arguments for the command
@param typed     (Optional)
function exec(command, env, sender, args, typed) {
    if (typeof command === 'string') {
        command = commands[command];
    }
    if (!command) {
        // TODO: Should we complain more than returning false?
        return false;
    }

    var request = new Request({
        sender: sender,
        command: command,
        args: args || {},
        typed: typed
    });
    
    /**
     * Executes the command and ensures request.done is called on the request in 
     * case it's not marked to be done already or async.
     */
    function execute() {
        command.exec(env, request.args, request);
        
        // If the request isn't asnync and isn't done, then make it done.
        if (!request.isAsync && !request.isDone) {
            request.done();
        }
    }
    
    
    if (request.getStatus() == Status.INVALID) {
        console.error("Canon.exec: Invalid parameter(s) passed to " + 
                            command.name);
        return false;   
    } 
    // If the request isn't complete yet, try to complete it.
    else if (request.getStatus() == Status.INCOMPLETE) {
        // Check if the sender has a ArgsProvider, otherwise use the default
        // build in one.
        var argsProvider;
        var senderObj = env[sender];
        if (!senderObj || !senderObj.getArgsProvider ||
            !(argsProvider = senderObj.getArgsProvider())) 
        {
            argsProvider = defaultArgsProvider;
        }

        // Ask the paramProvider to complete the request.
        argsProvider(request, function() {
            if (request.getStatus() == Status.VALID) {
                execute();
            }
        });
        return true;
    } else {
        execute();
        return true;
    }
}