001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.luni.internal.net.www.protocol.http;
019:
020: import java.util.ArrayList;
021: import java.util.Collections;
022: import java.util.HashMap;
023: import java.util.LinkedList;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.Map.Entry;
027:
028: /**
029: * The general structure for request / response header. It is essentially
030: * constructed by hashtable with key indexed in a vector for position lookup.
031: */
032: public class Header implements Cloneable {
033: /*
034: * we use the non-synchronized ArrayList and HashMap instead of the
035: * synchronized Vector and Hashtable
036: */
037: private ArrayList<String> props;
038:
039: private HashMap<String, LinkedList<String>> keyTable;
040:
041: private String statusLine;
042:
043: /**
044: * A generic header structure. Used mostly for request / response header.
045: * The key/value pair of the header may be inserted for later use. The key
046: * is stored in an array for indexed slot access.
047: */
048: public Header() {
049: super ();
050: this .props = new ArrayList<String>(20);
051: this .keyTable = new HashMap<String, LinkedList<String>>(20);
052: }
053:
054: /**
055: * The alternative constructor which sets the input map as its initial
056: * keyTable.
057: *
058: * @param map
059: * the initial keyTable as a map
060: */
061: public Header(Map<String, List<String>> map) {
062: this (); // initialize fields
063: for (Entry<String, List<String>> next : map.entrySet()) {
064: String key = next.getKey();
065: props.add(key);
066: List<String> value = next.getValue();
067: LinkedList<String> linkedList = new LinkedList<String>();
068: for (String element : value) {
069: linkedList.add(element);
070: props.add(element);
071: }
072: keyTable.put(key, linkedList);
073: }
074: }
075:
076: @SuppressWarnings("unchecked")
077: @Override
078: public Object clone() {
079: try {
080: Header clone = (Header) super .clone();
081: clone.props = (ArrayList<String>) props.clone();
082: clone.keyTable = new HashMap<String, LinkedList<String>>(20);
083: for (Map.Entry<String, LinkedList<String>> next : this .keyTable
084: .entrySet()) {
085: LinkedList<String> v = (LinkedList<String>) next
086: .getValue().clone();
087: clone.keyTable.put(next.getKey(), v);
088: }
089: return clone;
090: } catch (CloneNotSupportedException e) {
091: return null;
092: }
093: }
094:
095: /**
096: * Add a field with the specified value.
097: *
098: * @param key
099: * @param value
100: */
101: public void add(String key, String value) {
102: if (key == null) {
103: throw new NullPointerException();
104: }
105: LinkedList<String> list = keyTable.get(key);
106: if (list == null) {
107: list = new LinkedList<String>();
108: keyTable.put(key.toLowerCase(), list);
109: }
110: list.add(value);
111: props.add(key);
112: props.add(value);
113: }
114:
115: /**
116: * Set a field with the specified value. If the field is not found, it is
117: * added. If the field is found, the existing value(s) are overwritten.
118: *
119: * @param key
120: * @param value
121: */
122: public void set(String key, String value) {
123: if (key == null) {
124: throw new NullPointerException();
125: }
126: LinkedList<String> list = keyTable.get(key);
127: if (list == null) {
128: add(key, value);
129: } else {
130: list.clear();
131: list.add(value);
132: for (int i = 0; i < props.size(); i += 2) {
133: String propKey = props.get(i);
134: if (propKey != null && key.equals(propKey)) {
135: props.set(i + 1, value);
136: }
137: }
138: }
139: }
140:
141: /**
142: * Provides an unmodifiable map with all String header names mapped to their
143: * String values. The map keys are Strings and the values are unmodifiable
144: * Lists of Strings.
145: *
146: * @return an unmodifiable map of the headers
147: *
148: * @since 1.4
149: */
150: public Map<String, List<String>> getFieldMap() {
151: Map<String, List<String>> result = new HashMap<String, List<String>>(
152: keyTable.size());
153: for (Map.Entry<String, LinkedList<String>> next : keyTable
154: .entrySet()) {
155: List<String> v = next.getValue();
156: result.put(next.getKey(), Collections.unmodifiableList(v));
157: }
158: return Collections.unmodifiableMap(result);
159: }
160:
161: /**
162: * Answers the element at <code>pos</code>, null if no such element
163: * exist.
164: *
165: * @return java.lang.String the value of the key
166: * @param pos
167: * int the position to look for
168: */
169: public String get(int pos) {
170: if (pos >= 0 && pos < props.size() / 2) {
171: return props.get(pos * 2 + 1);
172: }
173: return null;
174: }
175:
176: /**
177: * Answers the key of this header at <code>pos</code>, null if there are
178: * fewer keys in the header
179: *
180: *
181: * @return the key the desired position
182: * @param pos
183: * the position to look for
184: */
185: public String getKey(int pos) {
186: if (pos >= 0 && pos < props.size() / 2) {
187: return props.get(pos * 2);
188: }
189: return null;
190: }
191:
192: /**
193: * Answers the value corresponding to the specified key.
194: *
195: * @param key
196: * the key to look up.
197: * @return Answers the value for the given key, or <code>null</code> if no
198: * such key exists.
199: */
200: public String get(String key) {
201: LinkedList<String> result = keyTable.get(key.toLowerCase());
202: if (result == null) {
203: return null;
204: }
205: return result.getLast();
206: }
207:
208: /**
209: * Answers the number of keys stored in this header
210: *
211: * @return the number of keys.
212: */
213: public int length() {
214: return props.size() / 2;
215: }
216:
217: /**
218: * Sets the status line in the header request example: GET / HTTP/1.1
219: * response example: HTTP/1.1 200 OK
220: *
221: * @param statusLine
222: */
223: public void setStatusLine(String statusLine) {
224: this .statusLine = statusLine;
225: /*
226: * we add the status line to the list of headers so that it is
227: * accessible from java.net.HttpURLConnection.getResponseCode() which
228: * calls
229: * org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnection.getHeaderField(0)
230: * to get it
231: */
232: props.add(0, null);
233: props.add(1, statusLine);
234: }
235:
236: /**
237: * Gets the status line in the header request example: GET / HTTP/1.1
238: * response example: HTTP/1.1 200 OK
239: *
240: * @return the status line
241: */
242: public String getStatusLine() {
243: return statusLine;
244: }
245: }
|