1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/HeaderGroup.java $
3 * $Revision: 659185 $
4 * $Date: 2008-05-22 11:07:36 -0700 (Thu, 22 May 2008) $
5 *
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements.  See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership.  The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License.  You may obtain a copy of the License at
14 *
15 *   http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied.  See the License for the
21 * specific language governing permissions and limitations
22 * under the License.
23 * ====================================================================
24 *
25 * This software consists of voluntary contributions made by many
26 * individuals on behalf of the Apache Software Foundation.  For more
27 * information on the Apache Software Foundation, please see
28 * <http://www.apache.org/>.
29 *
30 */
31
32package org.apache.http.message;
33
34import java.util.ArrayList;
35import java.util.List;
36import java.util.Locale;
37
38import org.apache.http.Header;
39import org.apache.http.HeaderIterator;
40import org.apache.http.util.CharArrayBuffer;
41
42/**
43 * A class for combining a set of headers.
44 * This class allows for multiple headers with the same name and
45 * keeps track of the order in which headers were added.
46 *
47 * @author Michael Becke
48 *
49 * @since 4.0
50 */
51public class HeaderGroup implements Cloneable {
52
53    /** The list of headers for this group, in the order in which they were added */
54    private List headers;
55
56    /**
57     * Constructor for HeaderGroup.
58     */
59    public HeaderGroup() {
60        this.headers = new ArrayList(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(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(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(Header header) {
103        if (header == null) {
104            return;
105        }
106        for (int i = 0; i < this.headers.size(); i++) {
107            Header current = (Header) 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(Header[] headers) {
124        clear();
125        if (headers == null) {
126            return;
127        }
128        for (int i = 0; i < headers.length; i++) {
129            this.headers.add(headers[i]);
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(String name) {
145        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            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(String name) {
174        ArrayList headersFound = new ArrayList();
175
176        for (int i = 0; i < headers.size(); i++) {
177            Header header = (Header) headers.get(i);
178            if (header.getName().equalsIgnoreCase(name)) {
179                headersFound.add(header);
180            }
181        }
182
183        return (Header[]) 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(String name) {
195        for (int i = 0; i < headers.size(); i++) {
196            Header 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(String name) {
213        // start at the end of the list and work backwards
214        for (int i = headers.size() - 1; i >= 0; i--) {
215            Header 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 (Header[]) 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(String name) {
243        for (int i = 0; i < headers.size(); i++) {
244            Header 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    public HeaderGroup copy() {
284        HeaderGroup clone = new HeaderGroup();
285        clone.headers.addAll(this.headers);
286        return clone;
287    }
288
289    public Object clone() throws CloneNotSupportedException {
290        HeaderGroup clone = (HeaderGroup) super.clone();
291        clone.headers = new ArrayList(this.headers);
292        return clone;
293    }
294
295}
296