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.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 final 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 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 &ge; 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 &ge; 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 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 }