This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch WW-5310-fragment in repository https://gitbox.apache.org/repos/asf/struts.git
commit 4a85b6c4f488a1b44038eb402fc48999e4f44899 Author: Lukasz Lenart <lukaszlen...@apache.org> AuthorDate: Tue Jun 20 21:23:57 2023 +0200 WW-5310 Deprecates the old API in favour of new one --- .../struts2/components/ServletUrlRenderer.java | 16 ++-- .../struts2/result/ServletDispatcherResult.java | 7 +- .../org/apache/struts2/url/QueryStringParser.java | 38 ++++++++- .../struts2/url/StrutsQueryStringParser.java | 97 +++++++++++++++++----- .../struts2/url/StrutsQueryStringParserTest.java | 23 ++--- 5 files changed, 140 insertions(+), 41 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java index d5281a8a0..853c0a389 100644 --- a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java +++ b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java @@ -159,10 +159,10 @@ public class ServletUrlRenderer implements UrlRenderer { } } - Map<String, Object> actionParams = null; + QueryStringParser.Result queryStringResult = queryStringParser.empty(); if (action != null && action.indexOf('?') > 0) { String queryString = action.substring(action.indexOf('?') + 1); - actionParams = queryStringParser.parse(queryString, false); + queryStringResult = queryStringParser.parse(queryString); action = action.substring(0, action.indexOf('?')); } @@ -176,7 +176,7 @@ public class ServletUrlRenderer implements UrlRenderer { ActionMapping mapping = new ActionMapping(actionName, namespace, actionMethod, formComponent.parameters); String result = urlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping), - formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true, false, false); + formComponent.request, formComponent.response, queryStringResult.getQueryParams(), scheme, formComponent.includeContext, true, false, false); formComponent.addParameter("action", result); // let's try to get the actual action class and name @@ -213,7 +213,7 @@ public class ServletUrlRenderer implements UrlRenderer { LOG.warn("No configuration found for the specified action: '{}' in namespace: '{}'. Form action defaulting to 'action' attribute's literal value.", actionName, namespace); } - String result = urlHelper.buildUrl(action, formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true); + String result = urlHelper.buildUrl(action, formComponent.request, formComponent.response, queryStringResult.getQueryParams(), scheme, formComponent.includeContext, true); formComponent.addParameter("action", result); // namespace: cut out anything between the start and the last / @@ -291,7 +291,11 @@ public class ServletUrlRenderer implements UrlRenderer { private void includeGetParameters(UrlProvider urlComponent) { String query = extractQueryString(urlComponent); - mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), queryStringParser.parse(query, false)); + QueryStringParser.Result result = queryStringParser.parse(query); + mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), result.getQueryParams()); + if (!result.getQueryFragment().isEmpty()) { + urlComponent.setAnchor(result.getQueryFragment()); + } } private String extractQueryString(UrlProvider urlComponent) { @@ -339,7 +343,7 @@ public class ServletUrlRenderer implements UrlRenderer { if (StringUtils.contains(value, "?")) { String queryString = value.substring(value.indexOf('?') + 1); - mergedParams = queryStringParser.parse(queryString, false); + mergedParams = new LinkedHashMap<>(queryStringParser.parse(queryString).getQueryParams()); for (Map.Entry<String, ?> entry : contextParameters.entrySet()) { if (!mergedParams.containsKey(entry.getKey())) { mergedParams.put(entry.getKey(), entry.getValue()); diff --git a/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java b/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java index 6bc86725d..d863a890b 100644 --- a/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java +++ b/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java @@ -33,7 +33,6 @@ import javax.servlet.RequestDispatcher; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.jsp.PageContext; -import java.util.Map; /** * <!-- START SNIPPET: description --> @@ -140,9 +139,9 @@ public class ServletDispatcherResult extends StrutsResultSupport { if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf('?') > 0) { String queryString = finalLocation.substring(finalLocation.indexOf('?') + 1); HttpParameters parameters = getParameters(invocation); - Map<String, Object> queryParams = queryStringParser.parse(queryString, true); - if (queryParams != null && !queryParams.isEmpty()) { - parameters = HttpParameters.create(queryParams).withParent(parameters).build(); + QueryStringParser.Result queryParams = queryStringParser.parse(queryString); + if (!queryParams.isEmpty()) { + parameters = HttpParameters.create(queryParams.getQueryParams()).withParent(parameters).build(); invocation.getInvocationContext().withParameters(parameters); // put to extraContext, see Dispatcher#createContextMap invocation.getInvocationContext().getContextMap().put("parameters", parameters); diff --git a/core/src/main/java/org/apache/struts2/url/QueryStringParser.java b/core/src/main/java/org/apache/struts2/url/QueryStringParser.java index 0f48cd293..84cfa45ed 100644 --- a/core/src/main/java/org/apache/struts2/url/QueryStringParser.java +++ b/core/src/main/java/org/apache/struts2/url/QueryStringParser.java @@ -22,11 +22,47 @@ import java.io.Serializable; import java.util.Map; /** - * Used to parse Http Query String into a Map of parameters + * Used to parse Http Query String into a map of parameters with support for fragment + * * @since Struts 6.1.0 */ public interface QueryStringParser extends Serializable { + /** + * @deprecated since Struts 6.2.0, use {@link #parse(String)} instead + */ + @Deprecated Map<String, Object> parse(String queryString, boolean forceValueArray); + /** + * @param queryString a query string to parse + * @return a {@link Result} of parsing the query string + * @since Struts 6.2.0 + */ + Result parse(String queryString); + + /** + * Return an empty {@link Result} + * @return empty result + */ + Result empty(); + + /** + * Represents result of parsing query string by implementation of {@link QueryStringParser} + */ + interface Result { + + Result addParam(String name, String value); + + Result withQueryFragment(String queryFragment); + + Map<String, Object> getQueryParams(); + + String getQueryFragment(); + + boolean contains(String name); + + boolean isEmpty(); + } + } diff --git a/core/src/main/java/org/apache/struts2/url/StrutsQueryStringParser.java b/core/src/main/java/org/apache/struts2/url/StrutsQueryStringParser.java index 4c31d4885..ec7521560 100644 --- a/core/src/main/java/org/apache/struts2/url/StrutsQueryStringParser.java +++ b/core/src/main/java/org/apache/struts2/url/StrutsQueryStringParser.java @@ -43,12 +43,17 @@ public class StrutsQueryStringParser implements QueryStringParser { @Override public Map<String, Object> parse(String queryString, boolean forceValueArray) { + return parse(queryString).getQueryParams(); + } + + @Override + public Result parse(String queryString) { if (StringUtils.isEmpty(queryString)) { LOG.debug("Query String is empty, returning an empty map"); - return Collections.emptyMap(); + return this.empty(); } - Map<String, Object> queryParams = new LinkedHashMap<>(); + Result queryParams = StrutsQueryStringParserResult.create(); String[] params = extractParams(queryString); for (String param : params) { if (StringUtils.isBlank(param)) { @@ -64,9 +69,14 @@ public class StrutsQueryStringParser implements QueryStringParser { } else { paramName = param; } - extractParam(paramName, paramValue, queryParams, forceValueArray); + queryParams = extractParam(paramName, paramValue, queryParams); } - return queryParams; + return queryParams.withQueryFragment(extractFragment(queryString)); + } + + @Override + public Result empty() { + return new StrutsQueryStringParserResult(Collections.emptyMap(), ""); } private String[] extractParams(String queryString) { @@ -81,27 +91,76 @@ public class StrutsQueryStringParser implements QueryStringParser { return params; } - private void extractParam(String paramName, String paramValue, Map<String, Object> queryParams, boolean forceValueArray) { + private Result extractParam(String paramName, String paramValue, Result queryParams) { String decodedParamName = decoder.decode(paramName, true); String decodedParamValue = decoder.decode(paramValue, true); + return queryParams.addParam(decodedParamName, decodedParamValue); + } - if (queryParams.containsKey(decodedParamName) || forceValueArray) { - // WW-1619 append new param value to existing value(s) - Object currentParam = queryParams.get(decodedParamName); - if (currentParam instanceof String) { - queryParams.put(decodedParamName, new String[]{(String) currentParam, decodedParamValue}); - } else { - String[] currentParamValues = (String[]) currentParam; - if (currentParamValues != null) { - List<String> paramList = new ArrayList<>(Arrays.asList(currentParamValues)); - paramList.add(decodedParamValue); - queryParams.put(decodedParamName, paramList.toArray(new String[0])); + private String extractFragment(String queryString) { + int fragmentIndex = queryString.lastIndexOf("#"); + if (fragmentIndex > -1) { + return queryString.substring(fragmentIndex + 1); + } + return ""; + } + + public static class StrutsQueryStringParserResult implements Result { + + private final Map<String, Object> queryParams; + private String queryFragment; + + static Result create() { + return new StrutsQueryStringParserResult(new LinkedHashMap<>(), ""); + } + + private StrutsQueryStringParserResult(Map<String, Object> queryParams, String queryFragment) { + this.queryParams = queryParams; + this.queryFragment = queryFragment; + } + + public Result addParam(String name, String value) { + if (queryParams.containsKey(name)) { + // WW-1619 append new param value to existing value(s) + Object currentParam = queryParams.get(name); + if (currentParam instanceof String) { + queryParams.put(name, new String[]{(String) currentParam, value}); } else { - queryParams.put(decodedParamName, new String[]{decodedParamValue}); + String[] currentParamValues = (String[]) currentParam; + if (currentParamValues != null) { + List<String> paramList = new ArrayList<>(Arrays.asList(currentParamValues)); + paramList.add(value); + queryParams.put(name, paramList.toArray(new String[0])); + } else { + queryParams.put(name, new String[]{value}); + } } + } else { + queryParams.put(name, value); } - } else { - queryParams.put(decodedParamName, decodedParamValue); + + return this; + } + + public Result withQueryFragment(String queryFragment) { + this.queryFragment = queryFragment; + return this; + } + + public Map<String, Object> getQueryParams() { + return Collections.unmodifiableMap(queryParams); + } + + public String getQueryFragment() { + return queryFragment; + } + + public boolean contains(String name) { + return queryParams.containsKey(name); + } + + public boolean isEmpty() { + return queryParams.isEmpty(); } } } diff --git a/core/src/test/java/org/apache/struts2/url/StrutsQueryStringParserTest.java b/core/src/test/java/org/apache/struts2/url/StrutsQueryStringParserTest.java index 93fd9bbaa..c8183725b 100644 --- a/core/src/test/java/org/apache/struts2/url/StrutsQueryStringParserTest.java +++ b/core/src/test/java/org/apache/struts2/url/StrutsQueryStringParserTest.java @@ -35,7 +35,7 @@ public class StrutsQueryStringParserTest { @Test public void testParseQuery() { - Map<String, Object> result = parser.parse("aaa=aaaval&bbb=bbbval&ccc=&%3Ca%22%3E=%3Cval%3E", false); + Map<String, Object> result = parser.parse("aaa=aaaval&bbb=bbbval&ccc=&%3Ca%22%3E=%3Cval%3E").getQueryParams(); assertEquals("aaaval", result.get("aaa")); assertEquals("bbbval", result.get("bbb")); @@ -45,7 +45,7 @@ public class StrutsQueryStringParserTest { @Test public void testParseQueryIntoArray() { - Map<String, Object> result = parser.parse("a=1&a=2&a=3", true); + Map<String, Object> result = parser.parse("a=1&a=2&a=3").getQueryParams(); Object actual = result.get("a"); assertThat(actual).isInstanceOf(String[].class); @@ -54,7 +54,7 @@ public class StrutsQueryStringParserTest { @Test public void testParseEmptyQuery() { - Map<String, Object> result = parser.parse("", false); + Map<String, Object> result = parser.parse("").getQueryParams(); assertNotNull(result); assertEquals(0, result.size()); @@ -62,7 +62,7 @@ public class StrutsQueryStringParserTest { @Test public void testParseNullQuery() { - Map<String, Object> result = parser.parse(null, false); + Map<String, Object> result = parser.parse(null).getQueryParams(); assertNotNull(result); assertEquals(0, result.size()); @@ -70,7 +70,7 @@ public class StrutsQueryStringParserTest { @Test public void testDecodeSpacesInQueryString() { - Map<String, Object> queryParameters = parser.parse("name=value+with+space", false); + Map<String, Object> queryParameters = parser.parse("name=value+with+space").getQueryParams(); assertTrue(queryParameters.containsKey("name")); assertEquals("value with space", queryParameters.get("name")); @@ -78,7 +78,7 @@ public class StrutsQueryStringParserTest { @Test public void shouldProperlySplitParamsWithDoubleEqualSign() { - Map<String, Object> queryParameters = parser.parse("id1=n123=&id2=n3456", false); + Map<String, Object> queryParameters = parser.parse("id1=n123=&id2=n3456").getQueryParams(); assertTrue(queryParameters.containsKey("id1")); assertTrue(queryParameters.containsKey("id2")); @@ -88,7 +88,7 @@ public class StrutsQueryStringParserTest { @Test public void shouldHandleParamWithNoValue1() { - Map<String, Object> queryParameters = parser.parse("paramNoValue", false); + Map<String, Object> queryParameters = parser.parse("paramNoValue").getQueryParams(); assertTrue(queryParameters.containsKey("paramNoValue")); assertEquals("", queryParameters.get("paramNoValue")); @@ -96,7 +96,7 @@ public class StrutsQueryStringParserTest { @Test public void shouldHandleParamWithNoValue2() { - Map<String, Object> queryParameters = parser.parse("paramNoValue¶m1=1234", false); + Map<String, Object> queryParameters = parser.parse("paramNoValue¶m1=1234").getQueryParams(); assertTrue(queryParameters.containsKey("paramNoValue")); assertTrue(queryParameters.containsKey("param1")); @@ -105,10 +105,11 @@ public class StrutsQueryStringParserTest { @Test public void shouldHandleParamAndFragment() { - Map<String, Object> queryParameters = parser.parse("param1=1234#test", false); + QueryStringParser.Result queryParameters = parser.parse("param1=1234#test"); - assertTrue(queryParameters.containsKey("param1")); - assertEquals("1234", queryParameters.get("param1")); + assertTrue(queryParameters.getQueryParams().containsKey("param1")); + assertEquals("1234", queryParameters.getQueryParams().get("param1")); + assertEquals("test", queryParameters.getQueryFragment()); } @Before