View Javadoc
1   /*
2    * Copyright 2002-2012 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  
17  package org.springframework.web.servlet;
18  
19  import java.util.Map;
20  
21  import org.springframework.ui.ModelMap;
22  import org.springframework.util.CollectionUtils;
23  
24  /**
25   * Holder for both Model and View in the web MVC framework.
26   * Note that these are entirely distinct. This class merely holds
27   * both to make it possible for a controller to return both model
28   * and view in a single return value.
29   *
30   * <p>Represents a model and view returned by a handler, to be resolved
31   * by a DispatcherServlet. The view can take the form of a String
32   * view name which will need to be resolved by a ViewResolver object;
33   * alternatively a View object can be specified directly. The model
34   * is a Map, allowing the use of multiple objects keyed by name.
35   *
36   * @author Rod Johnson
37   * @author Juergen Hoeller
38   * @author Rob Harrop
39   * @see DispatcherServlet
40   * @see ViewResolver
41   * @see HandlerAdapter#handle
42   * @see org.springframework.web.servlet.mvc.Controller#handleRequest
43   */
44  public class ModelAndView {
45  
46  	/** View instance or view name String */
47  	private Object view;
48  
49  	/** Model Map */
50  	private ModelMap model;
51  
52  	/** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */
53  	private boolean cleared = false;
54  
55  
56  	/**
57  	 * Default constructor for bean-style usage: populating bean
58  	 * properties instead of passing in constructor arguments.
59  	 * @see #setView(View)
60  	 * @see #setViewName(String)
61  	 */
62  	public ModelAndView() {
63  	}
64  
65  	/**
66  	 * Convenient constructor when there is no model data to expose.
67  	 * Can also be used in conjunction with {@code addObject}.
68  	 * @param viewName name of the View to render, to be resolved
69  	 * by the DispatcherServlet's ViewResolver
70  	 * @see #addObject
71  	 */
72  	public ModelAndView(String viewName) {
73  		this.view = viewName;
74  	}
75  
76  	/**
77  	 * Convenient constructor when there is no model data to expose.
78  	 * Can also be used in conjunction with {@code addObject}.
79  	 * @param view View object to render
80  	 * @see #addObject
81  	 */
82  	public ModelAndView(View view) {
83  		this.view = view;
84  	}
85  
86  	/**
87  	 * Creates new ModelAndView given a view name and a model.
88  	 * @param viewName name of the View to render, to be resolved
89  	 * by the DispatcherServlet's ViewResolver
90  	 * @param model Map of model names (Strings) to model objects
91  	 * (Objects). Model entries may not be {@code null}, but the
92  	 * model Map may be {@code null} if there is no model data.
93  	 */
94  	public ModelAndView(String viewName, Map<String, ?> model) {
95  		this.view = viewName;
96  		if (model != null) {
97  			getModelMap().addAllAttributes(model);
98  		}
99  	}
100 
101 	/**
102 	 * Creates new ModelAndView given a View object and a model.
103 	 * <emphasis>Note: the supplied model data is copied into the internal
104 	 * storage of this class. You should not consider to modify the supplied
105 	 * Map after supplying it to this class</emphasis>
106 	 * @param view View object to render
107 	 * @param model Map of model names (Strings) to model objects
108 	 * (Objects). Model entries may not be {@code null}, but the
109 	 * model Map may be {@code null} if there is no model data.
110 	 */
111 	public ModelAndView(View view, Map<String, ?> model) {
112 		this.view = view;
113 		if (model != null) {
114 			getModelMap().addAllAttributes(model);
115 		}
116 	}
117 
118 	/**
119 	 * Convenient constructor to take a single model object.
120 	 * @param viewName name of the View to render, to be resolved
121 	 * by the DispatcherServlet's ViewResolver
122 	 * @param modelName name of the single entry in the model
123 	 * @param modelObject the single model object
124 	 */
125 	public ModelAndView(String viewName, String modelName, Object modelObject) {
126 		this.view = viewName;
127 		addObject(modelName, modelObject);
128 	}
129 
130 	/**
131 	 * Convenient constructor to take a single model object.
132 	 * @param view View object to render
133 	 * @param modelName name of the single entry in the model
134 	 * @param modelObject the single model object
135 	 */
136 	public ModelAndView(View view, String modelName, Object modelObject) {
137 		this.view = view;
138 		addObject(modelName, modelObject);
139 	}
140 
141 
142 	/**
143 	 * Set a view name for this ModelAndView, to be resolved by the
144 	 * DispatcherServlet via a ViewResolver. Will override any
145 	 * pre-existing view name or View.
146 	 */
147 	public void setViewName(String viewName) {
148 		this.view = viewName;
149 	}
150 
151 	/**
152 	 * Return the view name to be resolved by the DispatcherServlet
153 	 * via a ViewResolver, or {@code null} if we are using a View object.
154 	 */
155 	public String getViewName() {
156 		return (this.view instanceof String ? (String) this.view : null);
157 	}
158 
159 	/**
160 	 * Set a View object for this ModelAndView. Will override any
161 	 * pre-existing view name or View.
162 	 */
163 	public void setView(View view) {
164 		this.view = view;
165 	}
166 
167 	/**
168 	 * Return the View object, or {@code null} if we are using a view name
169 	 * to be resolved by the DispatcherServlet via a ViewResolver.
170 	 */
171 	public View getView() {
172 		return (this.view instanceof View ? (View) this.view : null);
173 	}
174 
175 	/**
176 	 * Indicate whether or not this {@code ModelAndView} has a view, either
177 	 * as a view name or as a direct {@link View} instance.
178 	 */
179 	public boolean hasView() {
180 		return (this.view != null);
181 	}
182 
183 	/**
184 	 * Return whether we use a view reference, i.e. {@code true}
185 	 * if the view has been specified via a name to be resolved by the
186 	 * DispatcherServlet via a ViewResolver.
187 	 */
188 	public boolean isReference() {
189 		return (this.view instanceof String);
190 	}
191 
192 	/**
193 	 * Return the model map. May return {@code null}.
194 	 * Called by DispatcherServlet for evaluation of the model.
195 	 */
196 	protected Map<String, Object> getModelInternal() {
197 		return this.model;
198 	}
199 
200 	/**
201 	 * Return the underlying {@code ModelMap} instance (never {@code null}).
202 	 */
203 	public ModelMap getModelMap() {
204 		if (this.model == null) {
205 			this.model = new ModelMap();
206 		}
207 		return this.model;
208 	}
209 
210 	/**
211 	 * Return the model map. Never returns {@code null}.
212 	 * To be called by application code for modifying the model.
213 	 */
214 	public Map<String, Object> getModel() {
215 		return getModelMap();
216 	}
217 
218 
219 	/**
220 	 * Add an attribute to the model.
221 	 * @param attributeName name of the object to add to the model
222 	 * @param attributeValue object to add to the model (never {@code null})
223 	 * @see ModelMap#addAttribute(String, Object)
224 	 * @see #getModelMap()
225 	 */
226 	public ModelAndView addObject(String attributeName, Object attributeValue) {
227 		getModelMap().addAttribute(attributeName, attributeValue);
228 		return this;
229 	}
230 
231 	/**
232 	 * Add an attribute to the model using parameter name generation.
233 	 * @param attributeValue the object to add to the model (never {@code null})
234 	 * @see ModelMap#addAttribute(Object)
235 	 * @see #getModelMap()
236 	 */
237 	public ModelAndView addObject(Object attributeValue) {
238 		getModelMap().addAttribute(attributeValue);
239 		return this;
240 	}
241 
242 	/**
243 	 * Add all attributes contained in the provided Map to the model.
244 	 * @param modelMap a Map of attributeName -> attributeValue pairs
245 	 * @see ModelMap#addAllAttributes(Map)
246 	 * @see #getModelMap()
247 	 */
248 	public ModelAndView addAllObjects(Map<String, ?> modelMap) {
249 		getModelMap().addAllAttributes(modelMap);
250 		return this;
251 	}
252 
253 
254 	/**
255 	 * Clear the state of this ModelAndView object.
256 	 * The object will be empty afterwards.
257 	 * <p>Can be used to suppress rendering of a given ModelAndView object
258 	 * in the {@code postHandle} method of a HandlerInterceptor.
259 	 * @see #isEmpty()
260 	 * @see HandlerInterceptor#postHandle
261 	 */
262 	public void clear() {
263 		this.view = null;
264 		this.model = null;
265 		this.cleared = true;
266 	}
267 
268 	/**
269 	 * Return whether this ModelAndView object is empty,
270 	 * i.e. whether it does not hold any view and does not contain a model.
271 	 */
272 	public boolean isEmpty() {
273 		return (this.view == null && CollectionUtils.isEmpty(this.model));
274 	}
275 
276 	/**
277 	 * Return whether this ModelAndView object is empty as a result of a call to {@link #clear}
278 	 * i.e. whether it does not hold any view and does not contain a model.
279 	 * <p>Returns {@code false} if any additional state was added to the instance
280 	 * <strong>after</strong> the call to {@link #clear}.
281 	 * @see #clear()
282 	 */
283 	public boolean wasCleared() {
284 		return (this.cleared && isEmpty());
285 	}
286 
287 
288 	/**
289 	 * Return diagnostic information about this model and view.
290 	 */
291 	@Override
292 	public String toString() {
293 		StringBuilder sb = new StringBuilder("ModelAndView: ");
294 		if (isReference()) {
295 			sb.append("reference to view with name '").append(this.view).append("'");
296 		}
297 		else {
298 			sb.append("materialized View is [").append(this.view).append(']');
299 		}
300 		sb.append("; model is ").append(this.model);
301 		return sb.toString();
302 	}
303 
304 }