diff --git a/lib/simulator/behaviors/InclusiveGatewayBehavior.js b/lib/simulator/behaviors/InclusiveGatewayBehavior.js index bf5bb167..2ef49523 100644 --- a/lib/simulator/behaviors/InclusiveGatewayBehavior.js +++ b/lib/simulator/behaviors/InclusiveGatewayBehavior.js @@ -1,5 +1,5 @@ import { - filterSequenceFlows + filterSequenceFlows, isSequenceFlow } from '../util/ModelUtil'; @@ -14,7 +14,30 @@ export default function InclusiveGatewayBehavior( } InclusiveGatewayBehavior.prototype.enter = function(context) { - this._simulator.exit(context); + const { + scope, + element + } = context; + + const incomingSequenceFlows = filterSequenceFlows(element.incoming); + + if (incomingSequenceFlows.length === 1) { + return this._simulator.exit(context); + } + + const { + parent: parentScope + } = scope; + + const sameParentScopes = this._simulator.findScopes(scope => ( + scope.parent === parentScope && scope.element !== element)); + + // There are still some tokens to wait for. + if (this._canReachAnyScope(sameParentScopes, element)) { + return; + } + + this._join(context); }; InclusiveGatewayBehavior.prototype.exit = function(context) { @@ -52,7 +75,54 @@ InclusiveGatewayBehavior.prototype.exit = function(context) { } }; +InclusiveGatewayBehavior.prototype._join = function(context) { + const elementScopes = this._simulator.findScopes({ + parent: context.parent, + element: context.element + }); + + for (const childScope of elementScopes) { + + if (childScope !== context.scope) { + + // complete joining child scope + this._simulator.destroyScope(childScope.complete(), context.scope); + } + } + + this._simulator.exit(context); +}; + +InclusiveGatewayBehavior.prototype._canReachAnyScope = function(scopes, currentElement, traversed = new Set()) { + if (traversed.has(currentElement)) { + return false; + } + + if (anyScopeIsOnElement(scopes, currentElement)) { + return true; + } + + if (isSequenceFlow(currentElement)) { + return this._canReachAnyScope(scopes, currentElement.source, traversed); + } + + + const incomingFlows = filterSequenceFlows(currentElement.incoming); + + for (const flow of incomingFlows) { + if (this._canReachAnyScope(scopes, flow, traversed)) { + return true; + } + } + + return false; +}; + InclusiveGatewayBehavior.$inject = [ 'simulator', 'activityBehavior' -]; \ No newline at end of file +]; + +function anyScopeIsOnElement(scopes, element) { + return scopes.some(scope => scope.element === element); +}