UNPKG

@sap/xsodata

Version:

Expose data from a HANA database as OData V2 service with help of .xsodata files.

205 lines (171 loc) 5.58 kB
"use strict"; /* jshint ignore:start */ var EventEmitter = require("events").EventEmitter; var utils = require("util"); module.exports = StateMaschine; /** * Contructor. Create the StateMaschine object * *@param {object} context The state maschine context with states *@param {object} externalContext The external (i.e. Xsodata context) */ function StateMaschine(context, externalContext) { EventEmitter.call(this); this._context = context; this._currentState = null; this._isFinal = false; this._statesHistory = []; this._externalContext = externalContext; } utils.inherits(StateMaschine, EventEmitter); /** * Handles any error. This method also emits the error event via eventemitter * @event error {error} The error occured */ function handleError(error) { this.emit("error", error); } /** * Sets the current state of the state maschine * *@param {string} stateName Name of state to set */ function setCurrentState(stateName) { var state = this._context.states[stateName]; if (!state) { throw new Error("State '" + stateName + "' does not exist"); } this._currentState = { name: stateName, context: state }; } /** * Builds the internal state source. This is the first parameter of each state function * (i.e. action()) called. The state source is the previous state of the current state. * *@return {object} The current state source */ function buildCurrentStateSource() { if (this._statesHistory.length === 0) { return null; } return { name: this._statesHistory[this._statesHistory.length - 1].name }; } /** * Returns the external context. This is the object which was provided as the second parameter * to the contructor function. With this method the external context can be accessed via api * function though this.getExternalContext() inside any of state function like action() * *@return {any} The external context */ StateMaschine.prototype.getExternalContext = function getExternalContext() { return this._externalContext; }; /** * Returns the current state of the state maschine * *@return {object} The current state */ StateMaschine.prototype.getCurrentState = function getCurrentState() { return this._currentState; }; /** * Returns a list of previous states which had been passed before the current state. * *@return {array} List of previous states */ StateMaschine.prototype.getHistory = function getHistory() { return this._statesHistory; }; /** * Resets the state maschine to state it hed before initialize() method was called. */ StateMaschine.prototype.reset = function reset() { this._statesHistory = []; this._currentState = null; this._isFinal = false; }; /** * Starts the state maschine to run and process it's states. */ StateMaschine.prototype.initialize = function initialize() { this.next(this._context.initializeWith); }; /** * Returns true if the state maschine is in final state. State maschine get's into final state * if either this.setFinal(true) is called inside a states function or a state has a property * isFinal == true. */ StateMaschine.prototype.isFinal = function isFinal() { return this._isFinal; }; /** * Sets the state maschine to final state. This emits also a 'final' event. When state maschine is * in final state all calls regarding to state changing will be irgnored. * *@event final */ StateMaschine.prototype.setFinal = function setFinal() { this.emit("final"); this._isFinal = true; }; /** * Triggers the state maschine to do a transistion from one state to another state provided by * parameter stateName. If second agrument is provided all states functions of the next state will * recieve this argument as a second parameter. * *@param {string} stateName The name of the next state to transit into *@param {any} arg1 Any argument which the next state function should recieve */ StateMaschine.prototype.next = function next(stateName, arg1) { var resetCurrentState = false; var currentStateTemp = this._currentState; this._arg1 = arg1; if (currentStateTemp && currentStateTemp.context.isFinal === true) { this.setFinal(true); } if (this.isFinal() === true) { return; } try { if (!stateName) { throw new Error("You can not call .next() without any state target"); } buildCurrentStateSource.call(this); if (!this._currentState) { // We are in initial state // Be carefull with refactoring because of recusion setCurrentState.call(this, stateName); } else { setCurrentState.call(this, stateName); this.getHistory().push(currentStateTemp); resetCurrentState = true; } } catch (error) { if (resetCurrentState === true) { var lastState = this.getHistory().pop(); setCurrentState.call(this, lastState.name); } return handleError.call(this, error); } this.process(); }; /** * Starts to process a transition to the next state. This is done by calling the action() method * of the current states context. */ StateMaschine.prototype.process = function process() { try { if (this._currentState.context.action) { this._currentState.context.action.call(this, buildCurrentStateSource.call(this), this._arg1); } } catch (error) { return handleError.call(this, error); } }; /* jshint ignore:end */