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.List;
33 import java.util.Locale;
34
35 import org.apache.http.Header;
36 import org.apache.http.HeaderIterator;
37 import org.apache.http.annotation.NotThreadSafe;
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 @NotThreadSafe
49 public class HeaderGroup implements Cloneable, Serializable {
50
51 private static final long serialVersionUID = 2608834160639271617L;
52
53 /** The list of headers for this group, in the order in which they were added */
54 private final List<Header> headers;
55
56 /**
57 * Constructor for HeaderGroup.
58 */
59 public HeaderGroup() {
60 this.headers = new ArrayList<Header>(16);
61 }
62
63 /**
64 * Removes any contained headers.
65 */
66 public void clear() {
67 headers.clear();
68 }
69
70 /**
71 * Adds the given header to the group. The order in which this header was
72 * added is preserved.
73 *
74 * @param header the header to add
75 */
76 public void addHeader(final Header header) {
77 if (header == null) {
78 return;
79 }
80 headers.add(header);
81 }
82
83 /**
84 * Removes the given header.
85 *
86 * @param header the header to remove
87 */
88 public void removeHeader(final Header header) {
89 if (header == null) {
90 return;
91 }
92 headers.remove(header);
93 }
94
95 /**
96 * Replaces the first occurence of the header with the same name. If no header with
97 * the same name is found the given header is added to the end of the list.
98 *
99 * @param header the new header that should replace the first header with the same
100 * name if present in the list.
101 */
102 public void updateHeader(final Header header) {
103 if (header == null) {
104 return;
105 }
106 for (int i = 0; i < this.headers.size(); i++) {
107 final Header current = this.headers.get(i);
108 if (current.getName().equalsIgnoreCase(header.getName())) {
109 this.headers.set(i, header);
110 return;
111 }
112 }
113 this.headers.add(header);
114 }
115
116 /**
117 * Sets all of the headers contained within this group overriding any
118 * existing headers. The headers are added in the order in which they appear
119 * in the array.
120 *
121 * @param headers the headers to set
122 */
123 public void setHeaders(final Header[] headers) {
124 clear();
125 if (headers == null) {
126 return;
127 }
128 for (final Header header : headers) {
129 this.headers.add(header);
130 }
131 }
132
133 /**
134 * Gets a header representing all of the header values with the given name.
135 * If more that one header with the given name exists the values will be
136 * combined with a "," as per RFC 2616.
137 *
138 * <p>Header name comparison is case insensitive.
139 *
140 * @param name the name of the header(s) to get
141 * @return a header with a condensed value or <code>null</code> if no
142 * headers by the given name are present
143 */
144 public Header getCondensedHeader(final String name) {
145 final Header[] headers = getHeaders(name);
146
147 if (headers.length == 0) {
148 return null;
149 } else if (headers.length == 1) {
150 return headers[0];
151 } else {
152 final CharArrayBuffer valueBuffer = new CharArrayBuffer(128);
153 valueBuffer.append(headers[0].getValue());
154 for (int i = 1; i < headers.length; i++) {
155 valueBuffer.append(", ");
156 valueBuffer.append(headers[i].getValue());
157 }
158
159 return new BasicHeader(name.toLowerCase(Locale.ENGLISH), valueBuffer.toString());
160 }
161 }
162
163 /**
164 * Gets all of the headers with the given name. The returned array
165 * maintains the relative order in which the headers were added.
166 *
167 * <p>Header name comparison is case insensitive.
168 *
169 * @param name the name of the header(s) to get
170 *
171 * @return an array of length >= 0
172 */
173 public Header[] getHeaders(final String name) {
174 final List<Header> headersFound = new ArrayList<Header>();
175
176 for (int i = 0; i < headers.size(); i++) {
177 final Header header = headers.get(i);
178 if (header.getName().equalsIgnoreCase(name)) {
179 headersFound.add(header);
180 }
181 }
182
183 return headersFound.toArray(new Header[headersFound.size()]);
184 }
185
186 /**
187 * Gets the first header with the given name.
188 *
189 * <p>Header name comparison is case insensitive.
190 *
191 * @param name the name of the header to get
192 * @return the first header or <code>null</code>
193 */
194 public Header getFirstHeader(final String name) {
195 for (int i = 0; i < headers.size(); i++) {
196 final Header header = headers.get(i);
197 if (header.getName().equalsIgnoreCase(name)) {
198 return header;
199 }
200 }
201 return null;
202 }
203
204 /**
205 * Gets the last header with the given name.
206 *
207 * <p>Header name comparison is case insensitive.
208 *
209 * @param name the name of the header to get
210 * @return the last header or <code>null</code>
211 */
212 public Header getLastHeader(final String name) {
213 // start at the end of the list and work backwards
214 for (int i = headers.size() - 1; i >= 0; i--) {
215 final Header header = headers.get(i);
216 if (header.getName().equalsIgnoreCase(name)) {
217 return header;
218 }
219 }
220
221 return null;
222 }
223
224 /**
225 * Gets all of the headers contained within this group.
226 *
227 * @return an array of length >= 0
228 */
229 public Header[] getAllHeaders() {
230 return headers.toArray(new Header[headers.size()]);
231 }
232
233 /**
234 * Tests if headers with the given name are contained within this group.
235 *
236 * <p>Header name comparison is case insensitive.
237 *
238 * @param name the header name to test for
239 * @return <code>true</code> if at least one header with the name is
240 * contained, <code>false</code> otherwise
241 */
242 public boolean containsHeader(final String name) {
243 for (int i = 0; i < headers.size(); i++) {
244 final Header header = headers.get(i);
245 if (header.getName().equalsIgnoreCase(name)) {
246 return true;
247 }
248 }
249
250 return false;
251 }
252
253 /**
254 * Returns an iterator over this group of headers.
255 *
256 * @return iterator over this group of headers.
257 *
258 * @since 4.0
259 */
260 public HeaderIterator iterator() {
261 return new BasicListHeaderIterator(this.headers, null);
262 }
263
264 /**
265 * Returns an iterator over the headers with a given name in this group.
266 *
267 * @param name the name of the headers over which to iterate, or
268 * <code>null</code> for all headers
269 *
270 * @return iterator over some headers in this group.
271 *
272 * @since 4.0
273 */
274 public HeaderIterator iterator(final String name) {
275 return new BasicListHeaderIterator(this.headers, name);
276 }
277
278 /**
279 * Returns a copy of this object
280 *
281 * @return copy of this object
282 *
283 * @since 4.0
284 */
285 public HeaderGroup copy() {
286 final HeaderGroup clone = new HeaderGroup();
287 clone.headers.addAll(this.headers);
288 return clone;
289 }
290
291 @Override
292 public Object clone() throws CloneNotSupportedException {
293 return super.clone();
294 }
295
296 @Override
297 public String toString() {
298 return this.headers.toString();
299 }
300
301 }