GRAILS-1019: Allowing expressions to be used with the 'disabled' attribute for g...
[grails.git] / src / scaffolding / org / codehaus / groovy / grails / scaffolding / DefaultGrailsScaffolder.java
blob888b578d6043199c04d890cf9c5fce31c333923a
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.scaffolding;
18 import groovy.lang.Closure;
19 import groovy.lang.GroovyObject;
20 import org.codehaus.groovy.grails.scaffolding.exceptions.ScaffoldingException;
21 import org.codehaus.groovy.grails.web.metaclass.ControllerDynamicMethods;
22 import org.codehaus.groovy.grails.web.metaclass.RedirectDynamicMethod;
23 import org.codehaus.groovy.grails.web.metaclass.RenderDynamicMethod;
24 import org.codehaus.groovy.grails.web.servlet.mvc.exceptions.ControllerExecutionException;
25 import org.springframework.web.servlet.ModelAndView;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29 import java.io.IOException;
30 import java.lang.reflect.Constructor;
31 import java.lang.reflect.InvocationTargetException;
32 import java.util.HashMap;
33 import java.util.Map;
34 /**
35 * The default implementation of scaffolding for Grails domain class and controller. Implements the
36 * GrailsScaffolder interface.
38 * Requires a ScaffoldRequestHandler and ScaffoldResponseHandlerFactory. The ScaffoldRequestHandler is
39 * responsible for handling requests to CRUD operations, whilst the ScaffoldResponseHandlerFactory
40 * creates ScaffoldResponseHandler instances that are responsible for delivering the response
41 * to the user in whatever format is required.
43 * @see org.codehaus.groovy.grails.scaffolding.GrailsScaffolder
44 * @see org.codehaus.groovy.grails.scaffolding.ScaffoldRequestHandler
45 * @see org.codehaus.groovy.grails.scaffolding.ScaffoldResponseHandlerFactory
47 * @author Graeme Rocher
48 * @since 0.1
50 * Created 30 Nov 2005
52 public class DefaultGrailsScaffolder implements GrailsScaffolder {
55 private ScaffoldRequestHandler scaffoldRequestHandler;
56 private ScaffoldResponseHandlerFactory scaffoldResponseHandlerFactory;
58 /**
59 * Abstract base class that extends closure and retrieves the necessary arguments from the controller
60 * This is used to inject closure properties into controllers so controller actions appear as if by magic.
62 static abstract class AbstractAction extends Closure {
63 protected GroovyObject controller;
64 protected HttpServletRequest request;
65 protected HttpServletResponse response;
66 protected ScaffoldRequestHandler scaffoldRequestHandler;
67 protected ScaffoldResponseHandlerFactory scaffoldResponseFactory;
68 protected ScaffoldResponseHandler scaffoldResponseHandler;
70 public AbstractAction(Object owner) {
71 super(owner);
72 controller = (GroovyObject)getOwner();
73 request = (HttpServletRequest)controller.getProperty(ControllerDynamicMethods.REQUEST_PROPERTY);
74 response = (HttpServletResponse)controller.getProperty(ControllerDynamicMethods.RESPONSE_PROPERTY);
77 /**
78 * @param scaffoldRequestHandler The scaffoldRequestHandler to set.
80 public void setScaffoldRequestHandler(
81 ScaffoldRequestHandler scaffoldRequestHandler) {
82 this.scaffoldRequestHandler = scaffoldRequestHandler;
85 public void setScaffoldResponseHandlerFactory(ScaffoldResponseHandlerFactory factory) {
86 this.scaffoldResponseFactory = factory;
87 Object includeUri = request.getAttribute("javax.servlet.include.request_uri");
88 String uri;
89 if (includeUri != null) {
90 uri = (String) includeUri;
91 } else {
92 uri = request.getRequestURI();
94 this.scaffoldResponseHandler = this.scaffoldResponseFactory.getScaffoldResponseHandler(uri);
98 /**
99 * A closure that handles a call to a scaffolded list action
101 class ListAction extends AbstractAction {
102 public ListAction(Object owner) {
103 super(owner);
106 /* (non-Javadoc)
107 * @see groovy.lang.Closure#call(java.lang.Object[])
109 public Object call(Object[] args) {
110 Map model = this.scaffoldRequestHandler.handleList(request,response);
111 return scaffoldResponseHandler.handleResponse(request,response,LIST_ACTION,model);
117 * A closure that handles a call to a scaffolded list action
119 class IndexAction extends AbstractAction {
120 public IndexAction(Object owner) {
121 super(owner);
124 /* (non-Javadoc)
125 * @see groovy.lang.Closure#call(java.lang.Object[])
127 public Object call(Object[] args) {
128 Map arguments = new HashMap();
129 arguments.put( RedirectDynamicMethod.ARGUMENT_ACTION, LIST_ACTION );
130 return controller.invokeMethod(RedirectDynamicMethod.METHOD_SIGNATURE,new Object[]{ arguments });
136 * A closure that handles a call to a scaffolded list action
138 class CreateAction extends AbstractAction {
139 public CreateAction(Object owner) {
140 super(owner);
143 /* (non-Javadoc)
144 * @see groovy.lang.Closure#call(java.lang.Object[])
146 public Object call(Object[] args) {
147 Map model = this.scaffoldRequestHandler.handleCreate(request,response,new DefaultScaffoldCallback());
148 return scaffoldResponseHandler.handleResponse(request,response,CREATE_ACTION,model);
154 * A closure action that implements showing a scaffolded instance by id. If the id is not
155 * specified it redirects to the "list" action
157 class ShowAction extends AbstractAction {
159 public ShowAction(Object owner) {
160 super(owner);
163 /* (non-Javadoc)
164 * @see groovy.lang.Closure#call(java.lang.Object[])
166 public Object call(Object[] args) {
168 ScaffoldCallback callback = new DefaultScaffoldCallback();
169 Map model = this.scaffoldRequestHandler.handleShow(request,response, callback);
171 if(callback.isInvoked()) {
172 return scaffoldResponseHandler.handleResponse(request,response,SHOW_ACTION,model);
174 else {
175 Map arguments = new HashMap();
176 arguments.put( RedirectDynamicMethod.ARGUMENT_ACTION, LIST_ACTION );
177 return controller.invokeMethod(RedirectDynamicMethod.METHOD_SIGNATURE,new Object[]{ arguments });
184 * A closure action that implements editing a scaffolded instance by id. At the moment it is the same
185 * as ShowAction, but could differ in the future as it may load other model instances to support editing
186 * an instance including the GrailsDomainClass instance
188 class EditAction extends AbstractAction {
190 public EditAction(Object owner) {
191 super(owner);
194 /* (non-Javadoc)
195 * @see groovy.lang.Closure#call(java.lang.Object[])
197 public Object call(Object[] args) {
199 ScaffoldCallback callback = new DefaultScaffoldCallback();
200 Map model = this.scaffoldRequestHandler.handleShow(request,response, callback);
202 if(callback.isInvoked()) {
203 return scaffoldResponseHandler.handleResponse(request,response,EDIT_ACTION,model);
205 else {
206 Closure listAction = (Closure)controller.getProperty(LIST_ACTION);
207 Map arguments = new HashMap();
208 arguments.put( RedirectDynamicMethod.ARGUMENT_ACTION, listAction );
209 arguments.put( RedirectDynamicMethod.ARGUMENT_ERRORS, callback.getErrors() );
210 return controller.invokeMethod(RedirectDynamicMethod.METHOD_SIGNATURE,new Object[]{ arguments });
217 * A closure action that implements deletion of a scaffolded instance by id. The instance is deleted and then
218 * the action redirects to "list"
220 class DeleteAction extends AbstractAction {
222 public DeleteAction(Object owner) {
223 super(owner);
226 /* (non-Javadoc)
227 * @see groovy.lang.Closure#call(java.lang.Object[])
229 public Object call(Object[] args) {
230 if(!"POST".equals(request.getMethod())) {
231 try {
232 response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
233 return null;
234 } catch (IOException e) {
235 throw new ControllerExecutionException("I/O error sending 403 error",e);
239 ScaffoldCallback callback = new DefaultScaffoldCallback();
240 // delete
241 this.scaffoldRequestHandler.handleDelete(request,response, callback);
243 // now redirect to list
244 Closure listAction = (Closure)controller.getProperty(LIST_ACTION);
245 Map arguments = new HashMap();
246 arguments.put( RedirectDynamicMethod.ARGUMENT_ACTION, listAction );
247 return controller.invokeMethod(RedirectDynamicMethod.METHOD_SIGNATURE,new Object[]{ arguments });
253 * A closure action that implements the saving of new scaffoled instances. If the instance is created successfully
254 * the action redirects to "show" for the id, otherwise it redirects to "create"
256 class SaveAction extends AbstractAction {
258 public SaveAction(Object owner) {
259 super(owner);
262 /* (non-Javadoc)
263 * @see groovy.lang.Closure#call(java.lang.Object[])
265 public Object call(Object[] args) {
266 if(!"POST".equals(request.getMethod())) {
267 try {
268 response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
269 return null;
270 } catch (IOException e) {
271 throw new ControllerExecutionException("I/O error sending 403 error",e);
275 ScaffoldCallback callback = new DefaultScaffoldCallback();
276 // save
277 Map model = this.scaffoldRequestHandler.handleSave(request,response, callback);
278 if(callback.isInvoked()) {
279 Closure showAction = (Closure)controller.getProperty(SHOW_ACTION);
280 Map arguments = new HashMap();
281 arguments.put( RedirectDynamicMethod.ARGUMENT_ACTION, showAction );
282 arguments.put( RedirectDynamicMethod.ARGUMENT_ID, model.get(RedirectDynamicMethod.ARGUMENT_ID) );
283 return controller.invokeMethod(RedirectDynamicMethod.METHOD_SIGNATURE,new Object[]{ arguments });
285 else {
287 Map arguments = new HashMap();
288 arguments.put( RenderDynamicMethod.ARGUMENT_VIEW, CREATE_ACTION );
289 arguments.put( RenderDynamicMethod.ARGUMENT_MODEL,model );
290 controller.invokeMethod(RenderDynamicMethod.METHOD_SIGNATURE,new Object[]{ arguments });
293 ModelAndView mv = this.scaffoldResponseHandler.handleResponse(request, response,CREATE_ACTION,model);
294 controller.setProperty(ControllerDynamicMethods.MODEL_AND_VIEW_PROPERTY, mv);
295 return mv;
303 * A closure action that implements the updating of an existing scaffoled instances. If the instance is updated successfully
304 * the action redirects to "show" for the id, otherwise it redirects to "edit"
306 class UpdateAction extends AbstractAction {
308 public UpdateAction(Object owner) {
309 super(owner);
312 /* (non-Javadoc)
313 * @see groovy.lang.Closure#call(java.lang.Object[])
315 public Object call(Object[] args) {
316 if(!"POST".equals(request.getMethod())) {
317 try {
318 response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
319 return null;
320 } catch (IOException e) {
321 throw new ControllerExecutionException("I/O error sending 403 error",e);
325 ScaffoldCallback callback = new DefaultScaffoldCallback();
326 // save
327 Map model = this.scaffoldRequestHandler.handleUpdate(request,response, callback);
328 if(callback.isInvoked()) {
329 Closure showAction = (Closure)controller.getProperty(SHOW_ACTION);
330 Map arguments = new HashMap();
331 arguments.put( RedirectDynamicMethod.ARGUMENT_ACTION, showAction );
332 arguments.put( RedirectDynamicMethod.ARGUMENT_ID, model.get( RedirectDynamicMethod.ARGUMENT_ID ) );
334 return controller.invokeMethod(RedirectDynamicMethod.METHOD_SIGNATURE,new Object[]{ arguments });
336 else {
337 Map arguments = new HashMap();
338 arguments.put( RenderDynamicMethod.ARGUMENT_VIEW, EDIT_ACTION );
339 arguments.put( RenderDynamicMethod.ARGUMENT_MODEL,model );
340 ModelAndView mv = this.scaffoldResponseHandler.handleResponse(request, response,EDIT_ACTION,model);
341 controller.setProperty(ControllerDynamicMethods.MODEL_AND_VIEW_PROPERTY, mv);
342 return mv;
348 protected static Map actions = new HashMap();
349 protected static Map actionClassToNameMap = new HashMap();
351 static {
352 actions.put( INDEX_ACTION, IndexAction.class.getConstructors()[0] );
353 actionClassToNameMap.put(IndexAction.class, INDEX_ACTION);
355 actions.put( LIST_ACTION, ListAction.class.getConstructors()[0] );
356 actionClassToNameMap.put(ListAction.class, LIST_ACTION);
358 actions.put( SHOW_ACTION, ShowAction.class.getConstructors()[0] );
359 actionClassToNameMap.put(ShowAction.class, SHOW_ACTION);
361 actions.put( EDIT_ACTION, EditAction.class.getConstructors()[0] );
362 actionClassToNameMap.put(EditAction.class, EDIT_ACTION);
364 actions.put( DELETE_ACTION, DeleteAction.class.getConstructors()[0] );
365 actionClassToNameMap.put(DeleteAction.class, DELETE_ACTION);
367 actions.put( SAVE_ACTION, SaveAction.class.getConstructors()[0] );
368 actionClassToNameMap.put(SaveAction.class, SAVE_ACTION);
370 actions.put( UPDATE_ACTION, UpdateAction.class.getConstructors()[0] );
371 actionClassToNameMap.put(UpdateAction.class, UPDATE_ACTION);
373 actions.put( CREATE_ACTION, CreateAction.class.getConstructors()[0] );
374 actionClassToNameMap.put(CreateAction.class, CREATE_ACTION);
378 public boolean supportsAction(String actionName) {
379 return actions.containsKey(actionName);
382 public Closure getAction(GroovyObject controller,String actionName) {
383 Constructor c = (Constructor)actions.get(actionName);
384 AbstractAction action;
385 try {
386 action = (AbstractAction)c.newInstance(new Object[]{this, controller });
387 action.setScaffoldRequestHandler(this.scaffoldRequestHandler);
388 action.setScaffoldResponseHandlerFactory(this.scaffoldResponseHandlerFactory);
389 } catch (IllegalArgumentException e) {
390 throw new ScaffoldingException("Illegal argument instantiating action ["+actionName+"] for controller ["+controller.getClass().getName()+"]: " + e.getMessage(),e);
391 } catch (InstantiationException e) {
392 throw new ScaffoldingException("Error instantiating action ["+actionName+"] for controller ["+controller.getClass().getName()+"]: " + e.getMessage(),e);
393 } catch (IllegalAccessException e) {
394 throw new ScaffoldingException("Illegal access instantiating action ["+actionName+"] for controller ["+controller.getClass().getName()+"]: " + e.getMessage(),e);
395 } catch (InvocationTargetException e) {
396 throw new ScaffoldingException("Invocation error instantiating action ["+actionName+"] for controller ["+controller.getClass().getName()+"]: " + e.getMessage(),e);
398 return action;
404 * @param scaffoldRequestHandler The scaffoldRequestHandler to set.
406 public void setScaffoldRequestHandler(
407 ScaffoldRequestHandler scaffoldRequestHandler) {
408 this.scaffoldRequestHandler = scaffoldRequestHandler;
413 * @param scaffoldResponseHandlerFactory The scaffoldResponseHandlerFactory to set.
415 public void setScaffoldResponseHandlerFactory(
416 ScaffoldResponseHandlerFactory scaffoldResponseHandlerFactory) {
417 this.scaffoldResponseHandlerFactory = scaffoldResponseHandlerFactory;
420 public String[] getSupportedActionNames() {
421 return ACTION_NAMES;
424 public String getActionName(Closure action) {
425 return (String)actionClassToNameMap.get(action.getClass());
428 public ScaffoldRequestHandler getScaffoldRequestHandler() {
429 return this.scaffoldRequestHandler;