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}