1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.servlet.mvc.condition;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.Iterator;
23 import java.util.LinkedHashSet;
24 import java.util.List;
25 import java.util.Set;
26 import javax.servlet.http.HttpServletRequest;
27
28 import org.springframework.http.MediaType;
29 import org.springframework.web.HttpMediaTypeNotAcceptableException;
30 import org.springframework.web.accept.ContentNegotiationManager;
31 import org.springframework.web.bind.annotation.RequestMapping;
32 import org.springframework.web.context.request.ServletWebRequest;
33 import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition.HeaderExpression;
34
35
36
37
38
39
40
41
42
43
44
45
46 public final class ProducesRequestCondition extends AbstractRequestCondition<ProducesRequestCondition> {
47
48 private final List<ProduceMediaTypeExpression> MEDIA_TYPE_ALL_LIST =
49 Collections.singletonList(new ProduceMediaTypeExpression("*/*"));
50
51 private final List<ProduceMediaTypeExpression> expressions;
52
53 private final ContentNegotiationManager contentNegotiationManager;
54
55
56
57
58
59
60
61 public ProducesRequestCondition(String... produces) {
62 this(produces, (String[]) null);
63 }
64
65
66
67
68
69
70
71
72
73 public ProducesRequestCondition(String[] produces, String[] headers) {
74 this(produces, headers, null);
75 }
76
77
78
79
80
81
82
83
84 public ProducesRequestCondition(String[] produces, String[] headers, ContentNegotiationManager manager) {
85 this.expressions = new ArrayList<ProduceMediaTypeExpression>(parseExpressions(produces, headers));
86 Collections.sort(this.expressions);
87 this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
88 }
89
90
91
92
93 private ProducesRequestCondition(Collection<ProduceMediaTypeExpression> expressions, ContentNegotiationManager manager) {
94 this.expressions = new ArrayList<ProduceMediaTypeExpression>(expressions);
95 Collections.sort(this.expressions);
96 this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
97 }
98
99
100 private Set<ProduceMediaTypeExpression> parseExpressions(String[] produces, String[] headers) {
101 Set<ProduceMediaTypeExpression> result = new LinkedHashSet<ProduceMediaTypeExpression>();
102 if (headers != null) {
103 for (String header : headers) {
104 HeaderExpression expr = new HeaderExpression(header);
105 if ("Accept".equalsIgnoreCase(expr.name)) {
106 for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
107 result.add(new ProduceMediaTypeExpression(mediaType, expr.isNegated));
108 }
109 }
110 }
111 }
112 if (produces != null) {
113 for (String produce : produces) {
114 result.add(new ProduceMediaTypeExpression(produce));
115 }
116 }
117 return result;
118 }
119
120
121
122
123 public Set<MediaTypeExpression> getExpressions() {
124 return new LinkedHashSet<MediaTypeExpression>(this.expressions);
125 }
126
127
128
129
130 public Set<MediaType> getProducibleMediaTypes() {
131 Set<MediaType> result = new LinkedHashSet<MediaType>();
132 for (ProduceMediaTypeExpression expression : this.expressions) {
133 if (!expression.isNegated()) {
134 result.add(expression.getMediaType());
135 }
136 }
137 return result;
138 }
139
140
141
142
143 public boolean isEmpty() {
144 return this.expressions.isEmpty();
145 }
146
147 @Override
148 protected List<ProduceMediaTypeExpression> getContent() {
149 return this.expressions;
150 }
151
152 @Override
153 protected String getToStringInfix() {
154 return " || ";
155 }
156
157
158
159
160
161
162 @Override
163 public ProducesRequestCondition combine(ProducesRequestCondition other) {
164 return (!other.expressions.isEmpty() ? other : this);
165 }
166
167
168
169
170
171
172
173
174
175
176
177 @Override
178 public ProducesRequestCondition getMatchingCondition(HttpServletRequest request) {
179 if (isEmpty()) {
180 return this;
181 }
182 Set<ProduceMediaTypeExpression> result = new LinkedHashSet<ProduceMediaTypeExpression>(expressions);
183 for (Iterator<ProduceMediaTypeExpression> iterator = result.iterator(); iterator.hasNext();) {
184 ProduceMediaTypeExpression expression = iterator.next();
185 if (!expression.match(request)) {
186 iterator.remove();
187 }
188 }
189 return (result.isEmpty()) ? null : new ProducesRequestCondition(result, this.contentNegotiationManager);
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209 @Override
210 public int compareTo(ProducesRequestCondition other, HttpServletRequest request) {
211 try {
212 List<MediaType> acceptedMediaTypes = getAcceptedMediaTypes(request);
213 for (MediaType acceptedMediaType : acceptedMediaTypes) {
214 int thisIndex = this.indexOfEqualMediaType(acceptedMediaType);
215 int otherIndex = other.indexOfEqualMediaType(acceptedMediaType);
216 int result = compareMatchingMediaTypes(this, thisIndex, other, otherIndex);
217 if (result != 0) {
218 return result;
219 }
220 thisIndex = this.indexOfIncludedMediaType(acceptedMediaType);
221 otherIndex = other.indexOfIncludedMediaType(acceptedMediaType);
222 result = compareMatchingMediaTypes(this, thisIndex, other, otherIndex);
223 if (result != 0) {
224 return result;
225 }
226 }
227 return 0;
228 }
229 catch (HttpMediaTypeNotAcceptableException ex) {
230
231 throw new IllegalStateException("Cannot compare without having any requested media types", ex);
232 }
233 }
234
235 private List<MediaType> getAcceptedMediaTypes(HttpServletRequest request) throws HttpMediaTypeNotAcceptableException {
236 List<MediaType> mediaTypes = this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
237 return mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes;
238 }
239
240 private int indexOfEqualMediaType(MediaType mediaType) {
241 for (int i = 0; i < getExpressionsToCompare().size(); i++) {
242 MediaType currentMediaType = getExpressionsToCompare().get(i).getMediaType();
243 if (mediaType.getType().equalsIgnoreCase(currentMediaType.getType()) &&
244 mediaType.getSubtype().equalsIgnoreCase(currentMediaType.getSubtype())) {
245 return i;
246 }
247 }
248 return -1;
249 }
250
251 private int indexOfIncludedMediaType(MediaType mediaType) {
252 for (int i = 0; i < getExpressionsToCompare().size(); i++) {
253 if (mediaType.includes(getExpressionsToCompare().get(i).getMediaType())) {
254 return i;
255 }
256 }
257 return -1;
258 }
259
260 private int compareMatchingMediaTypes(ProducesRequestCondition condition1, int index1,
261 ProducesRequestCondition condition2, int index2) {
262
263 int result = 0;
264 if (index1 != index2) {
265 result = index2 - index1;
266 }
267 else if (index1 != -1) {
268 ProduceMediaTypeExpression expr1 = condition1.getExpressionsToCompare().get(index1);
269 ProduceMediaTypeExpression expr2 = condition2.getExpressionsToCompare().get(index2);
270 result = expr1.compareTo(expr2);
271 result = (result != 0) ? result : expr1.getMediaType().compareTo(expr2.getMediaType());
272 }
273 return result;
274 }
275
276
277
278
279
280 private List<ProduceMediaTypeExpression> getExpressionsToCompare() {
281 return (this.expressions.isEmpty() ? MEDIA_TYPE_ALL_LIST : this.expressions);
282 }
283
284
285
286
287
288 class ProduceMediaTypeExpression extends AbstractMediaTypeExpression {
289
290 ProduceMediaTypeExpression(MediaType mediaType, boolean negated) {
291 super(mediaType, negated);
292 }
293
294 ProduceMediaTypeExpression(String expression) {
295 super(expression);
296 }
297
298 @Override
299 protected boolean matchMediaType(HttpServletRequest request) throws HttpMediaTypeNotAcceptableException {
300 List<MediaType> acceptedMediaTypes = getAcceptedMediaTypes(request);
301 for (MediaType acceptedMediaType : acceptedMediaTypes) {
302 if (getMediaType().isCompatibleWith(acceptedMediaType)) {
303 return true;
304 }
305 }
306 return false;
307 }
308 }
309
310 }