Skip to content

Commit dae1b7b

Browse files
authored
Merge pull request #137 from erosb/relative-json-pointer-format
V7 relative-json-pointer format
2 parents bad7f98 + 6e984e2 commit dae1b7b

File tree

5 files changed

+244
-3
lines changed

5 files changed

+244
-3
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package org.everit.json.schema.internal;
2+
3+
import java.util.Optional;
4+
5+
import org.everit.json.schema.FormatValidator;
6+
import org.json.JSONPointer;
7+
8+
public class RelativeJsonPointerFormatValidator implements FormatValidator {
9+
10+
private static class ParseException extends Exception {
11+
12+
public ParseException(String input) {
13+
super(String.format("[%s] is not a valid relative JSON Pointer", input));
14+
}
15+
}
16+
17+
private static final class Parser {
18+
19+
public static final int EOF = 26;
20+
21+
private static boolean isDigit(char c) {
22+
return '0' <= c && c <= '9';
23+
}
24+
25+
private String input;
26+
27+
private int pos = 0;
28+
29+
public Parser(String input) {
30+
this.input = input;
31+
}
32+
33+
public void parse() throws ParseException {
34+
parseUpwardsStepCount();
35+
parseJsonPointer();
36+
parseTrailingHashmark();
37+
}
38+
39+
private void parseTrailingHashmark() throws ParseException {
40+
if (pos == input.length()) {
41+
return;
42+
}
43+
if (pos == input.length() - 1 && input.charAt(pos) == '#') {
44+
return;
45+
}
46+
fail();
47+
}
48+
49+
private char next() {
50+
++pos;
51+
if (pos == input.length()) {
52+
return 26;
53+
}
54+
return curr();
55+
}
56+
57+
private char curr() {
58+
if (pos == input.length()) {
59+
return EOF;
60+
}
61+
return input.charAt(pos);
62+
}
63+
64+
private void parseUpwardsStepCount() throws ParseException {
65+
if (!isDigit(curr())) {
66+
fail();
67+
} else if (curr() == '0') {
68+
next();
69+
if (curr() == '/' || curr() == '#' || curr() == EOF) {
70+
pos--;
71+
} else {
72+
fail();
73+
}
74+
}
75+
for (char current = next(); isDigit(current) && pos < input.length(); current = next())
76+
;
77+
}
78+
79+
private void fail() throws ParseException {
80+
throw new ParseException(input);
81+
}
82+
83+
private void parseJsonPointer() throws ParseException {
84+
StringBuilder sb = new StringBuilder();
85+
char current = curr();
86+
while (pos < input.length() && current != '#') {
87+
sb.append(current);
88+
current = next();
89+
}
90+
String pointer = sb.toString();
91+
if (pointer.length() == 0) {
92+
return;
93+
}
94+
if (pointer.startsWith("#")) {
95+
fail();
96+
}
97+
try {
98+
new JSONPointer(pointer);
99+
} catch (IllegalArgumentException e) {
100+
fail();
101+
}
102+
}
103+
}
104+
105+
@Override
106+
107+
public Optional<String> validate(String subject) {
108+
try {
109+
new Parser(subject).parse();
110+
} catch (ParseException e) {
111+
return Optional.of(e.getMessage());
112+
}
113+
return Optional.empty();
114+
}
115+
116+
@Override public String formatName() {
117+
return "relative-json-pointer";
118+
}
119+
}

core/src/main/java/org/everit/json/schema/loader/SpecificationVersion.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.everit.json.schema.internal.IPV4Validator;
1919
import org.everit.json.schema.internal.IPV6Validator;
2020
import org.everit.json.schema.internal.JsonPointerFormatValidator;
21+
import org.everit.json.schema.internal.RelativeJsonPointerFormatValidator;
2122
import org.everit.json.schema.internal.TimeFormatValidator;
2223
import org.everit.json.schema.internal.URIFormatValidator;
2324
import org.everit.json.schema.internal.URIReferenceFormatValidator;
@@ -157,6 +158,7 @@ private static final List<String> keywords(String... keywords) {
157158
Map<String, FormatValidator> formatValidators = new HashMap<>(V6_VALIDATORS);
158159
formatValidators.put("date", new DateFormatValidator());
159160
formatValidators.put("time", new TimeFormatValidator());
161+
formatValidators.put("relative-json-pointer", new RelativeJsonPointerFormatValidator());
160162
V7_VALIDATORS = unmodifiableMap(formatValidators);
161163
}
162164

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package org.everit.json.schema.internal;
2+
3+
import static org.everit.json.schema.internal.ValidatorTestSupport.assertFailure;
4+
import static org.everit.json.schema.internal.ValidatorTestSupport.assertSuccess;
5+
import static org.junit.Assert.assertEquals;
6+
7+
import org.junit.Test;
8+
9+
public class RelativeJsonPointerFormatValidatorTest {
10+
11+
private static final RelativeJsonPointerFormatValidator SUBJECT = new RelativeJsonPointerFormatValidator();
12+
13+
@Test
14+
public void formatNameTest() {
15+
assertEquals("relative-json-pointer", SUBJECT.formatName());
16+
}
17+
18+
@Test
19+
public void onlyUpwardsStepCount() {
20+
assertSuccess("1", SUBJECT);
21+
}
22+
23+
@Test
24+
public void onlyUpwardsCount_multipleDigits() {
25+
assertSuccess("234", SUBJECT);
26+
}
27+
28+
@Test
29+
public void upwardsStepCountWithJsonPointer() {
30+
assertSuccess("23/foo/bar", SUBJECT);
31+
}
32+
33+
@Test
34+
public void multipleDigitsFollowedByPointer() {
35+
assertSuccess("123/a/b", SUBJECT);
36+
}
37+
38+
@Test
39+
public void multipleDigitsFollowedByHashmark() {
40+
assertSuccess("123#", SUBJECT);
41+
}
42+
43+
@Test
44+
public void upwardsStepCountWithHashmark() {
45+
assertSuccess("2#", SUBJECT);
46+
}
47+
48+
@Test
49+
public void negativeUpwardsStepCount() {
50+
assertFailure("-123", SUBJECT, "[-123] is not a valid relative JSON Pointer");
51+
}
52+
53+
@Test
54+
public void hashmarkIsNotTheLastChar() {
55+
assertFailure("3/ab/c#d", SUBJECT, "[3/ab/c#d] is not a valid relative JSON Pointer");
56+
}
57+
58+
@Test
59+
public void nonIntegerBeginning() {
60+
assertFailure("abc/d/e/f", SUBJECT, "[abc/d/e/f] is not a valid relative JSON Pointer");
61+
}
62+
63+
@Test
64+
public void upwardsStepCountFollowedByInvalidJsonPointer() {
65+
assertFailure("123asd~~b", SUBJECT, "[123asd~~b] is not a valid relative JSON Pointer");
66+
}
67+
68+
@Test
69+
public void upwardsStepCountFollowedByURLFormJsonPointer() {
70+
assertFailure("123#/a/b", SUBJECT, "[123#/a/b] is not a valid relative JSON Pointer");
71+
}
72+
73+
@Test
74+
public void noUpwardsStepCount() {
75+
assertFailure("/foo/bar", SUBJECT, "[/foo/bar] is not a valid relative JSON Pointer");
76+
}
77+
78+
@Test
79+
public void leadingZeroFailure() {
80+
assertFailure("0123", SUBJECT, "[0123] is not a valid relative JSON Pointer");
81+
}
82+
83+
@Test
84+
public void onlyLeadingZero() {
85+
assertSuccess("0", SUBJECT);
86+
}
87+
88+
@Test
89+
public void upwardsStepCountIsZeroFollowedByPointer() {
90+
assertSuccess("0/a/b", SUBJECT);
91+
}
92+
93+
@Test
94+
public void upwardsStepCountIsZeroFollowedByHashmark() {
95+
assertSuccess("0#", SUBJECT);
96+
}
97+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.everit.json.schema.internal;
2+
3+
import static org.junit.Assert.assertFalse;
4+
import static org.junit.Assert.assertTrue;
5+
6+
import org.junit.Test;
7+
8+
public class URIV4FormatValidatorTest {
9+
10+
@Test
11+
public void relativeURI() {
12+
assertFalse(new URIV4FormatValidator().validate("abc").isPresent());
13+
}
14+
15+
@Test
16+
public void absoluteURI() {
17+
assertFalse(new URIV4FormatValidator().validate("http://a.b.c").isPresent());
18+
}
19+
20+
@Test
21+
public void notURI() {
22+
assertTrue(new URIV4FormatValidator().validate("\\\\\\\\WINDOWS\\\\fileshare").isPresent());
23+
}
24+
}

core/src/test/java/org/everit/json/schema/internal/ValidatorTestSupport.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99

1010
public final class ValidatorTestSupport {
1111

12-
static void assertSuccess(final String subject, final FormatValidator format) {
12+
static void assertSuccess(String subject, FormatValidator format) {
1313
Optional<String> opt = format.validate(subject);
1414
Assert.assertNotNull("the optional is not null", opt);
1515
Assert.assertFalse("failure not exist", opt.isPresent());
1616
}
1717

18-
static void assertFailure(final String subject, final FormatValidator format,
19-
final String expectedFailure) {
18+
static void assertFailure(String subject, FormatValidator format, String expectedFailure) {
2019
Optional<String> opt = format.validate(subject);
2120
Assert.assertNotNull("the optional is not null", opt);
2221
assertTrue("failure exists", opt.isPresent());

0 commit comments

Comments
 (0)