Author: mrdon Date: Mon Feb 14 22:08:33 2005 New Revision: 153901 URL: http://svn.apache.org/viewcvs?view=rev&rev=153901 Log: Adding Hubert Rabago's patch that adds the ability to send request parameters with a redirect. PR: 866 Author: Hubert Rabago
Added: struts/core/trunk/src/share/org/apache/struts/action/ActionRedirect.java (with props) struts/core/trunk/src/test/org/apache/struts/action/TestActionRedirect.java (with props) Added: struts/core/trunk/src/share/org/apache/struts/action/ActionRedirect.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/share/org/apache/struts/action/ActionRedirect.java?view=auto&rev=153901 ============================================================================== --- struts/core/trunk/src/share/org/apache/struts/action/ActionRedirect.java (added) +++ struts/core/trunk/src/share/org/apache/struts/action/ActionRedirect.java Mon Feb 14 22:08:33 2005 @@ -0,0 +1,307 @@ +/* + * $Id$ + * + * Copyright 2000-2004 The Apache Software Foundation. + * + * Licensed 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.struts.action; + +import org.apache.struts.config.ForwardConfig; +import org.apache.struts.action.ActionForward; +import org.apache.struts.util.ResponseUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Map; +import java.util.HashMap; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.Iterator; + +/** + * A subclass of [EMAIL PROTECTED] ActionForward} which is designed for use + * in redirecting requests, with support for adding parameters + * at runtime. + * <br/> + * An [EMAIL PROTECTED] ForwardConfig} (or subclass) can be passed to the constructor + * to copy its configuration: + * <br/> + * <code> + * public ActionForward execute(ActionMapping mapping, + * ActionForm form, + * HttpServletRequest request, + * HttpServletResponse response) + * throws Exception { + * ActionRedirect redirect = + * new ActionRedirect(mapping.findForward("doRedirect")); + * redirect.addParameter("param1","value1"); + * redirect.addParameter("param2","2"); + * redirect.addParameter("param3","3.0"); + * return redirect; + * } + * </code> + * <p/> + * + * @version $Rev$ $Date$ + */ +public class ActionRedirect extends ActionForward { + + // ----------------------------------------------------- Static variables + + /** + * <p>Commons logging instance.</p> + */ + protected static final Log log = LogFactory.getLog(ActionRedirect.class); + + + // ----------------------------------------------------- Instance variables + + /** + * <p>Holds the redirect parameters. + * Each entry is either a String or a String[] depending on whether + * it has one or more entries.</p> + */ + protected Map parameterValues = null; + + + // ----------------------------------------------------- Constructors + + /** + * <p>Construct a new instance with redirect set to true + * and initialize parameter lists.</p> + */ + public ActionRedirect() { + setRedirect(true); + initializeParameters(); + } + + /** + * <p>Construct a new instance with the specified path + * and initialize parameter lists.</p> + * + * @param path Path for this instance + */ + public ActionRedirect(String path) { + super(path); + setRedirect(true); + initializeParameters(); + } + + /** + * <p>Construct a new instance with the specified values + * and initialize parameter lists.</p> + * + * @param name Name of this instance + * @param path Path for this instance + * @param module Module prefix, if any + */ + public ActionRedirect(String name, String path, String module) { + super(name, path, true); + setModule(module); + initializeParameters(); + } + + + /** + * <p>Construct a new instance with a [EMAIL PROTECTED] ForwardConfig} object + * to copy name, path, and contextRelative values from.</p> + * + * @param baseConfig the [EMAIL PROTECTED] ForwardConfig} + * to copy configuration values from + */ + public ActionRedirect(ForwardConfig baseConfig) { + setName(baseConfig.getName()); + setPath(baseConfig.getPath()); + setContextRelative(baseConfig.getContextRelative()); + setModule(baseConfig.getModule()); + setRedirect(true); + initializeParameters(); + } + + + + // ----------------------------------------------------- Private methods + + /** + * <p>Initializes the internal objects + * used to hold parameter values.</p> + */ + private void initializeParameters() { + parameterValues = new HashMap(); + } + + + // ----------------------------------------------------- Public methods + + /** + * <p>Adds the object's toString() to the list of parameters if it's + * not null, or an empty string with the given fieldName if it is.</p> + * + * @param fieldName the name to use for the parameter + * @param valueObj the value for this parameter + */ + public void addParameter(String fieldName, Object valueObj) { + + String value = (valueObj != null) ? valueObj.toString() : ""; + if (parameterValues == null) { + initializeParameters(); + } + + //try { + value = ResponseUtils.encodeURL(value); + //} catch (UnsupportedEncodingException uce) { + // this shouldn't happen since UTF-8 is the W3C Recommendation + // String errorMsg = "UTF-8 Character Encoding not supported"; + // log.error(errorMsg, uce); + // throw new RuntimeException(errorMsg, uce); + // } + + Object currentValue = parameterValues.get(fieldName); + if (currentValue == null) { + // there's no value for this param yet; add it to the map + parameterValues.put(fieldName, value); + + } else if (currentValue instanceof String) { + // there's already a value; let's use an array for these parameters + String[] newValue = new String[2]; + newValue[0] = (String) currentValue; + newValue[1] = value; + parameterValues.put(fieldName, newValue); + + } else if (currentValue instanceof String[]) { + // add the value to the list of existing values + List newValues = new ArrayList(Arrays.asList((Object[]) currentValue)); + newValues.add(value); + parameterValues.put(fieldName, (String[]) newValues.toArray(new String[newValues.size()])); + } + } + + + /** + * <p>Get the original path without the parameters added at runtime.</p> + * + * @return the original path as configured. + */ + public String getOriginalPath() { + return super.getPath(); + } + + + /** + * <p>Get the path for this object, including any parameters + * that may have been added at runtime.</p> + */ + public String getPath() { + // get the original path and the parameter string that was formed + String originalPath = getOriginalPath(); + String parameterString = getParameterString(); + + StringBuffer result = new StringBuffer(originalPath); + + if ((parameterString != null) && (parameterString.length() > 0)) { + // the parameter separator we're going to use + String paramSeparator = "?"; + + // true if we need to use a parameter separator after originalPath + boolean needsParamSeparator = true; + + // does the original path already have a "?"? + int paramStartIndex = originalPath.indexOf("?"); + if (paramStartIndex > 0) { + // did the path end with "?"? + needsParamSeparator = + (paramStartIndex != originalPath.length() - 1); + if (needsParamSeparator) { + paramSeparator = "&"; + } + } + + if (needsParamSeparator) { + result.append(paramSeparator); + } + result.append(parameterString); + } + + return result.toString(); + } + + + /** + * <p>Forms the string containing the parameters + * passed onto this object thru calls to addParameter().</p> + * + * @return a string which can be appended to the URLs. The + * return string does not include a leading question + * mark (?). + */ + public String getParameterString() { + StringBuffer strParam = new StringBuffer(256); + + // loop through all parameters + Iterator iterator = parameterValues.keySet().iterator(); + while (iterator.hasNext()) { + // get the parameter name + String name = (String) iterator.next(); + + // get the value for this parameter + Object value = parameterValues.get(name); + + if (value instanceof String) { + // just one value for this param + strParam.append(name) + .append("=") + .append(value); + + } else if (value instanceof String[]) { + // loop through all values for this param + String[] values = (String[]) value; + for (int i = 0; i < values.length; i++) { + strParam.append(name) + .append("=") + .append(values[i]); + if (i < values.length - 1) + strParam.append("&"); + } + } + + if (iterator.hasNext()) { + strParam.append("&"); + } + } + + return strParam.toString(); + } + + + // ----------------------------------------------------- toString() + + /** + * <p>Return a string description of this object.</p> + * + * @return a string containing the original path for this object + * and the parameters it currently holds + */ + public String toString() { + StringBuffer result = new StringBuffer(256); + result.append("ActionRedirect ["); + result.append("originalPath=").append(getOriginalPath()).append(";"); + result.append("parameterString=") + .append(getParameterString()).append("]"); + return result.toString(); + } + + +} Propchange: struts/core/trunk/src/share/org/apache/struts/action/ActionRedirect.java ------------------------------------------------------------------------------ svn:executable = * Added: struts/core/trunk/src/test/org/apache/struts/action/TestActionRedirect.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/test/org/apache/struts/action/TestActionRedirect.java?view=auto&rev=153901 ============================================================================== --- struts/core/trunk/src/test/org/apache/struts/action/TestActionRedirect.java (added) +++ struts/core/trunk/src/test/org/apache/struts/action/TestActionRedirect.java Mon Feb 14 22:08:33 2005 @@ -0,0 +1,183 @@ +/* + * $Id$ + * + * Copyright 2000-2004 The Apache Software Foundation. + * + * Licensed 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.struts.action; + +import junit.framework.TestSuite; +import junit.framework.TestCase; +import junit.framework.ComparisonFailure; +import junit.framework.AssertionFailedError; +import org.apache.struts.action.ActionForward; + +import java.util.Map; + +/** + * <p>Unit tests for the [EMAIL PROTECTED] ActionRedirect} class.</p> + * + * @version $Rev$ $Date$ + */ +public class TestActionRedirect extends TestCase { + + + public TestActionRedirect(String s) { + super(s); + } + + + public static TestSuite getSuite() { + return new TestSuite(TestActionRedirect.class); + } + + + public static void main(String[] args) { + junit.textui.TestRunner runner = new junit.textui.TestRunner(); + runner.doRun(TestActionRedirect.getSuite()); + } + + + + // ----------------------------------------------------- Test Methods + + + /** + * Check that the redirect flag is set. + */ + public void testActionRedirectRedirectFlag() { + ActionRedirect ar = new ActionRedirect("/path.do"); + assertTrue("Redirect flag should be set to true.",ar.getRedirect()); + } + + + /** + * Test all addParameter methods accepting different data types. + */ + public void testActionRedirectAddParameter() { + ActionRedirect ar = new ActionRedirect("/path.do"); + + ar.addParameter("st","test"); + ar.addParameter("obj",new StringBuffer("someString")); + + assertTrue("Incorrect path", ar.getPath().indexOf("/path.do") == 0); + assertHasParameter(ar.parameterValues, "st", "test"); + assertHasParameter(ar.parameterValues, "obj", "someString"); + } + + + /** + * Test adding parameters with the same name. + */ + public void testActionRedirectAddSameNameParameter() { + ActionRedirect ar = new ActionRedirect("/path.do"); + + ar.addParameter("param","param1"); + ar.addParameter("param","param2"); + ar.addParameter("param",new StringBuffer("someString")); + + assertTrue("Incorrect path", ar.getPath().indexOf("/path.do") == 0); + assertHasParameter(ar.parameterValues, "param", "param1"); + assertHasParameter(ar.parameterValues, "param", "param2"); + assertHasParameter(ar.parameterValues, "param", "someString"); + assertEquals("Incorrect number of parameters", 3, countParameters(ar.parameterValues, "param")); + } + + + /** + * Test creating an ActionRedirect which copies its configuration + * from an existing ActionForward. + */ + public void testActionRedirectFromExistingForward() { + ActionForward forward = new ActionForward("/path.do?param=param1"); + + ActionRedirect ar = new ActionRedirect(forward); + + ar.addParameter("param","param2"); + ar.addParameter("object1",new StringBuffer("someString")); + + assertTrue("Incorrect path", ar.getPath().indexOf("/path.do") == 0); + assertHasParameter(ar.parameterValues, "param", "param2"); + assertHasParameter(ar.parameterValues, "object1", "someString"); + assertEquals("Incorrect original path.",forward.getPath(),ar.getOriginalPath()); + } + + + + /** + * Assert that the given parameters contains an entry for + * <code>paramValue</code> under the <code>paramName</code> key. + * <p/> + * @param parameters the map of parameters to check into + * @param paramName the key of the value to be checked + * @param paramValue the value to check for + */ + static void assertHasParameter( + Map parameters, + String paramName, + String paramValue) { + Object value = parameters.get(paramName); + if (value == null) { + throw new AssertionFailedError("Parameter [" + paramName + "] not found"); + } + + if (value instanceof String) { + if (!paramValue.equals(value)) { + throw new ComparisonFailure("Incorrect value found", + paramValue, (String) value); + } + } else if (value instanceof String[]) { + // see if our value is among those in the array + String[] values = (String[]) value; + for (int i = 0; i < values.length; i++) { + if (paramValue.equals(values[i])) { + return; + } + } + throw new AssertionFailedError("Expected value not found for parameter [" + paramName + "]"); + } else { + // can't recognize the value + throw new AssertionFailedError("Unexpected type found as parameter value for [" + paramName + "]"); + } + } + + /** + * Determine the number of values that are available for a + * specific parameter. + * <p/> + * @param parameters the map of parameters to check into + * @param paramName the key of the value(s) to count + * @return the number of values for the specified parameter + */ + static int countParameters(Map parameters, + String paramName) { + Object value = parameters.get(paramName); + if (value == null) { + return 0; + } + + if (value instanceof String) { + return 1; + } else if (value instanceof String[]) { + String[] values = (String[]) value; + return values.length; + } else { + // can't recognize the value + throw new AssertionFailedError("Unexpected type found as parameter value for [" + paramName + "]"); + } + } + + +} Propchange: struts/core/trunk/src/test/org/apache/struts/action/TestActionRedirect.java ------------------------------------------------------------------------------ svn:executable = * --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]