| /* Headers.java -- | |
| Copyright (C) 2004, 2006 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.java.net.protocol.http; | |
| import gnu.java.lang.CPStringBuilder; | |
| import gnu.java.net.LineInputStream; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.lang.Iterable; | |
| import java.text.DateFormat; | |
| import java.text.ParseException; | |
| import java.util.ArrayList; | |
| import java.util.Collections; | |
| import java.util.Date; | |
| import java.util.Iterator; | |
| import java.util.LinkedHashMap; | |
| import java.util.List; | |
| import java.util.Map; | |
| /** | |
| * A collection of HTTP header names and associated values. The | |
| * values are {@link ArrayList ArrayLists} of Strings. Retrieval of | |
| * values is case insensitive. An iteration over the collection | |
| * returns the header names in the order they were received. | |
| * | |
| * @author Chris Burdess (dog@gnu.org) | |
| * @author David Daney (ddaney@avtrex.com) | |
| */ | |
| class Headers implements Iterable<Headers.HeaderElement> | |
| { | |
| /** | |
| * A list of HeaderElements | |
| */ | |
| private final ArrayList<HeaderElement> headers | |
| = new ArrayList<HeaderElement>(); | |
| /** | |
| * The HTTP dateformat used to parse date header fields. | |
| */ | |
| private static final DateFormat dateFormat = new HTTPDateFormat(); | |
| /** | |
| * Class for a Header element consisting of | |
| * a name and value String. | |
| */ | |
| static class HeaderElement | |
| { | |
| String name; | |
| String value; | |
| HeaderElement(String name, String value) | |
| { | |
| this.name = name; | |
| this.value = value; | |
| } | |
| } | |
| /** | |
| * Default constructor. | |
| */ | |
| public Headers() | |
| { | |
| // nothing to do | |
| } | |
| /** | |
| * Return an Iterator over this collection of headers. | |
| * Iterator.getNext() returns objects of type {@link HeaderElement}. | |
| * | |
| * @return the Iterator. | |
| */ | |
| public Iterator<HeaderElement> iterator() | |
| { | |
| return headers.iterator(); | |
| } | |
| /** | |
| * Returns the value of the specified header as a string. If | |
| * multiple values are present, the last one is returned. | |
| * | |
| * @param header the header name (case insensitive search) | |
| * @return The header value or <code>null</code> if not found. | |
| */ | |
| public String getValue(String header) | |
| { | |
| for (int i = headers.size() - 1; i >= 0; i--) | |
| { | |
| HeaderElement e = headers.get(i); | |
| if (e.name.equalsIgnoreCase(header)) | |
| { | |
| return e.value; | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Returns the value of the specified header as an integer. If | |
| * multiple values are present, the last one is returned. | |
| * | |
| * @param header the header name (case insensitive search) | |
| * @return The header value or <code>-1</code> if not present or | |
| * not an integer value. | |
| */ | |
| public int getIntValue(String header) | |
| { | |
| String val = getValue(header); | |
| if (val == null) | |
| { | |
| return -1; | |
| } | |
| try | |
| { | |
| return Integer.parseInt(val); | |
| } | |
| catch (NumberFormatException e) | |
| { | |
| // fall through | |
| } | |
| return -1; | |
| } | |
| /** | |
| * Returns the value of the specified header as a long. If | |
| * multiple values are present, the last one is returned. | |
| * | |
| * @param header the header name (case insensitive search) | |
| * @return The header value or <code>-1</code> if not present or | |
| * not a long value. | |
| */ | |
| public long getLongValue(String header) | |
| { | |
| String val = getValue(header); | |
| if (val == null) | |
| { | |
| return -1; | |
| } | |
| try | |
| { | |
| return Long.parseLong(val); | |
| } | |
| catch (NumberFormatException e) | |
| { | |
| // fall through | |
| } | |
| return -1; | |
| } | |
| /** | |
| * Returns the value of the specified header as a date. If | |
| * multiple values are present, the last one is returned. | |
| * | |
| * @param header the header name (case insensitive search) | |
| * @return The header value or <code>null</code> if not present or | |
| * not a date value. | |
| */ | |
| public Date getDateValue(String header) | |
| { | |
| String val = getValue(header); | |
| if (val == null) | |
| { | |
| return null; | |
| } | |
| try | |
| { | |
| return dateFormat.parse(val); | |
| } | |
| catch (ParseException e) | |
| { | |
| return null; | |
| } | |
| } | |
| /** | |
| * Add a header to this set of headers. If there is an existing | |
| * header with the same name it's value is replaced with the new value. | |
| * If multiple headers of the same name exist only the last one's value | |
| * is replaced. | |
| * | |
| * @param name the header name | |
| * @param value the header value | |
| * | |
| * @see #addValue(String, String) | |
| */ | |
| public void put(String name, String value) | |
| { | |
| for (int i = headers.size() - 1; i >= 0; i--) | |
| { | |
| HeaderElement e = headers.get(i); | |
| if (e.name.equalsIgnoreCase(name)) | |
| { | |
| e.value = value; | |
| return; | |
| } | |
| } | |
| // nothing was replaced so add it as new HeaderElement | |
| addValue(name, value); | |
| } | |
| /** | |
| * Add all headers from a set of headers to this set. Any existing header | |
| * with the same (case insensitive) name as one of the new headers will | |
| * be overridden. | |
| * | |
| * @param o the headers to be added | |
| */ | |
| public void putAll(Headers o) | |
| { | |
| for (Iterator<HeaderElement> it = o.iterator(); it.hasNext(); ) | |
| { | |
| HeaderElement e = it.next(); | |
| remove(e.name); | |
| addValue(e.name, e.value); | |
| } | |
| } | |
| /** | |
| * Remove a header from this set of headers. If there is more than | |
| * one instance of a header of the given name, they are all removed. | |
| * | |
| * @param name the header name | |
| */ | |
| public void remove(String name) | |
| { | |
| for (Iterator<HeaderElement> it = headers.iterator(); it.hasNext(); ) | |
| { | |
| HeaderElement e = it.next(); | |
| if (e.name.equalsIgnoreCase(name)) | |
| it.remove(); | |
| } | |
| } | |
| /** | |
| * Parse the specified InputStream, adding headers to this collection. | |
| * | |
| * @param in the InputStream. | |
| * @throws IOException if I/O error occured. | |
| */ | |
| public void parse(InputStream in) | |
| throws IOException | |
| { | |
| LineInputStream lin = (in instanceof LineInputStream) ? | |
| (LineInputStream) in : new LineInputStream(in); | |
| String name = null; | |
| CPStringBuilder value = new CPStringBuilder(); | |
| while (true) | |
| { | |
| String line = lin.readLine(); | |
| if (line == null) | |
| { | |
| if (name != null) | |
| { | |
| addValue(name, value.toString()); | |
| } | |
| break; | |
| } | |
| int len = line.length(); | |
| if (len < 2) | |
| { | |
| if (name != null) | |
| { | |
| addValue(name, value.toString()); | |
| } | |
| break; | |
| } | |
| char c1 = line.charAt(0); | |
| if (c1 == ' ' || c1 == '\t') | |
| { | |
| // Continuation | |
| int last = len - 1; | |
| if (line.charAt(last) != '\r') | |
| ++last; | |
| value.append(line.substring(0, last)); | |
| } | |
| else | |
| { | |
| if (name != null) | |
| { | |
| addValue(name, value.toString()); | |
| } | |
| int di = line.indexOf(':'); | |
| name = line.substring(0, di); | |
| value.setLength(0); | |
| do | |
| { | |
| di++; | |
| } | |
| while (di < len && line.charAt(di) == ' '); | |
| int last = len - 1; | |
| if (line.charAt(last) != '\r') | |
| ++last; | |
| value.append(line.substring(di, last)); | |
| } | |
| } | |
| } | |
| /** | |
| * Add a header to this set of headers. If there is an existing | |
| * header with the same name, it is not effected. | |
| * | |
| * @param name the header name | |
| * @param value the header value | |
| * | |
| * @see #put(String, String) | |
| */ | |
| public void addValue(String name, String value) | |
| { | |
| headers.add(headers.size(), new HeaderElement(name, value)); | |
| } | |
| /** | |
| * Get a new Map containing all the headers. The keys of the Map | |
| * are Strings (the header names). The headers will be included | |
| * case-sensitive in the map so that querying must be done with the | |
| * correct case of the needed header name. The values of the Map are | |
| * unmodifiable Lists containing Strings (the header values). | |
| * | |
| * <p> | |
| * The returned map is modifiable. Changing it will not effect this | |
| * collection of Headers in any way.</p> | |
| * | |
| * @return a Map containing all the headers. | |
| */ | |
| public Map<String,List<String>> getAsMap() | |
| { | |
| LinkedHashMap<String,List<String>> m = new LinkedHashMap<String,List<String>>(); | |
| for (Iterator<HeaderElement> it = headers.iterator(); it.hasNext(); ) | |
| { | |
| HeaderElement e = it.next(); | |
| ArrayList<String> l = (ArrayList<String>)m.get(e.name); | |
| if (l == null) | |
| { | |
| l = new ArrayList<String>(1); | |
| l.add(e.value); | |
| m.put(e.name, l); | |
| } | |
| else | |
| l.add(0, e.value); | |
| } | |
| for (Iterator<Map.Entry<String,List<String>>> it = m.entrySet().iterator(); it.hasNext(); ) | |
| { | |
| Map.Entry<String,List<String>> me = it.next(); | |
| List<String> l = me.getValue(); | |
| me.setValue(Collections.unmodifiableList(l)); | |
| } | |
| return m; | |
| } | |
| /** | |
| * Get the name of the Nth header. | |
| * | |
| * @param i the header index. | |
| * | |
| * @return The header name, or <code>null</code> if index outside of range. | |
| * | |
| * @see #getHeaderValue(int) | |
| */ | |
| public String getHeaderName(int i) | |
| { | |
| if (i >= headers.size() || i < 0) | |
| return null; | |
| return headers.get(i).name; | |
| } | |
| /** | |
| * Get the value of the Nth header. | |
| * | |
| * @param i the header index. | |
| * | |
| * @return the header value, or <code>null</code> if index outside of range. | |
| * | |
| * @see #getHeaderName(int) | |
| */ | |
| public String getHeaderValue(int i) | |
| { | |
| if (i >= headers.size() || i < 0) | |
| return null; | |
| return headers.get(i).value; | |
| } | |
| } |