Source: xxe/control/RemoteCommand.js

/**
 * The base class of all remote {@linkplain Command commands}, 
 * that is, commands executed server-side.
 */
export class RemoteCommand extends Command {
    constructor(commandName) {
        super();
        this._commandName = commandName;
    }
    
    get commandName() {
        return this._commandName;
    }

    execute(mode, docView, params, event=null) {
        if (mode === EXECUTE_TEST) {
            return docView.sendExecuteCommand(mode, this._commandName, params)
        }
        
        Command.consumeEvent(event);
        
        return docView.sendExecuteCommand(mode, this._commandName, params)
            .then((result) => {
                return this.commandExecuted(mode, docView, params, result);
            });
            // No need to catch errors here. At worst, sendExecuteCommand will
            // return a null Promise.
    }
    
    /**
     * Invoked when, in helper or normal modes, the command execution request 
     * has been sent to the server.
     */
    commandExecuted(mode, docView, params, result) {
        if (result === null) {
            // Generally means: cannot execute.
            // Also happens when sendExecuteCommand fails, in which case an
            // error is reported on the console by DocumentView.
            return null;
        } else {
            switch (result.status) {
            case COMMAND_RESULT_DONE:
                this.executionDone(mode, docView, params, result.value);
                break;
            case COMMAND_RESULT_STOPPED:
                {
                    let resume = [null, null];
                    
                    let stoppedValue = result.value;
                    if (stoppedValue !== null &&
                        stoppedValue.startsWith("xxeClientExecuteCommand")) {
                        stoppedValue = stoppedValue.substring(23).trim();
                        resume = Command.splitCmdString(stoppedValue, ' ');
                    }

                    if (resume[0]) {
                        return docView.executeCommand(EXECUTE_HELPER,
                                                      resume[0], resume[1]);
                    }
                }
        
                this.executionStopped(mode, docView, params, result.value);
                break;
            case COMMAND_RESULT_FAILED:
                this.executionFailed(mode, docView, params, result.value);
                break;
            }
            // Otherwise, COMMAND_RESULT_CANCELED.

            return result;
        }
    }

    /**
     * Invoked after the command execution is successful.
     * <p>Default implementation does nothing at all.
     */
    executionDone(mode, docView, params, resultValue) {}
    
    /**
     * Invoked after the command execution was stopped.
     * <p>Default implementation displays <code>resultValue</code>
     * (which in principle contains information about how to resume 
     * the execution of the command) if there; 
     * a generic error message otherwise.
     */
    executionStopped(mode, docView, params, resultValue) {
        this.reportExecutionError(docView, params, resultValue,
                                  /*stopped*/ true);
    }

    /**
     * Invoked after the command execution has failed.
     * <p>Default implementation displays <code>resultValue</code>
     * (which is assumed to be an error message) if there; 
     * a generic error message otherwise.
     */
    executionFailed(mode, docView, params, resultValue) {
        this.reportExecutionError(docView, params, resultValue,
                                  /*stopped*/ false);
    }

    reportExecutionError(docView, params, resultValue, stopped, quiet=true) {
        let msg;
        if (stopped || resultValue === null) {
            if (quiet) {
                // Generally there is no need to show an error alert when the
                // command was stopped or when the command has failed without
                // providing a reason for this failure.
                return;
            }

            // We want a message, even a low-level one, may be to debug the
            // command. Make it as detailed as possible.
            const cmdString = this.getCommandString(params);
            msg = stopped? `Command "${cmdString}" was stopped` :
                `Command "${cmdString}" has failed`;
            if (resultValue === null) {
                msg += ".";
            } else {
                msg += ":\n";
                msg += resultValue;
            }
        } else {
            // Command has failed and returned a hopefully meaningful error
            // message. Use it as is.
            msg = resultValue;
        }
        XUI.Alert.showError(msg, docView);
    }
}