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

changeset 184
e8eecee6aadf
parent 179
623c340058f3
child 195
9c7aff3cbb14
equal deleted inserted replaced
183:61669abf277f 184:e8eecee6aadf
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */ 24 */
25 25
26 package de.uapcore.lightpit 26 package de.uapcore.lightpit
27 27
28 import de.uapcore.lightpit.dao.DataAccessObject
29 import de.uapcore.lightpit.viewmodel.NavMenu
30 import de.uapcore.lightpit.viewmodel.View
31 import javax.servlet.http.HttpServletRequest
32 import javax.servlet.http.HttpServletResponse
33 import javax.servlet.http.HttpSession
28 import kotlin.math.min 34 import kotlin.math.min
29 35
30 /** 36 typealias MappingMethod = (HttpRequest, DataAccessObject) -> Unit
31 * Maps requests to methods. 37 typealias PathParameters = Map<String, String>
32 * 38
33 * This annotation is used to annotate methods within classes which 39 class HttpRequest(
34 * override [AbstractServlet]. 40 val request: HttpServletRequest,
35 */ 41 val response: HttpServletResponse,
36 @MustBeDocumented 42 val pathParams: PathParameters = emptyMap()
37 @Retention(AnnotationRetention.RUNTIME) 43 ) {
38 @Target(AnnotationTarget.FUNCTION) 44 val session: HttpSession = request.session
39 annotation class RequestMapping( 45
40 46 val remoteUser: String? = request.remoteUser
41 /** 47
42 * Specifies the HTTP method. 48 /**
43 * 49 * The name of the content page.
44 * @return the HTTP method handled by the annotated Java method 50 *
45 */ 51 * @see Constants#REQ_ATTR_CONTENT_PAGE
46 val method: HttpMethod, 52 */
47 53 var contentPage = ""
48 /** 54 set(value) {
49 * Specifies the request path relative to the module path. 55 field = value
50 * The trailing slash is important. 56 request.setAttribute(Constants.REQ_ATTR_CONTENT_PAGE, jspPath(value))
51 * A node may start with a dollar ($) sign. 57 }
52 * This part of the path is then treated as an path parameter. 58
53 * Path parameters can be obtained by including the [PathParameters] type in the signature. 59 /**
54 * 60 * A list of additional style sheets.
55 * @return the request path the annotated method should handle 61 *
56 */ 62 * @see Constants#REQ_ATTR_STYLESHEET
57 val requestPath: String = "/" 63 */
58 ) 64 var styleSheets = emptyList<String>()
59 65 set(value) {
60 class PathParameters : HashMap<String, String>() 66 field = value
67 request.setAttribute(Constants.REQ_ATTR_STYLESHEET,
68 value.map { it.withExt(".css") }
69 )
70 }
71
72 /**
73 * The name of the navigation menu JSP.
74 *
75 * @see Constants#REQ_ATTR_NAVIGATION
76 */
77 var navigationMenu: NavMenu? = null
78 set(value) {
79 field = value
80 request.setAttribute(Constants.REQ_ATTR_NAVIGATION, navigationMenu)
81 }
82
83 var redirectLocation = ""
84 set(value) {
85 field = value
86 request.setAttribute(Constants.REQ_ATTR_REDIRECT_LOCATION, baseHref + value)
87 }
88
89 /**
90 * The view object.
91 *
92 * @see Constants#REQ_ATTR_VIEWMODEL
93 */
94 var view: View? = null
95 set(value) {
96 field = value
97 request.setAttribute(Constants.REQ_ATTR_VIEWMODEL, value)
98 }
99
100 /**
101 * The base path of this application.
102 */
103 val baseHref get() = "${request.scheme}://${request.serverName}:${request.serverPort}${request.contextPath}/"
104
105 private fun String.withExt(ext: String) = if (endsWith(ext)) this else plus(ext)
106 private fun jspPath(name: String) = Constants.JSP_PATH_PREFIX.plus(name).withExt(".jsp")
107
108 fun param(name: String): String? = request.getParameter(name)
109 fun paramArray(name: String): Array<String> = request.getParameterValues(name) ?: emptyArray()
110
111 fun render(page: String? = null) {
112 page?.let { contentPage = it }
113 request.getRequestDispatcher(jspPath("site")).forward(request, response)
114 }
115
116 fun renderCommit(location: String? = null) {
117 location?.let { redirectLocation = it }
118 contentPage = Constants.JSP_COMMIT_SUCCESSFUL
119 render()
120 }
121 }
61 122
62 /** 123 /**
63 * A path pattern optionally containing placeholders. 124 * A path pattern optionally containing placeholders.
64 * 125 *
65 * The special directories . and .. are disallowed in the pattern. 126 * The special directories . and .. are disallowed in the pattern.
66 * Placeholders start with a $ sign. 127 * Placeholders start with a % sign.
67 * 128 *
68 * @param pattern the pattern 129 * @param pattern the pattern
69 */ 130 */
70 class PathPattern(pattern: String) { 131 class PathPattern(pattern: String) {
71 private val nodePatterns: List<String> 132 private val nodePatterns: List<String>
89 val nodes = parse(path) 150 val nodes = parse(path)
90 if (nodePatterns.size != nodes.size) return false 151 if (nodePatterns.size != nodes.size) return false
91 for (i in nodePatterns.indices) { 152 for (i in nodePatterns.indices) {
92 val pattern = nodePatterns[i] 153 val pattern = nodePatterns[i]
93 val node = nodes[i] 154 val node = nodes[i]
94 if (pattern.startsWith("$")) continue 155 if (pattern.startsWith("%")) continue
95 if (pattern != node) return false 156 if (pattern != node) return false
96 } 157 }
97 return true 158 return true
98 } 159 }
99 160
104 * @param path the path 165 * @param path the path
105 * @return the path parameters, if any, or an empty map 166 * @return the path parameters, if any, or an empty map
106 * @see .matches 167 * @see .matches
107 */ 168 */
108 fun obtainPathParameters(path: String): PathParameters { 169 fun obtainPathParameters(path: String): PathParameters {
109 val params = PathParameters() 170 val params = mutableMapOf<String, String>()
110 val nodes = parse(path) 171 val nodes = parse(path)
111 for (i in 0 until min(nodes.size, nodePatterns.size)) { 172 for (i in 0 until min(nodes.size, nodePatterns.size)) {
112 val pattern = nodePatterns[i] 173 val pattern = nodePatterns[i]
113 val node = nodes[i] 174 val node = nodes[i]
114 if (pattern.startsWith("$")) { 175 if (pattern.startsWith("%")) {
115 params[pattern.substring(1)] = node 176 params[pattern.substring(1)] = node
116 } 177 }
117 } 178 }
118 return params 179 return params
119 } 180 }
120 181
121 override fun hashCode(): Int { 182 override fun hashCode(): Int {
122 val str = StringBuilder() 183 val str = StringBuilder()
123 for (node in nodePatterns) { 184 for (node in nodePatterns) {
124 if (node.startsWith("$")) { 185 if (node.startsWith("%")) {
125 str.append("/$") 186 str.append("/%")
126 } else { 187 } else {
127 str.append('/') 188 str.append('/')
128 str.append(node) 189 str.append(node)
129 } 190 }
130 } 191 }
136 if (other is PathPattern) { 197 if (other is PathPattern) {
137 if (collection xor other.collection || nodePatterns.size != other.nodePatterns.size) return false 198 if (collection xor other.collection || nodePatterns.size != other.nodePatterns.size) return false
138 for (i in nodePatterns.indices) { 199 for (i in nodePatterns.indices) {
139 val left = nodePatterns[i] 200 val left = nodePatterns[i]
140 val right = other.nodePatterns[i] 201 val right = other.nodePatterns[i]
141 if (left.startsWith("$") && right.startsWith("$")) continue 202 if (left.startsWith("%") && right.startsWith("%")) continue
142 if (left != right) return false 203 if (left != right) return false
143 } 204 }
144 return true 205 return true
145 } else { 206 } else {
146 return false 207 return false

mercurial