Skip to content

Commit b534e65

Browse files
committed
Add a ProviderValidators for custom the behaviour of Custom Validators dynamically
1 parent a8dc686 commit b534e65

File tree

14 files changed

+307
-50
lines changed

14 files changed

+307
-50
lines changed

README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,84 @@ Schema schema = schemaLoader.load().build(); // the schema is created using the
430430
schema.validate(jsonDocument); // the document validation happens here
431431
```
432432

433+
### Custom Provider Validators
434+
The library use `DefaultProviderValidators` that collects all the validators used in the process of validation of json.
435+
To use a custom Provider of Validators basically you:
436+
437+
* create your own ProviderValidators in a class implementing the `org.everit.json.schema.loader.ProviderValidators` interface
438+
* bind your ProviderValidators in a `org.everit.json.schema.loader.SchemaLoader.SchemaLoaderBuilder.builder(IstanceOfNewProviderValidators` instance before loading the actual schema
439+
440+
#### Example for validate a field with a function javascript
441+
442+
Assume a schema with a simple function of validation that return true if the subject is equals to number 5:
443+
```java
444+
....
445+
"age": {
446+
...
447+
"format": "javascript: subject == 5"
448+
},
449+
...
450+
```
451+
452+
Let's create a custom ProviderValidators that catch the intent of launch a javascript function and istantiate a new custom validator
453+
```java
454+
class ExampleDefaultProviderValidators extends DefaultProviderValidators {
455+
@Override
456+
public FormatValidator getFormatValidator(String formatName) {
457+
458+
if (!this.getFormatValidators().containsKey(formatName)
459+
&& formatName.startsWith("javascript:")) {
460+
String script = formatName.substring(formatName.lastIndexOf("javascript:"),formatName.length());
461+
this.addFormatValidator(formatName, new JavascriptFormatValidator(formatName, script));
462+
}
463+
return super.getFormatValidator(formatName);
464+
}
465+
}
466+
467+
class JavascriptFormatValidator implements FormatValidator {
468+
469+
String script;
470+
String formatName;
471+
472+
public JavascriptFormatValidator(String formatName, String script) {
473+
this.formatName = formatName;
474+
this.script = script;
475+
}
476+
477+
@Override
478+
public Optional<String> validate(String subject) {
479+
480+
ScriptEngine javaScriptEngine = new ScriptEngineManager().getEngineByName("js");
481+
javaScriptEngine.put("subject",subject);
482+
try {
483+
Boolean result = (Boolean) javaScriptEngine.eval(script);
484+
if (!result) {
485+
return Optional.of(String.format("the length of string [%s] is greater than 5", subject));
486+
}
487+
} catch (ScriptException e) {
488+
e.printStackTrace();
489+
return Optional.of(String.format("Error on evalutation of [%s] ", subject));
490+
}
491+
return Optional.empty();
492+
}
493+
494+
@Override
495+
public String formatName() {
496+
return formatName;
497+
}
498+
}
499+
```
500+
501+
In the builder we can set this new ProviderValidators and launch the validation process.
502+
```java
503+
ExampleDefaultProviderValidators customProviderValidators = new ExampleDefaultProviderValidators();
504+
SchemaLoader schemaLoader = SchemaLoader.builder(customProviderValidators)
505+
.......
506+
.build();
507+
Schema schema = schemaLoader.load().build(); // the schema is created using the above created configuration
508+
schema.validate(jsonDocument); // the document validation happens here
509+
```
510+
433511

434512
## $ref resolution
435513

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.Map;
1212

1313
import org.everit.json.schema.FormatValidator;
14+
import org.everit.json.schema.loader.internal.DefaultProviderValidators;
1415
import org.everit.json.schema.loader.internal.DefaultSchemaClient;
1516
import org.everit.json.schema.regexp.JavaUtilRegexpFactory;
1617
import org.everit.json.schema.regexp.RegexpFactory;
@@ -21,12 +22,13 @@
2122
class LoaderConfig {
2223

2324
static LoaderConfig defaultV4Config() {
24-
return new LoaderConfig(new DefaultSchemaClient(), DRAFT_4.defaultFormatValidators(), DRAFT_4, false);
25+
return new LoaderConfig(new DefaultSchemaClient(), new DefaultProviderValidators(DRAFT_4.defaultFormatValidators()), DRAFT_4, false);
2526
}
2627

2728
final SchemaClient schemaClient;
2829

29-
final Map<String, FormatValidator> formatValidators;
30+
//final Map<String, FormatValidator> formatValidators;
31+
final ProviderValidators providerValidators;
3032

3133
final Map<URI, Object> schemasByURI;
3234

@@ -38,17 +40,17 @@ static LoaderConfig defaultV4Config() {
3840

3941
final RegexpFactory regexpFactory;
4042

41-
LoaderConfig(SchemaClient schemaClient, Map<String, FormatValidator> formatValidators,
43+
LoaderConfig(SchemaClient schemaClient, ProviderValidators providerValidators,
4244
SpecificationVersion specVersion, boolean useDefaults) {
43-
this(schemaClient, formatValidators, emptyMap(), specVersion, useDefaults, false, new JavaUtilRegexpFactory());
45+
this(schemaClient, providerValidators, emptyMap(), specVersion, useDefaults, false, new JavaUtilRegexpFactory());
4446
}
4547

46-
LoaderConfig(SchemaClient schemaClient, Map<String, FormatValidator> formatValidators,
48+
LoaderConfig(SchemaClient schemaClient, ProviderValidators providerValidators,
4749
Map<URI, Object> schemasByURI,
4850
SpecificationVersion specVersion, boolean useDefaults, boolean nullableSupport,
4951
RegexpFactory regexpFactory) {
5052
this.schemaClient = requireNonNull(schemaClient, "schemaClient cannot be null");
51-
this.formatValidators = requireNonNull(formatValidators, "formatValidators cannot be null");
53+
this.providerValidators = requireNonNull(providerValidators, "providerValidators cannot be null");
5254
if (schemasByURI == null) {
5355
this.schemasByURI = new HashMap<>();
5456
} else {
@@ -71,7 +73,7 @@ SchemaLoader.SchemaLoaderBuilder initLoader() {
7173
.useDefaults(this.useDefaults)
7274
.regexpFactory(this.regexpFactory)
7375
.nullableSupport(this.nullableSupport)
74-
.formatValidators(new HashMap<>(this.formatValidators));
76+
.formatValidators(new HashMap<>(this.providerValidators.getFormatValidators()));
7577
loaderBuilder.schemasByURI = schemasByURI;
7678
if (DRAFT_6.equals(specVersion)) {
7779
loaderBuilder.draftV6Support();
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.everit.json.schema.loader;
2+
3+
import org.everit.json.schema.FormatValidator;
4+
import java.util.Map;
5+
6+
/**
7+
* Interface for the provider of validators used in Schema
8+
*/
9+
public interface ProviderValidators {
10+
11+
/**
12+
* Return a FormatValidator with a name
13+
*
14+
* @param formatName Name of the FormatValidator
15+
* @return
16+
*/
17+
FormatValidator getFormatValidator(String formatName);
18+
19+
/**
20+
* Add a FormatValidator with a name
21+
*
22+
* @param formatName Name of the FormatValidator
23+
* @param formatValidator The FormatValidator to add
24+
*/
25+
void addFormatValidator(String formatName, FormatValidator formatValidator);
26+
27+
/**
28+
* Add a FormatValidator with a name if is or is not present
29+
*
30+
* @param formatName Name of the FormatValidator
31+
* @param formatValidator The FormatValidator to add
32+
* @param addIfAbsent If true, the FormatValidator can add on map
33+
*/
34+
void addFormatValidator(String formatName, FormatValidator formatValidator, boolean addIfAbsent);
35+
36+
/**
37+
* Add a map of FormatValidator to the base map
38+
*
39+
* @param formatValidators All the FormatValidator to add on a base map
40+
*/
41+
void addAllFormatValidators(Map<String, FormatValidator> formatValidators);
42+
43+
/**
44+
* Initialize the map of FormatValidator
45+
*
46+
* @param formatValidators the map of FormatValidator
47+
*/
48+
void initAllFormatValidators(Map<String, FormatValidator> formatValidators);
49+
50+
/**
51+
* Return all the validators
52+
*
53+
* @return The map of all Validators
54+
*/
55+
Map<String, FormatValidator> getFormatValidators();
56+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ NumberSchema.Builder buildNumberSchema() {
156156

157157
StringSchema.Builder buildStringSchema() {
158158
PropertySnifferSchemaExtractor.STRING_SCHEMA_PROPS.forEach(consumedKeys::keyConsumed);
159-
return new StringSchemaLoader(schemaJson.ls, config().formatValidators).load();
159+
return new StringSchemaLoader(schemaJson.ls, config().providerValidators).load();
160160
}
161161

162162
abstract List<Schema.Builder<?>> extract();

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

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.everit.json.schema.SchemaException;
2828
import org.everit.json.schema.SchemaLocation;
2929
import org.everit.json.schema.TrueSchema;
30+
import org.everit.json.schema.loader.internal.DefaultProviderValidators;
3031
import org.everit.json.schema.loader.internal.DefaultSchemaClient;
3132
import org.everit.json.schema.loader.internal.WrappingFormatValidator;
3233
import org.everit.json.schema.regexp.JavaUtilRegexpFactory;
@@ -61,7 +62,8 @@ public static class SchemaLoaderBuilder {
6162

6263
SchemaLocation pointerToCurrentObj = SchemaLocation.empty();
6364

64-
Map<String, FormatValidator> formatValidators = new HashMap<>();
65+
//Map<String, FormatValidator> formatValidators = new HashMap<>();
66+
ProviderValidators providerValidators = new DefaultProviderValidators();
6567

6668
SpecificationVersion specVersion;
6769

@@ -81,6 +83,11 @@ public SchemaLoaderBuilder() {
8183
setSpecVersion(DRAFT_4);
8284
}
8385

86+
public SchemaLoaderBuilder(final ProviderValidators providerValidators) {
87+
setSpecVersion(DRAFT_4);
88+
this.providerValidators = providerValidators;
89+
}
90+
8491
/**
8592
* Registers a format validator with the name returned by {@link FormatValidator#formatName()}.
8693
*
@@ -89,7 +96,7 @@ public SchemaLoaderBuilder() {
8996
* @return {@code this}
9097
*/
9198
public SchemaLoaderBuilder addFormatValidator(FormatValidator formatValidator) {
92-
formatValidators.put(formatValidator.formatName(), formatValidator);
99+
providerValidators.addFormatValidator(formatValidator.formatName(), formatValidator);
93100
return this;
94101
}
95102

@@ -106,9 +113,9 @@ public SchemaLoaderBuilder addFormatValidator(FormatValidator formatValidator) {
106113
public SchemaLoaderBuilder addFormatValidator(String formatName,
107114
final FormatValidator formatValidator) {
108115
if (!Objects.equals(formatName, formatValidator.formatName())) {
109-
formatValidators.put(formatName, new WrappingFormatValidator(formatName, formatValidator));
116+
providerValidators.addFormatValidator(formatName, new WrappingFormatValidator(formatName, formatValidator));
110117
} else {
111-
formatValidators.put(formatName, formatValidator);
118+
providerValidators.addFormatValidator(formatName, formatValidator);
112119
}
113120
return this;
114121
}
@@ -154,10 +161,10 @@ private void addBuiltInFormatValidators() {
154161

155162
if (enableOverrideOfBuiltInFormatValidators) {
156163
for (Entry<String, FormatValidator> entry : defaultFormatValidators.entrySet()) {
157-
formatValidators.putIfAbsent(entry.getKey(), entry.getValue());
164+
providerValidators.addFormatValidator(entry.getKey(), entry.getValue(), true);
158165
}
159166
} else {
160-
formatValidators.putAll(defaultFormatValidators);
167+
providerValidators.addAllFormatValidators(defaultFormatValidators);
161168
}
162169
}
163170
@Deprecated
@@ -229,7 +236,7 @@ public SchemaLoaderBuilder schemaJson(Object schema) {
229236
}
230237

231238
SchemaLoaderBuilder formatValidators(Map<String, FormatValidator> formatValidators) {
232-
this.formatValidators = formatValidators;
239+
this.providerValidators.initAllFormatValidators(formatValidators);
233240
return this;
234241
}
235242

@@ -279,6 +286,10 @@ public static SchemaLoaderBuilder builder() {
279286
return new SchemaLoaderBuilder();
280287
}
281288

289+
public static SchemaLoaderBuilder builder(ProviderValidators providerValidators) {
290+
return new SchemaLoaderBuilder(providerValidators);
291+
}
292+
282293
/**
283294
* Loads a JSON schema to a schema validator using a {@link DefaultSchemaClient default HTTP
284295
* client}.
@@ -342,7 +353,8 @@ public SchemaLoader(SchemaLoaderBuilder builder) {
342353
specVersion = builder.specVersion;
343354
}
344355
this.config = new LoaderConfig(builder.schemaClient,
345-
builder.formatValidators,
356+
//builder.formatValidators,
357+
builder.providerValidators,
346358
builder.schemasByURI,
347359
specVersion,
348360
builder.useDefaults,
@@ -477,7 +489,7 @@ SpecificationVersion specVersion() {
477489
*/
478490
@Deprecated
479491
Optional<FormatValidator> getFormatValidator(String formatName) {
480-
return Optional.ofNullable(config.formatValidators.get(formatName));
492+
return Optional.ofNullable(config.providerValidators.getFormatValidator(formatName));
481493
}
482494

483495
}

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import org.everit.json.schema.FormatValidator;
1010
import org.everit.json.schema.StringSchema;
11+
import org.everit.json.schema.loader.internal.DefaultProviderValidators;
1112

1213
/**
1314
* @author erosb
@@ -16,21 +17,22 @@ public class StringSchemaLoader {
1617

1718
private LoadingState ls;
1819

19-
private Map<String, FormatValidator> formatValidators;
20+
//private Map<String, FormatValidator> formatValidators;
21+
private ProviderValidators providerValidators;
2022

2123
/**
2224
* Creates an instance with {@link SpecificationVersion#defaultFormatValidators()} draft v4 format validators}.
2325
*
24-
* @deprecated explicitly specify the format validators with {@link #StringSchemaLoader(LoadingState, Map)} instead
26+
* @deprecated explicitly specify the format validators with {@link #StringSchemaLoader(LoadingState, ProviderValidators)} instead
2527
*/
2628
@Deprecated
2729
public StringSchemaLoader(LoadingState ls) {
28-
this(ls, DRAFT_4.defaultFormatValidators());
30+
this(ls, new DefaultProviderValidators(DRAFT_4.defaultFormatValidators()));
2931
}
3032

31-
StringSchemaLoader(LoadingState ls, Map<String, FormatValidator> formatValidators) {
33+
StringSchemaLoader(LoadingState ls, ProviderValidators providerValidators) {
3234
this.ls = requireNonNull(ls, "ls cannot be null");
33-
this.formatValidators = unmodifiableMap(requireNonNull(formatValidators, "formatValidators cannot be null"));
35+
this.providerValidators = requireNonNull(providerValidators, "providerValidators cannot be null");
3436
}
3537

3638
public StringSchema.Builder load() {
@@ -46,7 +48,7 @@ public StringSchema.Builder load() {
4648
}
4749

4850
private void addFormatValidator(StringSchema.Builder builder, String formatName) {
49-
FormatValidator formatValidator = formatValidators.get(formatName);
51+
FormatValidator formatValidator = providerValidators.getFormatValidator(formatName);
5052
if (formatValidator != null) {
5153
builder.formatValidator(formatValidator);
5254
}

0 commit comments

Comments
 (0)