Search in sources :

Example 1 with NavigationCase

use of jakarta.faces.application.NavigationCase in project myfaces by apache.

the class NavigationHandlerImpl method getNavigationCommandFromGlobalNavigationCases.

public NavigationCase getNavigationCommandFromGlobalNavigationCases(FacesContext facesContext, String viewId, NavigationContext navigationContext, String fromAction, String outcome) {
    Map<String, Set<NavigationCase>> casesMap = getNavigationCases();
    NavigationCase navigationCase = null;
    Set<? extends NavigationCase> casesSet;
    if (viewId != null) {
        casesSet = casesMap.get(viewId);
        if (casesSet != null) {
            // Exact match?
            navigationCase = calcMatchingNavigationCase(facesContext, casesSet, fromAction, outcome);
        }
    }
    if (navigationCase == null) {
        // Wildcard match?
        List<_WildcardPattern> wildcardPatterns = getSortedWildcardPatterns();
        for (int i = 0; i < wildcardPatterns.size(); i++) {
            _WildcardPattern wildcardPattern = wildcardPatterns.get(i);
            if (wildcardPattern.match(viewId)) {
                casesSet = casesMap.get(wildcardPattern.getPattern());
                if (casesSet != null) {
                    navigationCase = calcMatchingNavigationCase(facesContext, casesSet, fromAction, outcome);
                    if (navigationCase != null) {
                        break;
                    }
                }
            }
        }
    }
    return navigationCase;
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) NavigationCase(jakarta.faces.application.NavigationCase)

Example 2 with NavigationCase

use of jakarta.faces.application.NavigationCase in project myfaces by apache.

the class NavigationHandlerImpl method handleNavigation.

@Override
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome, String toFlowDocumentId) {
    NavigationContext navigationContext = new NavigationContext();
    NavigationCase navigationCase = null;
    try {
        navigationCase = getNavigationCommand(facesContext, navigationContext, fromAction, outcome, toFlowDocumentId);
    } finally {
        navigationContext.finish(facesContext);
    }
    if (navigationCase != null) {
        if (log.isLoggable(Level.FINEST)) {
            log.finest("handleNavigation fromAction=" + fromAction + " outcome=" + outcome + " toViewId =" + navigationCase.getToViewId(facesContext) + " redirect=" + navigationCase.isRedirect());
        }
        boolean isViewActionProcessingBroadcastAndRequiresRedirect = false;
        if (UIViewAction.isProcessingBroadcast(facesContext)) {
            // f:viewAction tag always triggers a redirect to enforce execution of
            // the lifecycle again. Note this requires enables flash scope
            // keepMessages automatically, because a view action can add messages
            // and these ones requires to be renderer afterwards.
            facesContext.getExternalContext().getFlash().setKeepMessages(true);
            String fromViewId = (facesContext.getViewRoot() == null) ? null : facesContext.getViewRoot().getViewId();
            String toViewId = navigationCase.getToViewId(facesContext);
            // lifecycle is not necessary.
            if (fromViewId == null && toViewId != null) {
                isViewActionProcessingBroadcastAndRequiresRedirect = true;
            } else if (fromViewId != null && !fromViewId.equals(toViewId)) {
                isViewActionProcessingBroadcastAndRequiresRedirect = true;
            }
        }
        if (navigationCase.isRedirect() || isViewActionProcessingBroadcastAndRequiresRedirect) {
            // Need to add the FlowHandler parameters here.
            FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
            List<Flow> activeFlows = FlowHandlerImpl.getActiveFlows(facesContext, flowHandler);
            Flow currentFlow = flowHandler.getCurrentFlow(facesContext);
            Flow targetFlow = calculateTargetFlow(facesContext, outcome, flowHandler, activeFlows, toFlowDocumentId);
            Map<String, List<String>> navigationCaseParameters = navigationCase.getParameters();
            // sourceFlow and targetFlow could both be null so need to have multiple checks here
            if (currentFlow != targetFlow) {
                // Ensure that at least one has a value and check for equality
                if ((currentFlow != null && !currentFlow.equals(targetFlow)) || (targetFlow != null && !targetFlow.equals(currentFlow))) {
                    if (navigationCaseParameters == null) {
                        navigationCaseParameters = new HashMap<>(5, 1f);
                    }
                    // include the following entries:
                    if (currentFlow != null && targetFlow == null) {
                        // Set the TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME parameter
                        navigationCaseParameters.put(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, Arrays.asList(FlowHandler.NULL_FLOW));
                        // Set the FLOW_ID_REQUEST_PARAM_NAME
                        navigationCaseParameters.put(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, Arrays.asList(""));
                    } else {
                        // If current flow (sourceFlow) is null and new flow (targetFlow) is not null,
                        // include the following entries:
                        // If we make it this far we know the above statement is true due to the other
                        // logical checks we have hit to this point.
                        // Set the TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME parameter
                        navigationCaseParameters.put(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, Arrays.asList((toFlowDocumentId == null ? "" : toFlowDocumentId)));
                        // Set the FLOW_ID_REQUEST_PARAM_NAME
                        navigationCaseParameters.put(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, Arrays.asList(targetFlow.getId()));
                    }
                }
            }
            ExternalContext externalContext = facesContext.getExternalContext();
            ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
            String toViewId = navigationCase.getToViewId(facesContext);
            String redirectPath = viewHandler.getRedirectURL(facesContext, toViewId, NavigationUtils.getEvaluatedNavigationParameters(facesContext, navigationCaseParameters), navigationCase.isIncludeViewParams());
            // The spec doesn't say anything about how to handle redirect but it is
            // better to apply the transition here where we have already calculated the
            // route than add the parameters and delegate to
            // FlowHandler.clientWindowTransition(facesContext)
            applyFlowTransition(facesContext, navigationContext);
            // Clear ViewMap if we are redirecting to other resource
            UIViewRoot viewRoot = facesContext.getViewRoot();
            if (viewRoot != null && !toViewId.equals(viewRoot.getViewId())) {
                // call getViewMap(false) to prevent unnecessary map creation
                Map<String, Object> viewMap = viewRoot.getViewMap(false);
                if (viewMap != null) {
                    viewMap.clear();
                }
            }
            // JSF 2.0 the javadoc of handleNavigation() says something like this
            // "...If the view has changed after an application action, call
            // PartialViewContext.setRenderAll(true)...". The effect is that ajax requests
            // are included on navigation.
            PartialViewContext partialViewContext = facesContext.getPartialViewContext();
            String viewId = facesContext.getViewRoot() != null ? facesContext.getViewRoot().getViewId() : null;
            if (partialViewContext.isPartialRequest() && !partialViewContext.isRenderAll() && toViewId != null && !toViewId.equals(viewId)) {
                partialViewContext.setRenderAll(true);
            }
            // Dispose view if the view has been marked as disposable by default action listener
            ViewPoolProcessor processor = ViewPoolProcessor.getInstance(facesContext);
            if (processor != null && processor.isViewPoolEnabledForThisView(facesContext, facesContext.getViewRoot())) {
                processor.disposeView(facesContext, facesContext.getViewRoot());
            }
            // JSF 2.0 Spec call Flash.setRedirect(true) to notify Flash scope and take proper actions
            externalContext.getFlash().setRedirect(true);
            try {
                externalContext.redirect(redirectPath);
                facesContext.responseComplete();
            } catch (IOException e) {
                throw new FacesException(e.getMessage(), e);
            }
        } else {
            ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
            // create new view
            String newViewId = navigationCase.getToViewId(facesContext);
            // JSF 2.0 the javadoc of handleNavigation() says something like this
            // "...If the view has changed after an application action, call
            // PartialViewContext.setRenderAll(true)...". The effect is that ajax requests
            // are included on navigation.
            PartialViewContext partialViewContext = facesContext.getPartialViewContext();
            String viewId = facesContext.getViewRoot() != null ? facesContext.getViewRoot().getViewId() : null;
            if (partialViewContext.isPartialRequest() && !partialViewContext.isRenderAll() && newViewId != null && !newViewId.equals(viewId)) {
                partialViewContext.setRenderAll(true);
            }
            if (facesContext.getViewRoot() != null && facesContext.getViewRoot().getAttributes().containsKey(CALL_PRE_DISPOSE_VIEW)) {
                try {
                    facesContext.getAttributes().put(MyFacesVisitHints.SKIP_ITERATION_HINT, Boolean.TRUE);
                    VisitContext visitContext = VisitContext.createVisitContext(facesContext, null, MyFacesVisitHints.SET_SKIP_ITERATION);
                    facesContext.getViewRoot().visitTree(visitContext, PreDisposeViewCallback.INSTANCE);
                } finally {
                    facesContext.getAttributes().remove(MyFacesVisitHints.SKIP_ITERATION_HINT);
                }
            }
            applyFlowTransition(facesContext, navigationContext);
            // Dispose view if the view has been marked as disposable by default action listener
            ViewPoolProcessor processor = ViewPoolProcessor.getInstance(facesContext);
            if (processor != null && processor.isViewPoolEnabledForThisView(facesContext, facesContext.getViewRoot())) {
                processor.disposeView(facesContext, facesContext.getViewRoot());
            }
            // create UIViewRoot for new view
            UIViewRoot viewRoot = null;
            String derivedViewId = viewHandler.deriveViewId(facesContext, newViewId);
            if (derivedViewId != null) {
                ViewDeclarationLanguage vdl = viewHandler.getViewDeclarationLanguage(facesContext, derivedViewId);
                if (vdl != null) {
                    ViewMetadata metadata = vdl.getViewMetadata(facesContext, newViewId);
                    if (metadata != null) {
                        viewRoot = metadata.createMetadataView(facesContext);
                    }
                }
            }
            // - viewHandler.deriveViewId() returned null
            if (viewRoot == null) {
                viewRoot = viewHandler.createView(facesContext, newViewId);
            }
            facesContext.setViewRoot(viewRoot);
            facesContext.renderResponse();
        }
    } else {
        // no navigationcase found, stay on current ViewRoot
        if (log.isLoggable(Level.FINEST)) {
            log.finest("handleNavigation fromAction=" + fromAction + " outcome=" + outcome + " no matching navigation-case found, staying on current ViewRoot");
        }
    }
}
Also used : ViewHandler(jakarta.faces.application.ViewHandler) VisitContext(jakarta.faces.component.visit.VisitContext) IOException(java.io.IOException) ViewDeclarationLanguage(jakarta.faces.view.ViewDeclarationLanguage) ViewPoolProcessor(org.apache.myfaces.view.facelets.ViewPoolProcessor) FacesException(jakarta.faces.FacesException) Flow(jakarta.faces.flow.Flow) NavigationCase(jakarta.faces.application.NavigationCase) ExternalContext(jakarta.faces.context.ExternalContext) List(java.util.List) ArrayList(java.util.ArrayList) PartialViewContext(jakarta.faces.context.PartialViewContext) FlowHandler(jakarta.faces.flow.FlowHandler) UIViewRoot(jakarta.faces.component.UIViewRoot) ViewMetadata(jakarta.faces.view.ViewMetadata)

Example 3 with NavigationCase

use of jakarta.faces.application.NavigationCase in project myfaces by apache.

the class NavigationHandlerImpl method getOutcomeNavigationCase.

/**
 * Performs the algorithm specified in 7.4.2 for situations where no navigation cases are defined and instead
 * the navigation case is to be determined from the outcome.
 *
 * TODO: cache results?
 */
private NavigationCase getOutcomeNavigationCase(FacesContext facesContext, String fromAction, String outcome) {
    String implicitViewId = null;
    boolean includeViewParams = false;
    int index;
    boolean isRedirect = false;
    String queryString = null;
    NavigationCase result = null;
    String viewId = facesContext.getViewRoot() != null ? facesContext.getViewRoot().getViewId() : null;
    StringBuilder viewIdToTest = SharedStringBuilder.get(facesContext, OUTCOME_NAVIGATION_SB);
    viewIdToTest.append(outcome);
    // If viewIdToTest contains a query string, remove it and set queryString with that value.
    index = viewIdToTest.indexOf("?");
    if (index != -1) {
        queryString = viewIdToTest.substring(index + 1);
        viewIdToTest.setLength(index);
        // If queryString contains "faces-redirect=true", set isRedirect to true.
        if (queryString.contains("faces-redirect=true")) {
            isRedirect = true;
        }
        // "faces-include-view-params=true", set includeViewParams to true.
        if (queryString.contains("includeViewParams=true") || queryString.contains("faces-include-view-params=true")) {
            includeViewParams = true;
        }
    }
    // If viewIdToTest does not have a "file extension", use the one from the current viewId.
    index = viewIdToTest.indexOf(".");
    if (index == -1) {
        if (viewId != null) {
            index = viewId.lastIndexOf('.');
            if (index != -1) {
                viewIdToTest.append(viewId.substring(index));
            }
        } else {
            // This case happens when for for example there is a ViewExpiredException,
            // and a custom ExceptionHandler try to navigate using implicit navigation.
            // In this case, there is no UIViewRoot set on the FacesContext, so viewId
            // is null.
            // In this case, it should try to derive the viewId of the view that was
            // not able to restore, to get the extension and apply it to
            // the implicit navigation.
            String tempViewId = getViewIdSupport().calculateViewId(facesContext);
            if (tempViewId != null) {
                index = tempViewId.lastIndexOf('.');
                if (index != -1) {
                    viewIdToTest.append(tempViewId.substring(index));
                }
            }
        }
        if (log.isLoggable(Level.FINEST)) {
            log.finest("getOutcomeNavigationCase -> viewIdToTest: " + viewIdToTest);
        }
    }
    // If viewIdToTest does not start with "/", look for the last "/" in viewId.  If not found, simply prepend "/".
    // Otherwise, prepend everything before and including the last "/" in viewId.
    boolean startWithSlash = false;
    if (viewIdToTest.length() > 0) {
        startWithSlash = viewIdToTest.charAt(0) == '/';
    }
    if (!startWithSlash) {
        index = -1;
        if (viewId != null) {
            index = viewId.lastIndexOf('/');
        }
        if (index == -1) {
            viewIdToTest.insert(0, '/');
        } else {
            viewIdToTest.insert(0, viewId, 0, index + 1);
        }
    }
    // Apply normalization
    String viewIdToTestString = null;
    boolean applyNormalization = false;
    for (int i = 0; i < viewIdToTest.length() - 1; i++) {
        if (viewIdToTest.charAt(i) == '.' && viewIdToTest.charAt(i + 1) == '/') {
            applyNormalization = true;
            break;
        }
    }
    if (applyNormalization) {
        viewIdToTestString = FilenameUtils.normalize(viewIdToTest.toString(), true);
    } else {
        viewIdToTestString = viewIdToTest.toString();
    }
    // Call ViewHandler.deriveViewId() and set the result as implicitViewId.
    implicitViewId = facesContext.getApplication().getViewHandler().deriveViewId(facesContext, viewIdToTestString);
    if (implicitViewId != null) {
        // Append all params from the queryString
        // (excluding faces-redirect, includeViewParams and faces-include-view-params)
        Map<String, List<String>> params = null;
        if (StringUtils.isNotBlank(queryString)) {
            // "&" or "&amp;"
            String[] splitQueryParams = AMP_PATTERN.split(queryString);
            params = new HashMap<>(splitQueryParams.length, 1f);
            for (String queryParam : splitQueryParams) {
                String[] splitParam = StringUtils.splitShortString(queryParam, '=');
                if (splitParam.length == 2) {
                    // valid parameter - add it to params
                    if ("includeViewParams".equals(splitParam[0]) || "faces-include-view-params".equals(splitParam[0]) || "faces-redirect".equals(splitParam[0])) {
                        // ignore includeViewParams, faces-include-view-params and faces-redirect
                        continue;
                    }
                    List<String> paramValues = params.get(splitParam[0]);
                    if (paramValues == null) {
                        paramValues = new ArrayList<>(5);
                        params.put(splitParam[0], paramValues);
                    }
                    paramValues.add(splitParam[1]);
                } else {
                    // invalid parameter
                    throw new FacesException("Invalid parameter \"" + queryParam + "\" in outcome " + outcome);
                }
            }
        }
        // Finally, create the NavigationCase.
        result = new NavigationCase(viewId, fromAction, outcome, null, implicitViewId, params, isRedirect, includeViewParams);
    }
    return result;
}
Also used : SharedStringBuilder(org.apache.myfaces.core.api.shared.lang.SharedStringBuilder) NavigationCase(jakarta.faces.application.NavigationCase) List(java.util.List) ArrayList(java.util.ArrayList) FacesException(jakarta.faces.FacesException)

Example 4 with NavigationCase

use of jakarta.faces.application.NavigationCase in project myfaces by apache.

the class NavigationHandlerImpl method calcMatchingNavigationCase.

private NavigationCase calcMatchingNavigationCase(FacesContext context, Set<? extends NavigationCase> casesList, String actionRef, String outcome) {
    NavigationCase noConditionCase = null;
    NavigationCase firstCase = null;
    NavigationCase firstCaseIf = null;
    NavigationCase secondCase = null;
    NavigationCase secondCaseIf = null;
    NavigationCase thirdCase = null;
    NavigationCase thirdCaseIf = null;
    NavigationCase fourthCase = null;
    NavigationCase fourthCaseIf = null;
    for (NavigationCase caze : casesList) {
        String cazeOutcome = caze.getFromOutcome();
        String cazeActionRef = caze.getFromAction();
        Boolean cazeIf = caze.getCondition(context);
        boolean ifMatches = cazeIf == null ? false : cazeIf;
        if (outcome == null && (cazeOutcome != null || cazeIf == null) && actionRef == null) {
            // To match an outcome value of null, the <from-outcome> must be absent and the <if> element present.
            continue;
        }
        // If there are no conditions on navigation case save it and return as last resort
        if (cazeOutcome == null && cazeActionRef == null && cazeIf == null && noConditionCase == null && outcome != null) {
            noConditionCase = caze;
        }
        if (cazeActionRef != null) {
            if (cazeOutcome != null) {
                if (actionRef != null && outcome != null && cazeActionRef.equals(actionRef) && cazeOutcome.equals(outcome)) {
                    if (cazeIf != null) {
                        if (ifMatches) {
                            firstCaseIf = caze;
                        // return caze;
                        }
                        continue;
                    } else {
                        firstCase = caze;
                    }
                }
            } else {
                if ((actionRef != null) && cazeActionRef.equals(actionRef)) {
                    // Caveat: if <if> is available, evaluate.  If not, only match if outcome is not null.
                    if (cazeIf != null) {
                        if (ifMatches) {
                            thirdCaseIf = caze;
                        }
                        continue;
                    } else {
                        if (outcome != null) {
                            thirdCase = caze;
                        }
                        continue;
                    }
                } else {
                    // it cazeActionRef match.
                    continue;
                }
            }
        } else {
            if (cazeOutcome != null && (outcome != null) && cazeOutcome.equals(outcome)) {
                if (cazeIf != null) {
                    if (ifMatches) {
                        secondCaseIf = caze;
                    }
                    continue;
                } else {
                    secondCase = caze;
                }
            }
        }
        // Fourth case: anything else matches if outcome is not null or <if> is specified.
        if (outcome != null && cazeIf != null) {
            // Again, if <if> present, evaluate.
            if (ifMatches) {
                fourthCaseIf = caze;
            }
            continue;
        }
        if ((cazeIf != null) && ifMatches) {
            fourthCase = caze;
        }
    }
    if (firstCaseIf != null) {
        return firstCaseIf;
    } else if (firstCase != null) {
        return firstCase;
    } else if (secondCaseIf != null) {
        return secondCaseIf;
    } else if (secondCase != null) {
        return secondCase;
    } else if (thirdCaseIf != null) {
        return thirdCaseIf;
    } else if (thirdCase != null) {
        return thirdCase;
    } else if (fourthCaseIf != null) {
        return fourthCaseIf;
    } else if (fourthCase != null) {
        return fourthCase;
    }
    return noConditionCase;
}
Also used : NavigationCase(jakarta.faces.application.NavigationCase)

Example 5 with NavigationCase

use of jakarta.faces.application.NavigationCase in project myfaces by apache.

the class FlowHandlerImpl method clientWindowTransition.

/**
 * The interpretation done for this issue is this:
 *
 * There are two basic cases: Enter into a flow and return from a flow.
 *
 * - FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME : value of the toFlowDocumentId property
 *   of the navigation case when enter into a flow OR FlowHandler.NULL_FLOW when return from a flow.
 *
 * - FlowHandler.FLOW_ID_REQUEST_PARAM_NAME : value of the fromOutcome property of the navigation case.
 * According to the intention it has multiple options:
 *
 *  1. It can be a flowId, which means enter into a flow.
 *  2. It can be a flow call id, which means enter into a flow.
 *  3. It can be a flow return id, which means return from a flow.
 *
 * - The javadoc of NavigationCase.getToFlowDocumentId() says this:
 * "... If this navigation case represents a flow invocation, this property is the documentId in
 * which the flow whose id is given by the return from getFromOutcome() is defined. Implementations
 * must override this method to return the value defined in the corresponding application
 * configuration resources element. The base implementation returns the empty string. ..."
 *
 * This is consistent with the previous interpretation, but we need to include the case where
 * toFlowDocumentId is FlowHandler.NULL_FLOW too, which is derived implicitly. The key of the trick
 * is override fromOutcome / toFlowDocumentId in the navigation algorithm to indicate when the
 * navigation case is entering into a flow or return from a flow. In that way, it is possible
 * to use ConfigurableNavigationHandler.getNavigationCase(...) to know the "route" using the
 * initial fromOutcome given in FLOW_ID_REQUEST_PARAM_NAME.
 *
 * @param context
 */
@Override
public void clientWindowTransition(FacesContext context) {
    String flowDocumentIdRequestParam = (String) context.getExternalContext().getRequestParameterMap().get(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME);
    if (flowDocumentIdRequestParam != null) {
        String flowIdRequestParam = (String) context.getExternalContext().getRequestParameterMap().get(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME);
        if (flowIdRequestParam == null) {
            // involved.
            return;
        }
        FlowHandler flowHandler = context.getApplication().getFlowHandler();
        ConfigurableNavigationHandler nh = (ConfigurableNavigationHandler) context.getApplication().getNavigationHandler();
        if (FlowHandler.NULL_FLOW.equals(flowDocumentIdRequestParam)) {
            // It is a return node. The trick here is we need to calculate
            // where the flow should return, because that information was not passed
            // in the parameters of the link.
            String toFlowDocumentId = FlowHandler.NULL_FLOW;
            String fromOutcome = flowIdRequestParam;
            // Flow sourceFlow = null;
            List<Flow> sourceFlows = null;
            List<Flow> targetFlows = null;
            boolean failed = false;
            int i = 0;
            while (FlowHandler.NULL_FLOW.equals(toFlowDocumentId) && !failed) {
                Flow currentFlow = flowHandler.getCurrentFlow(context);
                if (currentFlow == null) {
                    failed = true;
                    break;
                }
                String currentLastDisplayedViewId = flowHandler.getLastDisplayedViewId(context);
                FlowNode node = currentFlow.getNode(fromOutcome);
                if (node instanceof ReturnNode) {
                    if (targetFlows == null) {
                        sourceFlows = new ArrayList<Flow>(4);
                        targetFlows = new ArrayList<Flow>(4);
                    }
                    // Get the navigation case using the outcome
                    Flow sourceFlow = currentFlow;
                    flowHandler.pushReturnMode(context);
                    currentFlow = flowHandler.getCurrentFlow(context);
                    i++;
                    NavigationCase navCase = nh.getNavigationCase(context, null, ((ReturnNode) node).getFromOutcome(context), FlowHandler.NULL_FLOW);
                    if (navCase == null) {
                        if (currentLastDisplayedViewId != null) {
                            sourceFlows.add(sourceFlow);
                            if (currentFlow != null) {
                                toFlowDocumentId = currentFlow.getDefiningDocumentId();
                                targetFlows.add(currentFlow);
                            } else {
                                // No active flow
                                toFlowDocumentId = null;
                                targetFlows.add(null);
                            }
                        } else {
                            // Invalid state because no navCase and
                            // no saved lastDisplayedViewId into session
                            failed = true;
                        }
                    } else {
                        if (FlowHandler.NULL_FLOW.equals(navCase.getToFlowDocumentId())) {
                            fromOutcome = navCase.getFromOutcome();
                        } else {
                            sourceFlows.add(sourceFlow);
                            // The absence of FlowHandler.NULL_FLOW means the return went somewhere else.
                            if (currentFlow != null) {
                                toFlowDocumentId = currentFlow.getDefiningDocumentId();
                                targetFlows.add(currentFlow);
                            } else {
                                // No active flow
                                toFlowDocumentId = null;
                                targetFlows.add(null);
                            }
                        }
                    }
                } else {
                    // No return node found in current flow, push it and check
                    // the next flow
                    flowHandler.pushReturnMode(context);
                    currentFlow = flowHandler.getCurrentFlow(context);
                    i++;
                    if (currentFlow == null) {
                        failed = true;
                    }
                }
            }
            for (int j = 0; j < i; j++) {
                flowHandler.popReturnMode(context);
            }
            if (!failed) {
                // Call transitions.
                for (int j = 0; j < targetFlows.size(); j++) {
                    Flow sourceFlow = sourceFlows.get(j);
                    Flow targetFlow = targetFlows.get(j);
                    flowHandler.transition(context, sourceFlow, targetFlow, null, context.getViewRoot().getViewId());
                }
            }
        } else {
            // This transition is for start a new flow. In this case
            // FlowHandler.FLOW_ID_REQUEST_PARAM_NAME could be the flow name to enter
            // or the flow call node to activate.
            // 1. check if is a flow
            Flow targetFlow = flowHandler.getFlow(context, flowDocumentIdRequestParam, flowIdRequestParam);
            Flow currentFlow = null;
            FlowCallNode outboundCallNode = null;
            FlowNode node = null;
            if (targetFlow == null) {
                // Check if is a call flow node
                List<Flow> activeFlows = FlowHandlerImpl.getActiveFlows(context, flowHandler);
                for (Flow activeFlow : activeFlows) {
                    node = activeFlow != null ? activeFlow.getNode(flowIdRequestParam) : null;
                    if (node != null && node instanceof FlowCallNode) {
                        outboundCallNode = (FlowCallNode) node;
                        String calledFlowDocumentId = outboundCallNode.getCalledFlowDocumentId(context);
                        if (calledFlowDocumentId == null) {
                            calledFlowDocumentId = activeFlow.getDefiningDocumentId();
                        }
                        targetFlow = flowHandler.getFlow(context, calledFlowDocumentId, outboundCallNode.getCalledFlowId(context));
                        if (targetFlow == null && !"".equals(calledFlowDocumentId)) {
                            targetFlow = flowHandler.getFlow(context, "", outboundCallNode.getCalledFlowId(context));
                        }
                        if (targetFlow != null) {
                            currentFlow = activeFlow;
                            break;
                        }
                    }
                }
            }
            if (targetFlow != null) {
                if (flowHandler.isActive(context, targetFlow.getDefiningDocumentId(), targetFlow.getId())) {
                    Flow baseReturnFlow = flowHandler.getCurrentFlow();
                    if (!(baseReturnFlow.getDefiningDocumentId().equals(targetFlow.getDefiningDocumentId()) && baseReturnFlow.getId().equals(targetFlow.getId()))) {
                        flowHandler.transition(context, baseReturnFlow, targetFlow, outboundCallNode, context.getViewRoot().getViewId());
                    }
                    flowHandler.pushReturnMode(context);
                    Flow previousFlow = flowHandler.getCurrentFlow(context);
                    flowHandler.popReturnMode(context);
                    flowHandler.transition(context, targetFlow, previousFlow, outboundCallNode, context.getViewRoot().getViewId());
                }
                // Invoke transition
                flowHandler.transition(context, currentFlow, targetFlow, outboundCallNode, context.getViewRoot().getViewId());
                // Handle 2 or more flow consecutive start.
                boolean failed = false;
                String startNodeId = targetFlow.getStartNodeId();
                while (startNodeId != null && !failed) {
                    NavigationCase navCase = nh.getNavigationCase(context, null, startNodeId, targetFlow.getDefiningDocumentId());
                    if (navCase != null && navCase.getToFlowDocumentId() != null) {
                        currentFlow = flowHandler.getCurrentFlow(context);
                        node = currentFlow.getNode(navCase.getFromOutcome());
                        if (node != null && node instanceof FlowCallNode) {
                            outboundCallNode = (FlowCallNode) node;
                            String calledFlowDocumentId = outboundCallNode.getCalledFlowDocumentId(context);
                            if (calledFlowDocumentId == null) {
                                calledFlowDocumentId = currentFlow.getDefiningDocumentId();
                            }
                            targetFlow = flowHandler.getFlow(context, calledFlowDocumentId, outboundCallNode.getCalledFlowId(context));
                            if (targetFlow == null && !"".equals(calledFlowDocumentId)) {
                                targetFlow = flowHandler.getFlow(context, "", outboundCallNode.getCalledFlowId(context));
                            }
                        } else {
                            String calledFlowDocumentId = navCase.getToFlowDocumentId();
                            if (calledFlowDocumentId == null) {
                                calledFlowDocumentId = currentFlow.getDefiningDocumentId();
                            }
                            targetFlow = flowHandler.getFlow(context, calledFlowDocumentId, navCase.getFromOutcome());
                            if (targetFlow == null && !"".equals(calledFlowDocumentId)) {
                                targetFlow = flowHandler.getFlow(context, "", navCase.getFromOutcome());
                            }
                        }
                        if (targetFlow != null) {
                            flowHandler.transition(context, currentFlow, targetFlow, outboundCallNode, context.getViewRoot().getViewId());
                            startNodeId = targetFlow.getStartNodeId();
                        } else {
                            startNodeId = null;
                        }
                    } else {
                        startNodeId = null;
                    }
                }
            }
        }
    }
}
Also used : ReturnNode(jakarta.faces.flow.ReturnNode) NavigationCase(jakarta.faces.application.NavigationCase) FlowHandler(jakarta.faces.flow.FlowHandler) FlowCallNode(jakarta.faces.flow.FlowCallNode) ConfigurableNavigationHandler(jakarta.faces.application.ConfigurableNavigationHandler) Flow(jakarta.faces.flow.Flow) FlowNode(jakarta.faces.flow.FlowNode)

Aggregations

NavigationCase (jakarta.faces.application.NavigationCase)76 Test (org.junit.Test)39 ConfigurableNavigationHandler (jakarta.faces.application.ConfigurableNavigationHandler)25 Flow (jakarta.faces.flow.Flow)22 UICommand (jakarta.faces.component.UICommand)17 HashSet (java.util.HashSet)17 Application (jakarta.faces.application.Application)15 PrintWriter (java.io.PrintWriter)14 ArrayList (java.util.ArrayList)14 TCKNavigationCase (com.sun.ts.tests.jsf.common.navigation.TCKNavigationCase)13 FacesContext (jakarta.faces.context.FacesContext)13 FlowHandler (jakarta.faces.flow.FlowHandler)9 HashMap (java.util.HashMap)8 Set (java.util.Set)8 List (java.util.List)7 MutableNavigationCase (com.sun.faces.flow.builder.MutableNavigationCase)6 Map (java.util.Map)5 LinkedHashSet (java.util.LinkedHashSet)4 FacesException (jakarta.faces.FacesException)3 NavigationHandler (jakarta.faces.application.NavigationHandler)3