Skip to content

Commit d5590db

Browse files
committed
Refactor Dialect Registry and Schema Registry
1 parent 2d7a89f commit d5590db

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;
@@ -296,6 +297,47 @@ public static SchemaRegistry withDefaultDialectId(String dialectId, Consumer<Sch
296297
}
297298
return builder.build();
298299
}
300+
301+
302+
/**
303+
* Creates a new schema registry with a default schema dialect. The schema
304+
* dialect will only be used if the input does not specify a $schema.
305+
* <p>
306+
* This uses a dialect registry that contains all the supported standard
307+
* specification dialects, Draft 4, Draft 6, Draft 7, Draft 2019-09 and Draft
308+
* 2020-12.
309+
*
310+
* @param specificationVersion the default dialect id corresponding to the
311+
* specification version used when the schema does
312+
* not specify the $schema keyword
313+
* @return the factory
314+
*/
315+
public static SchemaRegistry withDefaultDialect(Dialect dialect) {
316+
return withDefaultDialect(dialect, null);
317+
}
318+
319+
/**
320+
* Creates a new schema registry with a default schema dialect. The schema
321+
* dialect will only be used if the input does not specify a $schema.
322+
* <p>
323+
* This uses a dialect registry that contains all the supported standard
324+
* specification dialects, Draft 4, Draft 6, Draft 7, Draft 2019-09 and Draft
325+
* 2020-12.
326+
*
327+
* @param specificationVersion the default dialect id corresponding to the
328+
* specification version used when the schema does
329+
* not specify the $schema keyword
330+
* @param customizer to customize the registry
331+
* @return the factory
332+
*/
333+
public static SchemaRegistry withDefaultDialect(Dialect dialect, Consumer<SchemaRegistry.Builder> customizer) {
334+
SchemaRegistry.Builder builder = builder().defaultDialectId(dialect.getId())
335+
.dialectRegistry(new DefaultDialectRegistry(dialect));
336+
if (customizer != null) {
337+
customizer.accept(builder);
338+
}
339+
return builder.build();
340+
}
299341

300342
/**
301343
* Gets a new schema registry that supports a specific dialect only.
@@ -312,6 +354,44 @@ public static SchemaRegistry withDialect(Dialect dialect) {
312354
return withDialect(dialect, null);
313355
}
314356

357+
/**
358+
* Gets a new schema registry that supports a list of specific dialects only.
359+
* <p>
360+
* Schemas that do not specify dialect using $schema will use the first dialect
361+
* on the list.
362+
* <p>
363+
* This uses a dialect registry that only contains the list of dialects and will
364+
* throw an exception for unknown dialects.
365+
*
366+
* @param dialect the dialect
367+
* @param customizer to customize the registry
368+
* @return the schema registry
369+
*/
370+
public static SchemaRegistry withDialect(Dialect dialect, Consumer<SchemaRegistry.Builder> customizer) {
371+
SchemaRegistry.Builder builder = builder().defaultDialectId(dialect.getId())
372+
.dialectRegistry(new BasicDialectRegistry(dialect));
373+
if (customizer != null) {
374+
customizer.accept(builder);
375+
}
376+
return builder.build();
377+
}
378+
379+
/**
380+
* Gets a new schema registry that supports a list of specific dialects only.
381+
* <p>
382+
* Schemas that do not specify dialect using $schema will use the first dialect
383+
* on the list.
384+
* <p>
385+
* This uses a dialect registry that only contains the list of dialects and will
386+
* throw an exception for unknown dialects.
387+
*
388+
* @param dialects the dialect
389+
* @return the schema registry
390+
*/
391+
public static SchemaRegistry withDialects(List<Dialect> dialects) {
392+
return withDialects(dialects, null);
393+
}
394+
315395
/**
316396
* Gets a new schema registry that supports a specific dialect only.
317397
* <p>
@@ -324,15 +404,16 @@ public static SchemaRegistry withDialect(Dialect dialect) {
324404
* @param customizer to customize the registry
325405
* @return the schema registry
326406
*/
327-
public static SchemaRegistry withDialect(Dialect dialect, Consumer<SchemaRegistry.Builder> customizer) {
328-
SchemaRegistry.Builder builder = builder().defaultDialectId(dialect.getId())
329-
.dialectRegistry(new BasicDialectRegistry(dialect));
407+
public static SchemaRegistry withDialects(List<Dialect> dialects, Consumer<SchemaRegistry.Builder> customizer) {
408+
SchemaRegistry.Builder builder = builder().defaultDialectId(dialects.get(0).getId())
409+
.dialectRegistry(new BasicDialectRegistry(dialects));
330410
if (customizer != null) {
331411
customizer.accept(builder);
332412
}
333413
return builder.build();
334414
}
335415

416+
336417
/**
337418
* Builder from an existing {@link SchemaRegistry}.
338419
* <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)