@@ -10,11 +10,13 @@ import io.github.optimumcode.json.schema.JsonSchema
10
10
import io.github.optimumcode.json.schema.JsonSchemaLoader
11
11
import io.github.optimumcode.json.schema.SchemaType
12
12
import io.github.optimumcode.json.schema.extension.ExternalAssertionFactory
13
+ import io.github.optimumcode.json.schema.findSchemaType
13
14
import io.github.optimumcode.json.schema.internal.ReferenceFactory.RefHolder
14
15
import io.github.optimumcode.json.schema.internal.ReferenceFactory.RefHolder.Recursive
15
16
import io.github.optimumcode.json.schema.internal.ReferenceFactory.RefHolder.Simple
16
17
import io.github.optimumcode.json.schema.internal.ReferenceValidator.PointerWithBaseId
17
18
import io.github.optimumcode.json.schema.internal.ReferenceValidator.ReferenceLocation
19
+ import io.github.optimumcode.json.schema.internal.SchemaLoaderConfig.Vocabulary
18
20
import io.github.optimumcode.json.schema.internal.factories.ExternalAssertionFactoryAdapter
19
21
import io.github.optimumcode.json.schema.internal.util.getString
20
22
import kotlinx.serialization.json.Json
@@ -30,13 +32,14 @@ internal class SchemaLoader : JsonSchemaLoader {
30
32
private val references: MutableMap <RefId , AssertionWithPath > = linkedMapOf()
31
33
private val usedRefs: MutableSet <ReferenceLocation > = linkedSetOf()
32
34
private val extensionFactories: MutableMap <String , AssertionFactory > = linkedMapOf()
35
+ private val customMetaSchemas: MutableMap <Uri , Pair <SchemaType , Vocabulary >> = linkedMapOf()
33
36
34
37
override fun register (
35
38
schema : JsonElement ,
36
39
draft : SchemaType ? ,
37
40
): JsonSchemaLoader =
38
41
apply {
39
- loadSchemaData(schema, LoadingParameters (draft, references, usedRefs, extensionFactories.values ))
42
+ loadSchemaData(schema, createParameters (draft))
40
43
}
41
44
42
45
override fun register (
@@ -56,12 +59,7 @@ internal class SchemaLoader : JsonSchemaLoader {
56
59
apply {
57
60
loadSchemaData(
58
61
schema,
59
- LoadingParameters (
60
- draft,
61
- references,
62
- usedRefs,
63
- extensionFactories = extensionFactories.values,
64
- ),
62
+ createParameters(draft),
65
63
Uri .parse(remoteUri),
66
64
)
67
65
}
@@ -99,7 +97,7 @@ internal class SchemaLoader : JsonSchemaLoader {
99
97
val assertion: JsonSchemaAssertion =
100
98
loadSchemaData(
101
99
schemaElement,
102
- LoadingParameters (draft, references, usedRefs, extensionFactories.values ),
100
+ createParameters (draft),
103
101
)
104
102
validateReferences(references, usedRefs)
105
103
return createSchema(
@@ -111,6 +109,20 @@ internal class SchemaLoader : JsonSchemaLoader {
111
109
)
112
110
}
113
111
112
+ private fun createParameters (draft : SchemaType ? ): LoadingParameters =
113
+ LoadingParameters (
114
+ defaultType = draft,
115
+ references = references,
116
+ usedRefs = usedRefs,
117
+ extensionFactories = extensionFactories.values,
118
+ registerMetaSchema = { uri, type, vocab ->
119
+ val prev = customMetaSchemas.put(uri, type to vocab)
120
+ require(prev == null ) { " duplicated meta-schema with uri '$uri '" }
121
+ },
122
+ resolveCustomVocabulary = { customMetaSchemas[it]?.second },
123
+ resolveCustomMetaSchemaType = { customMetaSchemas[it]?.first },
124
+ )
125
+
114
126
private fun addExtensionFactory (extensionFactory : ExternalAssertionFactory ) {
115
127
for (schemaType in SchemaType .entries) {
116
128
val match =
@@ -174,22 +186,35 @@ internal object IsolatedLoader : JsonSchemaLoader {
174
186
}
175
187
}
176
188
189
+ @Suppress(" detekt:LongParameterList" )
177
190
private class LoadingParameters (
178
191
val defaultType : SchemaType ? ,
179
192
val references : MutableMap <RefId , AssertionWithPath >,
180
193
val usedRefs : MutableSet <ReferenceLocation >,
181
194
val extensionFactories : Collection <AssertionFactory > = emptySet(),
195
+ val resolveCustomMetaSchemaType : (Uri ) -> SchemaType ? = { null },
196
+ val resolveCustomVocabulary : (Uri ) -> Vocabulary ? = { null },
197
+ val registerMetaSchema : (Uri , SchemaType , Vocabulary ) -> Unit = { _, _, _ -> },
182
198
)
183
199
184
200
private fun loadSchemaData (
185
201
schemaDefinition : JsonElement ,
186
202
parameters : LoadingParameters ,
187
203
externalUri : Uri ? = null,
188
204
): JsonSchemaAssertion {
189
- val schemaType = extractSchemaType(schemaDefinition, parameters.defaultType)
205
+ val schema: Uri ? = extractSchema(schemaDefinition)?.let (Uri ::parse)
206
+ val schemaType: SchemaType = resolveSchemaType(schema, parameters.defaultType, parameters.resolveCustomMetaSchemaType)
190
207
val baseId: Uri = extractID(schemaDefinition, schemaType.config) ? : externalUri ? : Uri .EMPTY
208
+ val schemaVocabulary: Vocabulary ? =
209
+ schemaType.config.createVocabulary(schemaDefinition)?.also {
210
+ parameters.registerMetaSchema(baseId, schemaType, it)
211
+ }
212
+ val vocabulary: Vocabulary =
213
+ schemaVocabulary
214
+ ? : schema?.let (parameters.resolveCustomVocabulary)
215
+ ? : schemaType.config.defaultVocabulary
191
216
val assertionFactories =
192
- schemaType.config.factories(schemaDefinition).let {
217
+ schemaType.config.factories(schemaDefinition, vocabulary ).let {
193
218
if (parameters.extensionFactories.isEmpty()) {
194
219
it
195
220
} else {
@@ -245,22 +270,31 @@ private class LoadResult(
245
270
val usedRefs : Set <RefId >,
246
271
)
247
272
248
- private fun extractSchemaType (
249
- schemaDefinition : JsonElement ,
273
+ private fun resolveSchemaType (
274
+ schema : Uri ? ,
250
275
defaultType : SchemaType ? ,
276
+ resolveCustomMetaSchemaType : (Uri ) -> SchemaType ? ,
251
277
): SchemaType {
252
278
val schemaType: SchemaType ? =
253
- if (schemaDefinition is JsonObject ) {
254
- schemaDefinition[SCHEMA_PROPERTY ]?.let {
255
- require(it is JsonPrimitive && it.isString) { " $SCHEMA_PROPERTY must be a string" }
256
- SchemaType .find(it.content) ? : throw IllegalArgumentException (" unsupported schema type ${it.content} " )
257
- }
258
- } else {
259
- null
279
+ schema?.let {
280
+ findSchemaType(it)
281
+ ? : resolveCustomMetaSchemaType(it)
282
+ ? : throw IllegalArgumentException (" unsupported schema type $it " )
260
283
}
261
284
return schemaType ? : defaultType ? : SchemaType .entries.last()
262
285
}
263
286
287
+ private fun extractSchema (schemaDefinition : JsonElement ): String? {
288
+ return if (schemaDefinition is JsonObject ) {
289
+ schemaDefinition[SCHEMA_PROPERTY ]?.let {
290
+ require(it is JsonPrimitive && it.isString) { " $SCHEMA_PROPERTY must be a string" }
291
+ it.content
292
+ }
293
+ } else {
294
+ null
295
+ }
296
+ }
297
+
264
298
private fun loadDefinitions (
265
299
schemaDefinition : JsonElement ,
266
300
context : DefaultLoadingContext ,
0 commit comments