Coverage Report - org.apache.myfaces.flow.FlowHandlerImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
FlowHandlerImpl
0%
0/441
0%
0/278
6.966
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.flow;
 20  
 
 21  
 import java.util.ArrayList;
 22  
 import java.util.Collections;
 23  
 import java.util.HashMap;
 24  
 import java.util.List;
 25  
 import java.util.Map;
 26  
 import java.util.concurrent.ConcurrentHashMap;
 27  
 import javax.faces.FacesWrapper;
 28  
 import javax.faces.application.ConfigurableNavigationHandler;
 29  
 import javax.faces.application.NavigationCase;
 30  
 import javax.faces.application.NavigationHandler;
 31  
 import javax.faces.application.NavigationHandlerWrapper;
 32  
 import javax.faces.context.FacesContext;
 33  
 import javax.faces.event.SystemEvent;
 34  
 import javax.faces.event.SystemEventListener;
 35  
 import javax.faces.flow.Flow;
 36  
 import javax.faces.flow.FlowCallNode;
 37  
 import javax.faces.flow.FlowHandler;
 38  
 import javax.faces.flow.FlowNode;
 39  
 import javax.faces.flow.Parameter;
 40  
 import javax.faces.flow.ReturnNode;
 41  
 import javax.faces.lifecycle.ClientWindow;
 42  
 import org.apache.myfaces.event.PostClientWindowAndViewInitializedEvent;
 43  
 import org.apache.myfaces.spi.FacesFlowProvider;
 44  
 import org.apache.myfaces.spi.FacesFlowProviderFactory;
 45  
 
 46  
 /**
 47  
  *
 48  
  * @since 2.2
 49  
  * @author Leonardo Uribe
 50  
  */
 51  
 public class FlowHandlerImpl extends FlowHandler implements SystemEventListener
 52  
 {
 53  
     private final static String CURRENT_FLOW_STACK = "oam.flow.STACK.";
 54  
     private final static String ROOT_LAST_VIEW_ID = "oam.flow.ROOT_LAST_VIEW_ID.";
 55  
     
 56  
     private final static String RETURN_MODE = "oam.flow.RETURN_MODE";
 57  
     private final static String FLOW_RETURN_STACK = "oam.flow.RETURN_STACK.";
 58  
     private final static String CURRENT_FLOW_REQUEST_STACK = "oam.flow.REQUEST_STACK.";
 59  
     
 60  
     private Map<String, Map<String, Flow>> _flowMapByDocumentId;
 61  
     private Map<String, Flow> _flowMapById;
 62  
     
 63  
     private FacesFlowProvider _facesFlowProvider;
 64  
     
 65  
     public FlowHandlerImpl()
 66  0
     {
 67  0
         _flowMapByDocumentId = new ConcurrentHashMap<String, Map<String, Flow>>();
 68  0
         _flowMapById = new ConcurrentHashMap<String, Flow>();
 69  0
     }
 70  
 
 71  
     @Override
 72  
     public Flow getFlow(FacesContext context, String definingDocumentId, String id)
 73  
     {
 74  0
         checkNull(context, "context");
 75  0
         checkNull(definingDocumentId, "definingDocumentId");
 76  0
         checkNull(id, "id");
 77  
         
 78  
         // First try the combination.
 79  0
         Map<String, Flow> flowMap = _flowMapByDocumentId.get(definingDocumentId);
 80  0
         if (flowMap != null)
 81  
         {
 82  0
             Flow flow = flowMap.get(id);
 83  0
             if (flow != null)
 84  
             {
 85  0
                 return flow;
 86  
             }
 87  
         }
 88  
         
 89  
         //if definingDocumentId is an empty string, 
 90  0
         if ("".equals(definingDocumentId))
 91  
         {
 92  0
             return _flowMapById.get(id);
 93  
         }
 94  0
         return null;
 95  
     }
 96  
 
 97  
     @Override
 98  
     public void addFlow(FacesContext context, Flow toAdd)
 99  
     {
 100  0
         checkNull(context, "context");
 101  0
         checkNull(toAdd, "toAdd");
 102  
         
 103  0
         String id = toAdd.getId();
 104  0
         String definingDocumentId = toAdd.getDefiningDocumentId();
 105  
         
 106  0
         if (id == null)
 107  
         {
 108  0
             throw new IllegalArgumentException("Flow must have a non null id");
 109  
         }
 110  0
         else if (id.length() == 0)
 111  
         {
 112  0
             throw new IllegalArgumentException("Flow must have a non empty id");
 113  
         }
 114  0
         if (definingDocumentId == null)
 115  
         {
 116  0
             throw new IllegalArgumentException("Flow must have a non null definingDocumentId");
 117  
         }
 118  
         
 119  0
         Map<String, Flow> flowMap = _flowMapByDocumentId.get(definingDocumentId);
 120  0
         if (flowMap == null)
 121  
         {
 122  0
             flowMap = new ConcurrentHashMap<String, Flow>();
 123  0
             _flowMapByDocumentId.put(definingDocumentId, flowMap);
 124  
         }
 125  0
         flowMap.put(id, toAdd);
 126  
         
 127  0
         Flow duplicateFlow = _flowMapById.get(id);
 128  0
         if (duplicateFlow != null)
 129  
         {
 130  
             // There are two flows with the same flowId.
 131  
             // Raise an exception if flows share a duplicate ID and definingDocumentId
 132  0
             if (toAdd.getDefiningDocumentId().equals(duplicateFlow.getDefiningDocumentId()))
 133  
             {
 134  0
                 throw new IllegalArgumentException("There cannot be multiple flows with both the" 
 135  
                                                    + " same ID and the same definingDocumentId");
 136  
             }
 137  
             // Give priority to the flow with no defining document id
 138  0
             else if ("".equals(toAdd.getDefiningDocumentId()))
 139  
             {
 140  0
                 _flowMapById.put(id, toAdd);
 141  
             }
 142  0
             else if ("".equals(duplicateFlow.getDefiningDocumentId()))
 143  
             {
 144  
                 // Already added, skip
 145  
             }
 146  
             else
 147  
             {
 148  
                 // Put the last one
 149  0
                 _flowMapById.put(id, toAdd);
 150  
             }
 151  
         }
 152  
         else
 153  
         {
 154  0
             _flowMapById.put(id, toAdd);
 155  
         }
 156  
 
 157  
         // Once the flow is added to the map, it is still necessary to 
 158  
         // pass the flow to the ConfigurableNavigationHandler, so it can be
 159  
         // inspected for navigation rules. This is the best place to do that because
 160  
         // the spec says "... Called by the flow system to cause the flow to 
 161  
         // be inspected for navigation rules... " (note it says "flow system" not
 162  
         // "configuration system" where the calls to addFlow() are done).
 163  0
         invokeInspectFlow(context, context.getApplication().getNavigationHandler(), toAdd);
 164  0
     }
 165  
 
 166  
     @Override
 167  
     public Flow getCurrentFlow(FacesContext context)
 168  
     {
 169  0
         Object session = context.getExternalContext().getSession(false);
 170  0
         if (session == null)
 171  
         {
 172  0
             return null;
 173  
         }
 174  0
         ClientWindow clientWindow = context.getExternalContext().getClientWindow();
 175  0
         if (clientWindow == null)
 176  
         {
 177  0
             return null;
 178  
         }
 179  
         
 180  
         
 181  0
         _FlowContextualInfo info = getCurrentFlowReference(context, clientWindow);
 182  0
         if (info == null)
 183  
         {
 184  0
             return null;
 185  
         }
 186  0
         FlowReference flowReference = info.getFlowReference();
 187  0
         return getFlow(context, flowReference.getDocumentId(), flowReference.getId());
 188  
     }
 189  
     
 190  
     @Override
 191  
     public void transition(FacesContext context, Flow sourceFlow, Flow targetFlow, 
 192  
         FlowCallNode outboundCallNode, String toViewId)
 193  
     {
 194  0
         checkNull(context, "context");
 195  0
         checkNull(toViewId, "toViewId");
 196  0
         ClientWindow clientWindow = context.getExternalContext().getClientWindow();
 197  0
         boolean outboundCallNodeProcessed = false;
 198  0
         if (clientWindow == null)
 199  
         {
 200  0
             return;
 201  
         }
 202  
         
 203  0
         if (sourceFlow == null && targetFlow == null)
 204  
         {
 205  0
             return;
 206  
         }
 207  
 
 208  
         // Calculate the parentFlowReference, since it will be used later.
 209  0
         FlowReference parentFlowReference = (outboundCallNode != null && sourceFlow != null) ?
 210  
             new FlowReference(sourceFlow.getDefiningDocumentId(), sourceFlow.getId()) : null;
 211  
         
 212  0
         if (sourceFlow == null)
 213  
         {
 214  
             // Entering a flow
 215  0
             Map<String, Object> outboundParameters = doBeforeEnterFlow(context, 
 216  
                 targetFlow, !outboundCallNodeProcessed ? outboundCallNode : null);
 217  0
             outboundCallNodeProcessed = true;
 218  0
             pushFlowReference(context, clientWindow, 
 219  
                     new FlowReference(targetFlow.getDefiningDocumentId(), targetFlow.getId()), 
 220  
                     toViewId, parentFlowReference);
 221  0
             doAfterEnterFlow(context, targetFlow, outboundParameters);
 222  0
         }
 223  0
         else if (targetFlow == null)
 224  
         {
 225  
             // Getting out of the flow, since targetFlow is null,
 226  
             // we need to take sourceFlow and take it out and all the chain
 227  0
             List<_FlowContextualInfo> currentFlowStack = getCurrentFlowStack(context, clientWindow);
 228  0
             if (currentFlowStack != null)
 229  
             {
 230  0
                 removeFlowFromStack(context, currentFlowStack, sourceFlow);
 231  
             }
 232  0
         }
 233  
         else
 234  
         {
 235  
             // Both sourceFlow and targetFlow are not null, if there is no call node set (force enter flow)
 236  
             // we need to check the direction
 237  
             // If targetFlow is on the stack, remove elements until get there.
 238  
             // If targetFlow is not there, add it to the stack.
 239  0
             List<_FlowContextualInfo> currentFlowStack = getCurrentFlowStack(context, clientWindow);
 240  0
             if (currentFlowStack != null && outboundCallNode == null)
 241  
             {
 242  0
                 FlowReference targetFlowReference = new FlowReference(
 243  
                         targetFlow.getDefiningDocumentId(), targetFlow.getId());
 244  0
                 int targetFlowIndex = -1;
 245  0
                 for (int j = currentFlowStack.size()-1; j >= 0; j--)
 246  
                 {
 247  0
                     if (targetFlowReference.equals(currentFlowStack.get(j).getFlowReference()))
 248  
                     {
 249  0
                         targetFlowIndex = j;
 250  0
                         break;
 251  
                     }
 252  
                 }
 253  0
                 if (targetFlowIndex >= 0)
 254  
                 {
 255  
                     // targetFlow is on the stack, so it is a return.
 256  0
                     removeFlowFromStack(context, currentFlowStack, sourceFlow);
 257  
                 }
 258  
                 else
 259  
                 {
 260  
                     // targetFlow is not on the stack, so it is flow call.
 261  0
                     Map<String, Object> outboundParameters = doBeforeEnterFlow(context,
 262  
                         targetFlow, !outboundCallNodeProcessed ? outboundCallNode : null);
 263  0
                     outboundCallNodeProcessed = true;
 264  0
                     pushFlowReference(context, clientWindow, 
 265  
                             new FlowReference(targetFlow.getDefiningDocumentId(), targetFlow.getId()), toViewId,
 266  
                             parentFlowReference);
 267  0
                     doAfterEnterFlow(context, targetFlow, outboundParameters);
 268  
                 }
 269  0
             }
 270  
             else
 271  
             {
 272  
                 // sourceFlow and targetFlow are not null, but there is no currentFlowStack. It that
 273  
                 // case just enter into targetFlow
 274  0
                 Map<String, Object> outboundParameters = doBeforeEnterFlow(context, 
 275  
                     targetFlow, !outboundCallNodeProcessed ? outboundCallNode : null);
 276  0
                 outboundCallNodeProcessed = true;
 277  0
                 pushFlowReference(context, clientWindow, 
 278  
                         new FlowReference(targetFlow.getDefiningDocumentId(), targetFlow.getId()), toViewId,
 279  
                         parentFlowReference);
 280  0
                 doAfterEnterFlow(context, targetFlow, outboundParameters);
 281  
             }
 282  
         }
 283  0
     }
 284  
     
 285  
     private void removeFlowFromStack(FacesContext context, List<_FlowContextualInfo> currentFlowStack, Flow sourceFlow)
 286  
     {
 287  
         // Steps to remove a flow:
 288  
         // 1. locate where is the flow in the chain
 289  0
         int sourceFlowIndex = -1;
 290  0
         FlowReference sourceFlowReference = new FlowReference(sourceFlow.getDefiningDocumentId(),
 291  
             sourceFlow.getId());
 292  0
         List<_FlowContextualInfo> flowsToRemove = new ArrayList<_FlowContextualInfo>();
 293  0
         for (int i = currentFlowStack.size()-1; i >= 0; i--)
 294  
         {
 295  0
             _FlowContextualInfo fci = currentFlowStack.get(i);
 296  0
             if (fci.getFlowReference().equals(sourceFlowReference))
 297  
             {
 298  0
                 sourceFlowIndex = i;
 299  0
                 flowsToRemove.add(fci);
 300  0
                 break;
 301  
             }
 302  
         }
 303  
 
 304  0
         if (sourceFlowIndex != -1)
 305  
         {
 306  
             // From sourceFlowIndex, remove(add to flowsToRemove list) all flows 
 307  0
             traverseDependantFlows(sourceFlowReference, sourceFlowIndex+1, currentFlowStack, flowsToRemove);
 308  
 
 309  
             // Remove all marked elements
 310  0
             if (!flowsToRemove.isEmpty())
 311  
             {
 312  0
                 for (int i = flowsToRemove.size()-1; i >= 0; i--)
 313  
                 {
 314  0
                     _FlowContextualInfo fci = flowsToRemove.get(i);
 315  0
                     FlowReference fr = fci.getFlowReference();
 316  0
                     doBeforeExitFlow(context, getFlow(context, fr.getDocumentId(), fr.getId()));
 317  
                     //popFlowReference(context, clientWindow, currentFlowStack, i);
 318  
 
 319  
                     //Remove flows from the last to the first to keep the right sequence.
 320  0
                     for (int j = currentFlowStack.size()-1; j >= 0; j--)
 321  
                     {
 322  0
                         if (currentFlowStack.get(j) == fci)
 323  
                         {
 324  0
                             currentFlowStack.remove(j);
 325  0
                             break;
 326  
                         }
 327  
                     }
 328  
                 }
 329  
             }
 330  
 
 331  0
             if (currentFlowStack.isEmpty())
 332  
             {
 333  
                 // Remove it from session but keep it in request scope.
 334  0
                 context.getAttributes().put(ROOT_LAST_VIEW_ID, 
 335  
                     context.getExternalContext().getSessionMap().remove(ROOT_LAST_VIEW_ID + 
 336  
                     context.getExternalContext().getClientWindow().getId()));
 337  
             }
 338  
         }
 339  0
     }
 340  
     
 341  
     private void traverseDependantFlows(FlowReference sourceFlowReference, 
 342  
         int index, List<_FlowContextualInfo> currentFlowStack, List<_FlowContextualInfo> flowsToRemove)
 343  
     {
 344  0
         if (index < currentFlowStack.size())
 345  
         {
 346  0
             for (int i = index; i < currentFlowStack.size(); i++)
 347  
             {
 348  0
                 _FlowContextualInfo info = currentFlowStack.get(i);
 349  0
                 if (sourceFlowReference.equals(info.getSourceFlowReference()) &&
 350  
                     !flowsToRemove.contains(info))
 351  
                 {
 352  0
                     flowsToRemove.add(info);
 353  0
                     traverseDependantFlows(info.getFlowReference(), i+1, currentFlowStack, flowsToRemove);
 354  
                 }
 355  
             }
 356  
         }
 357  0
     }
 358  
     
 359  
     private Map<String, Object> doBeforeEnterFlow(FacesContext context, Flow flow, FlowCallNode outboundCallNode)
 360  
     {
 361  0
         Map<String, Object> outboundParameters = null;
 362  0
         if (outboundCallNode != null && !outboundCallNode.getOutboundParameters().isEmpty())
 363  
         {
 364  0
             outboundParameters = new HashMap<String, Object>();
 365  0
             for (Map.Entry<String, Parameter> entry : outboundCallNode.getOutboundParameters().entrySet())
 366  
             {
 367  0
                 Parameter parameter = entry.getValue();
 368  0
                 if (parameter.getValue() != null)
 369  
                 {
 370  0
                     outboundParameters.put(entry.getKey(), parameter.getValue().getValue(context.getELContext()));
 371  
                 }
 372  0
             }
 373  
         }
 374  0
         return outboundParameters;
 375  
     }
 376  
     
 377  
     private void doAfterEnterFlow(FacesContext context, Flow flow, Map<String, Object> outboundParameters)
 378  
     {
 379  0
         getFacesFlowProvider(context).doAfterEnterFlow(context, flow);
 380  
         
 381  0
         if (outboundParameters != null)
 382  
         {
 383  0
             for (Map.Entry<String, Parameter> entry : flow.getInboundParameters().entrySet())
 384  
             {
 385  0
                 Parameter parameter = entry.getValue();
 386  0
                 if (parameter.getValue() != null && outboundParameters.containsKey(entry.getKey()))
 387  
                 {
 388  0
                     parameter.getValue().setValue(context.getELContext(), outboundParameters.get(entry.getKey()));
 389  
                 }
 390  0
             }
 391  
         }
 392  
 
 393  0
         if (flow.getInitializer() != null)
 394  
         {
 395  0
             flow.getInitializer().invoke(context.getELContext(), null);
 396  
         }
 397  0
     }
 398  
     
 399  
     public FacesFlowProvider getFacesFlowProvider(FacesContext facesContext)
 400  
     {
 401  0
         if (_facesFlowProvider == null)
 402  
         {
 403  0
             FacesFlowProviderFactory factory = 
 404  
                 FacesFlowProviderFactory.getFacesFlowProviderFactory(
 405  
                     facesContext.getExternalContext());
 406  0
             _facesFlowProvider = factory.getFacesFlowProvider(
 407  
                     facesContext.getExternalContext());
 408  
             
 409  0
             facesContext.getApplication().unsubscribeFromEvent(PostClientWindowAndViewInitializedEvent.class, this);
 410  0
             facesContext.getApplication().subscribeToEvent(PostClientWindowAndViewInitializedEvent.class, this);
 411  
         }
 412  0
         return _facesFlowProvider;
 413  
     }
 414  
     
 415  
     private void doBeforeExitFlow(FacesContext context, Flow flow)
 416  
     {
 417  0
         if (flow.getFinalizer() != null)
 418  
         {
 419  0
             flow.getFinalizer().invoke(context.getELContext(), null);
 420  
         }
 421  
         
 422  0
         getFacesFlowProvider(context).doBeforeExitFlow(context, flow);
 423  0
     }
 424  
 
 425  
     @Override
 426  
     public boolean isActive(FacesContext context, String definingDocumentId, String id)
 427  
     {
 428  0
         checkNull(context, "context");
 429  0
         checkNull(definingDocumentId, "definingDocumentId");
 430  0
         checkNull(id, "id");
 431  
         
 432  0
         Object session = context.getExternalContext().getSession(false);
 433  0
         if (session == null)
 434  
         {
 435  0
             return false;
 436  
         }
 437  0
         ClientWindow clientWindow = context.getExternalContext().getClientWindow();
 438  0
         if (clientWindow == null)
 439  
         {
 440  0
             return false;
 441  
         }
 442  0
         Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
 443  0
         String currentFlowMapKey = CURRENT_FLOW_STACK + clientWindow.getId();
 444  
 
 445  0
         List<_FlowContextualInfo> currentFlowStack = (List<_FlowContextualInfo>) sessionMap.get(currentFlowMapKey);
 446  0
         if (currentFlowStack == null)
 447  
         {
 448  0
             return false;
 449  
         }
 450  0
         FlowReference reference = new FlowReference(definingDocumentId, id);
 451  
         
 452  0
         for (_FlowContextualInfo info : currentFlowStack)
 453  
         {
 454  0
             if (reference.equals(info.getFlowReference()))
 455  
             {
 456  0
                 return true;
 457  
             }
 458  0
         }
 459  0
         return false;
 460  
     }
 461  
 
 462  
     @Override
 463  
     public Map<Object, Object> getCurrentFlowScope()
 464  
     {
 465  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 466  0
         return getFacesFlowProvider(facesContext).getCurrentFlowScope(facesContext);
 467  
     }
 468  
 
 469  
     /**
 470  
      * The interpretation done for this issue is this:
 471  
      * 
 472  
      * There are two basic cases: Enter into a flow and return from a flow.
 473  
      * 
 474  
      * - FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME : value of the toFlowDocumentId property 
 475  
      *   of the navigation case when enter into a flow OR FlowHandler.NULL_FLOW when return from a flow.
 476  
      * 
 477  
      * - FlowHandler.FLOW_ID_REQUEST_PARAM_NAME : value of the fromOutcome property of the navigation case.
 478  
      * According to the intention it has multiple options:
 479  
      * 
 480  
      *  1. It can be a flowId, which means enter into a flow.
 481  
      *  2. It can be a flow call id, which means enter into a flow.
 482  
      *  3. It can be a flow return id, which means return from a flow.
 483  
 
 484  
      * - The javadoc of NavigationCase.getToFlowDocumentId() says this:
 485  
      * "... If this navigation case represents a flow invocation, this property is the documentId in 
 486  
      * which the flow whose id is given by the return from getFromOutcome() is defined. Implementations 
 487  
      * must override this method to return the value defined in the corresponding application 
 488  
      * configuration resources element. The base implementation returns the empty string. ..."
 489  
      * 
 490  
      * This is consistent with the previous interpretation, but we need to include the case where 
 491  
      * toFlowDocumentId is FlowHandler.NULL_FLOW too, which is derived implicitly. The key of the trick 
 492  
      * is override fromOutcome / toFlowDocumentId in the navigation algorithm to indicate when the 
 493  
      * navigation case is entering into a flow or return from a flow. In that way, it is possible 
 494  
      * to use ConfigurableNavigationHandler.getNavigationCase(...) to know the "route" using the 
 495  
      * initial fromOutcome given in FLOW_ID_REQUEST_PARAM_NAME.
 496  
      * 
 497  
      * @param context 
 498  
      */
 499  
     @Override
 500  
     public void clientWindowTransition(FacesContext context)
 501  
     {
 502  0
         String flowDocumentIdRequestParam = (String) context.getExternalContext().
 503  
             getRequestParameterMap().get(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME);
 504  
         
 505  0
         if (flowDocumentIdRequestParam != null)
 506  
         {
 507  0
             String flowIdRequestParam = (String) context.getExternalContext().
 508  
                 getRequestParameterMap().get(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME);
 509  
             
 510  0
             if (flowIdRequestParam == null)
 511  
             {
 512  
                 // If we don't have an fromOutcome, it is not possible to calculate the transitions
 513  
                 // involved.
 514  0
                 return;
 515  
             }
 516  
             
 517  0
             FlowHandler flowHandler = context.getApplication().getFlowHandler();
 518  0
             ConfigurableNavigationHandler nh = 
 519  
                 (ConfigurableNavigationHandler) context.getApplication().getNavigationHandler();
 520  
             
 521  0
             if (FlowHandler.NULL_FLOW.equals(flowDocumentIdRequestParam))
 522  
             {
 523  
                 // It is a return node. The trick here is we need to calculate
 524  
                 // where the flow should return, because that information was not passed
 525  
                 // in the parameters of the link. 
 526  0
                 String toFlowDocumentId = FlowHandler.NULL_FLOW;
 527  0
                 String fromOutcome = flowIdRequestParam;
 528  
                 //Flow sourceFlow = null;
 529  0
                 List<Flow> sourceFlows = null;
 530  0
                 List<Flow> targetFlows = null;
 531  
                 
 532  0
                 boolean failed = false;
 533  0
                 int i = 0;
 534  0
                 while (FlowHandler.NULL_FLOW.equals(toFlowDocumentId) && !failed)
 535  
                 {
 536  0
                     Flow currentFlow = flowHandler.getCurrentFlow(context);
 537  0
                     if (currentFlow == null)
 538  
                     {
 539  0
                         failed = true;
 540  0
                         break;
 541  
                     }
 542  0
                     String currentLastDisplayedViewId = flowHandler.getLastDisplayedViewId(context);
 543  0
                     FlowNode node = currentFlow.getNode(fromOutcome);
 544  0
                     if (node instanceof ReturnNode)
 545  
                     {
 546  0
                         if (targetFlows == null)
 547  
                         {
 548  0
                             sourceFlows = new ArrayList<Flow>(4);
 549  0
                             targetFlows = new ArrayList<Flow>(4);
 550  
                         }
 551  
                         // Get the navigation case using the outcome
 552  0
                         Flow sourceFlow = currentFlow;
 553  0
                         flowHandler.pushReturnMode(context);
 554  0
                         currentFlow = flowHandler.getCurrentFlow(context);
 555  0
                         i++;
 556  
                         
 557  0
                         NavigationCase navCase = nh.getNavigationCase(context, null, 
 558  
                             ((ReturnNode) node).getFromOutcome(context), FlowHandler.NULL_FLOW);
 559  
 
 560  0
                         if (navCase == null)
 561  
                         {
 562  0
                             if (currentLastDisplayedViewId != null)
 563  
                             {
 564  0
                                 sourceFlows.add(sourceFlow);
 565  0
                                 if (currentFlow != null)
 566  
                                 {
 567  0
                                     toFlowDocumentId = currentFlow.getDefiningDocumentId();
 568  0
                                     targetFlows.add(currentFlow);
 569  
                                 }
 570  
                                 else
 571  
                                 {
 572  
                                     // No active flow
 573  0
                                     toFlowDocumentId = null;
 574  0
                                     targetFlows.add(null);
 575  
                                 }
 576  
                             }
 577  
                             else
 578  
                             {
 579  
                                 // Invalid state because no navCase and 
 580  
                                 // no saved lastDisplayedViewId into session
 581  0
                                 failed = true;
 582  
                             }
 583  
                         }
 584  
                         else
 585  
                         {
 586  0
                             if (FlowHandler.NULL_FLOW.equals(navCase.getToFlowDocumentId()))
 587  
                             {
 588  0
                                 fromOutcome = navCase.getFromOutcome();
 589  
                             }
 590  
                             else
 591  
                             {
 592  0
                                 sourceFlows.add(sourceFlow);
 593  
                                 // The absence of FlowHandler.NULL_FLOW means the return went somewhere else.
 594  0
                                 if (currentFlow != null)
 595  
                                 {
 596  0
                                     toFlowDocumentId = currentFlow.getDefiningDocumentId();
 597  0
                                     targetFlows.add(currentFlow);
 598  
                                 }
 599  
                                 else
 600  
                                 {
 601  
                                     // No active flow
 602  0
                                     toFlowDocumentId = null;
 603  0
                                     targetFlows.add(null);
 604  
                                 }
 605  
                             }
 606  
                         }
 607  0
                     }
 608  
                     else
 609  
                     {
 610  
                         // No return node found in current flow, push it and check 
 611  
                         // the next flow
 612  0
                         flowHandler.pushReturnMode(context);
 613  0
                         currentFlow = flowHandler.getCurrentFlow(context);
 614  0
                         i++;
 615  0
                         if (currentFlow == null)
 616  
                         {
 617  0
                             failed = true;
 618  
                         }
 619  
                     }
 620  0
                 }
 621  0
                 for (int j = 0; j<i; j++)
 622  
                 {
 623  0
                     flowHandler.popReturnMode(context);
 624  
                 }
 625  0
                 if (!failed)
 626  
                 {
 627  
                     //Call transitions.
 628  0
                     for (int j = 0; j < targetFlows.size(); j++)
 629  
                     {
 630  0
                         Flow sourceFlow = sourceFlows.get(j);
 631  0
                         Flow targetFlow = targetFlows.get(j);
 632  0
                         flowHandler.transition(context, 
 633  
                             sourceFlow,
 634  
                             targetFlow, null, context.getViewRoot().getViewId());
 635  
                         
 636  
                     }
 637  
                 }
 638  0
             }
 639  
             else
 640  
             {
 641  
                 // This transition is for start a new flow. In this case 
 642  
                 // FlowHandler.FLOW_ID_REQUEST_PARAM_NAME could be the flow name to enter
 643  
                 // or the flow call node to activate.
 644  
                 // 1. check if is a flow
 645  0
                 Flow targetFlow = flowHandler.getFlow(context, flowDocumentIdRequestParam, flowIdRequestParam);
 646  0
                 Flow currentFlow = null;
 647  0
                 FlowCallNode outboundCallNode = null;
 648  0
                 FlowNode node = null;
 649  0
                 if (targetFlow == null)
 650  
                 {
 651  
                     //Check if is a call flow node
 652  0
                     List<Flow> activeFlows = FlowHandlerImpl.getActiveFlows(context, flowHandler);
 653  0
                     for (Flow activeFlow : activeFlows)
 654  
                     {
 655  0
                         node = activeFlow != null ? activeFlow.getNode(flowIdRequestParam) : null;
 656  0
                         if (node != null && node instanceof FlowCallNode)
 657  
                         {
 658  0
                             outboundCallNode = (FlowCallNode) node;
 659  
 
 660  0
                             String calledFlowDocumentId = outboundCallNode.getCalledFlowDocumentId(context);
 661  0
                             if (calledFlowDocumentId == null)
 662  
                             {
 663  0
                                 calledFlowDocumentId = activeFlow.getDefiningDocumentId();
 664  
                             }
 665  0
                             targetFlow = flowHandler.getFlow(context, 
 666  
                                 calledFlowDocumentId, 
 667  
                                 outboundCallNode.getCalledFlowId(context));
 668  0
                             if (targetFlow == null && !"".equals(calledFlowDocumentId))
 669  
                             {
 670  0
                                 targetFlow = flowHandler.getFlow(context, "", 
 671  
                                     outboundCallNode.getCalledFlowId(context));
 672  
                             }
 673  0
                             if (targetFlow != null)
 674  
                             {
 675  0
                                 currentFlow = activeFlow;
 676  0
                                 break;
 677  
                             }
 678  
                         }
 679  0
                     }
 680  
                 }
 681  
                 
 682  0
                 if (targetFlow != null)
 683  
                 {
 684  0
                     if (flowHandler.isActive(context, targetFlow.getDefiningDocumentId(), targetFlow.getId()))
 685  
                     {
 686  0
                         Flow baseReturnFlow = flowHandler.getCurrentFlow();
 687  0
                         if (!(baseReturnFlow.getDefiningDocumentId().equals(targetFlow.getDefiningDocumentId()) &&
 688  
                              baseReturnFlow.getId().equals(targetFlow.getId())))
 689  
                         {
 690  0
                             flowHandler.transition(context, 
 691  
                                 baseReturnFlow, targetFlow, outboundCallNode, context.getViewRoot().getViewId());
 692  
                         }
 693  0
                         flowHandler.pushReturnMode(context);
 694  0
                         Flow previousFlow = flowHandler.getCurrentFlow(context);
 695  0
                         flowHandler.popReturnMode(context);
 696  0
                         flowHandler.transition(context, 
 697  
                                 targetFlow, previousFlow, outboundCallNode, context.getViewRoot().getViewId());
 698  
                     }
 699  
                     // Invoke transition
 700  0
                     flowHandler.transition(context, 
 701  
                         currentFlow, targetFlow, outboundCallNode, context.getViewRoot().getViewId());
 702  
 
 703  
                     // Handle 2 or more flow consecutive start.
 704  0
                     boolean failed = false;
 705  
                     
 706  0
                     String startNodeId = targetFlow.getStartNodeId();
 707  0
                     while (startNodeId != null && !failed)
 708  
                     {
 709  0
                         NavigationCase navCase = nh.getNavigationCase(context, null, 
 710  
                                     startNodeId, targetFlow.getDefiningDocumentId());
 711  
                         
 712  0
                         if (navCase != null && navCase.getToFlowDocumentId() != null)
 713  
                         {
 714  0
                             currentFlow = flowHandler.getCurrentFlow(context);
 715  0
                             node = currentFlow.getNode(navCase.getFromOutcome());
 716  0
                             if (node != null && node instanceof FlowCallNode)
 717  
                             {
 718  0
                                 outboundCallNode = (FlowCallNode) node;
 719  
                                 
 720  0
                                 String calledFlowDocumentId = outboundCallNode.getCalledFlowDocumentId(context);
 721  0
                                 if (calledFlowDocumentId == null)
 722  
                                 {
 723  0
                                     calledFlowDocumentId = currentFlow.getDefiningDocumentId();
 724  
                                 }
 725  0
                                 targetFlow = flowHandler.getFlow(context, 
 726  
                                     calledFlowDocumentId, 
 727  
                                     outboundCallNode.getCalledFlowId(context));
 728  0
                                 if (targetFlow == null && !"".equals(calledFlowDocumentId))
 729  
                                 {
 730  0
                                     targetFlow = flowHandler.getFlow(context, "", 
 731  
                                         outboundCallNode.getCalledFlowId(context));
 732  
                                 }
 733  0
                             }
 734  
                             else
 735  
                             {
 736  0
                                 String calledFlowDocumentId = navCase.getToFlowDocumentId();
 737  0
                                 if (calledFlowDocumentId == null)
 738  
                                 {
 739  0
                                     calledFlowDocumentId = currentFlow.getDefiningDocumentId();
 740  
                                 }
 741  0
                                 targetFlow = flowHandler.getFlow(context, 
 742  
                                     calledFlowDocumentId, 
 743  
                                     navCase.getFromOutcome());
 744  0
                                 if (targetFlow == null && !"".equals(calledFlowDocumentId))
 745  
                                 {
 746  0
                                     targetFlow = flowHandler.getFlow(context, "", 
 747  
                                         navCase.getFromOutcome());
 748  
                                 }
 749  
                             }
 750  0
                             if (targetFlow != null)
 751  
                             {
 752  0
                                 flowHandler.transition(context, 
 753  
                                     currentFlow, targetFlow, outboundCallNode, context.getViewRoot().getViewId());
 754  0
                                 startNodeId = targetFlow.getStartNodeId();
 755  
                             }
 756  
                             else
 757  
                             {
 758  0
                                 startNodeId = null;
 759  
                             }
 760  
                         }
 761  
                         else
 762  
                         {
 763  0
                             startNodeId = null;
 764  
                         }
 765  0
                     }
 766  
                 }
 767  
                 
 768  
             }
 769  
         }
 770  0
     }
 771  
     
 772  
     private void checkNull(final Object o, final String param)
 773  
     {
 774  0
         if (o == null)
 775  
         {
 776  0
             throw new NullPointerException(param + " can not be null.");
 777  
         }
 778  0
     }
 779  
     
 780  
     private void invokeInspectFlow(FacesContext context, NavigationHandler navHandler, Flow toAdd)
 781  
     {
 782  0
         if (navHandler instanceof ConfigurableNavigationHandler)
 783  
         {
 784  0
             ((ConfigurableNavigationHandler)navHandler).inspectFlow(context, toAdd);
 785  
         }
 786  0
         else if (navHandler instanceof NavigationHandlerWrapper)
 787  
         {
 788  0
             invokeInspectFlow(context, ((NavigationHandlerWrapper)navHandler).getWrapped(), toAdd);
 789  
         }
 790  0
     }
 791  
     
 792  
     private _FlowContextualInfo getCurrentFlowReference(FacesContext context, ClientWindow clientWindow)
 793  
     {
 794  0
         if ( Boolean.TRUE.equals(context.getAttributes().get(RETURN_MODE)) )
 795  
         {
 796  0
             List<_FlowContextualInfo> returnFlowList = getCurrentReturnModeFlowStack(
 797  
                     context, clientWindow, CURRENT_FLOW_REQUEST_STACK);
 798  0
             if (returnFlowList != null && !returnFlowList.isEmpty())
 799  
             {
 800  0
                 _FlowContextualInfo info = returnFlowList.get(returnFlowList.size()-1);
 801  0
                 return info;
 802  
             }
 803  0
             return null;
 804  
         }
 805  
         else
 806  
         {
 807  0
             Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
 808  0
             String currentFlowMapKey = CURRENT_FLOW_STACK + clientWindow.getId();
 809  0
             List<_FlowContextualInfo> currentFlowStack = 
 810  
                 (List<_FlowContextualInfo>) sessionMap.get(currentFlowMapKey);
 811  0
             if (currentFlowStack == null)
 812  
             {
 813  0
                 return null;
 814  
             }
 815  0
             return currentFlowStack.size() > 0 ? 
 816  
                 currentFlowStack.get(currentFlowStack.size()-1) : null;
 817  
         }
 818  
     }
 819  
     
 820  
     private void pushFlowReference(FacesContext context, ClientWindow clientWindow, FlowReference flowReference,
 821  
         String toViewId, FlowReference sourceFlowReference)
 822  
     {
 823  0
         Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
 824  0
         String currentFlowMapKey = CURRENT_FLOW_STACK + clientWindow.getId();
 825  0
         List<_FlowContextualInfo> currentFlowStack = (List<_FlowContextualInfo>) sessionMap.get(currentFlowMapKey);
 826  0
         if (currentFlowStack == null)
 827  
         {
 828  0
             currentFlowStack = new ArrayList<_FlowContextualInfo>(4);
 829  0
             sessionMap.put(currentFlowMapKey, currentFlowStack);
 830  
         }
 831  0
         if (!currentFlowStack.isEmpty())
 832  
         {
 833  0
             currentFlowStack.get(currentFlowStack.size()-1).setLastDisplayedViewId(context.getViewRoot().getViewId());
 834  
         }
 835  
         else
 836  
         {
 837  
             //Save root lastDisplayedViewId
 838  0
             context.getExternalContext().getSessionMap().put(ROOT_LAST_VIEW_ID + clientWindow.getId(), 
 839  
                 context.getViewRoot().getViewId());
 840  
         }
 841  0
         currentFlowStack.add(new _FlowContextualInfo(flowReference, toViewId, sourceFlowReference));
 842  0
     }
 843  
     
 844  
     private List<_FlowContextualInfo> getCurrentFlowStack(FacesContext context, ClientWindow clientWindow)
 845  
     {
 846  0
         Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
 847  0
         String currentFlowMapKey = CURRENT_FLOW_STACK + clientWindow.getId();
 848  0
         List<_FlowContextualInfo> currentFlowStack = (List<_FlowContextualInfo>) sessionMap.get(currentFlowMapKey);
 849  0
         return currentFlowStack;
 850  
     }
 851  
 
 852  
     @Override
 853  
     public String getLastDisplayedViewId(FacesContext context)
 854  
     {
 855  0
         Object session = context.getExternalContext().getSession(false);
 856  0
         if (session == null)
 857  
         {
 858  0
             return null;
 859  
         }
 860  0
         ClientWindow clientWindow = context.getExternalContext().getClientWindow();
 861  0
         if (clientWindow == null)
 862  
         {
 863  0
             return null;
 864  
         }
 865  
         
 866  0
         _FlowContextualInfo info = getCurrentFlowReference(context, clientWindow);
 867  0
         if (info == null)
 868  
         {
 869  0
             String lastDisplayedViewId = (String) context.getAttributes().get(ROOT_LAST_VIEW_ID);
 870  0
             if (lastDisplayedViewId == null)
 871  
             {
 872  0
                 lastDisplayedViewId = (String) context.getExternalContext().getSessionMap().
 873  
                     get(ROOT_LAST_VIEW_ID + clientWindow.getId());
 874  
             }
 875  0
             return lastDisplayedViewId;
 876  
         }
 877  0
         return info.getLastDisplayedViewId();
 878  
     }
 879  
 
 880  
     @Override
 881  
     public void pushReturnMode(FacesContext context)
 882  
     {
 883  
         // The return mode is a way to allow NavigationHandler to know the context
 884  
         // without expose it. The idea is call pushReturnMode()/popReturnMode() and
 885  
         // then check for getCurrentFlow(). 
 886  
         //
 887  
         // Remember the navigation algorithm is split in two parts:
 888  
         // - Calculates the navigation
 889  
         // - Perform the navigation
 890  
         //
 891  
         // Generated links requires only to perform the first one, but the operations
 892  
         // are only perfomed when the transition between pages occur or in a get request
 893  
         // when there is a pending navigation. 
 894  0
         ClientWindow clientWindow = context.getExternalContext().getClientWindow();
 895  
         
 896  0
         if (clientWindow == null)
 897  
         {
 898  0
             return;
 899  
         }
 900  
         
 901  0
         if ( !Boolean.TRUE.equals(context.getAttributes().get(RETURN_MODE)) )
 902  
         {
 903  
             // Return mode not active, activate it, copy the current flow stack.
 904  0
             List<_FlowContextualInfo> currentFlowStack = getCurrentFlowStack(context, clientWindow);
 905  
             
 906  0
             Map<Object, Object> attributesMap = context.getAttributes();
 907  0
             String returnFlowMapKey = CURRENT_FLOW_REQUEST_STACK + clientWindow.getId();
 908  0
             List<_FlowContextualInfo> returnFlowStack = new ArrayList<_FlowContextualInfo>(currentFlowStack);
 909  0
             attributesMap.put(returnFlowMapKey, returnFlowStack);
 910  0
             context.getAttributes().put(RETURN_MODE, Boolean.TRUE);
 911  
         }
 912  
         
 913  0
         _FlowContextualInfo flowReference = popFlowReferenceReturnMode(context, 
 914  
             clientWindow, CURRENT_FLOW_REQUEST_STACK);
 915  0
         pushFlowReferenceReturnMode(context, clientWindow, FLOW_RETURN_STACK, flowReference);
 916  0
     }
 917  
 
 918  
     @Override
 919  
     public void popReturnMode(FacesContext context)
 920  
     {
 921  0
         ClientWindow clientWindow = context.getExternalContext().getClientWindow();
 922  
         
 923  0
         if (clientWindow == null)
 924  
         {
 925  0
             return;
 926  
         }
 927  
         
 928  0
         _FlowContextualInfo flowReference = popFlowReferenceReturnMode(context, clientWindow, FLOW_RETURN_STACK);
 929  0
         pushFlowReferenceReturnMode(context, clientWindow, CURRENT_FLOW_REQUEST_STACK, flowReference);
 930  
         
 931  0
         Map<Object, Object> attributesMap = context.getAttributes();
 932  0
         String returnFlowMapKey = FLOW_RETURN_STACK + clientWindow.getId();
 933  0
         List<_FlowContextualInfo> returnFlowStack = (List<_FlowContextualInfo>) attributesMap.get(returnFlowMapKey);
 934  0
         if (returnFlowStack != null && returnFlowStack.isEmpty())
 935  
         {
 936  0
             context.getAttributes().put(RETURN_MODE, Boolean.FALSE);
 937  
         }
 938  0
     }
 939  
     
 940  
     public List<Flow> getActiveFlows(FacesContext context)
 941  
     {
 942  0
         Object session = context.getExternalContext().getSession(false);
 943  0
         if (session == null)
 944  
         {
 945  0
             return Collections.emptyList();
 946  
         }
 947  0
         ClientWindow clientWindow = context.getExternalContext().getClientWindow();
 948  0
         if (clientWindow == null)
 949  
         {
 950  0
             return Collections.emptyList();
 951  
         }
 952  0
         if ( Boolean.TRUE.equals(context.getAttributes().get(RETURN_MODE)) )
 953  
         {
 954  
             // Use the standard form
 955  0
             FlowHandler fh = context.getApplication().getFlowHandler();
 956  0
             Flow curFlow = fh.getCurrentFlow(context);
 957  0
             if (curFlow != null)
 958  
             {
 959  0
                 List<Flow> activeFlows = new ArrayList<Flow>();
 960  0
                 while (curFlow != null)
 961  
                 {
 962  0
                     activeFlows.add(curFlow);
 963  0
                     fh.pushReturnMode(context);
 964  0
                     curFlow = fh.getCurrentFlow(context);
 965  
                 }
 966  
 
 967  0
                 for (int i = 0; i < activeFlows.size(); i++)
 968  
                 {
 969  0
                     fh.popReturnMode(context);
 970  
                 }
 971  0
                 return activeFlows;
 972  
             }
 973  
             else
 974  
             {
 975  0
                 return Collections.emptyList();
 976  
             }
 977  
         }
 978  
         else
 979  
         {
 980  0
             Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
 981  0
             String currentFlowMapKey = CURRENT_FLOW_STACK + clientWindow.getId();
 982  
 
 983  0
             List<_FlowContextualInfo> currentFlowStack = (List<_FlowContextualInfo>) sessionMap.get(currentFlowMapKey);
 984  0
             if (currentFlowStack == null)
 985  
             {
 986  0
                 return Collections.emptyList();
 987  
             }
 988  
 
 989  0
             if (!currentFlowStack.isEmpty())
 990  
             {
 991  0
                 List<Flow> activeFlows = new ArrayList<Flow>();
 992  0
                 for(_FlowContextualInfo info : currentFlowStack)
 993  
                 {
 994  0
                     activeFlows.add(0, getFlow(context, 
 995  
                         info.getFlowReference().getDocumentId(), 
 996  
                         info.getFlowReference().getId()));
 997  0
                 }
 998  0
                 return activeFlows;
 999  
             }
 1000  
 
 1001  0
             return Collections.emptyList();
 1002  
         }
 1003  
     }
 1004  
 
 1005  
     private void pushFlowReferenceReturnMode(FacesContext context, ClientWindow clientWindow,
 1006  
             String stackKey, _FlowContextualInfo flowReference)
 1007  
     {
 1008  0
         Map<Object, Object> attributesMap = context.getAttributes();
 1009  0
         String currentFlowMapKey = stackKey + clientWindow.getId();
 1010  0
         List<_FlowContextualInfo> currentFlowStack = (List<_FlowContextualInfo>) attributesMap.get(currentFlowMapKey);
 1011  0
         if (currentFlowStack == null)
 1012  
         {
 1013  0
             currentFlowStack = new ArrayList<_FlowContextualInfo>(4);
 1014  0
             attributesMap.put(currentFlowMapKey, currentFlowStack);
 1015  
         }
 1016  0
         currentFlowStack.add(flowReference);
 1017  0
     }
 1018  
 
 1019  
     private _FlowContextualInfo popFlowReferenceReturnMode(FacesContext context, ClientWindow clientWindow,
 1020  
             String stackKey)
 1021  
     {
 1022  0
         Map<Object, Object> attributesMap = context.getAttributes();
 1023  0
         String currentFlowMapKey = stackKey + clientWindow.getId();
 1024  0
         List<_FlowContextualInfo> currentFlowStack = (List<_FlowContextualInfo>) attributesMap.get(currentFlowMapKey);
 1025  0
         if (currentFlowStack == null)
 1026  
         {
 1027  0
             return null;
 1028  
         }
 1029  0
         return currentFlowStack.size() > 0 ? currentFlowStack.remove(currentFlowStack.size()-1) : null;
 1030  
     }
 1031  
     
 1032  
     private List<_FlowContextualInfo> getCurrentReturnModeFlowStack(FacesContext context, ClientWindow clientWindow,
 1033  
             String stackKey)
 1034  
     {
 1035  0
         Map<Object, Object> attributesMap = context.getAttributes();
 1036  0
         String currentFlowMapKey = stackKey + clientWindow.getId();
 1037  0
         List<_FlowContextualInfo> currentFlowStack = (List<_FlowContextualInfo>) attributesMap.get(currentFlowMapKey);
 1038  0
         return currentFlowStack;
 1039  
     }
 1040  
     
 1041  
     public static List<Flow> getActiveFlows(FacesContext facesContext, FlowHandler fh)
 1042  
     {
 1043  0
         FlowHandler flowHandler = fh;
 1044  0
         while (flowHandler != null)
 1045  
         {
 1046  0
             if (flowHandler instanceof FlowHandlerImpl)
 1047  
             {
 1048  0
                 break;
 1049  
             }
 1050  0
             else if (flowHandler instanceof FacesWrapper)
 1051  
             {
 1052  0
                 flowHandler = ((FacesWrapper<FlowHandler>)flowHandler).getWrapped();
 1053  
             }
 1054  
             else
 1055  
             {
 1056  0
                 flowHandler = null;
 1057  
             }
 1058  
         }
 1059  0
         if (flowHandler == null)
 1060  
         {
 1061  
             // Use the standard form
 1062  0
             Flow curFlow = fh.getCurrentFlow(facesContext);
 1063  0
             if (curFlow != null)
 1064  
             {
 1065  0
                 List<Flow> activeFlows = new ArrayList<Flow>();
 1066  0
                 while (curFlow != null)
 1067  
                 {
 1068  0
                     activeFlows.add(curFlow);
 1069  0
                     fh.pushReturnMode(facesContext);
 1070  0
                     curFlow = fh.getCurrentFlow(facesContext);
 1071  
                 }
 1072  
 
 1073  0
                 for (int i = 0; i < activeFlows.size(); i++)
 1074  
                 {
 1075  0
                     fh.popReturnMode(facesContext);
 1076  
                 }
 1077  0
                 return activeFlows;
 1078  
             }
 1079  
             else
 1080  
             {
 1081  0
                 return Collections.emptyList();
 1082  
             }
 1083  
         }
 1084  
         else
 1085  
         {
 1086  0
             FlowHandlerImpl flowHandlerImpl = (FlowHandlerImpl) flowHandler;
 1087  0
             return flowHandlerImpl.getActiveFlows(facesContext);
 1088  
         }
 1089  
     }
 1090  
 
 1091  
     @Override
 1092  
     public boolean isListenerForSource(Object source)
 1093  
     {
 1094  0
         return source instanceof ClientWindow;
 1095  
     }
 1096  
 
 1097  
     @Override
 1098  
     public void processEvent(SystemEvent event)
 1099  
     {
 1100  
         // refresh client window to faces flow provider
 1101  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 1102  0
         FacesFlowProvider provider = getFacesFlowProvider(facesContext);
 1103  0
         provider.refreshClientWindow(facesContext);
 1104  0
     }
 1105  
 
 1106  
 }