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}