1 /* Copyright 2004-2005 Graeme Rocher
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
15 package org
.codehaus
.groovy
.grails
.web
.mapping
;
17 import groovy
.lang
.Closure
;
18 import org
.codehaus
.groovy
.grails
.commons
.GrailsClassUtils
;
19 import org
.codehaus
.groovy
.grails
.web
.mapping
.exceptions
.UrlMappingException
;
20 import org
.codehaus
.groovy
.grails
.web
.servlet
.mvc
.GrailsWebRequest
;
21 import org
.codehaus
.groovy
.grails
.web
.util
.WebUtils
;
22 import org
.codehaus
.groovy
.runtime
.DefaultGroovyMethods
;
23 import org
.springframework
.web
.context
.request
.RequestContextHolder
;
25 import javax
.servlet
.http
.HttpServletRequest
;
26 import java
.io
.UnsupportedEncodingException
;
27 import java
.net
.URLDecoder
;
31 * A Class that implements the UrlMappingInfo interface and holds information established from a matched
34 * @author Graeme Rocher
38 * Created: Mar 1, 2007
41 public class DefaultUrlMappingInfo
implements UrlMappingInfo
{
42 private Map params
= Collections
.EMPTY_MAP
;
43 private Object controllerName
;
44 private Object actionName
;
46 private static final String ID_PARAM
= "id";
47 private UrlMappingData urlData
;
48 private Object viewName
;
49 private URLDecoder decoder
;
51 private DefaultUrlMappingInfo(Map params
, UrlMappingData urlData
) {
52 this.params
= Collections
.unmodifiableMap(params
);
53 this.id
= params
.get(ID_PARAM
);
54 this.urlData
= urlData
;
57 public DefaultUrlMappingInfo(Object controllerName
, Object actionName
, Object viewName
, Map params
, UrlMappingData urlData
) {
58 this(params
, urlData
);
59 if (controllerName
== null && viewName
== null)
60 throw new IllegalArgumentException("URL mapping must either provide a controller or view name to map to!");
61 if (params
== null) throw new IllegalArgumentException("Argument [params] cannot be null");
62 this.controllerName
= controllerName
;
63 this.actionName
= actionName
;
64 if (actionName
== null)
65 this.viewName
= viewName
;
68 public DefaultUrlMappingInfo(Object viewName
, Map params
, UrlMappingData urlData
) {
69 this(params
, urlData
);
70 this.viewName
= viewName
;
71 if (viewName
== null) throw new IllegalArgumentException("Argument [viewName] cannot be null or blank");
75 public String
toString() {
76 return urlData
.getUrlPattern();
80 * Populates request parameters for the given UrlMappingInfo instance using the GrailsWebRequest
82 * @param dispatchParams The Map instance
83 * @see org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest
85 protected void populateParamsForMapping(Map dispatchParams
) {
86 Collection keys
= this.params
.keySet();
87 keys
= DefaultGroovyMethods
.toList(keys
);
88 Collections
.sort((List
) keys
, new Comparator() {
90 public int compare(Object leftKey
, Object rightKey
) {
91 Object leftValue
= params
.get(leftKey
);
92 Object rightValue
= params
.get(rightKey
);
93 boolean leftIsClosure
= leftValue
instanceof Closure
;
94 boolean rightIsClosure
= rightValue
instanceof Closure
;
95 if (leftIsClosure
&& rightIsClosure
) return 0;
96 else if (leftIsClosure
&& !rightIsClosure
) return 1;
97 else if (rightIsClosure
&& !leftIsClosure
) return -1;
101 for (Iterator j
= keys
.iterator(); j
.hasNext();) {
103 String name
= (String
) j
.next();
104 Object param
= this.params
.get(name
);
105 if(param
instanceof Closure
) {
106 param
= evaluateNameForValue(param
);
108 if (param
instanceof String
) {
110 param
= decoder
.decode((String
) param
, "UTF-8");
111 } catch (UnsupportedEncodingException e
) {
112 param
= evaluateNameForValue(param
);
115 dispatchParams
.put(name
, param
);
120 public Map
getParameters() {
124 public void configure(GrailsWebRequest webRequest
) {
125 populateParamsForMapping(webRequest
.getParams());
128 public String
getControllerName() {
129 String controllerName
= evaluateNameForValue(this.controllerName
);
130 if (controllerName
== null && getViewName() == null)
131 throw new UrlMappingException("Unable to establish controller name to dispatch for [" + this.controllerName
+ "]. Dynamic closure invocation returned null. Check your mapping file is correct, when assigning the controller name as a request parameter it cannot be an optional token!");
132 return controllerName
;
135 public String
getActionName() {
136 GrailsWebRequest webRequest
= (GrailsWebRequest
) RequestContextHolder
.getRequestAttributes();
138 String name
= webRequest
!=null ?
checkDispatchAction(webRequest
.getCurrentRequest(), null) : null;
140 name
= evaluateNameForValue(this.actionName
);
145 public String
getViewName() {
146 return evaluateNameForValue(this.viewName
);
149 public String
getId() {
150 return evaluateNameForValue(this.id
);
153 private String
evaluateNameForValue(Object value
) {
154 GrailsWebRequest webRequest
= (GrailsWebRequest
) RequestContextHolder
.getRequestAttributes();
155 return evaluateNameForValue(value
, webRequest
);
158 private String
evaluateNameForValue(Object value
, GrailsWebRequest webRequest
) {
163 if (value
instanceof Closure
) {
164 Closure callable
= (Closure
) value
;
165 Object result
= ((Closure
) callable
.clone()).call();
166 name
= result
!= null ? result
.toString() : null;
167 } else if (value
instanceof Map
) {
168 Map httpMethods
= (Map
) value
;
169 name
= (String
) httpMethods
.get(webRequest
.getCurrentRequest().getMethod());
171 name
= value
.toString();
176 private String
checkDispatchAction(HttpServletRequest request
, String actionName
) {
177 for (Enumeration e
= request
.getParameterNames(); e
.hasMoreElements();) {
178 String name
= (String
) e
.nextElement();
179 if (name
.startsWith(WebUtils
.DISPATCH_ACTION_PARAMETER
)) {
180 // remove .x suffix in case of submit image
181 if (name
.endsWith(".x") || name
.endsWith(".y")) {
182 name
= name
.substring(0, name
.length() - 2);
184 actionName
= GrailsClassUtils
.getPropertyNameRepresentation(name
.substring((WebUtils
.DISPATCH_ACTION_PARAMETER
).length()));