public NavigationCase getNavigationCommand(
FacesContext facesContext, NavigationContext navigationContext, String fromAction, String outcome,
String toFlowDocumentId)
{
String viewId = facesContext.getViewRoot() != null ? facesContext.getViewRoot().getViewId() : null;
NavigationCase navigationCase = getNavigationCommandFromGlobalNavigationCases(
facesContext, viewId, navigationContext, fromAction, outcome);
if (outcome != null && navigationCase == null)
{
FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
List<Flow> activeFlows = FlowHandlerImpl.getActiveFlows(facesContext, flowHandler);
// JSF 2.2 section 7.4.2: "... When outside of a flow, view identifier
// has the additional possibility of being a flow id.
Flow targetFlow = calculateTargetFlow(facesContext, outcome, flowHandler, activeFlows, toFlowDocumentId);
Flow currentFlow = navigationContext.getCurrentFlow(facesContext);
FlowCallNode targetFlowCallNode = null;
boolean startFlow = false;
String startFlowDocumentId = null;
String startFlowId = null;
boolean checkFlowNode = false;
String outcomeToGo = outcome;
String actionToGo = fromAction;
if (currentFlow != null)
{
// JSF 2.2 section 7.4.2: When inside a flow, a view identifier has
// the additional possibility of being the id of any node within the
// current flow or the id of another flow
if (targetFlow != null)
{
startFlow = true;
}
else
{
// Check if thie
checkFlowNode = true;
}
}
else
{
if (targetFlow != null)
{
// start flow!
startFlow = true;
}
}
if (!startFlow)
{
for (Flow activeFlow : activeFlows)
{
FlowNode node = activeFlow.getNode(outcome);
if (node != null)
{
currentFlow = activeFlow;
break;
}
_FlowNavigationStructure flowNavigationStructure = _flowNavigationStructureMap.get(
activeFlow.getId());
navigationCase = getNavigationCaseFromFlowStructure(facesContext,
flowNavigationStructure, fromAction, outcome, viewId);
if (navigationCase != null)
{
currentFlow = activeFlow;
break;
}
}
}
// If is necessary to enter a flow or there is a current
// flow and it is necessary to check a flow node
if (startFlow || (checkFlowNode && currentFlow != null))
{
boolean complete = false;
boolean checkNavCase = true;
while (!complete && (startFlow || checkFlowNode))
{
if (startFlow)
{
if (flowHandler.isActive(facesContext, targetFlow.getDefiningDocumentId(), targetFlow.getId()))
{
// Add the transition to exit from the flow
Flow baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
// This is the part when the pseudo "recursive call" is done.
while (baseReturnFlow != null && !(baseReturnFlow.getDefiningDocumentId().equals(
targetFlow.getDefiningDocumentId()) &&
baseReturnFlow.getId().equals(targetFlow.getId())) )
{
navigationContext.popFlow(facesContext);
baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
}
navigationContext.popFlow(facesContext);
currentFlow = navigationContext.getCurrentFlow(facesContext);
navigationContext.addTargetFlow(baseReturnFlow, currentFlow, null);
}
if (startFlowId == null)
{
startFlowDocumentId = targetFlow.getDefiningDocumentId();
startFlowId = targetFlowCallNode == null ? targetFlow.getId() : targetFlowCallNode.getId();
}
navigationContext.addTargetFlow(currentFlow, targetFlow, targetFlowCallNode);
targetFlowCallNode = null;
// Since we start a new flow, the current flow is now the
// target flow.
navigationContext.pushFlow(facesContext, targetFlow);
currentFlow = targetFlow;
//No outboundCallNode.
//Resolve start node.
outcomeToGo = resolveStartNodeOutcome(targetFlow);
checkFlowNode = true;
startFlow = false;
}
if (checkFlowNode)
{
FlowNode flowNode = currentFlow.getNode(outcomeToGo);
if (flowNode != null)
{
checkNavCase = true;
if (!complete && flowNode instanceof SwitchNode)
{
outcomeToGo = calculateSwitchOutcome(facesContext, (SwitchNode) flowNode);
// Start over again checking if the node exists.
//fromAction = currentFlow.getId();
actionToGo = currentFlow.getId();
flowNode = currentFlow.getNode(outcomeToGo);
continue;
}
if (!complete && flowNode instanceof FlowCallNode)
{
// "... If the node is a FlowCallNode, save it aside as facesFlowCallNode. ..."
FlowCallNode flowCallNode = (FlowCallNode) flowNode;
targetFlow = calculateFlowCallTargetFlow(facesContext,
flowHandler, flowCallNode, currentFlow);
if (targetFlow != null)
{
targetFlowCallNode = flowCallNode;
startFlow = true;
continue;
}
else
{
// Ask the FlowHandler for a Flow for this flowId, flowDocumentId pair. Obtain a
// reference to the start node and execute this algorithm again, on that start node.
complete = true;
}
}
if (!complete && flowNode instanceof MethodCallNode)
{
MethodCallNode methodCallNode = (MethodCallNode) flowNode;
String vdlViewIdentifier = calculateVdlViewIdentifier(facesContext, methodCallNode);
// note a vdlViewIdentifier could be a flow node too
if (vdlViewIdentifier != null)
{
outcomeToGo = vdlViewIdentifier;
actionToGo = currentFlow.getId();
continue;
}
else
{
complete = true;
}
}
if (!complete && flowNode instanceof ReturnNode)
{
ReturnNode returnNode = (ReturnNode) flowNode;
String fromOutcome = returnNode.getFromOutcome(facesContext);
actionToGo = currentFlow.getId();
Flow sourceFlow = currentFlow;
Flow baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
// This is the part when the pseudo "recursive call" is done.
while (baseReturnFlow != null && !(baseReturnFlow.getDefiningDocumentId().equals(
currentFlow.getDefiningDocumentId()) &&
baseReturnFlow.getId().equals(currentFlow.getId())) )
{
navigationContext.popFlow(facesContext);
baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
}
navigationContext.popFlow(facesContext);
currentFlow = navigationContext.getCurrentFlow(facesContext);
navigationContext.addTargetFlow(sourceFlow, currentFlow, null);
outcomeToGo = fromOutcome;
String lastDisplayedViewId = navigationContext.getLastDisplayedViewId(facesContext,
currentFlow);
// The part where FlowHandler.NULL_FLOW is passed as documentId causes the effect of
// do not take into account the documentId of the returned flow in the command. In
// theory there is no Flow with defining documentId as FlowHandler.NULL_FLOW. It has
// sense because the one who specify the return rules should be the current flow
// after it is returned.
navigationCase = getNavigationCommand(facesContext,
navigationContext, actionToGo, outcomeToGo, FlowHandler.NULL_FLOW);
if (navigationCase != null)
{
navigationCase = new FlowNavigationCase(navigationCase,
flowNode.getId(), FlowHandler.NULL_FLOW);
complete = true;
}
else
{
// No navigation case
if (lastDisplayedViewId != null)
{
navigationCase = createNavigationCase(
viewId, flowNode.getId(), lastDisplayedViewId, FlowHandler.NULL_FLOW);
complete = true;
}
}
if (currentFlow == null)
{
complete = true;
}
continue;
}
if (!complete && flowNode instanceof ViewNode)
{
ViewNode viewNode = (ViewNode) flowNode;
navigationCase = createNavigationCase(viewId, flowNode.getId(),
viewNode.getVdlDocumentId());
complete = true;
}
else
{
//Should not happen
complete = true;
}
}
else if (checkNavCase)
{
// Not found in current flow.
_FlowNavigationStructure flowNavigationStructure = _flowNavigationStructureMap.get(
currentFlow.getId());
navigationCase = getNavigationCaseFromFlowStructure(facesContext,
flowNavigationStructure, actionToGo, outcomeToGo, viewId);
// JSF 2.2 section 7.4.2 "... any text that references a view identifier, such as
// <from-view-id> or <to-view-id>,
// can also refer to a flow node ..."
if (navigationCase != null)
{
outcomeToGo = navigationCase.getToViewId(facesContext);
checkNavCase = false;
}
else
{
// No matter if navigationCase is null or not, complete the look.