1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27
28 package org.apache.http.message;
29
30 import java.io.Serializable;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Locale;
35
36 import org.apache.http.Header;
37 import org.apache.http.HeaderIterator;
38 import org.apache.http.util.CharArrayBuffer;
39
40 /**
41 * A class for combining a set of headers.
42 * This class allows for multiple headers with the same name and
43 * keeps track of the order in which headers were added.
44 *
45 *
46 * @since 4.0
47 */
48 public class HeaderGroup implements Cloneable, Serializable {
49
50 private static final long serialVersionUID = 2608834160639271617L;
51
52 private static final Headereader">Header[] EMPTY = new Header[] {};
53
54 /** The list of headers for this group, in the order in which they were added */
55 private final List<Header> headers;
56
57 /**
58 * Constructor for HeaderGroup.
59 */
60 public HeaderGroup() {
61 this.headers = new ArrayList<Header>(16);
62 }
63
64 /**
65 * Removes any contained headers.
66 */
67 public void clear() {
68 headers.clear();
69 }
70
71 /**
72 * Adds the given header to the group. The order in which this header was
73 * added is preserved.
74 *
75 * @param header the header to add
76 */
77 public void addHeader(final Header header) {
78 if (header == null) {
79 return;
80 }
81 headers.add(header);
82 }
83
84 /**
85 * Removes the given header.
86 *
87 * @param header the header to remove
88 */
89 public void removeHeader(final Header header) {
90 if (header == null) {
91 return;
92 }
93 headers.remove(header);
94 }
95
96 /**
97 * Replaces the first occurence of the header with the same name. If no header with
98 * the same name is found the given header is added to the end of the list.
99 *
100 * @param header the new header that should replace the first header with the same
101 * name if present in the list.
102 */
103 public void updateHeader(final Header header) {
104 if (header == null) {
105 return;
106 }
107 // HTTPCORE-361 : we don't use the for-each syntax, i.e.
108 // for (Header header : headers)
109 // as that creates an Iterator that needs to be garbage-collected
110 for (int i = 0; i < this.headers.size(); i++) {
111 final Header current = this.headers.get(i);
112 if (current.getName().equalsIgnoreCase(header.getName())) {
113 this.headers.set(i, header);
114 return;
115 }
116 }
117 this.headers.add(header);
118 }
119
120 /**
121 * Sets all of the headers contained within this group overriding any
122 * existing headers. The headers are added in the order in which they appear
123 * in the array.
124 *
125 * @param headers the headers to set
126 */
127 public void setHeaders(final Header[] headers) {
128 clear();
129 if (headers == null) {
130 return;
131 }
132 Collections.addAll(this.headers, headers);
133 }
134
135 /**
136 * Gets a header representing all of the header values with the given name.
137 * If more that one header with the given name exists the values will be
138 * combined with a "," as per RFC 2616.
139 *
140 * <p>Header name comparison is case insensitive.
141 *
142 * @param name the name of the header(s) to get
143 * @return a header with a condensed value or {@code null} if no
144 * headers by the given name are present
145 */
146 public Header getCondensedHeader(final String name) {
147 final Header[] hdrs = getHeaders(name);
148
149 if (hdrs.length == 0) {
150 return null;
151 } else if (hdrs.length == 1) {
152 return hdrs[0];
153 } else {
154 final CharArrayBufferCharArrayBuffer">CharArrayBuffer valueBuffer = new CharArrayBuffer(128);
155 valueBuffer.append(hdrs[0].getValue());
156 for (int i = 1; i < hdrs.length; i++) {
157 valueBuffer.append(", ");
158 valueBuffer.append(hdrs[i].getValue());
159 }
160
161 return new BasicHeader(name.toLowerCase(Locale.ROOT), valueBuffer.toString());
162 }
163 }
164
165 /**
166 * Gets all of the headers with the given name. The returned array
167 * maintains the relative order in which the headers were added.
168 *
169 * <p>Header name comparison is case insensitive.
170 *
171 * @param name the name of the header(s) to get
172 *
173 * @return an array of length ≥ 0
174 */
175 public Header[] getHeaders(final String name) {
176 List<Header> headersFound = null;
177 // HTTPCORE-361 : we don't use the for-each syntax, i.e.
178 // for (Header header : headers)
179 // as that creates an Iterator that needs to be garbage-collected
180 for (int i = 0; i < this.headers.size(); i++) {
181 final Header header = this.headers.get(i);
182 if (header.getName().equalsIgnoreCase(name)) {
183 if (headersFound == null) {
184 headersFound = new ArrayList<Header>();
185 }
186 headersFound.add(header);
187 }
188 }
189 return headersFound != null ? headersFound.toArray(new Header[headersFound.size()]) : EMPTY;
190 }
191
192 /**
193 * Gets the first header with the given name.
194 *
195 * <p>Header name comparison is case insensitive.
196 *
197 * @param name the name of the header to get
198 * @return the first header or {@code null}
199 */
200 public Header getFirstHeader(final String name) {
201 // HTTPCORE-361 : we don't use the for-each syntax, i.e.
202 // for (Header header : headers)
203 // as that creates an Iterator that needs to be garbage-collected
204 for (int i = 0; i < this.headers.size(); i++) {
205 final Header header = this.headers.get(i);
206 if (header.getName().equalsIgnoreCase(name)) {
207 return header;
208 }
209 }
210 return null;
211 }
212
213 /**
214 * Gets the last header with the given name.
215 *
216 * <p>Header name comparison is case insensitive.
217 *
218 * @param name the name of the header to get
219 * @return the last header or {@code null}
220 */
221 public Header getLastHeader(final String name) {
222 // start at the end of the list and work backwards
223 for (int i = headers.size() - 1; i >= 0; i--) {
224 final Header header = headers.get(i);
225 if (header.getName().equalsIgnoreCase(name)) {
226 return header;
227 }
228 }
229
230 return null;
231 }
232
233 /**
234 * Gets all of the headers contained within this group.
235 *
236 * @return an array of length ≥ 0
237 */
238 public Header[] getAllHeaders() {
239 return headers.toArray(new Header[headers.size()]);
240 }
241
242 /**
243 * Tests if headers with the given name are contained within this group.
244 *
245 * <p>Header name comparison is case insensitive.
246 *
247 * @param name the header name to test for
248 * @return {@code true} if at least one header with the name is
249 * contained, {@code false} otherwise
250 */
251 public boolean containsHeader(final String name) {
252 // HTTPCORE-361 : we don't use the for-each syntax, i.e.
253 // for (Header header : headers)
254 // as that creates an Iterator that needs to be garbage-collected
255 for (int i = 0; i < this.headers.size(); i++) {
256 final Header header = this.headers.get(i);
257 if (header.getName().equalsIgnoreCase(name)) {
258 return true;
259 }
260 }
261
262 return false;
263 }
264
265 /**
266 * Returns an iterator over this group of headers.
267 *
268 * @return iterator over this group of headers.
269 *
270 * @since 4.0
271 */
272 public HeaderIterator iterator() {
273 return new BasicListHeaderIterator(this.headers, null);
274 }
275
276 /**
277 * Returns an iterator over the headers with a given name in this group.
278 *
279 * @param name the name of the headers over which to iterate, or
280 * {@code null} for all headers
281 *
282 * @return iterator over some headers in this group.
283 *
284 * @since 4.0
285 */
286 public HeaderIterator iterator(final String name) {
287 return new BasicListHeaderIterator(this.headers, name);
288 }
289
290 /**
291 * Returns a copy of this object
292 *
293 * @return copy of this object
294 *
295 * @since 4.0
296 */
297 public HeaderGroup copy() {
298 final HeaderGroupoup.html#HeaderGroup">HeaderGroup clone = new HeaderGroup();
299 clone.headers.addAll(this.headers);
300 return clone;
301 }
302
303 @Override
304 public Object clone() throws CloneNotSupportedException {
305 return super.clone();
306 }
307
308 @Override
309 public String toString() {
310 return this.headers.toString();
311 }
312
313 }