@@ -10,11 +10,13 @@ import io.github.optimumcode.json.schema.JsonSchema
1010import io.github.optimumcode.json.schema.JsonSchemaLoader
1111import io.github.optimumcode.json.schema.SchemaType
1212import io.github.optimumcode.json.schema.extension.ExternalAssertionFactory
13+ import io.github.optimumcode.json.schema.findSchemaType
1314import io.github.optimumcode.json.schema.internal.ReferenceFactory.RefHolder
1415import io.github.optimumcode.json.schema.internal.ReferenceFactory.RefHolder.Recursive
1516import io.github.optimumcode.json.schema.internal.ReferenceFactory.RefHolder.Simple
1617import io.github.optimumcode.json.schema.internal.ReferenceValidator.PointerWithBaseId
1718import io.github.optimumcode.json.schema.internal.ReferenceValidator.ReferenceLocation
19+ import io.github.optimumcode.json.schema.internal.SchemaLoaderConfig.Vocabulary
1820import io.github.optimumcode.json.schema.internal.factories.ExternalAssertionFactoryAdapter
1921import io.github.optimumcode.json.schema.internal.util.getString
2022import kotlinx.serialization.json.Json
@@ -30,13 +32,14 @@ internal class SchemaLoader : JsonSchemaLoader {
3032 private val references: MutableMap <RefId , AssertionWithPath > = linkedMapOf()
3133 private val usedRefs: MutableSet <ReferenceLocation > = linkedSetOf()
3234 private val extensionFactories: MutableMap <String , AssertionFactory > = linkedMapOf()
35+ private val customMetaSchemas: MutableMap <Uri , Pair <SchemaType , Vocabulary >> = linkedMapOf()
3336
3437 override fun register (
3538 schema : JsonElement ,
3639 draft : SchemaType ? ,
3740 ): JsonSchemaLoader =
3841 apply {
39- loadSchemaData(schema, LoadingParameters (draft, references, usedRefs, extensionFactories.values ))
42+ loadSchemaData(schema, createParameters (draft))
4043 }
4144
4245 override fun register (
@@ -56,12 +59,7 @@ internal class SchemaLoader : JsonSchemaLoader {
5659 apply {
5760 loadSchemaData(
5861 schema,
59- LoadingParameters (
60- draft,
61- references,
62- usedRefs,
63- extensionFactories = extensionFactories.values,
64- ),
62+ createParameters(draft),
6563 Uri .parse(remoteUri),
6664 )
6765 }
@@ -99,7 +97,7 @@ internal class SchemaLoader : JsonSchemaLoader {
9997 val assertion: JsonSchemaAssertion =
10098 loadSchemaData(
10199 schemaElement,
102- LoadingParameters (draft, references, usedRefs, extensionFactories.values ),
100+ createParameters (draft),
103101 )
104102 validateReferences(references, usedRefs)
105103 return createSchema(
@@ -111,6 +109,20 @@ internal class SchemaLoader : JsonSchemaLoader {
111109 )
112110 }
113111
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+
114126 private fun addExtensionFactory (extensionFactory : ExternalAssertionFactory ) {
115127 for (schemaType in SchemaType .entries) {
116128 val match =
@@ -174,22 +186,35 @@ internal object IsolatedLoader : JsonSchemaLoader {
174186 }
175187}
176188
189+ @Suppress(" detekt:LongParameterList" )
177190private class LoadingParameters (
178191 val defaultType : SchemaType ? ,
179192 val references : MutableMap <RefId , AssertionWithPath >,
180193 val usedRefs : MutableSet <ReferenceLocation >,
181194 val extensionFactories : Collection <AssertionFactory > = emptySet(),
195+ val resolveCustomMetaSchemaType : (Uri ) -> SchemaType ? = { null },
196+ val resolveCustomVocabulary : (Uri ) -> Vocabulary ? = { null },
197+ val registerMetaSchema : (Uri , SchemaType , Vocabulary ) -> Unit = { _, _, _ -> },
182198)
183199
184200private fun loadSchemaData (
185201 schemaDefinition : JsonElement ,
186202 parameters : LoadingParameters ,
187203 externalUri : Uri ? = null,
188204): 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)
190207 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
191216 val assertionFactories =
192- schemaType.config.factories(schemaDefinition).let {
217+ schemaType.config.factories(schemaDefinition, vocabulary ).let {
193218 if (parameters.extensionFactories.isEmpty()) {
194219 it
195220 } else {
@@ -245,22 +270,31 @@ private class LoadResult(
245270 val usedRefs : Set <RefId >,
246271)
247272
248- private fun extractSchemaType (
249- schemaDefinition : JsonElement ,
273+ private fun resolveSchemaType (
274+ schema : Uri ? ,
250275 defaultType : SchemaType ? ,
276+ resolveCustomMetaSchemaType : (Uri ) -> SchemaType ? ,
251277): SchemaType {
252278 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 " )
260283 }
261284 return schemaType ? : defaultType ? : SchemaType .entries.last()
262285}
263286
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+
264298private fun loadDefinitions (
265299 schemaDefinition : JsonElement ,
266300 context : DefaultLoadingContext ,
0 commit comments