# Source: 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);
        }
    }