Skip to content

Commit 2d12181

Browse files
authored
Merge pull request #145 from everit-org/rw-only
readOnly / writeOnly keyword support
2 parents a5494b3 + 2e0b5dc commit 2d12181

File tree

13 files changed

+287
-64
lines changed

13 files changed

+287
-64
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.everit.json.schema;
2+
3+
public enum ReadWriteContext {
4+
READ, WRITE
5+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.everit.json.schema;
2+
3+
interface ReadWriteValidator {
4+
5+
static ReadWriteValidator createForContext(ReadWriteContext context, ValidationFailureReporter failureReporter) {
6+
if (context == null) {
7+
return NONE;
8+
} else if (context == ReadWriteContext.READ) {
9+
return new WriteOnlyValidator(failureReporter);
10+
} else if (context == ReadWriteContext.WRITE) {
11+
return new ReadOnlyValidator(failureReporter);
12+
}
13+
throw new IllegalArgumentException("unknown ReadWriteContext: " + context);
14+
}
15+
16+
ReadWriteValidator NONE = (schema, subject) -> {
17+
};
18+
19+
void validate(Schema schema, Object subject);
20+
21+
}
22+
23+
class ReadOnlyValidator implements ReadWriteValidator {
24+
25+
private final ValidationFailureReporter failureReporter;
26+
27+
ReadOnlyValidator(ValidationFailureReporter failureReporter) {
28+
this.failureReporter = failureReporter;
29+
}
30+
31+
@Override public void validate(Schema schema, Object subject) {
32+
if (schema.isReadOnly() == Boolean.TRUE && subject != null) {
33+
failureReporter.failure("value is read-only", "readOnly");
34+
}
35+
}
36+
}
37+
38+
class WriteOnlyValidator implements ReadWriteValidator {
39+
40+
private final ValidationFailureReporter failureReporter;
41+
42+
WriteOnlyValidator(ValidationFailureReporter failureReporter) {
43+
this.failureReporter = failureReporter;
44+
}
45+
46+
@Override public void validate(Schema schema, Object subject) {
47+
if (schema.isWriteOnly() == Boolean.TRUE && subject != null) {
48+
failureReporter.failure("value is write-only", "writeOnly");
49+
}
50+
}
51+
}

core/src/main/java/org/everit/json/schema/Schema.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public abstract static class Builder<S extends Schema> {
3333

3434
private Boolean nullable = null;
3535

36+
private Boolean readOnly = null;
37+
38+
private Boolean writeOnly = null;
39+
3640
public Builder<S> title(String title) {
3741
this.title = title;
3842
return this;
@@ -63,10 +67,20 @@ public Builder<S> nullable(Boolean nullable) {
6367
return this;
6468
}
6569

66-
public abstract S build();
70+
public Builder<S> readOnly(Boolean readOnly) {
71+
this.readOnly = readOnly;
72+
return this;
73+
}
6774

75+
public Builder<S> writeOnly(Boolean writeOnly) {
76+
this.writeOnly = writeOnly;
77+
return this;
78+
}
79+
80+
public abstract S build();
6881

6982
}
83+
7084
private final String title;
7185

7286
private final String description;
@@ -79,6 +93,10 @@ public Builder<S> nullable(Boolean nullable) {
7993

8094
private final Boolean nullable;
8195

96+
private final Boolean readOnly;
97+
98+
private final Boolean writeOnly;
99+
82100
/**
83101
* Constructor.
84102
*
@@ -92,6 +110,8 @@ protected Schema(Builder<?> builder) {
92110
this.schemaLocation = builder.schemaLocation;
93111
this.defaultValue = builder.defaultValue;
94112
this.nullable = builder.nullable;
113+
this.readOnly = builder.readOnly;
114+
this.writeOnly = builder.writeOnly;
95115
}
96116

97117
/**
@@ -165,15 +185,17 @@ public boolean equals(Object o) {
165185
Objects.equals(defaultValue, schema.defaultValue) &&
166186
Objects.equals(description, schema.description) &&
167187
Objects.equals(id, schema.id) &&
168-
Objects.equals(nullable, schema.nullable);
188+
Objects.equals(nullable, schema.nullable) &&
189+
Objects.equals(readOnly, schema.readOnly) &&
190+
Objects.equals(writeOnly, schema.writeOnly);
169191
} else {
170192
return false;
171193
}
172194
}
173195

174196
@Override
175197
public int hashCode() {
176-
return Objects.hash(title, description, id, defaultValue, nullable);
198+
return Objects.hash(title, description, id, defaultValue, nullable, readOnly, writeOnly);
177199
}
178200

179201
public String getTitle() {
@@ -203,6 +225,15 @@ public boolean hasDefaultValue() {
203225
public Boolean isNullable() {
204226
return nullable;
205227
}
228+
229+
public Boolean isReadOnly() {
230+
return readOnly;
231+
}
232+
233+
public Boolean isWriteOnly() {
234+
return writeOnly;
235+
}
236+
206237
/**
207238
* Describes the instance as a JSONObject to {@code writer}.
208239
* <p>
@@ -221,6 +252,8 @@ public void describeTo(JSONPrinter writer) {
221252
writer.ifPresent("id", id);
222253
writer.ifPresent("default", defaultValue);
223254
writer.ifPresent("nullable", nullable);
255+
writer.ifPresent("readOnly", readOnly);
256+
writer.ifPresent("writeOnly", writeOnly);
224257
describePropertiesTo(writer);
225258
writer.endObject();
226259
}

core/src/main/java/org/everit/json/schema/ValidatingVisitor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,21 @@ private static boolean isNull(Object obj) {
1919

2020
private ValidationFailureReporter failureReporter;
2121

22+
private final ReadWriteValidator readWriteValidator;
23+
2224
@Override
2325
void visit(Schema schema) {
2426
if (schema.isNullable() == Boolean.FALSE && isNull(subject)) {
2527
failureReporter.failure("value cannot be null", "nullable");
2628
}
29+
readWriteValidator.validate(schema, subject);
2730
super.visit(schema);
2831
}
2932

30-
ValidatingVisitor(Object subject, ValidationFailureReporter failureReporter) {
33+
ValidatingVisitor(Object subject, ValidationFailureReporter failureReporter, ReadWriteValidator readWriteValidator) {
3134
this.subject = subject;
3235
this.failureReporter = failureReporter;
36+
this.readWriteValidator = readWriteValidator;
3337
}
3438

3539
@Override

core/src/main/java/org/everit/json/schema/Validator.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,20 @@ class ValidatorBuilder {
88

99
private boolean failEarly = false;
1010

11+
private ReadWriteContext readWriteContext;
12+
1113
public ValidatorBuilder failEarly() {
1214
this.failEarly = true;
1315
return this;
1416
}
1517

18+
public ValidatorBuilder readWriteContext(ReadWriteContext readWriteContext) {
19+
this.readWriteContext = readWriteContext;
20+
return this;
21+
}
22+
1623
public Validator build() {
17-
return new DefaultValidator(failEarly);
24+
return new DefaultValidator(failEarly, readWriteContext);
1825
}
1926

2027
}
@@ -32,13 +39,17 @@ class DefaultValidator implements Validator {
3239

3340
private boolean failEarly;
3441

35-
public DefaultValidator(boolean failEarly) {
42+
private final ReadWriteContext readWriteContext;
43+
44+
DefaultValidator(boolean failEarly, ReadWriteContext readWriteContext) {
3645
this.failEarly = failEarly;
46+
this.readWriteContext = readWriteContext;
3747
}
3848

3949
@Override public void performValidation(Schema schema, Object input) {
4050
ValidationFailureReporter failureReporter = createFailureReporter(schema);
41-
ValidatingVisitor visitor = new ValidatingVisitor(input, failureReporter);
51+
ReadWriteValidator readWriteValidator = ReadWriteValidator.createForContext(readWriteContext, failureReporter);
52+
ValidatingVisitor visitor = new ValidatingVisitor(input, failureReporter, readWriteValidator);
4253
visitor.visit(schema);
4354
visitor.failIfErrorFound();
4455
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,10 @@ private void loadCommonSchemaProperties(Schema.Builder builder) {
391391
ls.schemaJson().maybe(config.specVersion.idKeyword()).map(JsonValue::requireString).ifPresent(builder::id);
392392
ls.schemaJson().maybe("title").map(JsonValue::requireString).ifPresent(builder::title);
393393
ls.schemaJson().maybe("description").map(JsonValue::requireString).ifPresent(builder::description);
394+
if (ls.specVersion() == DRAFT_7) {
395+
ls.schemaJson().maybe("readOnly").map(JsonValue::requireBoolean).ifPresent(builder::readOnly);
396+
ls.schemaJson().maybe("writeOnly").map(JsonValue::requireBoolean).ifPresent(builder::writeOnly);
397+
}
394398
if (config.nullableSupport) {
395399
builder.nullable(ls.schemaJson()
396400
.maybe("nullable")

core/src/test/java/org/everit/json/schema/StringSchemaTest.java

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@
1515
*/
1616
package org.everit.json.schema;
1717

18-
import nl.jqno.equalsverifier.EqualsVerifier;
19-
import nl.jqno.equalsverifier.Warning;
18+
import static org.everit.json.schema.ObjectComparator.deepEquals;
19+
import static org.everit.json.schema.TestSupport.buildWithLocation;
20+
import static org.junit.Assert.assertTrue;
21+
22+
import java.util.Optional;
23+
2024
import org.everit.json.schema.loader.SchemaLoader;
2125
import org.json.JSONObject;
2226
import org.junit.Assert;
2327
import org.junit.Test;
2428

25-
import java.util.Optional;
26-
27-
import static org.everit.json.schema.TestSupport.buildWithLocation;
28-
import static org.junit.Assert.assertTrue;
29+
import nl.jqno.equalsverifier.EqualsVerifier;
30+
import nl.jqno.equalsverifier.Warning;
2931

3032
public class StringSchemaTest {
3133

@@ -124,39 +126,48 @@ public void equalsVerifier() {
124126
public void toStringTest() {
125127
JSONObject rawSchemaJson = ResourceLoader.DEFAULT.readObj("tostring/stringschema.json");
126128
String actual = SchemaLoader.load(rawSchemaJson).toString();
127-
assertTrue(ObjectComparator.deepEquals(rawSchemaJson, new JSONObject(actual)));
129+
assertTrue(deepEquals(rawSchemaJson, new JSONObject(actual)));
128130
}
129131

130132
@Test
131133
public void toStringWithNullableTrueTest() {
132134
JSONObject rawSchemaJson = ResourceLoader.DEFAULT.readObj("tostring/stringschema.json");
133135
rawSchemaJson.put("nullable", true);
134136
String actual = loadWithNullableSupport(rawSchemaJson).toString();
135-
assertTrue(ObjectComparator.deepEquals(rawSchemaJson, new JSONObject(actual)));
137+
assertTrue(deepEquals(rawSchemaJson, new JSONObject(actual)));
136138
}
137139

138140
@Test
139141
public void toStringWithNullableFalseTest() {
140142
JSONObject rawSchemaJson = ResourceLoader.DEFAULT.readObj("tostring/stringschema.json");
141143
rawSchemaJson.put("nullable", false);
142144
String actual = loadWithNullableSupport(rawSchemaJson).toString();
143-
assertTrue(ObjectComparator.deepEquals(rawSchemaJson, new JSONObject(actual)));
145+
assertTrue(deepEquals(rawSchemaJson, new JSONObject(actual)));
144146
}
145147

146148
@Test
147149
public void toStringNoFormat() {
148150
JSONObject rawSchemaJson = ResourceLoader.DEFAULT.readObj("tostring/stringschema.json");
149151
rawSchemaJson.remove("format");
150152
String actual = SchemaLoader.load(rawSchemaJson).toString();
151-
assertTrue(ObjectComparator.deepEquals(rawSchemaJson, new JSONObject(actual)));
153+
assertTrue(deepEquals(rawSchemaJson, new JSONObject(actual)));
152154
}
153155

154156
@Test
155157
public void toStringNoExplicitType() {
156158
JSONObject rawSchemaJson = ResourceLoader.DEFAULT.readObj("tostring/stringschema.json");
157159
rawSchemaJson.remove("type");
158160
String actual = SchemaLoader.load(rawSchemaJson).toString();
159-
assertTrue(ObjectComparator.deepEquals(rawSchemaJson, new JSONObject(actual)));
161+
assertTrue(deepEquals(rawSchemaJson, new JSONObject(actual)));
162+
}
163+
164+
@Test
165+
public void toString_ReadOnlyWriteOnly() {
166+
Schema subject = StringSchema.builder().readOnly(true).writeOnly(false).build();
167+
JSONObject actual = new JSONObject(subject.toString());
168+
169+
JSONObject expected = ResourceLoader.DEFAULT.readObj("tostring/stringschema-readonly-true-writeonly-false.json");
170+
assertTrue(deepEquals(actual, expected));
160171
}
161172

162173
@Test

0 commit comments

Comments
 (0)