src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt

Tue, 05 Jan 2021 19:19:31 +0100

author
Mike Becker <universe@uap-core.de>
date
Tue, 05 Jan 2021 19:19:31 +0100
changeset 179
623c340058f3
child 184
e8eecee6aadf
permissions
-rw-r--r--

migrates the utility classes for the AbstractServlet

universe@179 1 /*
universe@179 2 * Copyright 2021 Mike Becker. All rights reserved.
universe@179 3 *
universe@179 4 * Redistribution and use in source and binary forms, with or without
universe@179 5 * modification, are permitted provided that the following conditions are met:
universe@179 6 *
universe@179 7 * 1. Redistributions of source code must retain the above copyright
universe@179 8 * notice, this list of conditions and the following disclaimer.
universe@179 9 *
universe@179 10 * 2. Redistributions in binary form must reproduce the above copyright
universe@179 11 * notice, this list of conditions and the following disclaimer in the
universe@179 12 * documentation and/or other materials provided with the distribution.
universe@179 13 *
universe@179 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@179 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@179 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
universe@179 17 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
universe@179 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
universe@179 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
universe@179 20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
universe@179 21 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
universe@179 22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
universe@179 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
universe@179 24 */
universe@179 25
universe@179 26 package de.uapcore.lightpit
universe@179 27
universe@179 28 import kotlin.math.min
universe@179 29
universe@179 30 /**
universe@179 31 * Maps requests to methods.
universe@179 32 *
universe@179 33 * This annotation is used to annotate methods within classes which
universe@179 34 * override [AbstractServlet].
universe@179 35 */
universe@179 36 @MustBeDocumented
universe@179 37 @Retention(AnnotationRetention.RUNTIME)
universe@179 38 @Target(AnnotationTarget.FUNCTION)
universe@179 39 annotation class RequestMapping(
universe@179 40
universe@179 41 /**
universe@179 42 * Specifies the HTTP method.
universe@179 43 *
universe@179 44 * @return the HTTP method handled by the annotated Java method
universe@179 45 */
universe@179 46 val method: HttpMethod,
universe@179 47
universe@179 48 /**
universe@179 49 * Specifies the request path relative to the module path.
universe@179 50 * The trailing slash is important.
universe@179 51 * A node may start with a dollar ($) sign.
universe@179 52 * This part of the path is then treated as an path parameter.
universe@179 53 * Path parameters can be obtained by including the [PathParameters] type in the signature.
universe@179 54 *
universe@179 55 * @return the request path the annotated method should handle
universe@179 56 */
universe@179 57 val requestPath: String = "/"
universe@179 58 )
universe@179 59
universe@179 60 class PathParameters : HashMap<String, String>()
universe@179 61
universe@179 62 /**
universe@179 63 * A path pattern optionally containing placeholders.
universe@179 64 *
universe@179 65 * The special directories . and .. are disallowed in the pattern.
universe@179 66 * Placeholders start with a $ sign.
universe@179 67 *
universe@179 68 * @param pattern the pattern
universe@179 69 */
universe@179 70 class PathPattern(pattern: String) {
universe@179 71 private val nodePatterns: List<String>
universe@179 72 private val collection: Boolean
universe@179 73
universe@179 74 private fun parse(pattern: String): List<String> {
universe@179 75 val nodes = pattern.split("/").filter { it.isNotBlank() }.toList()
universe@179 76 require(nodes.none { it == "." || it == ".." }) { "Path must not contain '.' or '..' nodes." }
universe@179 77 return nodes
universe@179 78 }
universe@179 79
universe@179 80 /**
universe@179 81 * Matches a path against this pattern.
universe@179 82 * The path must be canonical in the sense that no . or .. parts occur.
universe@179 83 *
universe@179 84 * @param path the path to match
universe@179 85 * @return true if the path matches the pattern, false otherwise
universe@179 86 */
universe@179 87 fun matches(path: String): Boolean {
universe@179 88 if (collection xor path.endsWith("/")) return false
universe@179 89 val nodes = parse(path)
universe@179 90 if (nodePatterns.size != nodes.size) return false
universe@179 91 for (i in nodePatterns.indices) {
universe@179 92 val pattern = nodePatterns[i]
universe@179 93 val node = nodes[i]
universe@179 94 if (pattern.startsWith("$")) continue
universe@179 95 if (pattern != node) return false
universe@179 96 }
universe@179 97 return true
universe@179 98 }
universe@179 99
universe@179 100 /**
universe@179 101 * Returns the path parameters found in the specified path using this pattern.
universe@179 102 * The return value of this method is undefined, if the patter does not match.
universe@179 103 *
universe@179 104 * @param path the path
universe@179 105 * @return the path parameters, if any, or an empty map
universe@179 106 * @see .matches
universe@179 107 */
universe@179 108 fun obtainPathParameters(path: String): PathParameters {
universe@179 109 val params = PathParameters()
universe@179 110 val nodes = parse(path)
universe@179 111 for (i in 0 until min(nodes.size, nodePatterns.size)) {
universe@179 112 val pattern = nodePatterns[i]
universe@179 113 val node = nodes[i]
universe@179 114 if (pattern.startsWith("$")) {
universe@179 115 params[pattern.substring(1)] = node
universe@179 116 }
universe@179 117 }
universe@179 118 return params
universe@179 119 }
universe@179 120
universe@179 121 override fun hashCode(): Int {
universe@179 122 val str = StringBuilder()
universe@179 123 for (node in nodePatterns) {
universe@179 124 if (node.startsWith("$")) {
universe@179 125 str.append("/$")
universe@179 126 } else {
universe@179 127 str.append('/')
universe@179 128 str.append(node)
universe@179 129 }
universe@179 130 }
universe@179 131 if (collection) str.append('/')
universe@179 132 return str.toString().hashCode()
universe@179 133 }
universe@179 134
universe@179 135 override fun equals(other: Any?): Boolean {
universe@179 136 if (other is PathPattern) {
universe@179 137 if (collection xor other.collection || nodePatterns.size != other.nodePatterns.size) return false
universe@179 138 for (i in nodePatterns.indices) {
universe@179 139 val left = nodePatterns[i]
universe@179 140 val right = other.nodePatterns[i]
universe@179 141 if (left.startsWith("$") && right.startsWith("$")) continue
universe@179 142 if (left != right) return false
universe@179 143 }
universe@179 144 return true
universe@179 145 } else {
universe@179 146 return false
universe@179 147 }
universe@179 148 }
universe@179 149
universe@179 150 init {
universe@179 151 nodePatterns = parse(pattern)
universe@179 152 collection = pattern.endsWith("/")
universe@179 153 }
universe@179 154 }
universe@179 155

mercurial