All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.http4k.core.UriTemplate.kt Maven / Gradle / Ivy

package org.http4k.core

import java.util.regex.Pattern

data class UriTemplate private constructor(private val template: String) {
    private val templateRegex = template.replace(URI_TEMPLATE_FORMAT,
        { notMatched -> Pattern.quote(notMatched) },
        { matched ->
            when {
                matched.groupValues[2].isBlank() -> "([^/]+)"
                else -> "(${matched.groupValues[2]})"
            }
        }).toRegex()

    private val matches = URI_TEMPLATE_FORMAT.findAll(template)
    private val parameterNames = matches.map { it.groupValues[1] }.toList()

    companion object {
        private val URI_TEMPLATE_FORMAT = "\\{([^}]+?)(?::([^}]+))?\\}".toRegex() // ignore redundant warning #100
        fun from(template: String) = UriTemplate(template.trimSlashes())

        private fun String.trimSlashes() = "^(/)?(.*?)(/)?$".toRegex().replace(this) { result -> result.groupValues[2] }
    }

    fun matches(uri: String): Boolean = templateRegex.matches(uri.trimSlashes())

    fun extract(uri: String): Map =
        parameterNames.zip(templateRegex.findParameterValues(uri.trimSlashes())).toMap()

    fun generate(parameters: Map): String =
        template.replace(URI_TEMPLATE_FORMAT) { matchResult ->
            val paramValue = parameters[matchResult.groupValues[1]] ?: ""
            if (paramValue.contains("/")) paramValue else paramValue.toPathSegmentEncoded()
        }

    private fun Regex.findParameterValues(uri: String): List =
        findAll(uri).first().groupValues.drop(1).map { it.toPathSegmentDecoded() }

    private fun String.replace(regex: Regex, notMatched: (String) -> String, matched: (MatchResult) -> String): String {
        val matches = regex.findAll(this)
        val builder = StringBuilder()
        var position = 0
        for (matchResult in matches) {
            val before = substring(position, matchResult.range.first)
            if (before.isNotEmpty()) builder.append(notMatched(before))
            builder.append(matched(matchResult))
            position = matchResult.range.last + 1
        }
        val after = substring(position, length)
        if (after.isNotEmpty()) builder.append(notMatched(after))
        return builder.toString()
    }

    override fun toString(): String = template
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy