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 }