Skip to content

Commit ad3ff30

Browse files
fduttonFaron Dutton
andauthored
Corrects Java's failure to match an end anchor when immediately preceded by a quantifier. (#815)
Resolves #814 Co-authored-by: Faron Dutton <[email protected]>
1 parent ac6ecd0 commit ad3ff30

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

src/main/java/com/networknt/schema/regex/JDKRegularExpression.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,19 @@ class JDKRegularExpression implements RegularExpression {
99
private final boolean hasEndAnchor;
1010

1111
JDKRegularExpression(String regex) {
12-
this.pattern = Pattern.compile(regex);
12+
// The patterns in JSON Schema are not implicitly anchored so we must
13+
// use Matcher.find(). However, this method does not honor the end
14+
// anchor when immediately preceded by a quantifier (e.g., ?, *, +).
15+
// To make this work in all cases, we wrap the pattern in a group.
1316
this.hasStartAnchor = '^' == regex.charAt(0);
1417
this.hasEndAnchor = '$' == regex.charAt(regex.length() - 1);
18+
String pattern = regex;
19+
if (this.hasEndAnchor) {
20+
pattern = pattern.substring(this.hasStartAnchor ? 1 : 0, pattern.length() - 1);
21+
pattern = '(' + pattern + ")$";
22+
if (this.hasStartAnchor) pattern = '^' + pattern;
23+
}
24+
this.pattern = Pattern.compile(pattern);
1525
}
1626

1727
@Override
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.networknt.schema.regex;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
class Issue814Test {
8+
9+
@Test
10+
void jdkTypePattern() {
11+
JDKRegularExpression ex = new JDKRegularExpression("^list|date|time|string|enum|int|double|long|boolean|number$");
12+
assertTrue(ex.matches("list"));
13+
assertTrue(ex.matches("string"));
14+
assertTrue(ex.matches("boolean"));
15+
assertTrue(ex.matches("number"));
16+
assertTrue(ex.matches("enum"));
17+
}
18+
19+
@Test
20+
void jdkOptionsPattern() {
21+
JDKRegularExpression ex = new JDKRegularExpression("^\\d*|[a-zA-Z_]+$");
22+
assertTrue(ex.matches("external"));
23+
assertTrue(ex.matches("external_gte"));
24+
assertTrue(ex.matches("force"));
25+
assertTrue(ex.matches("internal"));
26+
}
27+
28+
@Test
29+
void joniTypePattern() {
30+
JoniRegularExpression ex = new JoniRegularExpression("^list|date|time|string|enum|int|double|long|boolean|number$");
31+
assertTrue(ex.matches("list"));
32+
assertTrue(ex.matches("string"));
33+
assertTrue(ex.matches("boolean"));
34+
assertTrue(ex.matches("number"));
35+
assertTrue(ex.matches("enum"));
36+
}
37+
38+
@Test
39+
void joniOptionsPattern() {
40+
JoniRegularExpression ex = new JoniRegularExpression("^\\d*|[a-zA-Z_]+$");
41+
assertTrue(ex.matches("internal"));
42+
assertTrue(ex.matches("external"));
43+
assertTrue(ex.matches("external_gte"));
44+
assertTrue(ex.matches("force"));
45+
}
46+
47+
}

0 commit comments

Comments
 (0)