2 * Copyright 2004-2005 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.codehaus
.groovy
.grails
.commons
;
18 import groovy
.lang
.Closure
;
19 import groovy
.lang
.GroovyObject
;
20 import org
.apache
.commons
.lang
.StringUtils
;
21 import org
.apache
.commons
.lang
.WordUtils
;
22 import org
.codehaus
.groovy
.grails
.scaffolding
.GrailsScaffolder
;
23 import org
.springframework
.util
.AntPathMatcher
;
25 import java
.beans
.PropertyDescriptor
;
29 * A class that evaluates the conventions contained within controllers to perform auto-configuration
31 * @author Graeme Rocher
32 * @author Steven Devijver
36 * Created: Jul 2, 2005
38 public class DefaultGrailsControllerClass
extends AbstractInjectableGrailsClass
39 implements GrailsControllerClass
{
41 public static final String CONTROLLER
= "Controller";
43 private static final String SLASH
= "/";
44 private static final String VIEW
= "View";
45 private static final String DEFAULT_CLOSURE_PROPERTY
= "defaultAction";
46 private static final String SCAFFOLDING_PROPERTY
= "scaffold";
47 private static final String ALLOWED_HTTP_METHODS_PROPERTY
= "allowedMethods";
49 private static final String EXCEPT
= "except";
50 private static final String ONLY
= "only";
51 private static final String FLOW_SUFFIX
= "Flow";
54 private static final String ACTION
= "action";
55 private Map uri2viewMap
= null;
56 private Map uri2closureMap
= null;
57 private Map viewNames
= null;
58 private String
[] uris
= null;
61 private AntPathMatcher pathMatcher
= new AntPathMatcher();
62 private boolean scaffolding
;
64 private Class scaffoldedClass
;
65 private final Set commandObjectActions
= new HashSet();
66 private final Set commandObjectClasses
= new HashSet();
67 private Map flows
= new HashMap();
68 private String defaultActionName
;
71 public DefaultGrailsControllerClass(Class clazz
) {
72 super(clazz
, CONTROLLER
);
73 //this.uri = SLASH + (StringUtils.isNotBlank(getPackageName()) ? getPackageName().replace('.', '/') + SLASH : "" ) + WordUtils.uncapitalize(getName());
74 this.uri
= SLASH
+ WordUtils
.uncapitalize(getName());
75 defaultActionName
= (String
)getPropertyOrStaticPropertyOrFieldValue(DEFAULT_CLOSURE_PROPERTY
, String
.class);
76 if(defaultActionName
== null) {
77 defaultActionName
= INDEX_ACTION
;
79 this.scaffoldedClass
= (Class
)getPropertyOrStaticPropertyOrFieldValue(SCAFFOLDING_PROPERTY
, Class
.class);
80 if(this.scaffoldedClass
!= null) {
81 this.scaffolding
= true;
84 if(getReference().isReadableProperty(SCAFFOLDING_PROPERTY
)) {
85 Object tmp
= getReference().getPropertyValue(SCAFFOLDING_PROPERTY
);
86 if(tmp
instanceof Boolean
) {
87 this.scaffolding
= ((Boolean
)tmp
).booleanValue();
92 Collection closureNames
= new ArrayList();
93 this.uri2viewMap
= new HashMap();
94 this.uri2closureMap
= new HashMap();
95 this.viewNames
= new HashMap();
97 final String controllerPath
= uri
+ SLASH
;
98 if(this.scaffolding
) {
99 this.uri2closureMap
.put(uri
, defaultActionName
);
100 this.uri2closureMap
.put(controllerPath
, defaultActionName
);
101 this.uri2viewMap
.put(controllerPath
, controllerPath
+ defaultActionName
);
102 this.uri2viewMap
.put(uri
, controllerPath
+ defaultActionName
);
103 this.viewNames
.put( defaultActionName
, controllerPath
+ defaultActionName
);
105 for(int i
= 0; i
< GrailsScaffolder
.ACTION_NAMES
.length
;i
++) {
106 closureNames
.add(GrailsScaffolder
.ACTION_NAMES
[i
]);
107 String viewName
= (String
)getPropertyOrStaticPropertyOrFieldValue(GrailsScaffolder
.ACTION_NAMES
[i
] + VIEW
, String
.class);
108 // if no explicity view name is specified just use action name
109 if(viewName
== null) {
110 viewName
=GrailsScaffolder
.ACTION_NAMES
[i
];
112 String tmpUri
= controllerPath
+ GrailsScaffolder
.ACTION_NAMES
[i
];
113 String viewUri
= controllerPath
+ viewName
;
114 if (StringUtils
.isNotBlank(viewName
)) {
115 this.uri2viewMap
.put(tmpUri
, viewUri
);
116 this.viewNames
.put( GrailsScaffolder
.ACTION_NAMES
[i
], viewUri
);
118 this.uri2closureMap
.put(tmpUri
, GrailsScaffolder
.ACTION_NAMES
[i
]);
119 this.uri2closureMap
.put(tmpUri
+ SLASH
+"**", GrailsScaffolder
.ACTION_NAMES
[i
]);
123 PropertyDescriptor
[] propertyDescriptors
= getReference().getPropertyDescriptors();
124 for (int i
= 0; i
< propertyDescriptors
.length
; i
++) {
125 PropertyDescriptor propertyDescriptor
= propertyDescriptors
[i
];
126 Closure closure
= (Closure
)getPropertyOrStaticPropertyOrFieldValue(propertyDescriptor
.getName(), Closure
.class);
127 if (closure
!= null) {
129 String closureName
= propertyDescriptor
.getName();
130 if(closureName
.endsWith(FLOW_SUFFIX
)) {
131 String flowId
= closureName
.substring(0, closureName
.length()-FLOW_SUFFIX
.length());
132 flows
.put(flowId
, closure
);
133 closureName
= flowId
;
136 Class
[] parameterTypes
= closure
.getParameterTypes();
137 if(parameterTypes
!= null && parameterTypes
.length
> 0) {
138 for(int j
= 0; j
< parameterTypes
.length
; j
++) {
139 Class parameterType
= parameterTypes
[j
];
140 if(GroovyObject
.class.isAssignableFrom(parameterType
)) {
141 commandObjectActions
.add(closureName
);
142 commandObjectClasses
.add(parameterType
);
148 closureNames
.add(closureName
);
149 String viewName
= (String
)getPropertyOrStaticPropertyOrFieldValue(propertyDescriptor
.getName() + VIEW
, String
.class);
150 // if no explicity view name is specified just use property name
151 if(viewName
== null) {
152 viewName
= closureName
;
155 String tmpUri
= controllerPath
+ closureName
;
156 String viewUri
= controllerPath
+ viewName
;
158 uri2closureMap
.put(tmpUri
,closureName
);
159 uri2closureMap
.put(tmpUri
+ SLASH
+ "**",closureName
);
160 if (StringUtils
.isNotBlank(viewName
)) {
161 this.uri2viewMap
.put(tmpUri
, viewUri
);
162 this.viewNames
.put( closureName
, viewUri
);
167 if (getReference().isReadableProperty(defaultActionName
)) {
168 this.uri2closureMap
.put(uri
, defaultActionName
);
169 this.uri2closureMap
.put(controllerPath
, defaultActionName
);
170 this.uri2viewMap
.put(controllerPath
, controllerPath
+ defaultActionName
);
171 this.uri2viewMap
.put(uri
, controllerPath
+ defaultActionName
);
172 this.viewNames
.put( defaultActionName
, controllerPath
+ defaultActionName
);
175 if (closureNames
.size() == 1) {
176 String closureName
= ((String
)closureNames
.iterator().next());
177 this.defaultActionName
= closureName
;
178 this.uri2closureMap
.put(uri
, closureName
);
179 this.uri2closureMap
.put(controllerPath
, closureName
);
180 if (!this.uri2viewMap
.isEmpty()) {
181 this.uri2viewMap
.put(uri
, this.uri2viewMap
.values().iterator().next());
185 this.uris
= (String
[])this.uri2closureMap
.keySet().toArray(new String
[this.uri2closureMap
.keySet().size()]);
188 public String
[] getURIs() {
192 public boolean mapsToURI(String uri
) {
193 for (int i
= 0; i
< uris
.length
; i
++) {
194 if (pathMatcher
.match( uris
[i
], uri
)) {
201 public String
getViewByURI(String uri
) {
202 return (String
)this.uri2viewMap
.get(uri
);
205 public String
getClosurePropertyName(String uri
) {
206 return (String
)this.uri2closureMap
.get(uri
);
209 public String
getViewByName(String viewName
) {
210 if(this.viewNames
.containsKey(viewName
)) {
211 return (String
)this.viewNames
.get(viewName
);
214 return this.uri
+ SLASH
+ viewName
;
218 public boolean isScaffolding() {
219 return this.scaffolding
;
222 public Class
getScaffoldedClass() {
223 return this.scaffoldedClass
;
227 * @param scaffolding The scaffolding to set.
229 public void setScaffolding(boolean scaffolding
) {
230 this.scaffolding
= scaffolding
;
233 public boolean isInterceptedBefore(GroovyObject controller
, String action
) {
234 return controller
.getMetaClass().hasProperty(controller
, BEFORE_INTERCEPTOR
) != null && isIntercepted(controller
.getProperty(BEFORE_INTERCEPTOR
), action
);
237 private boolean isIntercepted(Object bip
, String action
) {
238 if(bip
instanceof Map
) {
239 Map bipMap
= (Map
)bip
;
240 if(bipMap
.containsKey(EXCEPT
)) {
241 Object excepts
= bipMap
.get(EXCEPT
);
242 if(excepts
instanceof String
) {
243 if(!excepts
.equals(action
))
246 else if(excepts
instanceof List
) {
247 if(!((List
)excepts
).contains(action
))
251 else if(bipMap
.containsKey(ONLY
)) {
252 Object onlys
= bipMap
.get(ONLY
);
253 if(onlys
instanceof String
) {
254 if(onlys
.equals(action
))
257 else if(onlys
instanceof List
) {
258 if(((List
)onlys
).contains(action
))
265 else if(bip
instanceof Closure
) {
271 public boolean isHttpMethodAllowedForAction(GroovyObject controller
, String httpMethod
, String actionName
) {
272 boolean isAllowed
= true;
273 Object methodRestrictionsProperty
= null;
274 if(controller
.getMetaClass().hasProperty(controller
, ALLOWED_HTTP_METHODS_PROPERTY
) != null) {
275 methodRestrictionsProperty
= controller
.getProperty(ALLOWED_HTTP_METHODS_PROPERTY
);
277 if(methodRestrictionsProperty
instanceof Map
) {
278 Map map
= (Map
)methodRestrictionsProperty
;
279 if(map
.containsKey(actionName
)) {
280 Object value
= map
.get(actionName
);
281 if(value
instanceof List
) {
282 List listOfMethods
= (List
) value
;
283 isAllowed
= listOfMethods
.contains(httpMethod
);
284 } else if(value
instanceof String
) {
285 isAllowed
= value
.equals(httpMethod
);
292 public boolean isInterceptedAfter(GroovyObject controller
, String action
) {
293 return controller
.getMetaClass().hasProperty(controller
, AFTER_INTERCEPTOR
) != null && isIntercepted(controller
.getProperty(AFTER_INTERCEPTOR
), action
);
296 public Closure
getBeforeInterceptor(GroovyObject controller
) {
297 if(getReference().isReadableProperty(BEFORE_INTERCEPTOR
)) {
298 return getInterceptor(controller
.getProperty(BEFORE_INTERCEPTOR
));
303 public Closure
getAfterInterceptor(GroovyObject controller
) {
304 if(getReference().isReadableProperty(AFTER_INTERCEPTOR
)) {
305 return getInterceptor(controller
.getProperty(AFTER_INTERCEPTOR
));
310 private Closure
getInterceptor(Object ip
) {
311 if(ip
instanceof Map
) {
313 if(ipMap
.containsKey(ACTION
)) {
314 return (Closure
)ipMap
.get(ACTION
);
317 else if(ip
instanceof Closure
) {
323 public Set
getCommandObjectActions() {
324 return commandObjectActions
;
327 public Set
getCommandObjectClasses() {
328 return Collections
.unmodifiableSet(commandObjectClasses
);
331 public Map
getFlows() {
335 public boolean isFlowAction(String actionName
) {
336 return this.flows
.containsKey(actionName
);
339 public String
getDefaultAction() {
340 return this.defaultActionName
;