From 926aed2c827d61c999fb1fe482d234c1504b7177 Mon Sep 17 00:00:00 2001 From: Derrell Lipman Date: Sun, 21 Jan 2007 20:15:06 +0000 Subject: r20937: Update to latest Finite State Machine with properly-handled blocked events (This used to be commit 98eeba919c63e58782aedde45dd9e9b3e400baf8) --- .../source/class/qx/util/fsm/FiniteStateMachine.js | 94 ++++++----- .../source/class/swat/main/AbstractModuleFsm.js | 188 ++++++++++++--------- .../swat/source/class/swat/module/ldbbrowse/Fsm.js | 26 ++- 3 files changed, 182 insertions(+), 126 deletions(-) diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/util/fsm/FiniteStateMachine.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/util/fsm/FiniteStateMachine.js index 7c293eb76b..a170eeb9dc 100644 --- a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/util/fsm/FiniteStateMachine.js +++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/util/fsm/FiniteStateMachine.js @@ -5,7 +5,7 @@ http://qooxdoo.org Copyright: - 2006 by Derrell Lipman + 2006, 2007 by Derrell Lipman License: LGPL 2.1: http://www.gnu.org/licenses/lgpl.html @@ -24,16 +24,11 @@ /** * A finite state machine. * - * See {@see qx.util.finitestatemacine.State} for details on creating States, - * and {@see qx.util.finitestatemacine.Transitions} for details on creating + * See {@link qx.util.finitestatemacine.State} for details on creating States, + * and {@link qx.util.finitestatemacine.Transitions} for details on creating * transitions between states. * - * *EXPERIMENTAL* - * The interface to the finite state machine, states, and transitions is - * experimental. It may change in non-backward-compatible ways as more - * experience is gained in its use. - * - * @param machineName {string} The name of this finite state machine + * @param machineName {String} The name of this finite state machine * */ qx.OO.defineClass("qx.util.fsm.FiniteStateMachine", qx.core.Target, @@ -201,7 +196,7 @@ qx.Proto.addState = function(state) * An object of class qx.util.fsm.State representing a state * which is to be a part of this finite state machine. * - * @param bDispose {boolean} + * @param bDispose {Boolean} * If true, then dispose the old state object. If false, the * old state object is returned for disposing by the caller. * @@ -242,7 +237,7 @@ qx.Proto.replaceState = function(state, bDispose) * Add an object (typically a widget) that is to be accessed during state * transitions, to the finite state machine. * - * @param friendlyName {string} + * @param friendlyName {String} * The friendly name to used for access to the object being added. * * @param obj {Object} @@ -301,9 +296,9 @@ qx.Proto.addObject = function(friendlyName, obj, groupNames) /** - * Remove an object which had previously been added by {@see #addObject}. + * Remove an object which had previously been added by {@link #addObject}. * - * @param friendlyName {string} + * @param friendlyName {String} * The friendly name associated with an object, specifying which object is * to be removed. */ @@ -330,10 +325,10 @@ qx.Proto.removeObject = function(friendlyName) /** - * Retrieve an object previously saved via {@see #addObject}, using its + * Retrieve an object previously saved via {@link #addObject}, using its * Friendly Name. * - * @param friendlyName {string} + * @param friendlyName {String} * The friendly name of the object to be retrieved. * * @return {Object} @@ -351,8 +346,8 @@ qx.Proto.getObject = function(friendlyName) * * @param obj {Object} The object for which the friendly name is desired * - * @return {string} - * If the object has been previously registered via {@see #addObject}, then + * @return {String} + * If the object has been previously registered via {@link #addObject}, then * the friendly name of the object is returned; otherwise, null. */ qx.Proto.getFriendlyName = function(obj) @@ -363,10 +358,10 @@ qx.Proto.getFriendlyName = function(obj) /** - * Retrieve the list of objects which have registered, via {@see addObject} as + * Retrieve the list of objects which have registered, via {@link addObject} as * being members of the specified group. * - * @param groupName {string} + * @param groupName {String} * The name of the group for which the member list is desired. * * @return {Array} @@ -417,7 +412,7 @@ qx.Proto.displayAllObjects = function() * @param obj {Object} * The object to be recursively displayed */ -qx.Proto.debugObject = function(obj) +qx.Proto.debugObject = function(obj, initialMessage) { thisClass = this; @@ -457,6 +452,11 @@ qx.Proto.debugObject = function(obj) } } + if (initialMessage) + { + this.debug(initialMessage); + } + displayObj(obj, 0); }; @@ -523,7 +523,7 @@ qx.Proto.start = function() * which will cause the next state to be whatever is at the top of the * saved-state stack, and remove that top element from the saved-state stack. * - * @param bCurrent {boolean} + * @param bCurrent {Boolean} * When true, then push the current state onto the stack. This might * be used in a transition, before the state has changed. When * false, then push the previous state onto the stack. This might be @@ -593,7 +593,7 @@ qx.Proto.copyEvent = function(event) * @param event {qx.event.type.Event} * The event to be enqueued * - * @param bAddAtHead {boolean} + * @param bAddAtHead {Boolean} * If true, put the event at the head of the queue for immediate * processing. If false, place the event at the tail of the queue so * that it receives in-order processing. @@ -603,7 +603,7 @@ qx.Proto.enqueueEvent = function(event, bAddAtHead) // Add the event to the event queue if (bAddAtHead) { - // Put event at the head of the queue + // Put event at the head of the queue this._eventQueue.push(event); } else @@ -673,10 +673,13 @@ qx.Proto._processEvents = function() var event = this._eventQueue.pop(); // Run the finite state machine with this event - this._run(event); + var bDispose = this._run(event); - // We can now dispose the event - event.dispose(); + // If we didn't block (and re-queue) the event, dispose it. + if (bDispose) + { + event.dispose(); + } } // We're no longer processing events @@ -691,6 +694,10 @@ qx.Proto._processEvents = function() * current state handles this event type), queued (if the current state * blocks this event type), or discarded (if the current state neither * handles nor blocks this event type). + * + * @return {Boolean} + * Whether the event should be disposed. If it was blocked, we've pushed it + * back onto the event queue, and it should not be disposed. */ qx.Proto._run = function(event) { @@ -749,12 +756,12 @@ qx.Proto._run = function(event) // See if we actually found this event type if (! e) { - if (this.debugEvents) + if (debugEvents) { this.debug(this.getName() + ": Event '" + event.getType() + "'" + " not handled. Ignoring."); } - return; + return true; } // We might have found a constant (PREDICATE or BLOCKED) or an object with @@ -772,7 +779,7 @@ qx.Proto._run = function(event) this.debug(this.getName() + ": Could not find friendly name for '" + event.getType() + "' on '" + event.getTarget() + "'"); } - return; + return true; } action = e[friendly]; @@ -790,8 +797,13 @@ qx.Proto._run = function(event) case qx.util.fsm.FiniteStateMachine.EventHandling.BLOCKED: // This event is blocked. Enqueue it for later, and get outta here. + if (debugEvents) + { + this.debug(this.getName() + ": Event '" + event.getType() + "'" + + " blocked. Re-queuing."); + } this._blockedEvents.unshift(event); - return; + return false; default: // See if we've been given an explicit transition name @@ -833,12 +845,11 @@ qx.Proto._run = function(event) case null: // Transition indicates not to try further transitions - return; + return true; default: throw new Error("Transition " + thisState + ":" + t + " returned a value other than true, false, or null."); - return; } // We think we can transition to the next state. Set next state. @@ -971,15 +982,14 @@ qx.Proto._run = function(event) } currentState.getAutoActionsAfterOnentry()(this); - // Add the blocked events to the pending event queue - if (this._blockedEvents.length > 0) + // Add any blocked events back onto the pending event queue + var e; + for (var i = 0; i < this._blockedEvents.length; i++) { - this._eventQueue.unshift(this._blockedEvents); + e = this._blockedEvents.pop(); + this._eventQueue.unshift(e); } - // The blocked event list is now empty - this._blockedEvents = [ ]; - // Ensure that all actions have been flushed qx.ui.core.Widget.flushGlobalQueues(); @@ -990,7 +1000,7 @@ qx.Proto._run = function(event) } // See ya! - return; + return true; } if (debugTransitions) @@ -999,6 +1009,8 @@ qx.Proto._run = function(event) ": event '" + event.getType() + "'" + ": no transition found. No state change."); } + + return true; }; @@ -1137,7 +1149,7 @@ qx.Settings.setDefault( * }; * * - * @param actionType {string} + * @param actionType {String} * The name of the action being validated (for debug messages) * * @param propValue {Object} @@ -1383,4 +1395,4 @@ qx.Proto.dispose = function() this._states = null; return qx.core.Target.prototype.dispose.call(this); -} +}; diff --git a/webapps/swat/source/class/swat/main/AbstractModuleFsm.js b/webapps/swat/source/class/swat/main/AbstractModuleFsm.js index 8c11c7fadf..5ff8e69f40 100644 --- a/webapps/swat/source/class/swat/main/AbstractModuleFsm.js +++ b/webapps/swat/source/class/swat/main/AbstractModuleFsm.js @@ -26,7 +26,7 @@ qx.Proto.buildFsm = function(module) "to build its custom finite state machine."); }; -qx.Proto.addAwaitRpcResultState = function(module) +qx.Proto.addAwaitRpcResultState = function(module, blockedEvents) { var fsm = module.fsm; var _this = this; @@ -47,104 +47,124 @@ qx.Proto.addAwaitRpcResultState = function(module) * "failed" (on RPC) * "execute" on swat.main.fsmUtils.abort_rpc */ - var state = new qx.util.fsm.State( - "State_AwaitRpcResult", + + var stateInfo = + { + "autoActionsBeforeOnentry" : { - "autoActionsBeforeOnentry" : - { - // The name of a function. - "setEnabled" : - [ - { - // We want to enable objects in the group - // swat.main.fsmUtils.enable_during_rpc - "parameters" : [ true ], + // The name of a function. + "setEnabled" : + [ + { + // We want to enable objects in the group + // swat.main.fsmUtils.enable_during_rpc + "parameters" : [ true ], + + // Call this.getObject().setEnabled(true) on + // state entry, for each in the group called + // "swat.main.fsmUtils.enable_during_rpc". + "groups" : [ "swat.main.fsmUtils.enable_during_rpc" ] + }, - // Call this.getObject().setEnabled(true) on - // state entry, for each in the group called - // "swat.main.fsmUtils.enable_during_rpc". - "groups" : [ "swat.main.fsmUtils.enable_during_rpc" ] - }, + { + // We want to disable objects in the group + // swat.main.fsmUtils.disable_during_rpc + "parameters" : [ false ], + + // Call this.getObject().setEnabled(false) on + // state entry, for each in the group called + // "swat.main.fsmUtils.disable_during_rpc". + "groups" : [ "swat.main.fsmUtils.disable_during_rpc" ] + } + ] + }, - { - // We want to disable objects in the group - // swat.main.fsmUtils.disable_during_rpc - "parameters" : [ false ], - - // Call this.getObject().setEnabled(false) on - // state entry, for each in the group called - // "swat.main.fsmUtils.disable_during_rpc". - "groups" : [ "swat.main.fsmUtils.disable_during_rpc" ] - } - ] - }, + "autoActionsBeforeOnexit" : + { + // The name of a function. + "setEnabled" : + [ + { + // We want to re-disable objects we had enabled, in the group + // swat.main.fsmUtils.enable_during_rpc + "parameters" : [ false ], + + // Call this.getObject().setEnabled(false) on + // state entry, for each in the group called + // "swat.main.fsmUtils.enable_during_rpc". + "groups" : [ "swat.main.fsmUtils.enable_during_rpc" ] + }, + + { + // We want to re-enable objects we had disabled, in the group + // swat.main.fsmUtils.disable_during_rpc + "parameters" : [ true ], + + // Call this.getObject().setEnabled(true) on + // state entry, for each in the group called + // "swat.main.fsmUtils.disable_during_rpc". + "groups" : [ "swat.main.fsmUtils.disable_during_rpc" ] + } + ] + }, - "autoActionsBeforeOnexit" : + "onentry" : + function(fsm, event) { - // The name of a function. - "setEnabled" : - [ - { - // We want to re-disable objects we had enabled, in the group - // swat.main.fsmUtils.enable_during_rpc - "parameters" : [ false ], + var bAuthCompleted = false; - // Call this.getObject().setEnabled(false) on - // state entry, for each in the group called - // "swat.main.fsmUtils.enable_during_rpc". - "groups" : [ "swat.main.fsmUtils.enable_during_rpc" ] - }, + // See if we just completed an authentication + if (fsm.getPreviousState() == "State_Authenticate" && + event.getType() == "complete") + { + bAuthCompleted = true; + } - { - // We want to re-enable objects we had disabled, in the group - // swat.main.fsmUtils.disable_during_rpc - "parameters" : [ true ], - - // Call this.getObject().setEnabled(true) on - // state entry, for each in the group called - // "swat.main.fsmUtils.disable_during_rpc". - "groups" : [ "swat.main.fsmUtils.disable_during_rpc" ] - } - ] + // If we didn't just complete an authentication and we're coming + // from some other state... + if (! bAuthCompleted && + fsm.getPreviousState() != "State_AwaitRpcResult") + { + // ... then push the previous state onto the state stack + fsm.pushState(false); + } }, - "onentry" : - function(fsm, event) - { - var bAuthCompleted = false; + "events" : + { + "execute" : + { + "swat.main.fsmUtils.abort_rpc" : + "Transition_AwaitRpcResult_to_AwaitRpcResult_via_button_abort" + }, - // See if we just completed an authentication - if (fsm.getPreviousState() == "State_Authenticate" && - event.getType() == "complete") - { - bAuthCompleted = true; - } + "completed" : + "Transition_AwaitRpcResult_to_PopStack_via_complete", - // If we didn't just complete an authentication and we're coming - // from some other state... - if (! bAuthCompleted && - fsm.getPreviousState() != "State_AwaitRpcResult") - { - // ... then push the previous state onto the state stack - fsm.pushState(false); - } - }, + "failed" : + qx.util.fsm.FiniteStateMachine.EventHandling.PREDICATE + } + }; - "events" : + // If there are blocked events specified... + if (blockedEvents) + { + // ... then add them to the state info events object + for (var blockedEvent in blockedEvents) + { + // Ensure it's not already there. Avoid programmer headaches. + if (stateInfo["events"][blockedEvent]) { - "execute" : - { - "swat.main.fsmUtils.abort_rpc" : - "Transition_AwaitRpcResult_to_AwaitRpcResult_via_button_abort" - }, + throw new Error("Attempt to add blocked event " + + blockedEvent + " but it is already handled"); + } - "completed" : - "Transition_AwaitRpcResult_to_PopStack_via_complete", + // Add the event. + stateInfo["events"][blockedEvent] = blockedEvents[blockedEvent]; + } + } - "failed" : - qx.util.fsm.FiniteStateMachine.EventHandling.PREDICATE - } - }); + var state = new qx.util.fsm.State( "State_AwaitRpcResult", stateInfo); fsm.addState(state); /*** Transitions that use a PREDICATE appear first ***/ diff --git a/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js b/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js index 9843fa6ee2..6d436f4aa7 100644 --- a/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js +++ b/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js @@ -377,8 +377,32 @@ qx.Proto.buildFsm = function(module) }); state.addTransition(trans); + // Create the list of events that should be blocked while we're awaiting the + // results of another RPC request + blockedEvents = + { + // If a previously unexpanded tree node is expanded, issue a request + // to retrieve its contents. + "treeOpenWhileEmpty": + { + "tree" : + qx.util.fsm.FiniteStateMachine.EventHandling.BLOCKED + }, + + // If the selection changes, issue a request to retrieve contents to + // populate the attribute/value table. + "changeSelection": + { + "tree:manager" : + qx.util.fsm.FiniteStateMachine.EventHandling.BLOCKED, + + "dbName": + qx.util.fsm.FiniteStateMachine.EventHandling.BLOCKED + } + } + // Add the AwaitRpcResult state and all of its transitions - this.addAwaitRpcResultState(module); + this.addAwaitRpcResultState(module, blockedEvents); }; -- cgit