View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.web.servlet.mvc.condition;
18  
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.LinkedHashSet;
22  import java.util.Set;
23  import javax.servlet.http.HttpServletRequest;
24  
25  import org.springframework.web.bind.annotation.RequestMapping;
26  
27  /**
28   * A logical conjunction (' && ') request condition that matches a request against
29   * a set of header expressions with syntax defined in {@link RequestMapping#headers()}.
30   *
31   * <p>Expressions passed to the constructor with header names 'Accept' or
32   * 'Content-Type' are ignored. See {@link ConsumesRequestCondition} and
33   * {@link ProducesRequestCondition} for those.
34   *
35   * @author Arjen Poutsma
36   * @author Rossen Stoyanchev
37   * @since 3.1
38   */
39  public final class HeadersRequestCondition extends AbstractRequestCondition<HeadersRequestCondition> {
40  
41  	private final Set<HeaderExpression> expressions;
42  
43  
44  	/**
45  	 * Create a new instance from the given header expressions. Expressions with
46  	 * header names 'Accept' or 'Content-Type' are ignored. See {@link ConsumesRequestCondition}
47  	 * and {@link ProducesRequestCondition} for those.
48  	 * @param headers media type expressions with syntax defined in {@link RequestMapping#headers()};
49  	 * if 0, the condition will match to every request
50  	 */
51  	public HeadersRequestCondition(String... headers) {
52  		this(parseExpressions(headers));
53  	}
54  
55  	private HeadersRequestCondition(Collection<HeaderExpression> conditions) {
56  		this.expressions = Collections.unmodifiableSet(new LinkedHashSet<HeaderExpression>(conditions));
57  	}
58  
59  
60  	private static Collection<HeaderExpression> parseExpressions(String... headers) {
61  		Set<HeaderExpression> expressions = new LinkedHashSet<HeaderExpression>();
62  		if (headers != null) {
63  			for (String header : headers) {
64  				HeaderExpression expr = new HeaderExpression(header);
65  				if ("Accept".equalsIgnoreCase(expr.name) || "Content-Type".equalsIgnoreCase(expr.name)) {
66  					continue;
67  				}
68  				expressions.add(expr);
69  			}
70  		}
71  		return expressions;
72  	}
73  
74  	/**
75  	 * Return the contained request header expressions.
76  	 */
77  	public Set<NameValueExpression<String>> getExpressions() {
78  		return new LinkedHashSet<NameValueExpression<String>>(this.expressions);
79  	}
80  
81  	@Override
82  	protected Collection<HeaderExpression> getContent() {
83  		return this.expressions;
84  	}
85  
86  	@Override
87  	protected String getToStringInfix() {
88  		return " && ";
89  	}
90  
91  	/**
92  	 * Returns a new instance with the union of the header expressions
93  	 * from "this" and the "other" instance.
94  	 */
95  	@Override
96  	public HeadersRequestCondition combine(HeadersRequestCondition other) {
97  		Set<HeaderExpression> set = new LinkedHashSet<HeaderExpression>(this.expressions);
98  		set.addAll(other.expressions);
99  		return new HeadersRequestCondition(set);
100 	}
101 
102 	/**
103 	 * Returns "this" instance if the request matches all expressions;
104 	 * or {@code null} otherwise.
105 	 */
106 	@Override
107 	public HeadersRequestCondition getMatchingCondition(HttpServletRequest request) {
108 		for (HeaderExpression expression : expressions) {
109 			if (!expression.match(request)) {
110 				return null;
111 			}
112 		}
113 		return this;
114 	}
115 
116 	/**
117 	 * Returns:
118 	 * <ul>
119 	 * <li>0 if the two conditions have the same number of header expressions
120 	 * <li>Less than 0 if "this" instance has more header expressions
121 	 * <li>Greater than 0 if the "other" instance has more header expressions
122 	 * </ul>
123 	 * <p>It is assumed that both instances have been obtained via
124 	 * {@link #getMatchingCondition(HttpServletRequest)} and each instance
125 	 * contains the matching header expression only or is otherwise empty.
126 	 */
127 	@Override
128 	public int compareTo(HeadersRequestCondition other, HttpServletRequest request) {
129 		return other.expressions.size() - this.expressions.size();
130 	}
131 
132 
133 	/**
134 	 * Parses and matches a single header expression to a request.
135 	 */
136 	static class HeaderExpression extends AbstractNameValueExpression<String> {
137 
138 		public HeaderExpression(String expression) {
139 			super(expression);
140 		}
141 
142 		@Override
143 		protected boolean isCaseSensitiveName() {
144 			return false;
145 		}
146 
147 		@Override
148 		protected String parseValue(String valueExpression) {
149 			return valueExpression;
150 		}
151 
152 		@Override
153 		protected boolean matchName(HttpServletRequest request) {
154 			return request.getHeader(name) != null;
155 		}
156 
157 		@Override
158 		protected boolean matchValue(HttpServletRequest request) {
159 			return value.equals(request.getHeader(name));
160 		}
161 	}
162 
163 }