@@ -195,8 +195,8 @@ private data class DefaultLoadingContext(
195195 private val baseId : Uri ,
196196 override val schemaPath : JsonPointer = JsonPointer .ROOT ,
197197 val additionalIDs : Set <IdWithLocation > = linkedSetOf(IdWithLocation (baseId, schemaPath)),
198- val references : MutableMap <RefId , AssertionWithPath > = hashMapOf (),
199- val usedRef : MutableSet <ReferenceLocation > = hashSetOf (),
198+ val references : MutableMap <RefId , AssertionWithPath > = linkedMapOf (),
199+ val usedRef : MutableSet <ReferenceLocation > = linkedSetOf (),
200200) : LoadingContext {
201201 override fun at (property : String ): DefaultLoadingContext {
202202 return copy(schemaPath = schemaPath / property)
@@ -237,7 +237,7 @@ private data class DefaultLoadingContext(
237237 copy(
238238 additionalIDs = additionalIDs.run {
239239 this + IdWithLocation (
240- baseId.buildUpon().encodedPath( additionalId.path).build( ),
240+ additionalIDs.resolvePath( additionalId.path),
241241 schemaPath,
242242 )
243243 },
@@ -255,7 +255,10 @@ private data class DefaultLoadingContext(
255255 val refUri = Uri .parse(refId).buildUpon().build()
256256 return when {
257257 refUri.isAbsolute -> refUri.buildRefId()
258- ! refUri.path.isNullOrBlank() -> baseId.buildUpon().encodedPath(refUri.path).buildRefId()
258+ // the ref is absolute and should be resolved from current base URI host:port part
259+ refId.startsWith(' /' ) -> additionalIDs.last().id.buildUpon().encodedPath(refUri.path).buildRefId()
260+ // in this case the ref must be resolved from the current base ID
261+ ! refUri.path.isNullOrBlank() -> additionalIDs.resolvePath(refUri.path).buildRefId()
259262 refUri.fragment != null -> additionalIDs.last().id.buildUpon().encodedFragment(refUri.fragment).buildRefId()
260263 else -> throw IllegalArgumentException (" invalid reference $refId " )
261264 }.also { usedRef + = ReferenceLocation (schemaPath, it) }
@@ -271,7 +274,7 @@ private data class DefaultLoadingContext(
271274 when {
272275 ! id.path.isNullOrBlank() -> register(
273276 // register JSON schema by related path
274- baseId.buildUpon().encodedPath (id.path).buildRefId(),
277+ additionalIDs.resolvePath (id.path).buildRefId(),
275278 assertion,
276279 )
277280
@@ -294,6 +297,24 @@ private data class DefaultLoadingContext(
294297 }
295298}
296299
300+ private fun Set<IdWithLocation>.resolvePath (path : String? ): Uri {
301+ return last().id.appendPathToParent(requireNotNull(path) { " path is null" })
302+ }
303+ private fun Uri.appendPathToParent (path : String ): Uri {
304+ val hasLastEmptySegment = toString().endsWith(' /' )
305+ return if (hasLastEmptySegment) {
306+ buildUpon() // don't need to drop anything. just add the path because / in the end means empty segment
307+ } else {
308+ buildUpon()
309+ .path(null ) // reset path in builder
310+ .apply {
311+ pathSegments.asSequence()
312+ .take(pathSegments.size - 1 ) // drop last path segment
313+ .forEach(this ::appendPath)
314+ }
315+ }.appendEncodedPath(path)
316+ .build()
317+ }
297318private fun Uri.buildRefId (): RefId = RefId (this )
298319
299320private fun Builder.buildRefId (): RefId = build().buildRefId()
0 commit comments