View Javadoc

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.annotation.NotThreadSafe;
39  import org.apache.http.util.CharArrayBuffer;
40  
41  /**
42   * A class for combining a set of headers.
43   * This class allows for multiple headers with the same name and
44   * keeps track of the order in which headers were added.
45   *
46   *
47   * @since 4.0
48   */
49  @NotThreadSafe
50  public class HeaderGroup implements Cloneable, Serializable {
51  
52      private static final long serialVersionUID = 2608834160639271617L;
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</code> 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 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         final List<Header> headersFound = new ArrayList<Header>();
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                 headersFound.add(header);
184             }
185         }
186 
187         return headersFound.toArray(new Header[headersFound.size()]);
188     }
189 
190     /**
191      * Gets the first header with the given name.
192      *
193      * <p>Header name comparison is case insensitive.
194      *
195      * @param name the name of the header to get
196      * @return the first header or <code>null</code>
197      */
198     public Header getFirstHeader(final String name) {
199         // HTTPCORE-361 : we don't use the for-each syntax, i.e.
200         //     for (Header header : headers)
201         // as that creates an Iterator that needs to be garbage-collected
202         for (int i = 0; i < this.headers.size(); i++) {
203             final Header header = this.headers.get(i);
204             if (header.getName().equalsIgnoreCase(name)) {
205                 return header;
206             }
207         }
208         return null;
209     }
210 
211     /**
212      * Gets the last header with the given name.
213      *
214      * <p>Header name comparison is case insensitive.
215      *
216      * @param name the name of the header to get
217      * @return the last header or <code>null</code>
218      */
219     public Header getLastHeader(final String name) {
220         // start at the end of the list and work backwards
221         for (int i = headers.size() - 1; i >= 0; i--) {
222             final Header header = headers.get(i);
223             if (header.getName().equalsIgnoreCase(name)) {
224                 return header;
225             }
226         }
227 
228         return null;
229     }
230 
231     /**
232      * Gets all of the headers contained within this group.
233      *
234      * @return an array of length >= 0
235      */
236     public Header[] getAllHeaders() {
237         return headers.toArray(new Header[headers.size()]);
238     }
239 
240     /**
241      * Tests if headers with the given name are contained within this group.
242      *
243      * <p>Header name comparison is case insensitive.
244      *
245      * @param name the header name to test for
246      * @return <code>true</code> if at least one header with the name is
247      * contained, <code>false</code> otherwise
248      */
249     public boolean containsHeader(final String name) {
250         // HTTPCORE-361 : we don't use the for-each syntax, i.e.
251         //     for (Header header : headers)
252         // as that creates an Iterator that needs to be garbage-collected
253         for (int i = 0; i < this.headers.size(); i++) {
254             final Header header = this.headers.get(i);
255             if (header.getName().equalsIgnoreCase(name)) {
256                 return true;
257             }
258         }
259 
260         return false;
261     }
262 
263     /**
264      * Returns an iterator over this group of headers.
265      *
266      * @return iterator over this group of headers.
267      *
268      * @since 4.0
269      */
270     public HeaderIterator iterator() {
271         return new BasicListHeaderIterator(this.headers, null);
272     }
273 
274     /**
275      * Returns an iterator over the headers with a given name in this group.
276      *
277      * @param name      the name of the headers over which to iterate, or
278      *                  <code>null</code> for all headers
279      *
280      * @return iterator over some headers in this group.
281      *
282      * @since 4.0
283      */
284     public HeaderIterator iterator(final String name) {
285         return new BasicListHeaderIterator(this.headers, name);
286     }
287 
288     /**
289      * Returns a copy of this object
290      *
291      * @return copy of this object
292      *
293      * @since 4.0
294      */
295     public HeaderGroup copy() {
296         final HeaderGroup clone = new HeaderGroup();
297         clone.headers.addAll(this.headers);
298         return clone;
299     }
300 
301     @Override
302     public Object clone() throws CloneNotSupportedException {
303         return super.clone();
304     }
305 
306     @Override
307     public String toString() {
308         return this.headers.toString();
309     }
310 
311 }