You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2013/09/03 16:49:39 UTC
svn commit: r1519698 - in /myfaces/core/trunk:
impl/src/main/java/org/apache/myfaces/application/
impl/src/main/java/org/apache/myfaces/flow/
impl/src/main/java/org/apache/myfaces/lifecycle/
impl/src/test/java/org/apache/myfaces/application/flow/ impl/...
Author: lu4242
Date: Tue Sep 3 14:49:39 2013
New Revision: 1519698
URL: http://svn.apache.org/r1519698
Log:
MYFACES-3765 Implement FlowHandler.clientWindowTransition(FacesContext context) and MYFACES-3764 Implement FlowHandler.getLastDisplayedViewId(FacesContext context) logic (fix algorithm)
Added:
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/FlowNavigationCase.java (with props)
myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/util/OutcomeTargetUtils.java (with props)
Modified:
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/FlowHandlerImpl.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/lifecycle/RestoreViewExecutor.java
myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java
myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/lifecycle/RestoreViewExecutorTest.java
myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/WEB-INF/flow1-flow.xml
myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/JSFAttr.java
myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRendererUtils.java
Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/FlowNavigationCase.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/FlowNavigationCase.java?rev=1519698&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/FlowNavigationCase.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/FlowNavigationCase.java Tue Sep 3 14:49:39 2013
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.application;
+
+import javax.faces.application.NavigationCase;
+import javax.faces.application.NavigationCaseWrapper;
+
+/**
+ * Wrapper that helps overriding toFlowDocumentId and fromOutcome, to build
+ * correctly a navigation case that cause a flow action (enter into a flow
+ * or return from a flow).
+ *
+ * The idea is if is necessary to enter into a flow set fromOutcome as the
+ * flow id and toFlowDocumentId as the flow document id. If it is a return,
+ * set fromOutcome as the return node and toFlowDocumentId as FlowHandler.NULL_FLOW
+ *
+ * @author Leonardo Uribe
+ */
+public class FlowNavigationCase extends NavigationCaseWrapper
+{
+
+ private NavigationCase _delegate;
+ private String _fromOutcome;
+ private String _toFlowDocumentId;
+
+ public FlowNavigationCase(NavigationCase delegate, String fromOutcome, String toFlowDocumentId)
+ {
+ this._delegate = delegate;
+ this._fromOutcome = fromOutcome;
+ this._toFlowDocumentId = toFlowDocumentId;
+ }
+
+ @Override
+ public NavigationCase getWrapped()
+ {
+ return _delegate;
+ }
+
+ @Override
+ public String getFromOutcome()
+ {
+ return _fromOutcome;
+ }
+
+ @Override
+ public String getToFlowDocumentId()
+ {
+ return _toFlowDocumentId;
+ }
+}
Propchange: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/FlowNavigationCase.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java?rev=1519698&r1=1519697&r2=1519698&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java Tue Sep 3 14:49:39 2013
@@ -216,7 +216,7 @@ public class NavigationHandlerImpl
ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
//create new view
String newViewId = navigationCase.getToViewId(facesContext);
- String lastDisplayedViewId = null;
+
// 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
@@ -233,8 +233,6 @@ public class NavigationHandlerImpl
if (facesContext.getViewRoot() != null)
{
- lastDisplayedViewId = facesContext.getViewRoot().getViewId();
-
if (facesContext.getViewRoot().getAttributes().containsKey("oam.CALL_PRE_DISPOSE_VIEW"))
{
facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
@@ -257,9 +255,15 @@ public class NavigationHandlerImpl
for (int i = 0; i < navigationContext.getTargetFlows().size(); i++)
{
Flow targetFlow = navigationContext.getTargetFlows().get(i);
+ // TODO: Here there is an inconsistency with the spec. The javadoc
+ // for transition says "... toViewId - the viewId of the view
+ // being displayed as a result of this transition. This parameter
+ // makes it possible to implement getLastDisplayedViewId(
+ // javax.faces.context.FacesContext). ...", but JSF 2.2 section
+ // 7.4.2.1 says "... ..."
flowHandler.transition(facesContext, sourceFlow, targetFlow,
navigationContext.getFlowCallNodes().get(i),
- lastDisplayedViewId);
+ navigationContext.getNavigationCase().getToViewId(facesContext));
sourceFlow = targetFlow;
}
}
@@ -428,6 +432,8 @@ public class NavigationHandlerImpl
FlowCallNode targetFlowCallNode = null;
boolean startFlow = false;
+ String startFlowDocumentId = null;
+ String startFlowId = null;
boolean checkFlowNode = false;
if (currentFlow != null)
{
@@ -469,6 +475,11 @@ public class NavigationHandlerImpl
{
if (startFlow)
{
+ if (startFlowId == null)
+ {
+ startFlowDocumentId = targetFlow.getDefiningDocumentId();
+ startFlowId = targetFlowCallNode == null ? targetFlow.getId() : targetFlowCallNode.getId();
+ }
navigationContext.setSourceFlow(currentFlow);
navigationContext.addTargetFlow(targetFlow, targetFlowCallNode);
targetFlowCallNode = null;
@@ -531,28 +542,8 @@ public class NavigationHandlerImpl
{
// "... If the node is a FlowCallNode, save it aside as facesFlowCallNode. ..."
FlowCallNode flowCallNode = (FlowCallNode) flowNode;
-
- // " ... Let flowId be the value of its calledFlowId property and flowDocumentId
- // be the value of its calledFlowDocumentId property. .."
-
- // " ... If no flowDocumentId exists for the node, let it be the string
- // resulting from flowId + "/" + flowId + ".xhtml". ..."
- // -=Leonardo Uribe=- flowDocumentId is inconsistent, waiting for answer of the EG,
- // for not let it be null.
-
- String calledFlowDocumentId = flowCallNode.getCalledFlowDocumentId(facesContext);
- if (calledFlowDocumentId == null)
- {
- calledFlowDocumentId = currentFlow.getDefiningDocumentId();
- }
- targetFlow = flowHandler.getFlow(facesContext,
- calledFlowDocumentId,
- flowCallNode.getCalledFlowId(facesContext));
- if (targetFlow == null && !"".equals(calledFlowDocumentId))
- {
- targetFlow = flowHandler.getFlow(facesContext, "",
- flowCallNode.getCalledFlowId(facesContext));
- }
+ targetFlow = calculateFlowCallTargetFlow(facesContext,
+ flowHandler, flowCallNode, currentFlow);
if (targetFlow != null)
{
targetFlowCallNode = flowCallNode;
@@ -565,30 +556,14 @@ public class NavigationHandlerImpl
// reference to the start node and execute this algorithm again, on that start node.
complete = true;
}
- //complete = true;
}
if (!complete && flowNode instanceof MethodCallNode)
{
MethodCallNode methodCallNode = (MethodCallNode) flowNode;
- String vdlViewIdentifier = null;
- MethodExpression method = methodCallNode.getMethodExpression();
- if (method != null)
- {
- Object value = invokeMethodCallNode(facesContext, methodCallNode);
- if (value != null)
- {
- vdlViewIdentifier = value.toString();
- }
- else if (methodCallNode.getOutcome() != null)
- {
- vdlViewIdentifier = (String) methodCallNode.getOutcome().getValue(
- facesContext.getELContext());
- }
- }
+ String vdlViewIdentifier = calculateVdlViewIdentifier(facesContext, methodCallNode);
// note a vdlViewIdentifier could be a flow node too
if (vdlViewIdentifier != null)
{
- //navigationCase = createNavigationCase(viewId, outcomeToGo, vdlViewIdentifier);
outcomeToGo = vdlViewIdentifier;
actionToGo = currentFlow.getId();
continue;
@@ -609,14 +584,14 @@ public class NavigationHandlerImpl
navigationContext.setSourceFlow(
navigationContext.getCurrentFlow(facesContext));
}
- String lastDisplayedViewId = navigationContext.getLastDisplayedViewId(facesContext,
- currentFlow);
-
// This is the part when the pseudo "recursive call" is done.
navigationContext.popFlow(facesContext);
currentFlow = navigationContext.getCurrentFlow(facesContext);
navigationContext.addTargetFlow(currentFlow, null);
outcomeToGo = fromOutcome;
+ String lastDisplayedViewId = navigationContext.getLastDisplayedViewId(facesContext,
+ currentFlow);
+
// The part where FlowHandler.NULL_FLOW is passed as documentId causes the effect of
// do not take into account the documentId of the returned flow in the command. In
// theory there is no Flow with defining documentId as FlowHandler.NULL_FLOW. It has
@@ -626,6 +601,8 @@ public class NavigationHandlerImpl
navigationContext, actionToGo, outcomeToGo, FlowHandler.NULL_FLOW);
if (navigationCase != null)
{
+ navigationCase = new FlowNavigationCase(navigationCase,
+ flowNode.getId(), FlowHandler.NULL_FLOW);
complete = true;
}
else
@@ -634,7 +611,7 @@ public class NavigationHandlerImpl
if (lastDisplayedViewId != null)
{
navigationCase = createNavigationCase(
- viewId, flowNode.getId(), lastDisplayedViewId);
+ viewId, flowNode.getId(), lastDisplayedViewId, FlowHandler.NULL_FLOW);
complete = true;
}
}
@@ -692,6 +669,10 @@ public class NavigationHandlerImpl
navigationCase = getOutcomeNavigationCase (facesContext, actionToGo, outcomeToGo);
}
}
+ if (startFlowId != null)
+ {
+ navigationCase = new FlowNavigationCase(navigationCase, startFlowId, startFlowDocumentId);
+ }
}
if (outcome != null && navigationCase == null)
{
@@ -714,6 +695,54 @@ public class NavigationHandlerImpl
// if navigationCase == null, will stay on current view
}
+ private Flow calculateFlowCallTargetFlow(FacesContext facesContext, FlowHandler flowHandler,
+ FlowCallNode flowCallNode, Flow currentFlow)
+ {
+ Flow targetFlow = null;
+ // " ... Let flowId be the value of its calledFlowId property and flowDocumentId
+ // be the value of its calledFlowDocumentId property. .."
+
+ // " ... If no flowDocumentId exists for the node, let it be the string
+ // resulting from flowId + "/" + flowId + ".xhtml". ..."
+ // -=Leonardo Uribe=- flowDocumentId is inconsistent, waiting for answer of the EG,
+ // for not let it be null.
+
+ String calledFlowDocumentId = flowCallNode.getCalledFlowDocumentId(facesContext);
+ if (calledFlowDocumentId == null)
+ {
+ calledFlowDocumentId = currentFlow.getDefiningDocumentId();
+ }
+ targetFlow = flowHandler.getFlow(facesContext,
+ calledFlowDocumentId,
+ flowCallNode.getCalledFlowId(facesContext));
+ if (targetFlow == null && !"".equals(calledFlowDocumentId))
+ {
+ targetFlow = flowHandler.getFlow(facesContext, "",
+ flowCallNode.getCalledFlowId(facesContext));
+ }
+ return targetFlow;
+ }
+
+ private String calculateVdlViewIdentifier(FacesContext facesContext, MethodCallNode methodCallNode)
+ {
+ String vdlViewIdentifier = null;
+ MethodExpression method = methodCallNode.getMethodExpression();
+ if (method != null)
+ {
+ Object value = invokeMethodCallNode(facesContext, methodCallNode);
+ if (value != null)
+ {
+ vdlViewIdentifier = value.toString();
+ }
+ else if (methodCallNode.getOutcome() != null)
+ {
+ vdlViewIdentifier = (String) methodCallNode.getOutcome().getValue(
+ facesContext.getELContext());
+ }
+ }
+ return vdlViewIdentifier;
+ }
+
private Object invokeMethodCallNode(FacesContext facesContext, MethodCallNode methodCallNode)
{
MethodExpression method = methodCallNode.getMethodExpression();
@@ -801,6 +830,13 @@ public class NavigationHandlerImpl
return new NavigationCase(fromViewId, null, outcome, null, toViewId, null, false, false);
}
+ private NavigationCase createNavigationCase(String fromViewId, String outcome,
+ String toViewId, String toFlowDocumentId)
+ {
+ return new NavigationCase(fromViewId, null, outcome, null, toViewId,
+ toFlowDocumentId, null, false, false);
+ }
+
/**
* 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.
@@ -1386,6 +1422,7 @@ public class NavigationHandlerImpl
private List<FlowCallNode> targetFlowCallNodes;
private List<Flow> currentFlows;
private Map<Flow, String> lastDisplayedViewIdMap;
+ private String rootLastDisplayedViewId;
public NavigationContext()
{
@@ -1461,7 +1498,9 @@ public class NavigationHandlerImpl
flowHandler.pushReturnMode(facesContext);
curFlow = flowHandler.getCurrentFlow(facesContext);
}
-
+
+ rootLastDisplayedViewId = flowHandler.getLastDisplayedViewId(facesContext);
+
for (int i = 0; i < currentFlows.size(); i++)
{
flowHandler.popReturnMode(facesContext);
@@ -1497,6 +1536,10 @@ public class NavigationHandlerImpl
public String getLastDisplayedViewId(FacesContext facesContext, Flow flow)
{
+ if (flow == null)
+ {
+ return rootLastDisplayedViewId;
+ }
if (lastDisplayedViewIdMap != null)
{
return lastDisplayedViewIdMap.get(flow);
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/FlowHandlerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/FlowHandlerImpl.java?rev=1519698&r1=1519697&r2=1519698&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/FlowHandlerImpl.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/FlowHandlerImpl.java Tue Sep 3 14:49:39 2013
@@ -24,13 +24,16 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.faces.application.ConfigurableNavigationHandler;
+import javax.faces.application.NavigationCase;
import javax.faces.application.NavigationHandler;
import javax.faces.application.NavigationHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.faces.flow.Flow;
import javax.faces.flow.FlowCallNode;
import javax.faces.flow.FlowHandler;
+import javax.faces.flow.FlowNode;
import javax.faces.flow.Parameter;
+import javax.faces.flow.ReturnNode;
import javax.faces.lifecycle.ClientWindow;
import org.apache.myfaces.spi.FacesFlowProvider;
import org.apache.myfaces.spi.FacesFlowProviderFactory;
@@ -43,6 +46,7 @@ import org.apache.myfaces.spi.FacesFlowP
public class FlowHandlerImpl extends FlowHandler
{
private final static String CURRENT_FLOW_STACK = "oam.flow.STACK.";
+ private final static String ROOT_LAST_VIEW_ID = "oam.flow.ROOT_LAST_VIEW_ID.";
private final static String RETURN_MODE = "oam.flow.RETURN_MODE";
private final static String FLOW_RETURN_STACK = "oam.flow.RETURN_STACK.";
@@ -215,7 +219,7 @@ public class FlowHandlerImpl extends Flo
_FlowContextualInfo fci = currentFlowStack.get(i);
FlowReference fr = fci.getFlowReference();
doBeforeExitFlow(context, getFlow(context, fr.getDocumentId(), fr.getId()));
- currentFlowStack.remove(i);
+ popFlowReference(context, clientWindow, currentFlowStack, i);
}
}
}
@@ -245,7 +249,7 @@ public class FlowHandlerImpl extends Flo
_FlowContextualInfo fci = currentFlowStack.get(i);
FlowReference fr = fci.getFlowReference();
doBeforeExitFlow(context, getFlow(context, fr.getDocumentId(), fr.getId()));
- currentFlowStack.remove(i);
+ popFlowReference(context, clientWindow, currentFlowStack, i);
}
}
else
@@ -271,7 +275,7 @@ public class FlowHandlerImpl extends Flo
_FlowContextualInfo fci = currentFlowStack.get(i);
FlowReference fr = fci.getFlowReference();
doBeforeExitFlow(context, getFlow(context, fr.getDocumentId(), fr.getId()));
- currentFlowStack.remove(i);
+ popFlowReference(context, clientWindow, currentFlowStack, i);
}
Map<String, Object> outboundParameters = doBeforeEnterFlow(context,
@@ -405,9 +409,241 @@ public class FlowHandlerImpl extends Flo
@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)
+ {
+ // If we don't have an fromOutcome, it is not possible to calculate the transitions
+ // 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 targetFlowId = null;
+ String fromOutcome = flowIdRequestParam;
+
+ 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)
+ {
+ // Get the navigation case using the outcome
+ 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)
+ {
+ if (currentFlow != null)
+ {
+ toFlowDocumentId = currentFlow.getDefiningDocumentId();
+ targetFlowId = currentFlow.getId();
+ }
+ else
+ {
+ // No active flow
+ toFlowDocumentId = 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
+ {
+ // The absence of FlowHandler.NULL_FLOW means the return went somewhere else.
+ if (currentFlow != null)
+ {
+ toFlowDocumentId = currentFlow.getDefiningDocumentId();
+ targetFlowId = currentFlow.getId();
+ }
+ else
+ {
+ // No active flow
+ toFlowDocumentId = null;
+ }
+ }
+ }
+ }
+ else
+ {
+ failed = true;
+ }
+ }
+ for (int j = 0; j<i; j++)
+ {
+ flowHandler.popReturnMode(context);
+ }
+ if (failed)
+ {
+ // Do nothing.
+ }
+ else
+ {
+ Flow targetFlow = targetFlowId == null ? null :
+ getFlow(context, toFlowDocumentId, targetFlowId);
+ //Call transitions.
+ flowHandler.transition(context,
+ flowHandler.getCurrentFlow(context),
+ 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.
+ Flow currentFlow = flowHandler.getCurrentFlow(context);
+ FlowNode node = currentFlow != null ? currentFlow.getNode(flowIdRequestParam) : null;
+ Flow targetFlow = null;
+ FlowCallNode outboundCallNode = null;
+ 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));
+ }
+
+ //targetFlow = flowHandler.getFlow(context,
+ // outboundCallNode.getCalledFlowDocumentId(context),
+ // outboundCallNode.getCalledFlowId(context));
+ }
+ else
+ {
+ targetFlow = flowHandler.getFlow(context, flowDocumentIdRequestParam, flowIdRequestParam);
+ }
+
+ if (targetFlow != null)
+ {
+ // Invoke transition
+ flowHandler.transition(context,
+ currentFlow, targetFlow, outboundCallNode, context.getViewRoot().getViewId());
+
+ // TODO: Handle 2 or more flow 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));
+ }
+
+ //targetFlow = flowHandler.getFlow(context,
+ // outboundCallNode.getCalledFlowDocumentId(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());
+ }
+ //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;
+ }
+ }
+ }
+
+ }
+ }
// throw new UnsupportedOperationException("Not supported yet.");
}
-
+
private void checkNull(final Object o, final String param)
{
if (o == null)
@@ -457,7 +693,7 @@ public class FlowHandlerImpl extends Flo
}
private void pushFlowReference(FacesContext context, ClientWindow clientWindow, FlowReference flowReference,
- String lastDisplayedViewId)
+ String toViewId)
{
Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
String currentFlowMapKey = CURRENT_FLOW_STACK + clientWindow.getId();
@@ -467,7 +703,29 @@ public class FlowHandlerImpl extends Flo
currentFlowStack = new ArrayList<_FlowContextualInfo>(4);
sessionMap.put(currentFlowMapKey, currentFlowStack);
}
- currentFlowStack.add(new _FlowContextualInfo(flowReference, lastDisplayedViewId));
+ if (!currentFlowStack.isEmpty())
+ {
+ currentFlowStack.get(currentFlowStack.size()-1).setLastDisplayedViewId(context.getViewRoot().getViewId());
+ }
+ else
+ {
+ //Save root lastDisplayedViewId
+ context.getExternalContext().getSessionMap().put(ROOT_LAST_VIEW_ID + clientWindow.getId(),
+ context.getViewRoot().getViewId());
+ }
+ currentFlowStack.add(new _FlowContextualInfo(flowReference, toViewId));
+ }
+
+ private void popFlowReference(FacesContext context, ClientWindow clientWindow,
+ List<_FlowContextualInfo> currentFlowStack, int i)
+ {
+ currentFlowStack.remove(i);
+ if (currentFlowStack.isEmpty())
+ {
+ // Remove it from session but keep it in request scope.
+ context.getAttributes().put(ROOT_LAST_VIEW_ID,
+ context.getExternalContext().getSessionMap().remove(ROOT_LAST_VIEW_ID + clientWindow.getId()));
+ }
}
private List<_FlowContextualInfo> getCurrentFlowStack(FacesContext context, ClientWindow clientWindow)
@@ -495,7 +753,13 @@ public class FlowHandlerImpl extends Flo
_FlowContextualInfo info = getCurrentFlowReference(context, clientWindow);
if (info == null)
{
- return null;
+ String lastDisplayedViewId = (String) context.getAttributes().get(ROOT_LAST_VIEW_ID);
+ if (lastDisplayedViewId == null)
+ {
+ lastDisplayedViewId = (String) context.getExternalContext().getSessionMap().
+ get(ROOT_LAST_VIEW_ID + clientWindow.getId());
+ }
+ return lastDisplayedViewId;
}
return info.getLastDisplayedViewId();
}
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/lifecycle/RestoreViewExecutor.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/lifecycle/RestoreViewExecutor.java?rev=1519698&r1=1519697&r2=1519698&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/lifecycle/RestoreViewExecutor.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/lifecycle/RestoreViewExecutor.java Tue Sep 3 14:49:39 2013
@@ -35,6 +35,7 @@ import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PostAddToViewEvent;
+import javax.faces.flow.FlowHandler;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.faces.view.ViewDeclarationLanguage;
@@ -226,6 +227,12 @@ class RestoreViewExecutor extends PhaseE
// Store the new UIViewRoot instance in the FacesContext.
facesContext.setViewRoot(viewRoot);
+ FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
+ if (flowHandler != null)
+ {
+ flowHandler.clientWindowTransition(facesContext);
+ }
+
// Publish an AfterAddToParent event with the created UIViewRoot as the event source.
application.publishEvent(facesContext, PostAddToViewEvent.class, viewRoot);
}
Modified: myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java?rev=1519698&r1=1519697&r2=1519698&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java (original)
+++ myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java Tue Sep 3 14:49:39 2013
@@ -18,11 +18,14 @@
*/
package org.apache.myfaces.application.flow;
+import javax.el.ExpressionFactory;
import javax.faces.application.ConfigurableNavigationHandler;
import javax.faces.application.NavigationCase;
import javax.faces.application.StateManager;
import javax.faces.component.UICommand;
import javax.faces.flow.Flow;
+import javax.faces.flow.FlowHandler;
+import javax.faces.render.ResponseStateManager;
import org.apache.myfaces.mc.test.core.AbstractMyFacesRequestTestCase;
import org.apache.myfaces.shared.config.MyfacesConfig;
import org.junit.Test;
@@ -256,4 +259,225 @@ public class FlowMyFacesRequestTestCase
}
+ @Test
+ public void testFlow1_4() throws Exception
+ {
+ setupRequest("/flow1_2.xhtml");
+ processLifecycleExecute();
+
+ ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+
+ NavigationCase navCase = handler.getNavigationCase(facesContext, null, "flow1");
+
+ Assert.assertNotNull(navCase);
+
+ // Check begin view node
+ Assert.assertEquals("/flow1/begin.xhtml", navCase.getToViewId(facesContext));
+
+ processRender();
+
+ //Enter flow 1
+ UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow1");
+ submit(button);
+
+ processLifecycleExecute();
+
+ Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+ Assert.assertNotNull(currentFlow);
+ Assert.assertEquals("flow1", currentFlow.getId());
+
+ facesContext.getApplication().getFlowHandler().getCurrentFlowScope().put("flow1","value1");
+
+ processRender();
+
+ UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:call_flow2");
+ submit(button2);
+
+ processLifecycleExecute();
+
+ currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+ Assert.assertNotNull(currentFlow);
+ Assert.assertEquals("flow2", currentFlow.getId());
+ Assert.assertFalse(facesContext.getApplication().getFlowHandler().getCurrentFlowScope().containsKey("flow1"));
+ facesContext.getApplication().getFlowHandler().getCurrentFlowScope().put("flow2","value2");
+
+ processRender();
+
+ //Check current view is the begin of flow2
+ Assert.assertEquals("/flow2/begin.xhtml", facesContext.getViewRoot().getViewId());
+
+ UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:content");
+ submit(button3);
+ processLifecycleExecute();
+ processRender();
+
+ currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+ Assert.assertNotNull(currentFlow);
+ Assert.assertEquals("flow2", currentFlow.getId());
+
+ NavigationCase endCase = handler.getNavigationCase(facesContext, null, "back");
+ Assert.assertNotNull(endCase);
+ String toViewId = endCase.getToViewId(facesContext);
+ String fromOutcome = endCase.getFromOutcome();
+ String clientWindowId = facesContext.getExternalContext().getClientWindow().getId();
+
+ tearDownRequest();
+ setupRequest(toViewId);
+ request.addParameter(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, FlowHandler.NULL_FLOW);
+ request.addParameter(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, fromOutcome);
+ request.addParameter(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, clientWindowId);
+
+ processLifecycleExecute();
+
+ // Check it should go back to flow1
+ currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+ Assert.assertNotNull(currentFlow);
+ Assert.assertEquals("flow1", currentFlow.getId());
+ Assert.assertTrue(facesContext.getApplication().getFlowHandler().getCurrentFlowScope().containsKey("flow1"));
+ // Check lastDisplayedViewId
+ //Assert.assertEquals("/flow1/begin.xhtml", facesContext.getViewRoot().getViewId());
+
+ processRender();
+
+ endCase = handler.getNavigationCase(facesContext, null, "back");
+ Assert.assertNotNull(endCase);
+
+ toViewId = endCase.getToViewId(facesContext);
+ fromOutcome = endCase.getFromOutcome();
+ clientWindowId = facesContext.getExternalContext().getClientWindow().getId();
+
+ tearDownRequest();
+ setupRequest(toViewId);
+ request.addParameter(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, FlowHandler.NULL_FLOW);
+ request.addParameter(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, fromOutcome);
+ request.addParameter(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, clientWindowId);
+
+ processLifecycleExecute();
+
+ currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+ Assert.assertNull(currentFlow);
+ Assert.assertEquals("/flow1_2.xhtml", facesContext.getViewRoot().getViewId());
+ }
+
+ @Test
+ public void testFlow1_5() throws Exception
+ {
+ setupRequest("/flow1_2.xhtml");
+ processLifecycleExecute();
+
+ ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+
+ NavigationCase navCase = handler.getNavigationCase(facesContext, null, "flow3");
+
+ Assert.assertNotNull(navCase);
+
+ // Check begin view node
+ Assert.assertEquals("/flow1/begin.xhtml", navCase.getToViewId(facesContext));
+
+ processRender();
+ String clientWindowId = facesContext.getExternalContext().getClientWindow().getId();
+
+ setupRequest(navCase.getToViewId(facesContext));
+ request.addParameter(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, navCase.getToFlowDocumentId());
+ request.addParameter(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, navCase.getFromOutcome());
+ request.addParameter(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, clientWindowId);
+
+ //Enter flow 1
+ //UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow1");
+ //submit(button);
+
+ processLifecycleExecute();
+
+ Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+ Assert.assertNotNull(currentFlow);
+ Assert.assertEquals("flow1", currentFlow.getId());
+
+ facesContext.getApplication().getFlowHandler().getCurrentFlowScope().put("flow1","value1");
+
+ processRender();
+
+ //UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:call_flow2");
+ //submit(button2);
+ navCase = handler.getNavigationCase(facesContext, null, "call_flow2");
+
+ clientWindowId = facesContext.getExternalContext().getClientWindow().getId();
+
+ setupRequest(navCase.getToViewId(facesContext));
+ request.addParameter(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, navCase.getToFlowDocumentId());
+ request.addParameter(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, navCase.getFromOutcome());
+ request.addParameter(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, clientWindowId);
+
+ processLifecycleExecute();
+
+ currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+ Assert.assertNotNull(currentFlow);
+ Assert.assertEquals("flow2", currentFlow.getId());
+ Assert.assertFalse(facesContext.getApplication().getFlowHandler().getCurrentFlowScope().containsKey("flow1"));
+ facesContext.getApplication().getFlowHandler().getCurrentFlowScope().put("flow2","value2");
+
+ processRender();
+
+ //Check current view is the begin of flow2
+ Assert.assertEquals("/flow2/begin.xhtml", facesContext.getViewRoot().getViewId());
+
+ UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:content");
+ submit(button3);
+ processLifecycleExecute();
+ processRender();
+
+ currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+ Assert.assertNotNull(currentFlow);
+ Assert.assertEquals("flow2", currentFlow.getId());
+
+ NavigationCase endCase = handler.getNavigationCase(facesContext, null, "back");
+ Assert.assertNotNull(endCase);
+ String toViewId = endCase.getToViewId(facesContext);
+ String fromOutcome = endCase.getFromOutcome();
+ clientWindowId = facesContext.getExternalContext().getClientWindow().getId();
+
+ tearDownRequest();
+ setupRequest(toViewId);
+ request.addParameter(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, FlowHandler.NULL_FLOW);
+ request.addParameter(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, fromOutcome);
+ request.addParameter(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, clientWindowId);
+
+ processLifecycleExecute();
+
+ // Check it should go back to flow1
+ currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+ Assert.assertNotNull(currentFlow);
+ Assert.assertEquals("flow1", currentFlow.getId());
+ Assert.assertTrue(facesContext.getApplication().getFlowHandler().getCurrentFlowScope().containsKey("flow1"));
+ // Check lastDisplayedViewId (since it was GET, it should be the start viewId)
+ Assert.assertEquals("/flow2/begin.xhtml", facesContext.getViewRoot().getViewId());
+
+ processRender();
+
+ endCase = handler.getNavigationCase(facesContext, null, "switchBack");
+ Assert.assertNotNull(endCase);
+
+ toViewId = endCase.getToViewId(facesContext);
+ // Check if the dynamic outcome return hack has been correctly resolved.
+ Assert.assertEquals(toViewId, "/flow3/content.xhtml");
+ fromOutcome = endCase.getFromOutcome();
+ clientWindowId = facesContext.getExternalContext().getClientWindow().getId();
+
+ tearDownRequest();
+ setupRequest(toViewId);
+ request.addParameter(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, FlowHandler.NULL_FLOW);
+ request.addParameter(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, fromOutcome);
+ request.addParameter(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, clientWindowId);
+
+ processLifecycleExecute();
+
+ currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+ Assert.assertNotNull(currentFlow);
+
+ //Assert.assertEquals("/flow1_2.xhtml", facesContext.getViewRoot().getViewId());
+ }
+
+ protected ExpressionFactory createExpressionFactory()
+ {
+ return new org.apache.el.ExpressionFactoryImpl();
+ }
}
Modified: myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/lifecycle/RestoreViewExecutorTest.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/lifecycle/RestoreViewExecutorTest.java?rev=1519698&r1=1519697&r2=1519698&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/lifecycle/RestoreViewExecutorTest.java (original)
+++ myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/lifecycle/RestoreViewExecutorTest.java Tue Sep 3 14:49:39 2013
@@ -104,7 +104,7 @@ public class RestoreViewExecutorTest ext
expect(vdl.getViewMetadata(same(_facesContext), eq("calculatedViewId")))
.andReturn(null);
expect(_viewHandler.createView(same(_facesContext), eq("calculatedViewId"))).andReturn(viewRoot);
-
+ expect(_application.getFlowHandler()).andReturn(null);
_application.publishEvent(same(_facesContext), same(PostAddToViewEvent.class), same(viewRoot));
_facesContext.setViewRoot(same(viewRoot));
expect(_facesContext.getViewRoot()).andReturn(viewRoot);
Modified: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/WEB-INF/flow1-flow.xml
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/WEB-INF/flow1-flow.xml?rev=1519698&r1=1519697&r2=1519698&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/WEB-INF/flow1-flow.xml (original)
+++ myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/WEB-INF/flow1-flow.xml Tue Sep 3 14:49:39 2013
@@ -23,8 +23,18 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_2.xsd">
+ <name>flow_def_1</name>
+
<flow-definition id="flow1">
<start-node>begin</start-node>
+
+ <inbound-parameter>
+ <!-- The idea is when entering a flow pass the return outcome in
+ the flow call. This value will be saved in flowScope
+ and used later to calculate the returning outcome -->
+ <name>backOutcome</name>
+ <value>#{flowScope.backOutcome}</value>
+ </inbound-parameter>
<flow-return id="end">
<from-outcome>exit</from-outcome>
@@ -36,6 +46,21 @@
<from-outcome>back</from-outcome>
</flow-return>
+ <!-- The idea is use a switch combo to get back, since
+ the from-outcome of the switch cannot receive
+ EL expressions -->
+ <switch id="switchBack">
+ <case>
+ <if>#{not empty flowScope.backOutcome}</if>
+ <from-outcome>backEL</from-outcome>
+ </case>
+ <default-outcome>back</default-outcome>
+ </switch>
+
+ <flow-return id="backEL">
+ <from-outcome>#{flowScope.backOutcome}</from-outcome>
+ </flow-return>
+
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
@@ -97,6 +122,41 @@
</view>
</flow-definition>
+ <!-- The idea of this flow is call a second flow, so as soon as
+ the algorithm try to enter flow3 it should enter flow1. -->
+ <flow-definition id="flow3">
+ <start-node>call_flow1</start-node>
+
+ <flow-call id="call_flow1">
+ <flow-reference>
+ <flow-document-id>flow_def_1</flow-document-id>
+ <flow-id>flow1</flow-id>
+ </flow-reference>
+ <outbound-parameter>
+ <name>backOutcome</name>
+ <value>content</value>
+ </outbound-parameter>
+ </flow-call>
+
+ <navigation-rule>
+ <from-view-id>*</from-view-id>
+ <navigation-case>
+ <from-action>flow1</from-action>
+ <from-outcome>exit</from-outcome>
+ <to-view-id>content</to-view-id>
+ </navigation-case>
+ </navigation-rule>
+
+ <flow-return id="end">
+ <from-outcome>exit</from-outcome>
+ </flow-return>
+
+ <view id="content">
+ <vdl-document>/flow3/content.xhtml</vdl-document>
+ </view>
+
+ </flow-definition>
+
<navigation-rule>
<from-view-id>*</from-view-id>
<!-- End Flow 1 -->
@@ -105,6 +165,7 @@
<from-outcome>exit</from-outcome>
<to-view-id>/flow1_end.xhtml</to-view-id>
</navigation-case>
+
</navigation-rule>
</faces-config>
Modified: myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/JSFAttr.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/JSFAttr.java?rev=1519698&r1=1519697&r2=1519698&view=diff
==============================================================================
--- myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/JSFAttr.java (original)
+++ myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/JSFAttr.java Tue Sep 3 14:49:39 2013
@@ -18,6 +18,8 @@
*/
package org.apache.myfaces.shared.renderkit;
+import javax.faces.event.ActionListener;
+
/**
* Constant declarations for JSF tags
@@ -166,4 +168,6 @@ public interface JSFAttr
// HtmlSelectManyCheckbox attributes
String LAYOUT_WIDTH_ATTR = "layoutWidth";
+ //
+ String TO_FLOW_DOCUMENT_ID_ATTR = ActionListener.TO_FLOW_DOCUMENT_ID_ATTR_NAME;
}
Modified: myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRendererUtils.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRendererUtils.java?rev=1519698&r1=1519697&r2=1519698&view=diff
==============================================================================
--- myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRendererUtils.java (original)
+++ myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlRendererUtils.java Tue Sep 3 14:49:39 2013
@@ -34,11 +34,6 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.FacesException;
-import javax.faces.application.ConfigurableNavigationHandler;
-import javax.faces.application.NavigationCase;
-import javax.faces.application.NavigationHandler;
-import javax.faces.application.ProjectStage;
-import javax.faces.application.ViewHandler;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
@@ -64,7 +59,6 @@ import javax.faces.convert.Converter;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;
-import org.apache.myfaces.shared.application.NavigationUtils;
import org.apache.myfaces.shared.component.DisplayValueOnlyCapable;
import org.apache.myfaces.shared.component.EscapeCapable;
import org.apache.myfaces.shared.config.MyfacesConfig;
@@ -74,6 +68,7 @@ import org.apache.myfaces.shared.renderk
import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
import org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder;
import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
+import org.apache.myfaces.shared.renderkit.html.util.OutcomeTargetUtils;
/**
* @author Manfred Geiler (latest modification by $Author$)
@@ -1635,101 +1630,7 @@ public final class HtmlRendererUtils
public static String getOutcomeTargetHref(FacesContext facesContext,
UIOutcomeTarget component) throws IOException
{
- String outcome = component.getOutcome();
- outcome = (outcome == null) ? facesContext.getViewRoot().getViewId()
- : outcome;
- outcome = ((outcome == null) ? STR_EMPTY : outcome.trim());
- // Get the correct URL for the outcome.
- NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
- if (!(nh instanceof ConfigurableNavigationHandler))
- {
- throw new FacesException(
- "Navigation handler must be an instance of "
- + "ConfigurableNavigationHandler for using h:link or h:button");
- }
- ConfigurableNavigationHandler navigationHandler = (ConfigurableNavigationHandler) nh;
- // fromAction is null because there is no action method that was called to get the outcome
- NavigationCase navigationCase = navigationHandler.getNavigationCase(
- facesContext, null, outcome);
- // when navigation case is null, force the link or button to be disabled and log a warning
- if (navigationCase == null)
- {
- // log a warning
- log.warning("Could not determine NavigationCase for UIOutcomeTarget component "
- + RendererUtils.getPathToComponent(component));
-
- return null;
- }
- Map<String, List<String>> parameters = null;
- // handle URL parameters
- if (component.getChildCount() > 0)
- {
- List<UIParameter> validParams = getValidUIParameterChildren(
- facesContext, component.getChildren(), true, false);
- if (validParams.size() > 0)
- {
- parameters = new HashMap<String, List<String>>();
- }
- for (int i = 0, size = validParams.size(); i < size; i++)
- {
- UIParameter param = validParams.get(i);
- String name = param.getName();
- Object value = param.getValue();
- if (parameters.containsKey(name))
- {
- parameters.get(name).add(value.toString());
- }
- else
- {
- List<String> list = new ArrayList<String>(1);
- list.add(value.toString());
- parameters.put(name, list);
- }
- }
- }
- // handle NavigationCase parameters
- Map<String, List<String>> navigationCaseParams =
- NavigationUtils.getEvaluatedNavigationParameters(facesContext,
- navigationCase.getParameters());
- if (navigationCaseParams != null)
- {
- if (parameters == null)
- {
- parameters = new HashMap<String, List<String>>();
- }
- //parameters.putAll(navigationCaseParams);
- for (Map.Entry<String, List<String>> entry : navigationCaseParams
- .entrySet())
- {
- if (!parameters.containsKey(entry.getKey()))
- {
- parameters.put(entry.getKey(), entry.getValue());
- }
- }
- }
- if (parameters == null)
- {
- parameters = Collections.emptyMap();
- }
- // In theory the precedence order to deal with params is this:
- // component parameters, navigation-case parameters, view parameters
- // getBookmarkableURL deal with this details.
- ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
- String href = viewHandler.getBookmarkableURL(facesContext,
- navigationCase.getToViewId(facesContext),
- parameters, navigationCase.isIncludeViewParams() || component.isIncludeViewParams());
- // handle fragment (viewId#fragment)
- String fragment = (String) component.getAttributes().get("fragment");
- if (fragment != null)
- {
- fragment = fragment.trim();
-
- if (fragment.length() > 0)
- {
- href += "#" + fragment;
- }
- }
- return href;
+ return OutcomeTargetUtils.getOutcomeTargetHref(facesContext, component);
}
private static final String HTML_CONTENT_TYPE = "text/html";
@@ -2296,55 +2197,8 @@ public final class HtmlRendererUtils
FacesContext facesContext, List<UIComponent> children,
boolean skipNullValue, boolean skipUnrendered, boolean skipNullName)
{
- List<UIParameter> params = null;
- for (int i = 0, size = children.size(); i < size; i++)
- {
- UIComponent child = children.get(i);
- if (child instanceof UIParameter)
- {
- UIParameter param = (UIParameter) child;
- // check for the disable attribute (since 2.0)
- // and the render attribute (only if skipUnrendered is true)
- if (param.isDisable() || (skipUnrendered && !param.isRendered()))
- {
- // ignore this UIParameter and continue
- continue;
- }
- // check the name
- String name = param.getName();
- if (skipNullName && (name == null || STR_EMPTY.equals(name)))
- {
- // warn for a null-name
- log.log(Level.WARNING, "The UIParameter " + RendererUtils.getPathToComponent(param)
- + " has a name of null or empty string and thus will not be added to the URL.");
- // and skip it
- continue;
- }
- // check the value
- if (skipNullValue && param.getValue() == null)
- {
- if (facesContext.isProjectStage(ProjectStage.Development))
- {
- // inform the user about the null value when in Development stage
- log.log(Level.INFO, "The UIParameter " + RendererUtils.getPathToComponent(param)
- + " has a value of null and thus will not be added to the URL.");
- }
- // skip a null-value
- continue;
- }
- // add the param
- if (params == null)
- {
- params = new ArrayList<UIParameter>();
- }
- params.add(param);
- }
- }
- if (params == null)
- {
- params = Collections.emptyList();
- }
- return params;
+ return OutcomeTargetUtils.getValidUIParameterChildren(
+ facesContext, children, skipNullValue, skipUnrendered, skipNullName);
}
/**
Added: myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/util/OutcomeTargetUtils.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/util/OutcomeTargetUtils.java?rev=1519698&view=auto
==============================================================================
--- myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/util/OutcomeTargetUtils.java (added)
+++ myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/util/OutcomeTargetUtils.java Tue Sep 3 14:49:39 2013
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.shared.renderkit.html.util;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.faces.FacesException;
+import javax.faces.application.ConfigurableNavigationHandler;
+import javax.faces.application.NavigationCase;
+import javax.faces.application.NavigationHandler;
+import javax.faces.application.ProjectStage;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIOutcomeTarget;
+import javax.faces.component.UIParameter;
+import javax.faces.context.FacesContext;
+import javax.faces.flow.FlowHandler;
+import org.apache.myfaces.shared.application.NavigationUtils;
+import org.apache.myfaces.shared.renderkit.JSFAttr;
+import org.apache.myfaces.shared.renderkit.RendererUtils;
+import org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils;
+
+/**
+ * Utility methods for OutcomeTarget components.
+ *
+ * @author Leonardo Uribe
+ */
+public class OutcomeTargetUtils
+{
+
+ private static final Logger log = Logger.getLogger(OutcomeTargetUtils.class
+ .getName());
+
+ public static String getOutcomeTargetHref(FacesContext facesContext,
+ UIOutcomeTarget component) throws IOException
+ {
+ String outcome = component.getOutcome();
+ outcome = (outcome == null) ? facesContext.getViewRoot().getViewId()
+ : outcome;
+ outcome = ((outcome == null) ? HtmlRendererUtils.STR_EMPTY : outcome.trim());
+ // Get the correct URL for the outcome.
+ NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
+ if (!(nh instanceof ConfigurableNavigationHandler))
+ {
+ throw new FacesException(
+ "Navigation handler must be an instance of "
+ + "ConfigurableNavigationHandler for using h:link or h:button");
+ }
+ ConfigurableNavigationHandler navigationHandler = (ConfigurableNavigationHandler) nh;
+
+ // handle faces flow
+ // 1. check to-flow-document-id
+ String toFlowDocumentId = (String) component.getAttributes().get(
+ JSFAttr.TO_FLOW_DOCUMENT_ID_ATTR);
+
+ // fromAction is null because there is no action method that was called to get the outcome
+ NavigationCase navigationCase = null;
+ if (toFlowDocumentId == null)
+ {
+ navigationCase = navigationHandler.getNavigationCase(
+ facesContext, null, outcome);
+ }
+ else
+ {
+ navigationCase = navigationHandler.getNavigationCase(
+ facesContext, null, outcome, toFlowDocumentId);
+ }
+
+ // when navigation case is null, force the link or button to be disabled and log a warning
+ if (navigationCase == null)
+ {
+ // log a warning
+ log.warning("Could not determine NavigationCase for UIOutcomeTarget component "
+ + RendererUtils.getPathToComponent(component));
+
+ return null;
+ }
+ Map<String, List<String>> parameters = null;
+ // handle URL parameters
+ if (component.getChildCount() > 0)
+ {
+ List<UIParameter> validParams = getValidUIParameterChildren(
+ facesContext, component.getChildren(), true, false);
+ if (validParams.size() > 0)
+ {
+ parameters = new HashMap<String, List<String>>();
+ }
+ for (int i = 0, size = validParams.size(); i < size; i++)
+ {
+ UIParameter param = validParams.get(i);
+ String name = param.getName();
+ Object value = param.getValue();
+ if (parameters.containsKey(name))
+ {
+ parameters.get(name).add(value.toString());
+ }
+ else
+ {
+ List<String> list = new ArrayList<String>(1);
+ list.add(value.toString());
+ parameters.put(name, list);
+ }
+ }
+ }
+
+ // From the navigation case, use getToFlowDocumentId() to identify when
+ // a navigation case is a flow call or a flow return.
+ if (navigationCase.getToFlowDocumentId() != null)
+ {
+ if (parameters == null)
+ {
+ parameters = new HashMap<String, List<String>>();
+ }
+ if (!parameters.containsKey(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME))
+ {
+ List<String> list = new ArrayList<String>(1);
+ list.add(navigationCase.getToFlowDocumentId());
+ parameters.put(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, list);
+ }
+ if (!parameters.containsKey(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME))
+ {
+ List<String> list2 = new ArrayList<String>(1);
+ list2.add(navigationCase.getFromOutcome());
+ parameters.put(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, list2);
+ }
+ }
+
+ // handle NavigationCase parameters
+ Map<String, List<String>> navigationCaseParams =
+ NavigationUtils.getEvaluatedNavigationParameters(facesContext,
+ navigationCase.getParameters());
+ if (navigationCaseParams != null)
+ {
+ if (parameters == null)
+ {
+ parameters = new HashMap<String, List<String>>();
+ }
+ //parameters.putAll(navigationCaseParams);
+ for (Map.Entry<String, List<String>> entry : navigationCaseParams
+ .entrySet())
+ {
+ if (!parameters.containsKey(entry.getKey()))
+ {
+ parameters.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ if (parameters == null)
+ {
+ parameters = Collections.emptyMap();
+ }
+ // In theory the precedence order to deal with params is this:
+ // component parameters, navigation-case parameters, view parameters
+ // getBookmarkableURL deal with this details.
+ ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
+ String href = viewHandler.getBookmarkableURL(facesContext,
+ navigationCase.getToViewId(facesContext),
+ parameters, navigationCase.isIncludeViewParams() || component.isIncludeViewParams());
+ // handle fragment (viewId#fragment)
+ String fragment = (String) component.getAttributes().get("fragment");
+ if (fragment != null)
+ {
+ fragment = fragment.trim();
+
+ if (fragment.length() > 0)
+ {
+ href += "#" + fragment;
+ }
+ }
+ return href;
+ }
+
+ /**
+ * Calls getValidUIParameterChildren(facesContext, children, skipNullValue, skipUnrendered, true);
+ *
+ * @param facesContext
+ * @param children
+ * @param skipNullValue
+ * @param skipUnrendered
+ * @return ArrayList size > 0 if any parameter found
+ */
+ public static List<UIParameter> getValidUIParameterChildren(
+ FacesContext facesContext, List<UIComponent> children,
+ boolean skipNullValue, boolean skipUnrendered)
+ {
+ return getValidUIParameterChildren(facesContext, children,
+ skipNullValue, skipUnrendered, true);
+ }
+
+
+ /**
+ * Returns a List of all valid UIParameter children from the given children.
+ * Valid means that the UIParameter is not disabled, its name is not null
+ * (if skipNullName is true), its value is not null (if skipNullValue is true)
+ * and it is rendered (if skipUnrendered is true). This method also creates a
+ * warning for every UIParameter with a null-name (again, if skipNullName is true)
+ * and, if ProjectStage is Development and skipNullValue is true, it informs the
+ * user about every null-value.
+ *
+ * @param facesContext
+ * @param children
+ * @param skipNullValue should UIParameters with a null value be skipped
+ * @param skipUnrendered should UIParameters with isRendered() returning false be skipped
+ * @param skipNullName should UIParameters with a null name be skipped
+ * (normally true, but in the case of h:outputFormat false)
+ * @return ArrayList size > 0 if any parameter found
+ */
+ public static List<UIParameter> getValidUIParameterChildren(
+ FacesContext facesContext, List<UIComponent> children,
+ boolean skipNullValue, boolean skipUnrendered, boolean skipNullName)
+ {
+ List<UIParameter> params = null;
+ for (int i = 0, size = children.size(); i < size; i++)
+ {
+ UIComponent child = children.get(i);
+ if (child instanceof UIParameter)
+ {
+ UIParameter param = (UIParameter) child;
+ // check for the disable attribute (since 2.0)
+ // and the render attribute (only if skipUnrendered is true)
+ if (param.isDisable() || (skipUnrendered && !param.isRendered()))
+ {
+ // ignore this UIParameter and continue
+ continue;
+ }
+ // check the name
+ String name = param.getName();
+ if (skipNullName && (name == null || HtmlRendererUtils.STR_EMPTY.equals(name)))
+ {
+ // warn for a null-name
+ log.log(Level.WARNING, "The UIParameter " + RendererUtils.getPathToComponent(param)
+ + " has a name of null or empty string and thus will not be added to the URL.");
+ // and skip it
+ continue;
+ }
+ // check the value
+ if (skipNullValue && param.getValue() == null)
+ {
+ if (facesContext.isProjectStage(ProjectStage.Development))
+ {
+ // inform the user about the null value when in Development stage
+ log.log(Level.INFO, "The UIParameter " + RendererUtils.getPathToComponent(param)
+ + " has a value of null and thus will not be added to the URL.");
+ }
+ // skip a null-value
+ continue;
+ }
+ // add the param
+ if (params == null)
+ {
+ params = new ArrayList<UIParameter>();
+ }
+ params.add(param);
+ }
+ }
+ if (params == null)
+ {
+ params = Collections.emptyList();
+ }
+ return params;
+ }
+
+}
Propchange: myfaces/core/trunk/shared/src/main/java/org/apache/myfaces/shared/renderkit/html/util/OutcomeTargetUtils.java
------------------------------------------------------------------------------
svn:eol-style = native