Skip to content

Commit 6c39444

Browse files
committed
Support full regular expression on PatterMatcher.match()
1 parent dcd1ac8 commit 6c39444

File tree

4 files changed

+134
-4
lines changed

4 files changed

+134
-4
lines changed

spring-batch-infrastructure/src/main/java/org/springframework/batch/support/PatternMatcher.java

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2021 the original author or authors.
2+
* Copyright 2006-2023 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.
@@ -19,13 +19,15 @@
1919
import java.util.Comparator;
2020
import java.util.List;
2121
import java.util.Map;
22+
import java.util.regex.Pattern;
2223

2324
import org.springframework.util.Assert;
2425

2526
/**
2627
* @author Dave Syer
2728
* @author Dan Garrette
2829
* @author Marten Deinum
30+
* @author Injae Kim
2931
*/
3032
public class PatternMatcher<S> {
3133

@@ -180,6 +182,19 @@ public static boolean match(String pattern, String str) {
180182
return true;
181183
}
182184

185+
/**
186+
* Tests whether or not a string matches against a regular expression.
187+
* @param regex regular expression to match against. Must not be {@code null}.
188+
* @param str string which must be matched against the regular expression. Must not be {@code null}.
189+
* @return {@code true} if the string matches against the regular expression, or {@code false} otherwise.
190+
*/
191+
public static boolean matchRegex(String regex, String str) {
192+
Assert.notNull(regex, "Regex must not be null");
193+
Assert.notNull(str, "Str must not be null");
194+
195+
return Pattern.matches(regex, str);
196+
}
197+
183198
/**
184199
* <p>
185200
* This method takes a String key and a map from Strings to values of any type. During
@@ -202,12 +217,23 @@ public S match(String line) {
202217
Assert.notNull(line, "A non-null key must be provided to match against.");
203218

204219
for (String key : sorted) {
205-
if (PatternMatcher.match(key, line)) {
220+
if (match(key, line)) {
206221
value = map.get(key);
207222
break;
208223
}
209224
}
210225

226+
if (value == null) {
227+
for (String key : sorted) {
228+
try {
229+
if (matchRegex(key, line)) {
230+
value = map.get(key);
231+
break;
232+
}
233+
} catch (Throwable ignored) {}
234+
}
235+
}
236+
211237
if (value == null) {
212238
throw new IllegalStateException("Could not find a matching pattern for key=[" + line + "]");
213239
}

spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/PatternMatchingCompositeLineMapperTests.java

+22
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* @author Dan Garrette
3434
* @author Dave Syer
3535
* @author Mahmoud Ben Hassine
36+
* @author Injae Kim
3637
* @since 2.0
3738
*/
3839
class PatternMatchingCompositeLineMapperTests {
@@ -51,6 +52,7 @@ void testKeyFound() throws Exception {
5152
Map<String, LineTokenizer> tokenizers = new HashMap<>();
5253
tokenizers.put("foo*", line -> new DefaultFieldSet(new String[] { "a", "b" }));
5354
tokenizers.put("bar*", line -> new DefaultFieldSet(new String[] { "c", "d" }));
55+
tokenizers.put("regex.*", line -> new DefaultFieldSet(new String[] { "e", "f" }));
5456
mapper.setTokenizers(tokenizers);
5557

5658
Map<String, FieldSetMapper<Name>> fieldSetMappers = new HashMap<>();
@@ -62,15 +64,35 @@ void testKeyFound() throws Exception {
6264
assertEquals(new Name("d", "c", 0), name);
6365
}
6466

67+
@Test
68+
void testKeyFoundByRegex() throws Exception {
69+
Map<String, LineTokenizer> tokenizers = new HashMap<>();
70+
tokenizers.put("foo*", line -> new DefaultFieldSet(new String[] { "a", "b" }));
71+
tokenizers.put("bar*", line -> new DefaultFieldSet(new String[] { "c", "d" }));
72+
tokenizers.put("regex.*", line -> new DefaultFieldSet(new String[] { "e", "f" }));
73+
mapper.setTokenizers(tokenizers);
74+
75+
Map<String, FieldSetMapper<Name>> fieldSetMappers = new HashMap<>();
76+
fieldSetMappers.put("foo*", fs -> new Name(fs.readString(0), fs.readString(1), 0));
77+
fieldSetMappers.put("bar*", fs -> new Name(fs.readString(1), fs.readString(0), 0));
78+
fieldSetMappers.put("regex.*", fs -> new Name(fs.readString(0), fs.readString(1), 0));
79+
mapper.setFieldSetMappers(fieldSetMappers);
80+
81+
Name name = mapper.mapLine("regex-ABC_12345", 1);
82+
assertEquals(new Name("e", "f", 0), name);
83+
}
84+
6585
@Test
6686
void testMapperKeyNotFound() {
6787
Map<String, LineTokenizer> tokenizers = new HashMap<>();
6888
tokenizers.put("foo*", line -> new DefaultFieldSet(new String[] { "a", "b" }));
6989
tokenizers.put("bar*", line -> new DefaultFieldSet(new String[] { "c", "d" }));
90+
tokenizers.put("regex.*", line -> new DefaultFieldSet(new String[] { "e", "f" }));
7091
mapper.setTokenizers(tokenizers);
7192

7293
Map<String, FieldSetMapper<Name>> fieldSetMappers = new HashMap<>();
7394
fieldSetMappers.put("foo*", fs -> new Name(fs.readString(0), fs.readString(1), 0));
95+
fieldSetMappers.put("regex.*", fs -> new Name(fs.readString(0), fs.readString(1), 0));
7496
mapper.setFieldSetMappers(fieldSetMappers);
7597

7698
assertThrows(IllegalStateException.class, () -> mapper.mapLine("bar", 1));

spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/transform/PatternMatchingCompositeLineTokenizerTests.java

+23-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* @author Ben Hale
3131
* @author Dan Garrette
3232
* @author Dave Syer
33+
* @author Injae Kim
3334
*/
3435
class PatternMatchingCompositeLineTokenizerTests {
3536

@@ -45,6 +46,7 @@ void testEmptyKeyMatchesAnyLine() throws Exception {
4546
Map<String, LineTokenizer> map = new HashMap<>();
4647
map.put("*", new DelimitedLineTokenizer());
4748
map.put("foo", line -> null);
49+
map.put("regex.*", line -> null);
4850
tokenizer.setTokenizers(map);
4951
tokenizer.afterPropertiesSet();
5052
FieldSet fields = tokenizer.tokenize("abc");
@@ -53,23 +55,43 @@ void testEmptyKeyMatchesAnyLine() throws Exception {
5355

5456
@Test
5557
void testEmptyKeyDoesNotMatchWhenAlternativeAvailable() throws Exception {
56-
5758
Map<String, LineTokenizer> map = new LinkedHashMap<>();
5859
map.put("*", line -> null);
5960
map.put("foo*", new DelimitedLineTokenizer());
61+
map.put("regex.*", line -> null);
6062
tokenizer.setTokenizers(map);
6163
tokenizer.afterPropertiesSet();
6264
FieldSet fields = tokenizer.tokenize("foo,bar");
6365
assertEquals("bar", fields.readString(1));
6466
}
6567

68+
@Test
69+
void testMatchRegex() throws Exception {
70+
Map<String, LineTokenizer> map = new HashMap<>();
71+
map.put("foo", line -> null);
72+
map.put("regex.*", new DelimitedLineTokenizer());
73+
tokenizer.setTokenizers(map);
74+
tokenizer.afterPropertiesSet();
75+
FieldSet fields = tokenizer.tokenize("regex-ABC_12345,REGEX");
76+
assertEquals(2, fields.getFieldCount());
77+
assertEquals("regex-ABC_12345", fields.readString(0));
78+
assertEquals("REGEX", fields.readString(1));
79+
}
80+
6681
@Test
6782
void testNoMatch() throws Exception {
6883
tokenizer.setTokenizers(Collections.singletonMap("foo", (LineTokenizer) new DelimitedLineTokenizer()));
6984
tokenizer.afterPropertiesSet();
7085
assertThrows(IllegalStateException.class, () -> tokenizer.tokenize("nomatch"));
7186
}
7287

88+
@Test
89+
void testNoMatchRegex() throws Exception {
90+
tokenizer.setTokenizers(Collections.singletonMap("foo.*", (LineTokenizer) new DelimitedLineTokenizer()));
91+
tokenizer.afterPropertiesSet();
92+
assertThrows(IllegalStateException.class, () -> tokenizer.tokenize("nomatch"));
93+
}
94+
7395
@Test
7496
void testMatchWithPrefix() throws Exception {
7597
tokenizer.setTokenizers(

spring-batch-infrastructure/src/test/java/org/springframework/batch/support/PatternMatcherTests.java

+61-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2022 the original author or authors.
2+
* Copyright 2006-2023 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.
@@ -22,11 +22,13 @@
2222

2323
import java.util.HashMap;
2424
import java.util.Map;
25+
import java.util.regex.PatternSyntaxException;
2526

2627
import org.junit.jupiter.api.Test;
2728

2829
/**
2930
* @author Dan Garrette
31+
* @author Injae Kim
3032
* @since 2.0
3133
*/
3234
class PatternMatcherTests {
@@ -37,6 +39,7 @@ class PatternMatcherTests {
3739
map.put("an*", 3);
3840
map.put("a*", 2);
3941
map.put("big*", 4);
42+
map.put("bcd.*", 5);
4043
}
4144

4245
private static final Map<String, Integer> defaultMap = new HashMap<>();
@@ -49,6 +52,15 @@ class PatternMatcherTests {
4952
defaultMap.put("*", 1);
5053
}
5154

55+
private static final Map<String, Integer> regexMap = new HashMap<>();
56+
57+
static {
58+
regexMap.put("abc.*", 1);
59+
regexMap.put("a...e", 2);
60+
regexMap.put("123.[0-9][0-9]\\d", 3);
61+
regexMap.put("*............", 100); // invalid regex format
62+
}
63+
5264
@Test
5365
void testMatchNoWildcardYes() {
5466
assertTrue(PatternMatcher.match("abc", "abc"));
@@ -104,6 +116,29 @@ void testMatchStarNo() {
104116
assertFalse(PatternMatcher.match("a*c", "abdeg"));
105117
}
106118

119+
@Test
120+
void testMatchRegex() {
121+
assertTrue(PatternMatcher.matchRegex("abc.*", "abcde"));
122+
}
123+
124+
@Test
125+
void testMatchRegex_notMatched() {
126+
assertFalse(PatternMatcher.matchRegex("abc.*", "cdefg"));
127+
assertFalse(PatternMatcher.matchRegex("abc.", "abcde"));
128+
}
129+
130+
@Test
131+
void testMatchRegex_thrown_invalidRegexFormat() {
132+
assertThrows(PatternSyntaxException.class, () -> PatternMatcher.matchRegex("*..", "abc"));
133+
}
134+
135+
@Test
136+
void testMatchRegex_thrown_notNullParam() {
137+
assertThrows(IllegalArgumentException.class, () -> PatternMatcher.matchRegex("regex", null));
138+
assertThrows(IllegalArgumentException.class, () -> PatternMatcher.matchRegex(null, "str"));
139+
}
140+
141+
107142
@Test
108143
void testMatchPrefixSubsumed() {
109144
assertEquals(2, new PatternMatcher<>(map).match("apple").intValue());
@@ -119,6 +154,11 @@ void testMatchPrefixUnrelated() {
119154
assertEquals(4, new PatternMatcher<>(map).match("biggest").intValue());
120155
}
121156

157+
@Test
158+
void testMatchByRegex() {
159+
assertEquals(5, new PatternMatcher<>(map).match("bcdef12345").intValue());
160+
}
161+
122162
@Test
123163
void testMatchPrefixNoMatch() {
124164
PatternMatcher<Integer> matcher = new PatternMatcher<>(map);
@@ -140,4 +180,24 @@ void testMatchPrefixDefaultValueNoMatch() {
140180
assertEquals(1, new PatternMatcher<>(defaultMap).match("bat").intValue());
141181
}
142182

183+
@Test
184+
void testMatchRegexPrefix() {
185+
assertEquals(1, new PatternMatcher<>(regexMap).match("abcdefg").intValue());
186+
}
187+
188+
@Test
189+
void testMatchRegexWildCards() {
190+
assertEquals(2, new PatternMatcher<>(regexMap).match("a12De").intValue());
191+
}
192+
193+
@Test
194+
void testMatchRegexDigits() {
195+
assertEquals(3, new PatternMatcher<>(regexMap).match("123-789").intValue());
196+
}
197+
198+
@Test
199+
void testMatchRegexNotMatched() {
200+
assertThrows(IllegalStateException.class, () -> new PatternMatcher<>(regexMap).match("Hello world!"));
201+
}
202+
143203
}

0 commit comments

Comments
 (0)