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