001: /*
002: * Copyright 2004-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.springframework.webflow.engine.support;
017:
018: import java.io.Serializable;
019:
020: import org.springframework.binding.expression.Expression;
021: import org.springframework.core.style.ToStringCreator;
022: import org.springframework.util.Assert;
023: import org.springframework.util.StringUtils;
024: import org.springframework.webflow.engine.ViewSelector;
025: import org.springframework.webflow.engine.ViewState;
026: import org.springframework.webflow.execution.RequestContext;
027: import org.springframework.webflow.execution.ViewSelection;
028: import org.springframework.webflow.execution.support.ApplicationView;
029: import org.springframework.webflow.execution.support.FlowExecutionRedirect;
030:
031: /**
032: * Simple view selector that makes an {@link ApplicationView} selection using a
033: * view name expression.
034: * <p>
035: * This factory will treat all attributes returned from calling
036: * {@link RequestContext#getModel()} as the application model exposed to the
037: * view during rendering. This is typically the union of attributes in request,
038: * flow, and conversation scope.
039: * <p>
040: * This selector also supports setting a <i>redirect</i> flag that can be used
041: * to trigger a redirect to the {@link ApplicationView} at a bookmarkable URL
042: * using an {@link FlowExecutionRedirect}}.
043: *
044: * @see org.springframework.webflow.execution.support.ApplicationView
045: * @see org.springframework.webflow.execution.support.FlowExecutionRedirect
046: *
047: * @author Keith Donald
048: * @author Erwin Vervaet
049: */
050: public class ApplicationViewSelector implements ViewSelector,
051: Serializable {
052:
053: /**
054: * Flow execution attribute name that indicates that we should always render
055: * an application view via a redirect.
056: */
057: public static final String ALWAYS_REDIRECT_ON_PAUSE_ATTRIBUTE = "alwaysRedirectOnPause";
058:
059: /**
060: * The view name to render.
061: */
062: private Expression viewName;
063:
064: /**
065: * A flag indicating if a redirect to the selected application view should
066: * be requested.
067: * <p>
068: * Setting this allows you to redirect while the flow is in progress to a
069: * stable URL that can be safely refreshed.
070: */
071: private boolean redirect;
072:
073: /**
074: * Creates a application view selector that will make application view
075: * selections requesting that the specified view be rendered.
076: * @param viewName the view name expression
077: */
078: public ApplicationViewSelector(Expression viewName) {
079: this (viewName, false);
080: }
081:
082: /**
083: * Creates a application view selector that will make application view
084: * selections requesting that the specified view be rendered. No redirects
085: * will be done.
086: * @param viewName the view name expression
087: * @param redirect indicates if a redirect to the view should be initiated
088: */
089: public ApplicationViewSelector(Expression viewName, boolean redirect) {
090: Assert
091: .notNull(viewName,
092: "The view name expression is required");
093: this .viewName = viewName;
094: this .redirect = redirect;
095: }
096:
097: /**
098: * Returns the name of the view that should be rendered.
099: */
100: public Expression getViewName() {
101: return viewName;
102: }
103:
104: /**
105: * Returns if a redirect to the view should be done.
106: */
107: public boolean isRedirect() {
108: return redirect;
109: }
110:
111: public boolean isEntrySelectionRenderable(RequestContext context) {
112: return !shouldRedirect(context);
113: }
114:
115: public ViewSelection makeEntrySelection(RequestContext context) {
116: if (shouldRedirect(context)) {
117: return FlowExecutionRedirect.INSTANCE;
118: } else {
119: return makeRefreshSelection(context);
120: }
121: }
122:
123: public ViewSelection makeRefreshSelection(RequestContext context) {
124: String viewName = resolveViewName(context);
125: if (!StringUtils.hasText(viewName)) {
126: throw new IllegalStateException(
127: "Resolved application view name was empty; programmer error! -- "
128: + "The expression that was evaluated against the request context was '"
129: + getViewName() + "'");
130: }
131: return createApplicationView(viewName, context);
132: }
133:
134: // internal helpers
135:
136: /**
137: * Resolves the application view name from the request context.
138: * @param context the context
139: * @return the view name
140: */
141: protected String resolveViewName(RequestContext context) {
142: return (String) getViewName().evaluate(context, null);
143: }
144:
145: /**
146: * Creates the application view selection.
147: * @param viewName the resolved view name
148: * @param context the context
149: * @return the application view
150: */
151: protected ApplicationView createApplicationView(String viewName,
152: RequestContext context) {
153: return new ApplicationView(viewName, context.getModel().asMap());
154: }
155:
156: /**
157: * Determine whether or not a redirect should be used to render the
158: * application view.
159: * @param context the context
160: * @return true or false
161: */
162: protected boolean shouldRedirect(RequestContext context) {
163: return context.getCurrentState() instanceof ViewState
164: && (redirect || alwaysRedirectOnPause(context));
165: }
166:
167: /**
168: * Checks the {@link #ALWAYS_REDIRECT_ON_PAUSE_ATTRIBUTE} to see if every
169: * application view of the flow execution should be rendered via a redirect.
170: * @param context the flow execution request context
171: * @return true or false
172: */
173: protected boolean alwaysRedirectOnPause(RequestContext context) {
174: String attributeValue = String.valueOf(context
175: .getFlowExecutionContext().getAttributes().get(
176: ALWAYS_REDIRECT_ON_PAUSE_ATTRIBUTE, "false"));
177: return new Boolean(attributeValue).booleanValue();
178: }
179:
180: public String toString() {
181: return new ToStringCreator(this ).append("viewName", viewName)
182: .append("redirect", redirect).toString();
183: }
184: }
|