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
The following commit(s) were added to refs/heads/WW-5310-fragment by this push: new e5c7a30f3 WW-5310 Deprecates the old API in favour of new one e5c7a30f3 is described below commit e5c7a30f3d4c608f1728d949f1a0ef0a4167f4ba 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 | 17 +++-- .../struts2/result/ServletDispatcherResult.java | 8 +- .../org/apache/struts2/url/QueryStringParser.java | 14 +++- .../org/apache/struts2/url/QueryStringResult.java | 89 ++++++++++++++++++++++ .../struts2/url/StrutsQueryStringParser.java | 44 +++++------ .../struts2/url/StrutsQueryStringParserTest.java | 23 +++--- 6 files changed, 146 insertions(+), 49 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..18d866542 100644 --- a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java +++ b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java @@ -30,6 +30,7 @@ import org.apache.struts2.StrutsException; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.url.QueryStringParser; +import org.apache.struts2.url.QueryStringResult; import org.apache.struts2.views.util.UrlHelper; import java.io.IOException; @@ -159,10 +160,10 @@ public class ServletUrlRenderer implements UrlRenderer { } } - Map<String, Object> actionParams = null; + QueryStringResult queryStringResult = QueryStringResult.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 +177,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 +214,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 +292,11 @@ public class ServletUrlRenderer implements UrlRenderer { private void includeGetParameters(UrlProvider urlComponent) { String query = extractQueryString(urlComponent); - mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), queryStringParser.parse(query, false)); + QueryStringResult 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 +344,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..e1bc17eb8 100644 --- a/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java +++ b/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java @@ -28,12 +28,12 @@ import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsStatics; import org.apache.struts2.dispatcher.HttpParameters; import org.apache.struts2.url.QueryStringParser; +import org.apache.struts2.url.QueryStringResult; 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 +140,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(); + QueryStringResult 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..d607ab817 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,23 @@ 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 QueryStringResult} of parsing the query string + * @since Struts 6.2.0 + */ + QueryStringResult parse(String queryString); + } diff --git a/core/src/main/java/org/apache/struts2/url/QueryStringResult.java b/core/src/main/java/org/apache/struts2/url/QueryStringResult.java new file mode 100644 index 000000000..0836d2eb8 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/url/QueryStringResult.java @@ -0,0 +1,89 @@ +/* + * 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.struts2.url; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class QueryStringResult { + + private final Map<String, Object> queryParams; + private String queryFragment; + + public static QueryStringResult empty() { + return new QueryStringResult(Collections.emptyMap(), ""); + } + + public static QueryStringResult create() { + return new QueryStringResult(new LinkedHashMap<>(), ""); + } + + private QueryStringResult(Map<String, Object> queryParams, String queryFragment) { + this.queryParams = queryParams; + this.queryFragment = queryFragment; + } + + public QueryStringResult 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 { + 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); + } + + return this; + } + + public QueryStringResult 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/main/java/org/apache/struts2/url/StrutsQueryStringParser.java b/core/src/main/java/org/apache/struts2/url/StrutsQueryStringParser.java index 4c31d4885..5cc50a01f 100644 --- a/core/src/main/java/org/apache/struts2/url/StrutsQueryStringParser.java +++ b/core/src/main/java/org/apache/struts2/url/StrutsQueryStringParser.java @@ -23,11 +23,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; public class StrutsQueryStringParser implements QueryStringParser { @@ -43,12 +38,17 @@ public class StrutsQueryStringParser implements QueryStringParser { @Override public Map<String, Object> parse(String queryString, boolean forceValueArray) { + return parse(queryString).getQueryParams(); + } + + @Override + public QueryStringResult parse(String queryString) { if (StringUtils.isEmpty(queryString)) { LOG.debug("Query String is empty, returning an empty map"); - return Collections.emptyMap(); + return QueryStringResult.empty(); } - Map<String, Object> queryParams = new LinkedHashMap<>(); + QueryStringResult queryParams = QueryStringResult.create(); String[] params = extractParams(queryString); for (String param : params) { if (StringUtils.isBlank(param)) { @@ -64,9 +64,9 @@ 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)); } private String[] extractParams(String queryString) { @@ -81,27 +81,17 @@ public class StrutsQueryStringParser implements QueryStringParser { return params; } - private void extractParam(String paramName, String paramValue, Map<String, Object> queryParams, boolean forceValueArray) { + private QueryStringResult extractParam(String paramName, String paramValue, QueryStringResult 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])); - } else { - queryParams.put(decodedParamName, new String[]{decodedParamValue}); - } - } - } else { - queryParams.put(decodedParamName, decodedParamValue); + private String extractFragment(String queryString) { + int fragmentIndex = queryString.lastIndexOf("#"); + if (fragmentIndex > -1) { + return queryString.substring(fragmentIndex + 1); } + return ""; } } 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..7f44fe834 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); + QueryStringResult 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