Skip to content

Commit 2a3e7ea

Browse files
committed
Merge branch '6.2.x'
2 parents 014a395 + 5c5cf73 commit 2a3e7ea

File tree

4 files changed

+84
-12
lines changed

4 files changed

+84
-12
lines changed

Diff for: spring-core/src/main/java/org/springframework/util/PatternMatchUtils.java

+52-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,13 +37,24 @@ public abstract class PatternMatchUtils {
3737
* @return whether the String matches the given pattern
3838
*/
3939
public static boolean simpleMatch(@Nullable String pattern, @Nullable String str) {
40+
return simpleMatch(pattern, str, false);
41+
}
42+
43+
/**
44+
* Variant of {@link #simpleMatch(String, String)} that ignores upper/lower case.
45+
*/
46+
public static boolean simpleMatchIgnoreCase(@Nullable String pattern, @Nullable String str) {
47+
return simpleMatch(pattern, str, true);
48+
}
49+
50+
private static boolean simpleMatch(@Nullable String pattern, @Nullable String str, boolean ignoreCase) {
4051
if (pattern == null || str == null) {
4152
return false;
4253
}
4354

4455
int firstIndex = pattern.indexOf('*');
4556
if (firstIndex == -1) {
46-
return pattern.equals(str);
57+
return (ignoreCase ? pattern.equalsIgnoreCase(str) : pattern.equals(str));
4758
}
4859

4960
if (firstIndex == 0) {
@@ -52,25 +63,43 @@ public static boolean simpleMatch(@Nullable String pattern, @Nullable String str
5263
}
5364
int nextIndex = pattern.indexOf('*', 1);
5465
if (nextIndex == -1) {
55-
return str.endsWith(pattern.substring(1));
66+
String part = pattern.substring(1);
67+
return (ignoreCase ? StringUtils.endsWithIgnoreCase(str, part) : str.endsWith(part));
5668
}
5769
String part = pattern.substring(1, nextIndex);
5870
if (part.isEmpty()) {
59-
return simpleMatch(pattern.substring(nextIndex), str);
71+
return simpleMatch(pattern.substring(nextIndex), str, ignoreCase);
6072
}
61-
int partIndex = str.indexOf(part);
73+
int partIndex = indexOf(str, part, 0, ignoreCase);
6274
while (partIndex != -1) {
63-
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()))) {
75+
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()), ignoreCase)) {
6476
return true;
6577
}
66-
partIndex = str.indexOf(part, partIndex + 1);
78+
partIndex = indexOf(str, part, partIndex + 1, ignoreCase);
6779
}
6880
return false;
6981
}
7082

7183
return (str.length() >= firstIndex &&
72-
pattern.startsWith(str.substring(0, firstIndex)) &&
73-
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex)));
84+
checkStartsWith(pattern, str, firstIndex, ignoreCase) &&
85+
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex), ignoreCase));
86+
}
87+
88+
private static boolean checkStartsWith(String pattern, String str, int index, boolean ignoreCase) {
89+
String part = str.substring(0, index);
90+
return (ignoreCase ? StringUtils.startsWithIgnoreCase(pattern, part) : pattern.startsWith(part));
91+
}
92+
93+
private static int indexOf(String str, String otherStr, int startIndex, boolean ignoreCase) {
94+
if (!ignoreCase) {
95+
return str.indexOf(otherStr, startIndex);
96+
}
97+
for (int i = startIndex; i <= (str.length() - otherStr.length()); i++) {
98+
if (str.regionMatches(true, i, otherStr, 0, otherStr.length())) {
99+
return i;
100+
}
101+
}
102+
return -1;
74103
}
75104

76105
/**
@@ -94,4 +123,18 @@ public static boolean simpleMatch(String @Nullable [] patterns, @Nullable String
94123
return false;
95124
}
96125

126+
/**
127+
* Variant of {@link #simpleMatch(String[], String)} that ignores upper/lower case.
128+
*/
129+
public static boolean simpleMatchIgnoreCase(@Nullable String[] patterns, @Nullable String str) {
130+
if (patterns != null) {
131+
for (String pattern : patterns) {
132+
if (simpleMatch(pattern, str, true)) {
133+
return true;
134+
}
135+
}
136+
}
137+
return false;
138+
}
139+
97140
}

Diff for: spring-core/src/test/java/org/springframework/util/PatternMatchUtilsTests.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -53,18 +53,22 @@ void trivial() {
5353
assertMatches(new String[] { null, "" }, "");
5454
assertMatches(new String[] { null, "123" }, "123");
5555
assertMatches(new String[] { null, "*" }, "123");
56+
57+
testMixedCaseMatch("abC", "Abc");
5658
}
5759

5860
@Test
5961
void startsWith() {
6062
assertMatches("get*", "getMe");
6163
assertDoesNotMatch("get*", "setMe");
64+
testMixedCaseMatch("geT*", "GetMe");
6265
}
6366

6467
@Test
6568
void endsWith() {
6669
assertMatches("*Test", "getMeTest");
6770
assertDoesNotMatch("*Test", "setMe");
71+
testMixedCaseMatch("*TeSt", "getMeTesT");
6872
}
6973

7074
@Test
@@ -74,6 +78,10 @@ void between() {
7478
assertMatches("*stuff*", "stuffTest");
7579
assertMatches("*stuff*", "getstuff");
7680
assertMatches("*stuff*", "stuff");
81+
testMixedCaseMatch("*stuff*", "getStuffTest");
82+
testMixedCaseMatch("*stuff*", "StuffTest");
83+
testMixedCaseMatch("*stuff*", "getStuff");
84+
testMixedCaseMatch("*stuff*", "Stuff");
7785
}
7886

7987
@Test
@@ -82,6 +90,8 @@ void startsEnds() {
8290
assertMatches("on*Event", "onEvent");
8391
assertDoesNotMatch("3*3", "3");
8492
assertMatches("3*3", "33");
93+
testMixedCaseMatch("on*Event", "OnMyEvenT");
94+
testMixedCaseMatch("on*Event", "OnEvenT");
8595
}
8696

8797
@Test
@@ -122,18 +132,27 @@ void patternVariants() {
122132

123133
private void assertMatches(String pattern, String str) {
124134
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isTrue();
135+
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isTrue();
125136
}
126137

127138
private void assertDoesNotMatch(String pattern, String str) {
128139
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isFalse();
140+
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isFalse();
141+
}
142+
143+
private void testMixedCaseMatch(String pattern, String str) {
144+
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isFalse();
145+
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isTrue();
129146
}
130147

131148
private void assertMatches(String[] patterns, String str) {
132149
assertThat(PatternMatchUtils.simpleMatch(patterns, str)).isTrue();
150+
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(patterns, str)).isTrue();
133151
}
134152

135153
private void assertDoesNotMatch(String[] patterns, String str) {
136154
assertThat(PatternMatchUtils.simpleMatch(patterns, str)).isFalse();
155+
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(patterns, str)).isFalse();
137156
}
138157

139158
}

Diff for: spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -443,11 +443,10 @@ private MultiValueMap<String, String> expandQueryParams(UriTemplateVariables var
443443
UriTemplateVariables queryVariables = new QueryUriTemplateVariables(variables);
444444
this.queryParams.forEach((key, values) -> {
445445
String name = expandUriComponent(key, queryVariables, this.variableEncoder);
446-
List<String> expandedValues = new ArrayList<>(values.size());
446+
List<String> expandedValues = result.computeIfAbsent(name, k -> new ArrayList<>(values.size()));
447447
for (String value : values) {
448448
expandedValues.add(expandUriComponent(value, queryVariables, this.variableEncoder));
449449
}
450-
result.put(name, expandedValues);
451450
});
452451
return CollectionUtils.unmodifiableMultiValueMap(result);
453452
}

Diff for: spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java

+11
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,17 @@ void parseBuildAndExpandHierarchical(ParserType parserType) {
629629
assertThat(uri.toString()).isEqualTo("ws://example.org:7777/path?q=1#foo");
630630
}
631631

632+
@ParameterizedTest // gh-34783
633+
@EnumSource
634+
void parseBuildAndExpandQueryParamWithSameName(ParserType parserType) {
635+
UriComponents result = UriComponentsBuilder
636+
.fromUriString("/?{pk1}={pv1}&{pk2}={pv2}", parserType)
637+
.buildAndExpand("k1", "v1", "k1", "v2");
638+
639+
assertThat(result.getQuery()).isEqualTo("k1=v1&k1=v2");
640+
assertThat(result.getQueryParams()).containsExactly(Map.entry("k1", List.of("v1", "v2")));
641+
}
642+
632643
@ParameterizedTest
633644
@EnumSource
634645
void buildAndExpandOpaque(ParserType parserType) {

0 commit comments

Comments
 (0)