001/* 002 * Copyright 2002-2012 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.portlet; 018 019import java.util.Map; 020 021import org.springframework.ui.ModelMap; 022import org.springframework.util.CollectionUtils; 023 024/** 025 * Holder for both Model and View in the web MVC framework. 026 * Note that these are entirely distinct. This class merely holds 027 * both to make it possible for a controller to return both model 028 * and view in a single return value. 029 * 030 * <p>Represents a model and view returned by a handler, to be resolved 031 * by a DispatcherPortlet. The view can take the form of a String 032 * view name which will need to be resolved by a ViewResolver object; 033 * alternatively a view object can be specified directly. The model 034 * is a Map, allowing the use of multiple objects keyed by name. 035 * 036 * @author Juergen Hoeller 037 * @since 2.0 038 * @see org.springframework.web.portlet.DispatcherPortlet 039 * @see org.springframework.web.servlet.ViewResolver 040 * @see org.springframework.web.portlet.HandlerAdapter 041 * @see org.springframework.web.portlet.mvc.Controller 042 */ 043public class ModelAndView { 044 045 /** View instance or view name String */ 046 private Object view; 047 048 /** Model Map */ 049 private ModelMap model; 050 051 /** 052 * Indicates whether or not this instance has been cleared with a call to {@link #clear()}. 053 */ 054 private boolean cleared = false; 055 056 057 /** 058 * Default constructor for bean-style usage: populating bean 059 * properties instead of passing in constructor arguments. 060 * @see #setView(Object) 061 * @see #setViewName(String) 062 */ 063 public ModelAndView() { 064 } 065 066 /** 067 * Convenient constructor when there is no model data to expose. 068 * Can also be used in conjunction with {@code addObject}. 069 * @param viewName name of the View to render, to be resolved 070 * by the DispatcherPortlet's ViewResolver 071 * @see #addObject 072 */ 073 public ModelAndView(String viewName) { 074 this.view = viewName; 075 } 076 077 /** 078 * Convenient constructor when there is no model data to expose. 079 * Can also be used in conjunction with {@code addObject}. 080 * @param view View object to render (usually a Servlet MVC View object) 081 * @see #addObject 082 */ 083 public ModelAndView(Object view) { 084 this.view = view; 085 } 086 087 /** 088 * Create a new ModelAndView given a view name and a model. 089 * @param viewName name of the View to render, to be resolved 090 * by the DispatcherPortlet's ViewResolver 091 * @param model Map of model names (Strings) to model objects 092 * (Objects). Model entries may not be {@code null}, but the 093 * model Map may be {@code null} if there is no model data. 094 */ 095 public ModelAndView(String viewName, Map<String, ?> model) { 096 this.view = viewName; 097 if (model != null) { 098 getModelMap().addAllAttributes(model); 099 } 100 } 101 102 /** 103 * Create a new ModelAndView given a View object and a model. 104 * @param view View object to render (usually a Servlet MVC View object) 105 * @param model Map of model names (Strings) to model objects 106 * (Objects). Model entries may not be {@code null}, but the 107 * model Map may be {@code null} if there is no model data. 108 */ 109 public ModelAndView(Object view, Map<String, ?> model) { 110 this.view = view; 111 if (model != null) { 112 getModelMap().addAllAttributes(model); 113 } 114 } 115 116 /** 117 * Convenient constructor to take a single model object. 118 * @param viewName name of the View to render, to be resolved 119 * by the DispatcherPortlet's ViewResolver 120 * @param modelName name of the single entry in the model 121 * @param modelObject the single model object 122 */ 123 public ModelAndView(String viewName, String modelName, Object modelObject) { 124 this.view = viewName; 125 addObject(modelName, modelObject); 126 } 127 128 /** 129 * Convenient constructor to take a single model object. 130 * @param view View object to render (usually a Servlet MVC View object) 131 * @param modelName name of the single entry in the model 132 * @param modelObject the single model object 133 */ 134 public ModelAndView(Object view, String modelName, Object modelObject) { 135 this.view = view; 136 addObject(modelName, modelObject); 137 } 138 139 140 /** 141 * Set a view name for this ModelAndView, to be resolved by the 142 * DispatcherPortlet via a ViewResolver. Will override any 143 * pre-existing view name or View. 144 */ 145 public void setViewName(String viewName) { 146 this.view = viewName; 147 } 148 149 /** 150 * Return the view name to be resolved by the DispatcherPortlet 151 * via a ViewResolver, or {@code null} if we are using a view object. 152 */ 153 public String getViewName() { 154 return (this.view instanceof String ? (String) this.view : null); 155 } 156 157 /** 158 * Set a View object for this ModelAndView. Will override any 159 * pre-existing view name or View. 160 * <p>The given View object will usually be a Servlet MVC View object. 161 * This is nevertheless typed as Object to avoid a Servlet API dependency 162 * in the Portlet ModelAndView class. 163 */ 164 public void setView(Object view) { 165 this.view = view; 166 } 167 168 /** 169 * Return the View object, or {@code null} if we are using a view name 170 * to be resolved by the DispatcherPortlet via a ViewResolver. 171 */ 172 public Object getView() { 173 return (!(this.view instanceof String) ? this.view : null); 174 } 175 176 /** 177 * Indicate whether or not this {@code ModelAndView} has a view, either 178 * as a view name or as a direct view instance. 179 */ 180 public boolean hasView() { 181 return (this.view != null); 182 } 183 184 /** 185 * Return whether we use a view reference, i.e. {@code true} 186 * if the view has been specified via a name to be resolved by the 187 * DispatcherPortlet via a ViewResolver. 188 */ 189 public boolean isReference() { 190 return (this.view instanceof String); 191 } 192 193 /** 194 * Return the model map. May return {@code null}. 195 * Called by DispatcherPortlet for evaluation of the model. 196 */ 197 protected Map<String, Object> getModelInternal() { 198 return this.model; 199 } 200 201 /** 202 * Return the underlying {@code ModelMap} instance (never {@code null}). 203 */ 204 public ModelMap getModelMap() { 205 if (this.model == null) { 206 this.model = new ModelMap(); 207 } 208 return this.model; 209 } 210 211 /** 212 * Return the model map. Never returns {@code null}. 213 * To be called by application code for modifying the model. 214 */ 215 public Map<String, Object> getModel() { 216 return getModelMap(); 217 } 218 219 220 /** 221 * Add an attribute to the model. 222 * @param attributeName name of the object to add to the model 223 * @param attributeValue object to add to the model (never {@code null}) 224 * @see ModelMap#addAttribute(String, Object) 225 * @see #getModelMap() 226 */ 227 public ModelAndView addObject(String attributeName, Object attributeValue) { 228 getModelMap().addAttribute(attributeName, attributeValue); 229 return this; 230 } 231 232 /** 233 * Add an attribute to the model using parameter name generation. 234 * @param attributeValue the object to add to the model (never {@code null}) 235 * @see ModelMap#addAttribute(Object) 236 * @see #getModelMap() 237 */ 238 public ModelAndView addObject(Object attributeValue) { 239 getModelMap().addAttribute(attributeValue); 240 return this; 241 } 242 243 /** 244 * Add all attributes contained in the provided Map to the model. 245 * @param modelMap a Map of attributeName -> attributeValue pairs 246 * @see ModelMap#addAllAttributes(Map) 247 * @see #getModelMap() 248 */ 249 public ModelAndView addAllObjects(Map<String, ?> modelMap) { 250 getModelMap().addAllAttributes(modelMap); 251 return this; 252 } 253 254 255 /** 256 * Clear the state of this ModelAndView object. 257 * The object will be empty afterwards. 258 * <p>Can be used to suppress rendering of a given ModelAndView object 259 * in the {@code postHandleRender} method of a HandlerInterceptor. 260 * @see #isEmpty() 261 * @see HandlerInterceptor#postHandleRender 262 */ 263 public void clear() { 264 this.view = null; 265 this.model = null; 266 this.cleared = true; 267 } 268 269 /** 270 * Return whether this ModelAndView object is empty, 271 * i.e. whether it does not hold any view and does not contain a model. 272 */ 273 public boolean isEmpty() { 274 return (this.view == null && CollectionUtils.isEmpty(this.model)); 275 } 276 277 /** 278 * Return whether this ModelAndView object is empty as a result of a call to {@link #clear} 279 * i.e. whether it does not hold any view and does not contain a model. 280 * Returns {@code false} if any additional state was added to the instance 281 * <strong>after</strong> the call to {@link #clear}. 282 * @see #clear() 283 */ 284 public boolean wasCleared() { 285 return (this.cleared && isEmpty()); 286 } 287 288 289 /** 290 * Return diagnostic information about this model and view. 291 */ 292 @Override 293 public String toString() { 294 StringBuilder result = new StringBuilder("ModelAndView: "); 295 if (isReference()) { 296 result.append("reference to view with name '").append(this.view).append("'"); 297 } 298 else { 299 result.append("materialized View is [").append(this.view).append(']'); 300 } 301 result.append("; model is ").append(this.model); 302 return result.toString(); 303 } 304 305}