Skip to content

Commit e624ed5

Browse files
committed
Refactor Dialect Registry and Schema Registry
1 parent df888a8 commit e624ed5

File tree

5 files changed

+264
-48
lines changed

5 files changed

+264
-48
lines changed

src/main/java/com/networknt/schema/SchemaRegistry.java

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.io.IOException;
3737
import java.io.InputStream;
3838
import java.net.URI;
39+
import java.util.List;
3940
import java.util.Map;
4041
import java.util.concurrent.ConcurrentHashMap;
4142
import java.util.concurrent.ConcurrentMap;
@@ -300,6 +301,47 @@ public static SchemaRegistry withDefaultDialectId(String dialectId, Consumer<Sch
300301
}
301302
return builder.build();
302303
}
304+
305+
306+
/**
307+
* Creates a new schema registry with a default schema dialect. The schema
308+
* dialect will only be used if the input does not specify a $schema.
309+
* <p>
310+
* This uses a dialect registry that contains all the supported standard
311+
* specification dialects, Draft 4, Draft 6, Draft 7, Draft 2019-09 and Draft
312+
* 2020-12.
313+
*
314+
* @param specificationVersion the default dialect id corresponding to the
315+
* specification version used when the schema does
316+
* not specify the $schema keyword
317+
* @return the factory
318+
*/
319+
public static SchemaRegistry withDefaultDialect(Dialect dialect) {
320+
return withDefaultDialect(dialect, null);
321+
}
322+
323+
/**
324+
* Creates a new schema registry with a default schema dialect. The schema
325+
* dialect will only be used if the input does not specify a $schema.
326+
* <p>
327+
* This uses a dialect registry that contains all the supported standard
328+
* specification dialects, Draft 4, Draft 6, Draft 7, Draft 2019-09 and Draft
329+
* 2020-12.
330+
*
331+
* @param specificationVersion the default dialect id corresponding to the
332+
* specification version used when the schema does
333+
* not specify the $schema keyword
334+
* @param customizer to customize the registry
335+
* @return the factory
336+
*/
337+
public static SchemaRegistry withDefaultDialect(Dialect dialect, Consumer<SchemaRegistry.Builder> customizer) {
338+
SchemaRegistry.Builder builder = builder().defaultDialectId(dialect.getId())
339+
.dialectRegistry(new DefaultDialectRegistry(dialect));
340+
if (customizer != null) {
341+
customizer.accept(builder);
342+
}
343+
return builder.build();
344+
}
303345

304346
/**
305347
* Gets a new schema registry that supports a specific dialect only.
@@ -316,6 +358,44 @@ public static SchemaRegistry withDialect(Dialect dialect) {
316358
return withDialect(dialect, null);
317359
}
318360

361+
/**
362+
* Gets a new schema registry that supports a list of specific dialects only.
363+
* <p>
364+
* Schemas that do not specify dialect using $schema will use the first dialect
365+
* on the list.
366+
* <p>
367+
* This uses a dialect registry that only contains the list of dialects and will
368+
* throw an exception for unknown dialects.
369+
*
370+
* @param dialect the dialect
371+
* @param customizer to customize the registry
372+
* @return the schema registry
373+
*/
374+
public static SchemaRegistry withDialect(Dialect dialect, Consumer<SchemaRegistry.Builder> customizer) {
375+
SchemaRegistry.Builder builder = builder().defaultDialectId(dialect.getId())
376+
.dialectRegistry(new BasicDialectRegistry(dialect));
377+
if (customizer != null) {
378+
customizer.accept(builder);
379+
}
380+
return builder.build();
381+
}
382+
383+
/**
384+
* Gets a new schema registry that supports a list of specific dialects only.
385+
* <p>
386+
* Schemas that do not specify dialect using $schema will use the first dialect
387+
* on the list.
388+
* <p>
389+
* This uses a dialect registry that only contains the list of dialects and will
390+
* throw an exception for unknown dialects.
391+
*
392+
* @param dialects the dialect
393+
* @return the schema registry
394+
*/
395+
public static SchemaRegistry withDialects(List<Dialect> dialects) {
396+
return withDialects(dialects, null);
397+
}
398+
319399
/**
320400
* Gets a new schema registry that supports a specific dialect only.
321401
* <p>
@@ -328,15 +408,16 @@ public static SchemaRegistry withDialect(Dialect dialect) {
328408
* @param customizer to customize the registry
329409
* @return the schema registry
330410
*/
331-
public static SchemaRegistry withDialect(Dialect dialect, Consumer<SchemaRegistry.Builder> customizer) {
332-
SchemaRegistry.Builder builder = builder().defaultDialectId(dialect.getId())
333-
.dialectRegistry(new BasicDialectRegistry(dialect));
411+
public static SchemaRegistry withDialects(List<Dialect> dialects, Consumer<SchemaRegistry.Builder> customizer) {
412+
SchemaRegistry.Builder builder = builder().defaultDialectId(dialects.get(0).getId())
413+
.dialectRegistry(new BasicDialectRegistry(dialects));
334414
if (customizer != null) {
335415
customizer.accept(builder);
336416
}
337417
return builder.build();
338418
}
339419

420+
340421
/**
341422
* Builder from an existing {@link SchemaRegistry}.
342423
* <p>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.networknt.schema.dialect;
17+
18+
import java.util.Map;
19+
import java.util.Map.Entry;
20+
21+
import com.fasterxml.jackson.databind.JsonNode;
22+
import com.networknt.schema.Error;
23+
import com.networknt.schema.InvalidSchemaException;
24+
import com.networknt.schema.Schema;
25+
import com.networknt.schema.SchemaLocation;
26+
import com.networknt.schema.SchemaRegistry;
27+
import com.networknt.schema.SpecificationVersion;
28+
29+
/**
30+
* Abstract {@link DialectRegistry}.
31+
*/
32+
public abstract class AbstractDialectRegistry implements DialectRegistry {
33+
protected Dialect loadDialect(String iri, SchemaRegistry schemaFactory) {
34+
try {
35+
Dialect result = loadDialectBuilder(iri, schemaFactory).build();
36+
return result;
37+
} catch (InvalidSchemaException e) {
38+
throw e;
39+
} catch (Exception e) {
40+
Error error = Error.builder().message("Failed to load dialect ''{0}''").arguments(iri).build();
41+
throw new InvalidSchemaException(error, e);
42+
}
43+
}
44+
45+
protected Dialect.Builder loadDialectBuilder(String iri, SchemaRegistry schemaFactory) {
46+
Schema schema = schemaFactory.getSchema(SchemaLocation.of(iri));
47+
Dialect.Builder builder = Dialect.builder(iri, schema.getSchemaContext().getDialect());
48+
SpecificationVersion specification = schema.getSchemaContext().getDialect().getSpecificationVersion();
49+
if (specification != null) {
50+
if (specification.getOrder() >= SpecificationVersion.DRAFT_2019_09.getOrder()) {
51+
// Process vocabularies
52+
JsonNode vocabulary = schema.getSchemaNode().get("$vocabulary");
53+
if (vocabulary != null) {
54+
builder.vocabularies(Map::clear);
55+
for (Entry<String, JsonNode> vocabs : vocabulary.properties()) {
56+
builder.vocabulary(vocabs.getKey(), vocabs.getValue().booleanValue());
57+
}
58+
}
59+
}
60+
}
61+
return builder;
62+
}
63+
}

src/main/java/com/networknt/schema/dialect/BasicDialectRegistry.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,24 @@
1515
*/
1616
package com.networknt.schema.dialect;
1717

18+
import java.util.Collection;
19+
import java.util.HashMap;
20+
import java.util.Map;
1821
import java.util.function.Function;
1922

2023
import com.networknt.schema.Error;
2124
import com.networknt.schema.InvalidSchemaException;
2225
import com.networknt.schema.SchemaRegistry;
2326

24-
public class BasicDialectRegistry implements DialectRegistry {
25-
private Function<String, Dialect> dialects;
27+
/**
28+
* Basic {@link DialectRegistry}.
29+
*/
30+
public class BasicDialectRegistry extends AbstractDialectRegistry {
31+
protected final Function<String, Dialect> dialects;
32+
33+
protected BasicDialectRegistry() {
34+
this.dialects = null;
35+
}
2636

2737
public BasicDialectRegistry(Function<String, Dialect> dialects) {
2838
this.dialects = dialects;
@@ -32,6 +42,14 @@ public BasicDialectRegistry(Dialect dialect) {
3242
this.dialects = dialectId -> dialect.getId().equals(dialectId) ? dialect : null;
3343
}
3444

45+
public BasicDialectRegistry(Collection<Dialect> dialects) {
46+
Map<String, Dialect> result = new HashMap<>();
47+
for (Dialect dialect : dialects) {
48+
result.put(dialect.getId(), dialect);
49+
}
50+
this.dialects = result::get;
51+
}
52+
3553
@Override
3654
public Dialect getDialect(String dialectId, SchemaRegistry schemaRegistry) {
3755
Dialect dialect = dialects.apply(dialectId);

src/main/java/com/networknt/schema/dialect/DefaultDialectRegistry.java

Lines changed: 28 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,65 +15,50 @@
1515
*/
1616
package com.networknt.schema.dialect;
1717

18-
import java.util.Map;
19-
import java.util.Map.Entry;
18+
import java.util.Collection;
2019
import java.util.concurrent.ConcurrentHashMap;
2120
import java.util.concurrent.ConcurrentMap;
21+
import java.util.function.Function;
2222

23-
import com.fasterxml.jackson.databind.JsonNode;
24-
import com.networknt.schema.Error;
25-
import com.networknt.schema.InvalidSchemaException;
26-
import com.networknt.schema.Schema;
2723
import com.networknt.schema.SchemaRegistry;
28-
import com.networknt.schema.SchemaLocation;
2924
import com.networknt.schema.Specification;
30-
import com.networknt.schema.SpecificationVersion;
3125

3226
/**
3327
* Default {@link DialectRegistry}.
3428
*/
35-
public class DefaultDialectRegistry implements DialectRegistry {
36-
private final ConcurrentMap<String, Dialect> dialects = new ConcurrentHashMap<>();
37-
29+
public class DefaultDialectRegistry extends BasicDialectRegistry {
30+
private final ConcurrentMap<String, Dialect> loadedDialects = new ConcurrentHashMap<>();
31+
32+
public DefaultDialectRegistry() {
33+
super();
34+
}
35+
36+
public DefaultDialectRegistry(Function<String, Dialect> dialects) {
37+
super(dialects);
38+
}
39+
40+
public DefaultDialectRegistry(Dialect dialect) {
41+
super(dialect);
42+
}
43+
44+
public DefaultDialectRegistry(Collection<Dialect> dialects) {
45+
super(dialects);
46+
}
47+
3848
@Override
3949
public Dialect getDialect(String dialectId, SchemaRegistry schemaFactory) {
50+
if (this.dialects != null) {
51+
Dialect dialect = dialects.apply(dialectId);
52+
if (dialect != null) {
53+
return dialect;
54+
}
55+
}
4056
// Is it a well-known dialect?
4157
Dialect dialect = Specification.getDialect(dialectId);
4258
if (dialect != null) {
4359
return dialect;
4460
}
45-
return dialects.computeIfAbsent(dialectId, id -> loadDialect(id, schemaFactory));
46-
}
47-
48-
protected Dialect loadDialect(String iri, SchemaRegistry schemaFactory) {
49-
try {
50-
Dialect result = loadDialectBuilder(iri, schemaFactory).build();
51-
return result;
52-
} catch (InvalidSchemaException e) {
53-
throw e;
54-
} catch (Exception e) {
55-
Error error = Error.builder().message("Failed to load dialect ''{0}''").arguments(iri).build();
56-
throw new InvalidSchemaException(error, e);
57-
}
58-
}
59-
60-
protected Dialect.Builder loadDialectBuilder(String iri, SchemaRegistry schemaFactory) {
61-
Schema schema = schemaFactory.getSchema(SchemaLocation.of(iri));
62-
Dialect.Builder builder = Dialect.builder(iri, schema.getSchemaContext().getDialect());
63-
SpecificationVersion specification = schema.getSchemaContext().getDialect().getSpecificationVersion();
64-
if (specification != null) {
65-
if (specification.getOrder() >= SpecificationVersion.DRAFT_2019_09.getOrder()) {
66-
// Process vocabularies
67-
JsonNode vocabulary = schema.getSchemaNode().get("$vocabulary");
68-
if (vocabulary != null) {
69-
builder.vocabularies(Map::clear);
70-
for (Entry<String, JsonNode> vocabs : vocabulary.properties()) {
71-
builder.vocabulary(vocabs.getKey(), vocabs.getValue().booleanValue());
72-
}
73-
}
74-
}
75-
}
76-
return builder;
61+
return loadedDialects.computeIfAbsent(dialectId, id -> loadDialect(id, schemaFactory));
7762
}
7863

7964
private static class Holder {

0 commit comments

Comments
 (0)