docs: 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;
          //String viewIdToTest = outcome;
          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 = viewIdToTest.substring (0, index);
              viewIdToTest.setLength(index);
              
              // If queryString contains "faces-redirect=true", set isRedirect to true.
              if (queryString.indexOf ("faces-redirect=true") != -1)
              {
                  isRedirect = true;
              }
              
              // If queryString contains "includeViewParams=true" or 
              // "faces-include-view-params=true", set includeViewParams to true.
              if (queryString.indexOf("includeViewParams=true") != -1 
                      || queryString.indexOf("faces-include-view-params=true") != -1)
              {
                  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 += viewId.substring (index);
                      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 = getNavigationHandlerSupport().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.
          
          //if (!viewIdToTest.startsWith ("/") && viewId != null)
          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 = "/" + viewIdToTest;
                  viewIdToTest.insert(0,"/");
              }
              
              else
              {
                  //viewIdToTest = viewId.substring (0, index + 1) + viewIdToTest;
                  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.
          try
          {
              implicitViewId = facesContext.getApplication().getViewHandler().deriveViewId (
                      facesContext, viewIdToTestString);
          }
          
          catch (UnsupportedOperationException e)
          {
              // This is the case when a pre-JSF 2.0 ViewHandler is used.
              // In this case, the default algorithm must be used.
              // FIXME: I think we're always calling the "default" ViewHandler.deriveViewId() algorithm and we don't
              // distinguish between pre-JSF 2.0 and JSF 2.0 ViewHandlers.  This probably needs to be addressed.
          }
          
          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 (queryString != null && !"".equals(queryString))
              {
                  //String[] splitQueryParams = queryString.split("&(amp;)?"); // "&" or "&amp;"
                  String[] splitQueryParams = AMP_PATTERN.split(queryString); // "&" or "&amp;"
                  params = new HashMap<String, List<String>>(splitQueryParams.length, 
                          (splitQueryParams.length* 4 + 3) / 3);
                  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)
                          {
                              // no value for the given parameter yet
                              paramValues = new ArrayList<String>();
                              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;
      }