
RFC 3986–compliant URI parsing, resolving, normalizing and construction with precise host-type detection (IPv4/IPv6/IPvFuture), percent-encoding support, robust authority/path handling, charset-aware parsing and reliable relative-reference resolution.
This is a Kotlin Multiplatform library for working with RFC 3986, and is a fork of the Java library.
The original author describes some benefits of using the library over Java's URI library (which could be of interest to JVM/Android consumers) in the readme.
The library offers four key functionalities for robust URI management:
Each feature is designed to handle URIs accurately and effectively, ensuring reliable and precise management across various application contexts.
You can include the library from either Maven Central or Jitpack.
You can include the library in the common source set like this:
dependencies {
implementation("io.github.kotlingeekdev:uri-reference-kmp:1.0")
}Inside your root-level build.gradle(.kts) file, you should add jitpack:
// build.gradle.kts
allprojects {
repositories {
// ...
maven { setUrl("https://jitpack.io") }
}
// ...
}or
// build.gradle
allprojects {
repositories {
// ...
maven { url "https://jitpack.io" }
}
// ...
}In newer projects, you need to also update the settings.gradle(.kts) file's dependencyResolutionManagement block:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' } // <--
}
}
then, in your module's build.gradle(.kts), you need to add:
// build.gradle.kts
dependencies {
//...
implementation("com.github.KotlinGeekDev.uri-reference-kmp:uri-reference-kmp:v1.0")
}
If you're including it in an Android app, you can just add:
// app/build.gradle.kts
dependencies {
//...
implementation("com.github.KotlinGeekDev.uri-reference-kmp:uri-reference-kmp-android:1.0")
}Apache License, Version 2.0
To parse URI references, use URIReference.parse(uriRef: String) or URIReference.parse(uriRef: String, charset: Charset). Below are some examples of using URIReference.parse(uriRef: String).
val uriRef = URIReference.parse("http://example.com/a/b") // Parse.
println(uriRef.toString()) // "http://example.com/a/b"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "example.com"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "REGNAME"
println(uriRef.getHost().getValue()) // "example.com"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // "/a/b"
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // nullval uriRef = URIReference.parse("//example.com/a/b") // Parse.
println(uriRef.toString()) // "//example.com/a/b"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // null
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "example.com"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "REGNAME"
println(uriRef.getHost().getValue()) // "example.com"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // "/a/b"
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // nullval uriRef = URIReference.parse("http://101.102.103.104") // Parse.
println(uriRef.toString()) // "http://101.102.103.104"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "101.102.103.104"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "IPV4"
println(uriRef.getHost().getValue()) // "101.102.103.104"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // ""
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // nullval uriRef = URIReference.parse("http://[2001:0db8:0001:0000:0000:0ab9:C0A8:0102]") // Parse.
println(uriRef.toString()) // "http://[2001:0db8:0001:0000:0000:0ab9:C0A8:0102]"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "[2001:0db8:0001:0000:0000:0ab9:C0A8:0102]"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "IPV6"
println(uriRef.getHost().getValue()) // "[2001:0db8:0001:0000:0000:0ab9:C0A8:0102]"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // ""
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // nullval uriRef = URIReference.parse("http://[v9.abc:def]") // Parse.
println(uriRef.toString()) // "http://[v9.abc:def]"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "[v9.abc:def]"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "IPVFUTURE"
println(uriRef.getHost().getValue()) // "[v9.abc:def]"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // ""
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // nullval uriRef = URIReference.parse("http://%65%78%61%6D%70%6C%65.com") // Parse.
println(uriRef.toString()) // "http://%65%78%61%6D%70%6C%65.com"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "%65%78%61%6D%70%6C%65.com"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "REGNAME"
println(uriRef.getHost().getValue()) // "%65%78%61%6D%70%6C%65.com"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // ""
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // null[!WARNING] If parsing fails, those methods throws
NullPointerExceptionorIllegalArgumentException. See Java doc for more details.
To resolve a relative reference against a URI reference, use resolve(uriRef: String) or resolve(uriRef: URIReference). Below is an example demonstrating how to resolve a relative reference against a base URI.
// A base URI.
val baseUri = URIReference.parse("http://example.com")
// A relative reference.
val relRef = URIReference.parse("/a/b")
// Resolve the relative reference against the base URI.
val resolved = baseUri.resolve(relRef)
println(resolved.toString()) // "http://example.com/a/b"
println(resolved.isRelativeReference()) // false
println(resolved.getScheme()) // "http"
println(resolved.hasAuthority()) // true
println(resolved.getAuthority().toString()) // "example.com"
println(resolved.getUserinfo()) // null
println(resolved.getHost().getType()) // "REGNAME"
println(resolved.getHost().getValue()) // "example.com"
println(resolved.getPort()) // -1
println(resolved.getPath()) // "/a/b"
println(resolved.getQuery()) // null
println(resolved.getFragment()) // nullFor normalization, invoke normalize() on a URIReference instance to normalize.
val normalized = URIReference.parse("hTTp://example.com") // Parse.
.normalize() // Normalize.
println(normalized.toString()) // "http://example.com/"
println(normalized.isRelativeReference()) // false
println(normalized.getScheme()) // "http"
println(normalized.hasAuthority()) // true
println(normalized.getAuthority().toString()) // "example.com"
println(normalized.getUserinfo()) // null
println(normalized.getHost().getType()) // "REGNAME"
println(normalized.getHost().getValue()) // "example.com"
println(normalized.getPort()) // -1
println(normalized.getPath()) // "/"
println(normalized.getQuery()) // null
println(normalized.getFragment()) // nullval normalized = URIReference.parse("http://%65%78%61%6D%70%6C%65.com") // Parse.
.normalize() // Normalize.
println(normalized.toString()) // "http://example.com/"
println(normalized.isRelativeReference()) // false
println(normalized.getScheme()) // "http"
println(normalized.hasAuthority()) // true
println(normalized.getAuthority().toString()) // "example.com"
println(normalized.getUserinfo()) // null
println(normalized.getHost().getType()) // "REGNAME"
println(normalized.getHost().getValue()) // "example.com"
println(normalized.getPort()) // -1
println(normalized.getPath()) // "/"
println(normalized.getQuery()) // null
println(normalized.getFragment()) // nullval normalized = URIReference.parse("http://example.com/a/b/c/../d/") // Parse.
.normalize() // Normalize.
println(normalized.toString()) // "http://example.com/a/b/d/"
println(normalized.isRelativeReference()) // false
println(normalized.getScheme()) // "http"
println(normalized.hasAuthority()) // true
println(normalized.getAuthority().toString()) // "example.com"
println(normalized.getUserinfo()) // null
println(normalized.getHost().getType()) // "REGNAME"
println(normalized.getHost().getValue()) // "example.com"
println(normalized.getPort()) // -1
println(normalized.getPath()) // "/a/b/d/"
println(normalized.getQuery()) // null
println(normalized.getFragment()) // null// Parse a relative reference.
val relRef = URIReference.parse("/a/b/c/../d/")
// Resolve the relative reference against "http://example.com".
// NOTE: Relative references must be resolved before normalization.
val resolved = relRef.resolve("http://example.com")
// Normalize the resolved URI.
val normalized = resolved.normalize()
println(normalized.toString()) // "http://example.com/a/b/d/"
println(normalized.isRelativeReference()) // false
println(normalized.getScheme()) // "http"
println(normalized.hasAuthority()) // true
println(normalized.getAuthority().toString()) // "example.com"
println(normalized.getUserinfo()) // null
println(normalized.getHost().getType()) // "REGNAME"
println(normalized.getHost().getValue()) // "example.com"
println(normalized.getPort()) // -1
println(normalized.getPath()) // "/a/b/d/"
println(normalized.getQuery()) // null
println(normalized.getFragment()) // null[!CAUTION] Relative reference must be resolved before normalization as RFC 3986, 5.2.1 states as below.
RFC 3986, 5.2.1. Pre-parse the Base URI
A URI reference must be transformed to its target URI before it can be normalized.---
To construct URI references, use URIReferenceBuilder class.
val uriRef = URIReferenceBuilder()
.setScheme("http")
.setHost("example.com")
.setPath("/a/b/c")
.appendQueryParam("k1", "v1")
.build()
println(uriRef.toString()) // "http://example.com/a/b/c?k1=v1"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "example.com"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "REGNAME"
println(uriRef.getHost().getValue()) // "example.com"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // "/a/b/c"
println(uriRef.getQuery()) // "k1=v1"
println(uriRef.getFragment()) // nullval uriRef = URIReferenceBuilder()
.fromURIReference("http://example.com/a/b/c?k1=v1")
.appendPath("d", "e", "f")
.appendQueryParam("k2", "v2")
.build()
println(uriRef.toString()) // "http://example.comd/a/b/c/d/e/f?k1=v1&k2=v2"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "example.com"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "REGNAME"
println(uriRef.getHost().getValue()) // "example.com"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // "/a/b/c/d/e/f"
println(uriRef.getQuery()) // "k1=v1&k2=&v2"
println(uriRef.getFragment()) // null[!WARNING] The current implementation of
URIReferenceBuilderclass won't throw an exception untilbuild()method is invoked even if invalid input is given since validation for each URI component is performed only whenbuild()is called.
This library designs most classes such as URIReference to be immutable. Here are some examples.
// Example 1: Invoking the "normalize()" method creates a new URIReference instance.
val normalized = URIReference.parse("hTTp://example.com").normalize()
// Example 2: Invoking the "resolve(String uriRef)" method creates a new URIReference instance.
val resolved = URIReference.parse("http://example.com").resolve("/a/b")This is a Kotlin Multiplatform library for working with RFC 3986, and is a fork of the Java library.
The original author describes some benefits of using the library over Java's URI library (which could be of interest to JVM/Android consumers) in the readme.
The library offers four key functionalities for robust URI management:
Each feature is designed to handle URIs accurately and effectively, ensuring reliable and precise management across various application contexts.
You can include the library from either Maven Central or Jitpack.
You can include the library in the common source set like this:
dependencies {
implementation("io.github.kotlingeekdev:uri-reference-kmp:1.0")
}Inside your root-level build.gradle(.kts) file, you should add jitpack:
// build.gradle.kts
allprojects {
repositories {
// ...
maven { setUrl("https://jitpack.io") }
}
// ...
}or
// build.gradle
allprojects {
repositories {
// ...
maven { url "https://jitpack.io" }
}
// ...
}In newer projects, you need to also update the settings.gradle(.kts) file's dependencyResolutionManagement block:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' } // <--
}
}
then, in your module's build.gradle(.kts), you need to add:
// build.gradle.kts
dependencies {
//...
implementation("com.github.KotlinGeekDev.uri-reference-kmp:uri-reference-kmp:v1.0")
}
If you're including it in an Android app, you can just add:
// app/build.gradle.kts
dependencies {
//...
implementation("com.github.KotlinGeekDev.uri-reference-kmp:uri-reference-kmp-android:1.0")
}Apache License, Version 2.0
To parse URI references, use URIReference.parse(uriRef: String) or URIReference.parse(uriRef: String, charset: Charset). Below are some examples of using URIReference.parse(uriRef: String).
val uriRef = URIReference.parse("http://example.com/a/b") // Parse.
println(uriRef.toString()) // "http://example.com/a/b"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "example.com"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "REGNAME"
println(uriRef.getHost().getValue()) // "example.com"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // "/a/b"
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // nullval uriRef = URIReference.parse("//example.com/a/b") // Parse.
println(uriRef.toString()) // "//example.com/a/b"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // null
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "example.com"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "REGNAME"
println(uriRef.getHost().getValue()) // "example.com"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // "/a/b"
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // nullval uriRef = URIReference.parse("http://101.102.103.104") // Parse.
println(uriRef.toString()) // "http://101.102.103.104"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "101.102.103.104"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "IPV4"
println(uriRef.getHost().getValue()) // "101.102.103.104"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // ""
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // nullval uriRef = URIReference.parse("http://[2001:0db8:0001:0000:0000:0ab9:C0A8:0102]") // Parse.
println(uriRef.toString()) // "http://[2001:0db8:0001:0000:0000:0ab9:C0A8:0102]"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "[2001:0db8:0001:0000:0000:0ab9:C0A8:0102]"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "IPV6"
println(uriRef.getHost().getValue()) // "[2001:0db8:0001:0000:0000:0ab9:C0A8:0102]"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // ""
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // nullval uriRef = URIReference.parse("http://[v9.abc:def]") // Parse.
println(uriRef.toString()) // "http://[v9.abc:def]"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "[v9.abc:def]"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "IPVFUTURE"
println(uriRef.getHost().getValue()) // "[v9.abc:def]"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // ""
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // nullval uriRef = URIReference.parse("http://%65%78%61%6D%70%6C%65.com") // Parse.
println(uriRef.toString()) // "http://%65%78%61%6D%70%6C%65.com"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "%65%78%61%6D%70%6C%65.com"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "REGNAME"
println(uriRef.getHost().getValue()) // "%65%78%61%6D%70%6C%65.com"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // ""
println(uriRef.getQuery()) // null
println(uriRef.getFragment()) // null[!WARNING] If parsing fails, those methods throws
NullPointerExceptionorIllegalArgumentException. See Java doc for more details.
To resolve a relative reference against a URI reference, use resolve(uriRef: String) or resolve(uriRef: URIReference). Below is an example demonstrating how to resolve a relative reference against a base URI.
// A base URI.
val baseUri = URIReference.parse("http://example.com")
// A relative reference.
val relRef = URIReference.parse("/a/b")
// Resolve the relative reference against the base URI.
val resolved = baseUri.resolve(relRef)
println(resolved.toString()) // "http://example.com/a/b"
println(resolved.isRelativeReference()) // false
println(resolved.getScheme()) // "http"
println(resolved.hasAuthority()) // true
println(resolved.getAuthority().toString()) // "example.com"
println(resolved.getUserinfo()) // null
println(resolved.getHost().getType()) // "REGNAME"
println(resolved.getHost().getValue()) // "example.com"
println(resolved.getPort()) // -1
println(resolved.getPath()) // "/a/b"
println(resolved.getQuery()) // null
println(resolved.getFragment()) // nullFor normalization, invoke normalize() on a URIReference instance to normalize.
val normalized = URIReference.parse("hTTp://example.com") // Parse.
.normalize() // Normalize.
println(normalized.toString()) // "http://example.com/"
println(normalized.isRelativeReference()) // false
println(normalized.getScheme()) // "http"
println(normalized.hasAuthority()) // true
println(normalized.getAuthority().toString()) // "example.com"
println(normalized.getUserinfo()) // null
println(normalized.getHost().getType()) // "REGNAME"
println(normalized.getHost().getValue()) // "example.com"
println(normalized.getPort()) // -1
println(normalized.getPath()) // "/"
println(normalized.getQuery()) // null
println(normalized.getFragment()) // nullval normalized = URIReference.parse("http://%65%78%61%6D%70%6C%65.com") // Parse.
.normalize() // Normalize.
println(normalized.toString()) // "http://example.com/"
println(normalized.isRelativeReference()) // false
println(normalized.getScheme()) // "http"
println(normalized.hasAuthority()) // true
println(normalized.getAuthority().toString()) // "example.com"
println(normalized.getUserinfo()) // null
println(normalized.getHost().getType()) // "REGNAME"
println(normalized.getHost().getValue()) // "example.com"
println(normalized.getPort()) // -1
println(normalized.getPath()) // "/"
println(normalized.getQuery()) // null
println(normalized.getFragment()) // nullval normalized = URIReference.parse("http://example.com/a/b/c/../d/") // Parse.
.normalize() // Normalize.
println(normalized.toString()) // "http://example.com/a/b/d/"
println(normalized.isRelativeReference()) // false
println(normalized.getScheme()) // "http"
println(normalized.hasAuthority()) // true
println(normalized.getAuthority().toString()) // "example.com"
println(normalized.getUserinfo()) // null
println(normalized.getHost().getType()) // "REGNAME"
println(normalized.getHost().getValue()) // "example.com"
println(normalized.getPort()) // -1
println(normalized.getPath()) // "/a/b/d/"
println(normalized.getQuery()) // null
println(normalized.getFragment()) // null// Parse a relative reference.
val relRef = URIReference.parse("/a/b/c/../d/")
// Resolve the relative reference against "http://example.com".
// NOTE: Relative references must be resolved before normalization.
val resolved = relRef.resolve("http://example.com")
// Normalize the resolved URI.
val normalized = resolved.normalize()
println(normalized.toString()) // "http://example.com/a/b/d/"
println(normalized.isRelativeReference()) // false
println(normalized.getScheme()) // "http"
println(normalized.hasAuthority()) // true
println(normalized.getAuthority().toString()) // "example.com"
println(normalized.getUserinfo()) // null
println(normalized.getHost().getType()) // "REGNAME"
println(normalized.getHost().getValue()) // "example.com"
println(normalized.getPort()) // -1
println(normalized.getPath()) // "/a/b/d/"
println(normalized.getQuery()) // null
println(normalized.getFragment()) // null[!CAUTION] Relative reference must be resolved before normalization as RFC 3986, 5.2.1 states as below.
RFC 3986, 5.2.1. Pre-parse the Base URI
A URI reference must be transformed to its target URI before it can be normalized.---
To construct URI references, use URIReferenceBuilder class.
val uriRef = URIReferenceBuilder()
.setScheme("http")
.setHost("example.com")
.setPath("/a/b/c")
.appendQueryParam("k1", "v1")
.build()
println(uriRef.toString()) // "http://example.com/a/b/c?k1=v1"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "example.com"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "REGNAME"
println(uriRef.getHost().getValue()) // "example.com"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // "/a/b/c"
println(uriRef.getQuery()) // "k1=v1"
println(uriRef.getFragment()) // nullval uriRef = URIReferenceBuilder()
.fromURIReference("http://example.com/a/b/c?k1=v1")
.appendPath("d", "e", "f")
.appendQueryParam("k2", "v2")
.build()
println(uriRef.toString()) // "http://example.comd/a/b/c/d/e/f?k1=v1&k2=v2"
println(uriRef.isRelativeReference()) // false
println(uriRef.getScheme()) // "http"
println(uriRef.hasAuthority()) // true
println(uriRef.getAuthority().toString()) // "example.com"
println(uriRef.getUserinfo()) // null
println(uriRef.getHost().getType()) // "REGNAME"
println(uriRef.getHost().getValue()) // "example.com"
println(uriRef.getPort()) // -1
println(uriRef.getPath()) // "/a/b/c/d/e/f"
println(uriRef.getQuery()) // "k1=v1&k2=&v2"
println(uriRef.getFragment()) // null[!WARNING] The current implementation of
URIReferenceBuilderclass won't throw an exception untilbuild()method is invoked even if invalid input is given since validation for each URI component is performed only whenbuild()is called.
This library designs most classes such as URIReference to be immutable. Here are some examples.
// Example 1: Invoking the "normalize()" method creates a new URIReference instance.
val normalized = URIReference.parse("hTTp://example.com").normalize()
// Example 2: Invoking the "resolve(String uriRef)" method creates a new URIReference instance.
val resolved = URIReference.parse("http://example.com").resolve("/a/b")