public void handleNavigation()

in impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java [121:368]


    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);
                // A redirect is required only if the viewId changes. If the viewId
                // does not change, section 7.4.2 says that a redirect/restart Faces
                // 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();
                
                // Spec: If this navigation is a flow transition (where current flow is not the same as the new flow)
                // 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)))
                    {
                        // MYFACES-4504: check isEmpty()
                        if (navigationCaseParameters == null || navigationCaseParameters.isEmpty())
                        {
                            navigationCaseParameters = new HashMap<>(5, 1f);
                        }
                        
                        // If current flow (sourceFlow) is not null and new flow (targetFlow) is null,
                        // 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();
                    }
                }
                
                // Faces 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());
                }
                
                // Faces 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);

                // Faces 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);
                        }
                    }
                }
                
                // viewRoot can be null here, if ...
                //   - we don't have a ViewDeclarationLanguage (e.g. when using facelets-1.x)
                //   - there is no view metadata or metadata.createMetadataView() returned null
                //   - 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");
            }
        }
    }