001/*
002 * Copyright 2002-2017 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.servlet.mvc.support;
018
019import java.util.Collection;
020import java.util.Map;
021
022import org.springframework.ui.ModelMap;
023import org.springframework.validation.DataBinder;
024
025/**
026 * A {@link ModelMap} implementation of {@link RedirectAttributes} that formats
027 * values as Strings using a {@link DataBinder}. Also provides a place to store
028 * flash attributes so they can survive a redirect without the need to be
029 * embedded in the redirect URL.
030 *
031 * @author Rossen Stoyanchev
032 * @since 3.1
033 */
034@SuppressWarnings("serial")
035public class RedirectAttributesModelMap extends ModelMap implements RedirectAttributes {
036
037        private final DataBinder dataBinder;
038
039        private final ModelMap flashAttributes = new ModelMap();
040
041
042        /**
043         * Default constructor without a DataBinder.
044         * Attribute values are converted to String via {@link #toString()}.
045         */
046        public RedirectAttributesModelMap() {
047                this(null);
048        }
049
050        /**
051         * Constructor with a DataBinder.
052         * @param dataBinder used to format attribute values as Strings
053         */
054        public RedirectAttributesModelMap(DataBinder dataBinder) {
055                this.dataBinder = dataBinder;
056        }
057
058
059        /**
060         * Return the attributes candidate for flash storage or an empty Map.
061         */
062        @Override
063        public Map<String, ?> getFlashAttributes() {
064                return this.flashAttributes;
065        }
066
067        /**
068         * {@inheritDoc}
069         * <p>Formats the attribute value as a String before adding it.
070         */
071        @Override
072        public RedirectAttributesModelMap addAttribute(String attributeName, Object attributeValue) {
073                super.addAttribute(attributeName, formatValue(attributeValue));
074                return this;
075        }
076
077        private String formatValue(Object value) {
078                if (value == null) {
079                        return null;
080                }
081                return (this.dataBinder != null ? this.dataBinder.convertIfNecessary(value, String.class) : value.toString());
082        }
083
084        /**
085         * {@inheritDoc}
086         * <p>Formats the attribute value as a String before adding it.
087         */
088        @Override
089        public RedirectAttributesModelMap addAttribute(Object attributeValue) {
090                super.addAttribute(attributeValue);
091                return this;
092        }
093
094        /**
095         * {@inheritDoc}
096         * <p>Each attribute value is formatted as a String before being added.
097         */
098        @Override
099        public RedirectAttributesModelMap addAllAttributes(Collection<?> attributeValues) {
100                super.addAllAttributes(attributeValues);
101                return this;
102        }
103
104        /**
105         * {@inheritDoc}
106         * <p>Each attribute value is formatted as a String before being added.
107         */
108        @Override
109        public RedirectAttributesModelMap addAllAttributes(Map<String, ?> attributes) {
110                if (attributes != null) {
111                        for (String key : attributes.keySet()) {
112                                addAttribute(key, attributes.get(key));
113                        }
114                }
115                return this;
116        }
117
118        /**
119         * {@inheritDoc}
120         * <p>Each attribute value is formatted as a String before being merged.
121         */
122        @Override
123        public RedirectAttributesModelMap mergeAttributes(Map<String, ?> attributes) {
124                if (attributes != null) {
125                        for (String key : attributes.keySet()) {
126                                if (!containsKey(key)) {
127                                        addAttribute(key, attributes.get(key));
128                                }
129                        }
130                }
131                return this;
132        }
133
134        @Override
135        public Map<String, Object> asMap() {
136                return this;
137        }
138
139        /**
140         * {@inheritDoc}
141         * <p>The value is formatted as a String before being added.
142         */
143        @Override
144        public Object put(String key, Object value) {
145                return super.put(key, formatValue(value));
146        }
147
148        /**
149         * {@inheritDoc}
150         * <p>Each value is formatted as a String before being added.
151         */
152        @Override
153        public void putAll(Map<? extends String, ? extends Object> map) {
154                if (map != null) {
155                        for (String key : map.keySet()) {
156                                put(key, formatValue(map.get(key)));
157                        }
158                }
159        }
160
161        @Override
162        public RedirectAttributes addFlashAttribute(String attributeName, Object attributeValue) {
163                this.flashAttributes.addAttribute(attributeName, attributeValue);
164                return this;
165        }
166
167        @Override
168        public RedirectAttributes addFlashAttribute(Object attributeValue) {
169                this.flashAttributes.addAttribute(attributeValue);
170                return this;
171        }
172
173}