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

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

mercurial