GRAILS-1019: Allowing expressions to be used with the 'disabled' attribute for g...
[grails.git] / src / commons / org / codehaus / groovy / grails / commons / DefaultGrailsControllerClass.java
blob2db324b536696ac663062d8e8cb802a3456fcaab
1 /*
2 * Copyright 2004-2005 the original author or authors.
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
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;
26 import java.util.*;
28 /**
29 * A class that evaluates the conventions contained within controllers to perform auto-configuration
31 * @author Graeme Rocher
32 * @author Steven Devijver
34 * @since 0.1
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;
59 private String uri;
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;
83 else {
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;
135 else {
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() {
189 return this.uris;
192 public boolean mapsToURI(String uri) {
193 for (int i = 0; i < uris.length; i++) {
194 if (pathMatcher.match( uris[i], uri)) {
195 return true;
198 return false;
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);
213 else {
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))
244 return true;
246 else if(excepts instanceof List) {
247 if(!((List)excepts).contains(action))
248 return true;
251 else if(bipMap.containsKey(ONLY)) {
252 Object onlys = bipMap.get(ONLY);
253 if(onlys instanceof String) {
254 if(onlys.equals(action))
255 return true;
257 else if(onlys instanceof List) {
258 if(((List)onlys).contains(action))
259 return true;
261 }else{
262 return true;
265 else if(bip instanceof Closure) {
266 return true;
268 return false;
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);
289 return isAllowed;
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));
300 return null;
303 public Closure getAfterInterceptor(GroovyObject controller) {
304 if(getReference().isReadableProperty(AFTER_INTERCEPTOR)) {
305 return getInterceptor(controller.getProperty(AFTER_INTERCEPTOR));
307 return null;
310 private Closure getInterceptor(Object ip) {
311 if(ip instanceof Map) {
312 Map ipMap = (Map)ip;
313 if(ipMap.containsKey(ACTION)) {
314 return (Closure)ipMap.get(ACTION);
317 else if(ip instanceof Closure) {
318 return (Closure)ip;
320 return null;
323 public Set getCommandObjectActions() {
324 return commandObjectActions;
327 public Set getCommandObjectClasses() {
328 return Collections.unmodifiableSet(commandObjectClasses);
331 public Map getFlows() {
332 return this.flows;
335 public boolean isFlowAction(String actionName) {
336 return this.flows.containsKey(actionName);
339 public String getDefaultAction() {
340 return this.defaultActionName;