/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.luni.internal.net.www.protocol.http; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * The general structure for request / response header. It is essentially * constructed by hashtable with key indexed in a vector for position lookup. */ public class Header implements Cloneable { /* * we use the non-synchronized ArrayList and HashMap instead of the * synchronized Vector and Hashtable */ private ArrayList<String> props; private HashMap<String, LinkedList<String>> keyTable; private String statusLine; /** * A generic header structure. Used mostly for request / response header. * The key/value pair of the header may be inserted for later use. The key * is stored in an array for indexed slot access. */ public Header() { super(); this.props = new ArrayList<String>(20); this.keyTable = new HashMap<String, LinkedList<String>>(20); } /** * The alternative constructor which sets the input map as its initial * keyTable. * * @param map * the initial keyTable as a map */ public Header(Map<String, List<String>> map) { this(); // initialize fields for (Entry<String, List<String>> next : map.entrySet()) { String key = next.getKey(); props.add(key); List<String> value = next.getValue(); LinkedList<String> linkedList = new LinkedList<String>(); for (String element : value) { linkedList.add(element); props.add(element); } keyTable.put(key, linkedList); } } @SuppressWarnings("unchecked") @Override public Object clone() { try { Header clone = (Header) super.clone(); clone.props = (ArrayList<String>) props.clone(); clone.keyTable = new HashMap<String, LinkedList<String>>(20); for (Map.Entry<String, LinkedList<String>> next : this.keyTable .entrySet()) { LinkedList<String> v = (LinkedList<String>) next.getValue() .clone(); clone.keyTable.put(next.getKey(), v); } return clone; } catch (CloneNotSupportedException e) { return null; } } /** * Add a field with the specified value. * * @param key * @param value */ public void add(String key, String value) { if (key == null) { throw new NullPointerException(); } // BEGIN android-changed key = key.toLowerCase(); LinkedList<String> list = keyTable.get(key); if (list == null) { list = new LinkedList<String>(); keyTable.put(key, list); } // END android-changed list.add(value); props.add(key); props.add(value); } /** * Set a field with the specified value. If the field is not found, it is * added. If the field is found, the existing value(s) are overwritten. * * @param key * @param value */ public void set(String key, String value) { if (key == null) { throw new NullPointerException(); } // BEGIN android-added key = key.toLowerCase(); // END android-added LinkedList<String> list = keyTable.get(key); if (list == null) { add(key, value); } else { list.clear(); list.add(value); for (int i = 0; i < props.size(); i += 2) { String propKey = props.get(i); if (propKey != null && key.equals(propKey)) { props.set(i + 1, value); } } } } /** * Provides an unmodifiable map with all String header names mapped to their * String values. The map keys are Strings and the values are unmodifiable * Lists of Strings. * * @return an unmodifiable map of the headers * * @since 1.4 */ public Map<String, List<String>> getFieldMap() { Map<String, List<String>> result = new HashMap<String, List<String>>( keyTable.size()); for (Map.Entry<String, LinkedList<String>> next : keyTable.entrySet()) { List<String> v = next.getValue(); result.put(next.getKey(), Collections.unmodifiableList(v)); } return Collections.unmodifiableMap(result); } /** * Returns the element at <code>pos</code>, null if no such element * exist. * * @return java.lang.String the value of the key * @param pos * int the position to look for */ public String get(int pos) { if (pos >= 0 && pos < props.size() / 2) { return props.get(pos * 2 + 1); } return null; } /** * Returns the key of this header at <code>pos</code>, null if there are * fewer keys in the header * * * @return the key the desired position * @param pos * the position to look for */ public String getKey(int pos) { if (pos >= 0 && pos < props.size() / 2) { return props.get(pos * 2); } return null; } /** * Returns the value corresponding to the specified key. * * @param key * the key to look up. * @return Answers the value for the given key, or <code>null</code> if no * such key exists. */ public String get(String key) { LinkedList<String> result = keyTable.get(key.toLowerCase()); if (result == null) { return null; } return result.getLast(); } /** * Returns the number of keys stored in this header * * @return the number of keys. */ public int length() { return props.size() / 2; } /** * Sets the status line in the header request example: GET / HTTP/1.1 * response example: HTTP/1.1 200 OK * * @param statusLine */ public void setStatusLine(String statusLine) { this.statusLine = statusLine; /* * we add the status line to the list of headers so that it is * accessible from java.net.HttpURLConnection.getResponseCode() which * calls * org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnection.getHeaderField(0) * to get it */ props.add(0, null); props.add(1, statusLine); } /** * Gets the status line in the header request example: GET / HTTP/1.1 * response example: HTTP/1.1 200 OK * * @return the status line */ public String getStatusLine() { return statusLine; } }